trustwallet / wallet-core

Cross-platform, cross-blockchain wallet library.
https://developer.trustwallet.com/wallet-core
Apache License 2.0
2.84k stars 1.59k forks source link

The generated bip49 address is incorrect. #1551

Closed onesand closed 3 years ago

onesand commented 3 years ago
        String bip49DerivationPath = "m/49'/0'/0'/0/0";
        PrivateKey bip49PrivateKey = wallet.getKey(CoinType.BITCOIN,bip49DerivationPath);
        PublicKey publicKey = bip49PrivateKey.getPublicKeySecp256k1(true);
        BitcoinAddress bitcoinAddress = new BitcoinAddress(publicKey,CoinType.BITCOIN.p2shPrefix());

bitcoinAddress.description() = 31tg3fdy2Nbm7KtQQZ5stx3op2FdxoL4aY

But real address is 3KMuxpzDyXkaFEa54AiToSvnyqhBAG6gK5

optout21 commented 3 years ago

In Trust Wallet we use derivation path m/84'/0'/0'/0/0, and SegwitAddress (native segwit).

Please double check derivation path, and prefix used. How did you derive expected address? If you provide an example with a private key that can be shared, we can check.

onesand commented 3 years ago
StoredKey storedKey = StoredKey.importHDWallet(mnemonic,"test",newPassword.getBytes(),CoinType.ETHEREUM);
HDWallet wallet = storedKey.wallet(newPassword.getBytes());

String bip49DerivationPath = "m/49'/0'/0'/0/0";
PrivateKey bip49PrivateKey = wallet.getKey(CoinType.BITCOIN,bip49DerivationPath);
PublicKey publicKey = bip49PrivateKey.getPublicKeySecp256k1(true);
BitcoinAddress bitcoinAddress = new BitcoinAddress(publicKey,CoinType.BITCOIN.p2shPrefix());
LogUtils.ln("bitcoinAddress.description()",bitcoinAddress.description());

print:33KPW4uKuyVFsCEh4YgDMF58zprnb817jZ

But in this(https://iancoleman.io/bip39/) is 3PQ5BD39rDikf7YW6pJ9a9tbS3QhvwvzTG

my mnemonic:cluster model myself pet impose acquire flip breeze pass energy inch grant

optout21 commented 3 years ago

I have verified that public key matches ('039d645d2ce630c2a9a6dbe0cbd0a8fcb7b70241cb8a48424f25593290af2494b9'), this means derivation path, mnemonic, private key also matches. Derived address is indeed different. Investigating.

optout21 commented 3 years ago
  1. mnemonic: cluster model myself pet impose acquire flip breeze pass energy inch grant
  2. derived priv key with dp m/49'/0'/0'/0/0 and coin 0: aa3858e668530a3cc6e1b4c685c190994a7dc6adfd4423cd48ad1599a897c48c
  3. derived pub key: 039d645d2ce630c2a9a6dbe0cbd0a8fcb7b70241cb8a48424f25593290af2494b9
  4. hash of pubkey (sha256ripemd): 11d91ce1cc681f95583da3f4a6841c174be950c7
  5. with P2SH prefix 05 prepended, and Base58-encoded: '33KPW4uKuyVFsCEh4YgDMF58zprnb817jZ'

The reference iancoleman test produces the same 2. & 3., 4. is not visible, and the address 5. is different: '3PQ5BD39rDikf7YW6pJ9a9tbS3QhvwvzTG' This corresponds to data 05ee1e69460b59027d9df0a79ca2c92aa382a25fb7. The difference seems to be in getting the address from public key (so not with HD derivation, derivation path or prefix byte).

optout21 commented 3 years ago

Here is the answer: The Bitcoin::Address class is used for both p2pkh and p2sh addresses. The pubkey-based constructor makes sense only for p2pkh addresses, but not for p2sh. For p2sh one has to take the pubkey hash, build a script (prepend 0014), take the script hash, prepend 05, and create Address from that data.

See also: https://github.com/trustwallet/wallet-core/discussions/1342

Test code:

        const auto wallet = HDWallet("cluster model myself pet impose acquire flip breeze pass energy inch grant", "");
        const auto privateKey = wallet.getKey(TWCoinTypeBitcoin, DerivationPath("m/49'/0'/0'/0/0")); // aa3858e668530a3cc6e1b4c685c190994a7dc6adfd4423cd48ad1599a897c48c
        const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); // 039d645d2ce630c2a9a6dbe0cbd0a8fcb7b70241cb8a48424f25593290af2494b9
        const auto pubKeyHash = publicKey.hash({}); // 11d91ce1cc681f95583da3f4a6841c174be950c7
        const auto script = Script::buildPayToWitnessProgram(pubKeyHash); // 0014 11d91ce1cc681f95583da3f4a6841c174be950c
        const auto scriptHash = Hash::sha256ripemd(script.bytes.data(), script.bytes.size()); // ee1e69460b59027d9df0a79ca2c92aa382a25fb7
        Data addrdat = {TWCoinTypeP2shPrefix(TWCoinTypeBitcoin)};
        TW::append(addrdat, scriptHash); // 05ee1e69460b59027d9df0a79ca2c92aa382a25fb7
        const auto addr = Address(addrdat); // 3PQ5BD39rDikf7YW6pJ9a9tbS3QhvwvzTG
onesand commented 3 years ago

Thank you for your help.

But how to write in java or kotlin?I can't find buildPayToWitnessProgram

Here is the answer: The Bitcoin::Address class is used for both p2pkh and p2sh addresses. The pubkey-based constructor makes sense only for p2pkh addresses, but not for p2sh. For p2sh one has to take the pubkey hash, build a script (prepend 0014), take the script hash, prepend 05, and create Address from that data.

See also: #1342

Test code:

        const auto wallet = HDWallet("cluster model myself pet impose acquire flip breeze pass energy inch grant", "");
        const auto privateKey = wallet.getKey(TWCoinTypeBitcoin, DerivationPath("m/49'/0'/0'/0/0")); // aa3858e668530a3cc6e1b4c685c190994a7dc6adfd4423cd48ad1599a897c48c
        const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); // 039d645d2ce630c2a9a6dbe0cbd0a8fcb7b70241cb8a48424f25593290af2494b9
        const auto pubKeyHash = publicKey.hash({}); // 11d91ce1cc681f95583da3f4a6841c174be950c7
        const auto script = Script::buildPayToWitnessProgram(pubKeyHash); // 0014 11d91ce1cc681f95583da3f4a6841c174be950c
        const auto scriptHash = Hash::sha256ripemd(script.bytes.data(), script.bytes.size()); // ee1e69460b59027d9df0a79ca2c92aa382a25fb7
        Data addrdat = {TWCoinTypeP2shPrefix(TWCoinTypeBitcoin)};
        TW::append(addrdat, scriptHash); // 05ee1e69460b59027d9df0a79ca2c92aa382a25fb7
        const auto addr = Address(addrdat); // 3PQ5BD39rDikf7YW6pJ9a9tbS3QhvwvzTG
optout21 commented 3 years ago

Check out TWHashRIPEMD in TWHash (it is exposed to java side)

weixuefeng commented 1 year ago

can i spend p2sh's address utxo? @optout21 , i found buildPayToWitnessScriptHash, buildPayToWitnessPubkeyHash, not found buildPayToWitnessProgram.