diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/binance/TestBinanceAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/binance/TestBinanceAddress.kt index 13b91a5e527..f2c62428f64 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/binance/TestBinanceAddress.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/binance/TestBinanceAddress.kt @@ -2,6 +2,8 @@ package com.trustwallet.core.app.blockchains.binance import com.trustwallet.core.app.utils.toHexBytes import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue import org.junit.Test import wallet.core.jni.* import com.trustwallet.core.app.utils.toHex @@ -22,6 +24,23 @@ class TestBinanceAddress { assertEquals("bnb1grpf0955h0ykzq3ar5nmum7y6gdfl6lxfn46h2", address.description()) } + @Test + fun testIsValid() { + assertTrue(AnyAddress.isValid("bnb12vtaxl9952zm6rwf7v8jerq74pvaf77fcmvzhw", CoinType.BINANCE)); + + assertFalse(AnyAddress.isValid("bad1devga6q804tx9fqrnx0vtu5r36kxgp9tqx8h9k", CoinType.BINANCE)); + assertFalse(AnyAddress.isValid("tbnb1devga6q804tx9fqrnx0vtu5r36kxgp9t4ruzk2", CoinType.BINANCE)); + } + + @Test + fun testIsValidBech32() { + assertTrue(AnyAddress.isValidBech32("bnb12vtaxl9952zm6rwf7v8jerq74pvaf77fcmvzhw", CoinType.BINANCE, "bnb")); + assertTrue(AnyAddress.isValidBech32("tbnb1devga6q804tx9fqrnx0vtu5r36kxgp9t4ruzk2", CoinType.BINANCE, "tbnb")); + + assertFalse(AnyAddress.isValidBech32("bnb12vtaxl9952zm6rwf7v8jerq74pvaf77fcmvzhw", CoinType.BINANCE, "tbnb")); + assertFalse(AnyAddress.isValidBech32("tbnb1devga6q804tx9fqrnx0vtu5r36kxgp9t4ruzk2", CoinType.BINANCE, "bnb")); + } + @Test fun testBinanceMainnet() { val wallet = HDWallet("rabbit tilt arm protect banner ill produce vendor april bike much identify pond upset front easily glass gallery address hair priority focus forest angle", "") @@ -31,4 +50,15 @@ class TestBinanceAddress { assertEquals("0x727f677b390c151caf9c206fd77f77918f56904b5504243db9b21e51182c4c06", key.data().toHex()) assertEquals("bnb1devga6q804tx9fqrnx0vtu5r36kxgp9tmk4xkm", address) } + + @Test + fun testBinanceTestnet() { + val wallet = HDWallet("rabbit tilt arm protect banner ill produce vendor april bike much identify pond upset front easily glass gallery address hair priority focus forest angle", "") + val privateKey = wallet.getKeyForCoin(CoinType.BINANCE) + val publicKey = privateKey.getPublicKeySecp256k1(true) + val address = AnyAddress(publicKey, CoinType.BINANCE, "tbnb") + + assertEquals("0x727f677b390c151caf9c206fd77f77918f56904b5504243db9b21e51182c4c06", privateKey.data().toHex()) + assertEquals("tbnb1devga6q804tx9fqrnx0vtu5r36kxgp9t4ruzk2", address.description()) + } } diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestHDWallet.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestHDWallet.kt index 45806099afb..22af2bfc37f 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestHDWallet.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestHDWallet.kt @@ -144,6 +144,9 @@ class TestHDWallet { val address2 = wallet.getAddressDerivation(coin, Derivation.BITCOINLEGACY) assertEquals(address2, "1PeUvjuxyf31aJKX6kCXuaqxhmG78ZUdL1") + + val address3 = wallet.getAddressDerivation(coin, Derivation.BITCOINTESTNET) + assertEquals(address3, "tb1qwgpxgwn33z3ke9s7q65l976pseh4edrzfmyvl0") } @Test diff --git a/src/Binance/Address.cpp b/src/Binance/Address.cpp index 3f107872a7a..1230b2362a4 100644 --- a/src/Binance/Address.cpp +++ b/src/Binance/Address.cpp @@ -21,6 +21,11 @@ bool Address::isValid(const std::string& addr) { return decode(addr, addrNotUsed); } +bool Address::isValid(const std::string& addr, const std::string& hrp) { + Address addrNotUsed; + return Bech32Address::decode(addr, addrNotUsed, hrp); +} + bool Address::decode(const std::string& addr, Address& obj_out) { for (const auto& hrp : validHrps) { if (Bech32Address::decode(addr, obj_out, hrp)) { diff --git a/src/Binance/Address.h b/src/Binance/Address.h index 2dd06526fb5..ca25df2089c 100644 --- a/src/Binance/Address.h +++ b/src/Binance/Address.h @@ -19,6 +19,7 @@ class Address: public Bech32Address { static const std::string hrpValidator; // HRP_BINANCE static bool isValid(const std::string& addr); + static bool isValid(const std::string& addr, const std::string& hrp); Address() : Bech32Address(_hrp) {} @@ -27,6 +28,7 @@ class Address: public Bech32Address { /// Initializes an address with a public key. Address(const PublicKey& publicKey) : Bech32Address(_hrp, Hash::HasherSha256ripemd, publicKey) {} + Address(const PublicKey& publicKey, const std::string hrp) : Bech32Address(hrp, Hash::HasherSha256ripemd, publicKey) {} static bool decode(const std::string& addr, Address& obj_out); }; diff --git a/src/Binance/Entry.cpp b/src/Binance/Entry.cpp index 6d7006255be..e2c22118e2c 100644 --- a/src/Binance/Entry.cpp +++ b/src/Binance/Entry.cpp @@ -13,11 +13,19 @@ namespace TW::Binance { bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { + if (std::holds_alternative(addressPrefix)) { + if (const auto hrp = std::get(addressPrefix); hrp) { + return Address::isValid(address, hrp); + } + } + + // Use the default validation, which handles a specific set of valid HRPs. return Address::isValid(address); } std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { - return Address(publicKey).string(); + const std::string hrp = getFromPrefixHrpOrDefault(addressPrefix, coin); + return Address(publicKey, hrp).string(); } Data Entry::addressToData([[maybe_unused]] TWCoinType coin, const std::string& address) const { diff --git a/swift/Tests/Addresses/BinanceAddressTests.swift b/swift/Tests/Addresses/BinanceAddressTests.swift new file mode 100644 index 00000000000..4de244ee115 --- /dev/null +++ b/swift/Tests/Addresses/BinanceAddressTests.swift @@ -0,0 +1,25 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +import WalletCore +import XCTest + +class BinanceAddressTests: XCTestCase { + func testIsValid() { + XCTAssertTrue(AnyAddress.isValid(string: "bnb12vtaxl9952zm6rwf7v8jerq74pvaf77fcmvzhw", coin: .binance)) + + XCTAssertFalse(AnyAddress.isValid(string: "bad1devga6q804tx9fqrnx0vtu5r36kxgp9tqx8h9k", coin: .binance)) + XCTAssertFalse(AnyAddress.isValid(string: "tbnb1devga6q804tx9fqrnx0vtu5r36kxgp9t4ruzk2", coin: .binance)) + } + + func testIsValidBech32() { + XCTAssertTrue(AnyAddress.isValidBech32(string: "bnb12vtaxl9952zm6rwf7v8jerq74pvaf77fcmvzhw", coin: .binance, hrp: "bnb")); + XCTAssertTrue(AnyAddress.isValidBech32(string: "tbnb1devga6q804tx9fqrnx0vtu5r36kxgp9t4ruzk2", coin: .binance, hrp: "tbnb")); + + XCTAssertFalse(AnyAddress.isValidBech32(string: "bnb12vtaxl9952zm6rwf7v8jerq74pvaf77fcmvzhw", coin: .binance, hrp: "tbnb")); + XCTAssertFalse(AnyAddress.isValidBech32(string: "tbnb1devga6q804tx9fqrnx0vtu5r36kxgp9t4ruzk2", coin: .binance, hrp: "bnb")); + } +} diff --git a/swift/Tests/Blockchains/BinanceChainTests.swift b/swift/Tests/Blockchains/BinanceChainTests.swift index a1e0a021715..90df7dd3573 100644 --- a/swift/Tests/Blockchains/BinanceChainTests.swift +++ b/swift/Tests/Blockchains/BinanceChainTests.swift @@ -27,6 +27,16 @@ class BinanceChainTests: XCTestCase { XCTAssertEqual("bnb1devga6q804tx9fqrnx0vtu5r36kxgp9tmk4xkm", address.description) } + func testBinanceTestnet() { + let wallet = HDWallet(mnemonic: "rabbit tilt arm protect banner ill produce vendor april bike much identify pond upset front easily glass gallery address hair priority focus forest angle", passphrase: "")! + let privateKey = wallet.getKeyForCoin(coin: .binance) + let publicKey = privateKey.getPublicKeySecp256k1(compressed: true) + let address = AnyAddress(publicKey: publicKey, coin: .binance, hrp: "tbnb") + + XCTAssertEqual(privateKey.data.hexString, "727f677b390c151caf9c206fd77f77918f56904b5504243db9b21e51182c4c06") + XCTAssertEqual("tbnb1devga6q804tx9fqrnx0vtu5r36kxgp9t4ruzk2", address.description) + } + func testSignSendOrder() { let privateKey = PrivateKey(data: Data(hexString: "95949f757db1f57ca94a5dff23314accbe7abee89597bf6a3c7382c84d7eb832")!)! let publicKey = privateKey.getPublicKeySecp256k1(compressed: true) diff --git a/swift/Tests/HDWalletTests.swift b/swift/Tests/HDWalletTests.swift index ea077ee52e0..d6df8bafa73 100644 --- a/swift/Tests/HDWalletTests.swift +++ b/swift/Tests/HDWalletTests.swift @@ -121,6 +121,9 @@ class HDWalletTests: XCTestCase { let address2 = wallet.getAddressDerivation(coin: coin, derivation: .bitcoinLegacy) XCTAssertEqual(address2, "1PeUvjuxyf31aJKX6kCXuaqxhmG78ZUdL1") + + let address3 = wallet.getAddressDerivation(coin: coin, derivation: .bitcoinTestnet) + XCTAssertEqual(address3, "tb1qwgpxgwn33z3ke9s7q65l976pseh4edrzfmyvl0") } func testDerive() { @@ -243,6 +246,15 @@ class HDWalletTests: XCTestCase { XCTAssertEqual("bnb1wk7kxw0qrvxe2pj9mk6ydjx0t4j9jla8pja0td", address.description) } + func testDeriveBinanceTestnet() { + let binance = CoinType.binance + let wallet = HDWallet.test + let key = wallet.getKeyForCoin(coin: binance) + let address = AnyAddress(publicKey: key.getPublicKeySecp256k1(compressed: true), coin: binance, hrp: "tbnb") + + XCTAssertEqual("tbnb1wk7kxw0qrvxe2pj9mk6ydjx0t4j9jla8085ttu", address.description) + } + func testDeriveZcash() { let zcash = CoinType.zcash let wallet = HDWallet.test diff --git a/tests/common/CoinAddressValidationTests.cpp b/tests/common/CoinAddressValidationTests.cpp index dbbae8459dd..383329f393f 100644 --- a/tests/common/CoinAddressValidationTests.cpp +++ b/tests/common/CoinAddressValidationTests.cpp @@ -45,7 +45,10 @@ TEST(Coin, validateAddressBitcoin) { TEST(Coin, ValidateAddressBinance) { EXPECT_TRUE(validateAddress(TWCoinTypeBinance, "bnb12vtaxl9952zm6rwf7v8jerq74pvaf77fcmvzhw")); - EXPECT_FALSE(validateAddress(TWCoinTypeBinance, "tbnb12vtaxl9952zm6rwf7v8jerq74pvaf77fkw9xhl")); + EXPECT_TRUE(validateAddress(TWCoinTypeBinance, "tbnb1devga6q804tx9fqrnx0vtu5r36kxgp9t4ruzk2", "tbnb")); + + EXPECT_FALSE(validateAddress(TWCoinTypeBinance, "tbnb1devga6q804tx9fqrnx0vtu5r36kxgp9t4ruzk2")); + EXPECT_FALSE(validateAddress(TWCoinTypeBinance, "bad1devga6q804tx9fqrnx0vtu5r36kxgp9tqx8h9k")); } TEST(Coin, ValidateAddressLitecoin) {