Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Export bech32 and bech32m functions #4101

Merged
merged 5 commits into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.trustwallet.core.app.utils

import org.junit.Assert.*
import org.junit.Test
import wallet.core.jni.Bech32

class TestBech32 {
init {
System.loadLibrary("TrustWalletCore");
}

@Test
fun testEncode() {
val data = Numeric.hexStringToByteArray("00443214c74254b635cf84653a56d7c675be77df")
assertEquals(Bech32.encode("abcdef", data), "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw")
}

@Test
fun testDecode() {
val decoded = Bech32.decode("abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw")
assertEquals(Numeric.toHexString(decoded), "0x00443214c74254b635cf84653a56d7c675be77df")
}

@Test
fun testDecodeWrongChecksumVariant() {
// This is a Bech32m variant, not Bech32 variant. So it should fail using Bech32 decoder.
val decoded = Bech32.decode("abcdef1l7aum6echk45nj3s0wdvt2fg8x9yrzpqzd3ryx")
assertNull(decoded)
}

@Test
fun testEncodeM() {
val data = Numeric.hexStringToByteArray("ffbbcdeb38bdab49ca307b9ac5a928398a418820")
assertEquals(Bech32.encodeM("abcdef", data), "abcdef1l7aum6echk45nj3s0wdvt2fg8x9yrzpqzd3ryx")
}

@Test
fun testDecodeM() {
val decoded = Bech32.decodeM("abcdef1l7aum6echk45nj3s0wdvt2fg8x9yrzpqzd3ryx")
assertEquals(Numeric.toHexString(decoded), "0xffbbcdeb38bdab49ca307b9ac5a928398a418820")
}

@Test
fun testDecodeMWrongChecksumVariant() {
// This is a Bech32 variant, not Bech32m variant. So it should fail using Bech32M decoder.
val decoded = Bech32.decodeM("abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw")
assertNull(decoded)
}
}
78 changes: 78 additions & 0 deletions codegen-v2/manifest/TWBech32.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
name: TWBech32
structs:
- name: TWBech32
is_public: true
is_class: false
functions:
- name: TWBech32Encode
is_public: true
is_static: true
params:
- name: hrp
type:
variant: string
is_constant: true
is_nullable: false
is_pointer: true
- name: data
type:
variant: data
is_constant: true
is_nullable: false
is_pointer: true
return_type:
variant: string
is_constant: true
is_nullable: false
is_pointer: true
- name: TWBech32Decode
is_public: true
is_static: true
params:
- name: string
type:
variant: string
is_constant: true
is_nullable: false
is_pointer: true
return_type:
variant: data
is_constant: true
is_nullable: true
is_pointer: true
- name: TWBech32EncodeM
is_public: true
is_static: true
params:
- name: hrp
type:
variant: string
is_constant: true
is_nullable: false
is_pointer: true
- name: data
type:
variant: data
is_constant: true
is_nullable: false
is_pointer: true
return_type:
variant: string
is_constant: true
is_nullable: false
is_pointer: true
- name: TWBech32DecodeM
is_public: true
is_static: true
params:
- name: string
type:
variant: string
is_constant: true
is_nullable: false
is_pointer: true
return_type:
variant: data
is_constant: true
is_nullable: true
is_pointer: true
4 changes: 2 additions & 2 deletions include/TrustWalletCore/TWBase58.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,14 @@ TWString *_Nonnull TWBase58EncodeNoCheck(TWData *_Nonnull data);
/// Decodes a Base58 string, checking the checksum. Returns null if the string is not a valid Base58 string.
///
/// \param string The Base58 string to decode.
/// \return the decoded data, empty if the string is not a valid Base58 string with checksum.
/// \return the decoded data, null if the string is not a valid Base58 string with checksum.
TW_EXPORT_STATIC_METHOD
TWData *_Nullable TWBase58Decode(TWString *_Nonnull string);

/// Decodes a Base58 string, w/o checking the checksum. Returns null if the string is not a valid Base58 string.
///
/// \param string The Base58 string to decode.
/// \return the decoded data, empty if the string is not a valid Base58 string without checksum.
/// \return the decoded data, null if the string is not a valid Base58 string without checksum.
TW_EXPORT_STATIC_METHOD
TWData *_Nullable TWBase58DecodeNoCheck(TWString *_Nonnull string);

Expand Down
47 changes: 47 additions & 0 deletions include/TrustWalletCore/TWBech32.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// SPDX-License-Identifier: Apache-2.0
//
// Copyright © 2017 Trust Wallet.

#pragma once

#include "TWBase.h"
#include "TWData.h"
#include "TWString.h"

TW_EXTERN_C_BEGIN

/// Bech32 encode / decode functions
TW_EXPORT_STRUCT
struct TWBech32;

/// Encodes data as a Bech32 string.
///
/// \param hrp The human-readable part.
/// \param data The data part.
/// \return the encoded Bech32 string.
TW_EXPORT_STATIC_METHOD
TWString *_Nonnull TWBech32Encode(TWString* _Nonnull hrp, TWData *_Nonnull data);

/// Decodes a Bech32 string. Returns null if the string is not a valid Bech32 string.
///
/// \param string The Bech32 string to decode.
/// \return the decoded data, null if the string is not a valid Bech32 string. Note that the human-readable part is not returned.
TW_EXPORT_STATIC_METHOD
TWData *_Nullable TWBech32Decode(TWString *_Nonnull string);

/// Encodes data as a Bech32m string.
///
/// \param hrp The human-readable part.
/// \param data The data part.
/// \return the encoded Bech32m string.
TW_EXPORT_STATIC_METHOD
TWString *_Nonnull TWBech32EncodeM(TWString* _Nonnull hrp, TWData *_Nonnull data);

/// Decodes a Bech32m string. Returns null if the string is not a valid Bech32m string.
///
/// \param string The Bech32m string to decode.
/// \return the decoded data, null if the string is not a valid Bech32m string. Note that the human-readable part is not returned.
TW_EXPORT_STATIC_METHOD
TWData *_Nullable TWBech32DecodeM(TWString *_Nonnull string);

TW_EXTERN_C_END
4 changes: 2 additions & 2 deletions src/Bech32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ Data create_checksum(const std::string& hrp, const Data& values, ChecksumVariant

} // namespace

/** Encode a Bech32 string. */
/** Encode a Bech32 string. Note that the values must each encode 5 bits, normally get from convertBits<8, 5, true> */
std::string encode(const std::string& hrp, const Data& values, ChecksumVariant variant) {
Data checksum = create_checksum(hrp, values, variant);
Data combined = values;
Expand All @@ -116,7 +116,7 @@ std::string encode(const std::string& hrp, const Data& values, ChecksumVariant v
return ret;
}

/** Decode a Bech32 string. */
/** Decode a Bech32 string. Note that the returned values are 5 bits each, you may want to use convertBits<5, 8, false> */
std::tuple<std::string, Data, ChecksumVariant> decode(const std::string& str) {
if (str.length() > 120 || str.length() < 2) {
// too long or too short
Expand Down
60 changes: 60 additions & 0 deletions src/interface/TWBech32.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// SPDX-License-Identifier: Apache-2.0
//
// Copyright © 2017 Trust Wallet.

#include <TrustWalletCore/TWBech32.h>

#include "../Bech32.h"

#include <string>

using namespace TW;

static TWString *_Nonnull encodeGeneric(TWString* _Nonnull hrp, TWData *_Nonnull data, const Bech32::ChecksumVariant variant) {
const auto cppHrp = *static_cast<const std::string*>(hrp);
const auto cppData = *static_cast<const Data*>(data);
Data enc;
if (!Bech32::convertBits<8, 5, true>(enc, cppData)) {
return TWStringCreateWithUTF8Bytes("");
}
const auto result = Bech32::encode(cppHrp, enc, variant);
return TWStringCreateWithUTF8Bytes(result.c_str());
}

static TWData *_Nullable decodeGeneric(TWString *_Nonnull string, const Bech32::ChecksumVariant variant) {
const auto cppString = *static_cast<const std::string*>(string);
const auto decoded = Bech32::decode(cppString);

const auto data = std::get<1>(decoded);
if (data.empty()) { // Failed to decode
return nullptr;
}

if (std::get<2>(decoded) != variant) { // Wrong ChecksumVariant
return nullptr;
}

// Bech bits conversion
Data conv;
if (!Bech32::convertBits<5, 8, false>(conv, data)) {
return nullptr;
}

return TWDataCreateWithBytes(conv.data(), conv.size());
}

TWString *_Nonnull TWBech32Encode(TWString* _Nonnull hrp, TWData *_Nonnull data) {
return encodeGeneric(hrp, data, Bech32::ChecksumVariant::Bech32);
}

TWString *_Nonnull TWBech32EncodeM(TWString* _Nonnull hrp, TWData *_Nonnull data) {
return encodeGeneric(hrp, data, Bech32::ChecksumVariant::Bech32M);
}

TWData *_Nullable TWBech32Decode(TWString *_Nonnull string) {
return decodeGeneric(string, Bech32::ChecksumVariant::Bech32);
}

TWData *_Nullable TWBech32DecodeM(TWString *_Nonnull string) {
return decodeGeneric(string, Bech32::ChecksumVariant::Bech32M);
}
42 changes: 42 additions & 0 deletions swift/Tests/Bech32Tests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// SPDX-License-Identifier: Apache-2.0
//
// Copyright © 2017 Trust Wallet.

import XCTest
import WalletCore

class Bech32Tests: XCTestCase {
func testEncode() {
let data = Data(hexString: "00443214c74254b635cf84653a56d7c675be77df")!
let encoded = Bech32.encode(hrp: "abcdef", data: data)
XCTAssertEqual(encoded, "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw")
}

func testDecode() {
let decoded = Bech32.decode(string: "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw")!
XCTAssertEqual(decoded.hexString, "00443214c74254b635cf84653a56d7c675be77df")
}

func testDecodeWrongChecksumVariant() {
// This is a Bech32m variant, not Bech32 variant. So it should fail using Bech32 decoder.
let decoded = Bech32.decode(string: "abcdef1l7aum6echk45nj3s0wdvt2fg8x9yrzpqzd3ryx")
XCTAssertNil(decoded)
}

func testEncodeM() {
let data = Data(hexString: "ffbbcdeb38bdab49ca307b9ac5a928398a418820")!
let encoded = Bech32.encodeM(hrp: "abcdef", data: data)
XCTAssertEqual(encoded, "abcdef1l7aum6echk45nj3s0wdvt2fg8x9yrzpqzd3ryx")
}

func testDecodeM() {
let decoded = Bech32.decodeM(string: "abcdef1l7aum6echk45nj3s0wdvt2fg8x9yrzpqzd3ryx")!
XCTAssertEqual(decoded.hexString, "ffbbcdeb38bdab49ca307b9ac5a928398a418820")
}

func testDecodeMWrongChecksumVariant() {
// This is a Bech32 variant, not Bech32m variant. So it should fail using Bech32M decoder.
let decoded = Bech32.decodeM(string: "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw")
XCTAssertNil(decoded)
}
}
49 changes: 49 additions & 0 deletions tests/interface/TWBech32Tests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// SPDX-License-Identifier: Apache-2.0
//
// Copyright © 2017 Trust Wallet.

#include "TestUtilities.h"

#include <TrustWalletCore/TWBech32.h>

#include <gtest/gtest.h>

TEST(TWBech32, Encode) {
const auto hrp = STRING("abcdef");
const auto data = DATA("00443214c74254b635cf84653a56d7c675be77df");
const auto result = WRAPS(TWBech32Encode(hrp.get(), data.get()));
assertStringsEqual(result, "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw");
}

TEST(TWBech32, Decode) {
const auto input = STRING("abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw");
const auto result = WRAPD(TWBech32Decode(input.get()));
assertHexEqual(result, "00443214c74254b635cf84653a56d7c675be77df");
}

TEST(TWBech32, Decode_WrongChecksumVariant) {
// This is a Bech32m variant, not Bech32 variant. So it should fail using Bech32 decoder.
const auto input = STRING("abcdef1l7aum6echk45nj3s0wdvt2fg8x9yrzpqzd3ryx");
const auto result = WRAPD(TWBech32Decode(input.get()));
ASSERT_EQ(result.get(), nullptr);
}

TEST(TWBech32, EncodeM) {
const auto hrp = STRING("abcdef");
const auto data = DATA("ffbbcdeb38bdab49ca307b9ac5a928398a418820");
const auto result = WRAPS(TWBech32EncodeM(hrp.get(), data.get()));
assertStringsEqual(result, "abcdef1l7aum6echk45nj3s0wdvt2fg8x9yrzpqzd3ryx");
}

TEST(TWBech32, DecodeM) {
const auto input = STRING("abcdef1l7aum6echk45nj3s0wdvt2fg8x9yrzpqzd3ryx");
auto result = WRAPD(TWBech32DecodeM(input.get()));
assertHexEqual(result, "ffbbcdeb38bdab49ca307b9ac5a928398a418820");
}

TEST(TWBech32, DecodeM_WrongChecksumVariant) {
// This is a Bech32 variant, not Bech32m variant. So it should fail using Bech32M decoder.
const auto input = STRING("abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw");
const auto result = WRAPD(TWBech32DecodeM(input.get()));
ASSERT_EQ(result.get(), nullptr);
}
Loading