ebellocchia / bip_utils

Generation of mnemonics, seeds, private/public keys and addresses for different types of cryptocurrencies
MIT License
319 stars 86 forks source link

PubKey verification failed in solana #27

Closed aleixisp closed 3 years ago

aleixisp commented 3 years ago

Using solana CLI to verify seed and pub_key raises an error.

Steps to Reproduce error

 Code Used in Python

    # Generate mnemonic
    entropy_bytes = Bip39EntropyGenerator(Bip39EntropyBitLen.BIT_LEN_256).Generate()
    mnemonic = Bip39MnemonicGenerator(Bip39Languages.ENGLISH).FromEntropy(entropy_bytes)
    print("mnemonic")
    print(mnemonic)
>>> "mnemonic"
>>> "sting limb crash ribbon delay close until easily attract dog recipe cake smooth release august found disagree treat want argue salute entire urge describe"

    # Generate seed
    seed_bytes = Bip39SeedGenerator(mnemonic, Bip39Languages.ENGLISH).Generate()
    print("seed without passphrase")
    print([x for x in seed_bytes])
>>> "seed without passphrase"
>>> [228, 156, 216, 41, 147, 240, 115, 137, 23, 81, 246, 115, 182, 158, 55, 95, 186, 10, 179, 168, 187, 50, 144, 101, 131, 29, 114, 243, 23, 4, 14, 30, 9, 137, 206, 174, 178, 52, 51, 34, 184, 43, 206, 219, 68, 121, 176, 174, 2, 164, 103, 112, 153, 37, 28, 19, 7, 213, 122, 94, 149, 219, 73, 158]

    # Derive a Valid Solana PubKey from seed
    bip44_ctx = Bip44.FromSeed(seed_bytes, Bip44Coins.SOLANA).DeriveDefaultPath()
    print("bip44_ctx_pubkey")
    print(bip44_ctx.PublicKey().ToAddress())
>>> "bip44_ctx_pubkey"
>>> "5cHj95qPtR4Xwo8wBkX3b129D8PApqn64LUh5XMVYFo6"

To validate credentials using Solana CLI.

  1. Download Solana cli sh -c "$(curl -sSfL https://release.solana.com/v1.7.3/install)"
  2. Store seed array into file
  3. open terminal, navigate to location where the seed_array file is located and run the following command solana-keygen verify <public_key> <file_name_where_seed_array_is_stored>

Using the same output in this example i got the following:

$ solana-keygen verify 5cHj95qPtR4Xwo8wBkX3b129D8PApqn64LUh5XMVYFo6 pytohn_test.json
"Verification for public key: 5cHj95qPtR4Xwo8wBkX3b129D8PApqn64LUh5XMVYFo6: Failed"

If I recover the account using the same mnemonic from the example using the following command: solana-keygen recover --force ASK The recovered public_key is the following one XhYnQXtkTdbycmGvaJ7xBaSzKMpYzLPm7mn36PkRhQ3

If I validate this pub_key with the seed_array running the command above:

$ solana-keygen verify XhYnQXtkTdbycmGvaJ7xBaSzKMpYzLPm7mn36PkRhQ3 pytohn_test.json
"Verification for public key: XhYnQXtkTdbycmGvaJ7xBaSzKMpYzLPm7mn36PkRhQ3: Success"

Any thoughts on why is it failing?

ebellocchia commented 3 years ago

Hi, I imported the same mnemonic in sollet.io and TrustWallet and I got the same address 5cHj95qPtR4Xwo8wBkX3b129D8PApqn64LUh5XMVYFo6 (and there is no XhYnQXtkTdbycmGvaJ7xBaSzKMpYzLPm7mn36PkRhQ3 address), so the computed address and keys are correct. I'm not very familiar with solana-cli and I don't know exactly what the verify command is doing, maybe it's a problem of data format. I'll try to play around with it.

ebellocchia commented 3 years ago

Hi, I took a look at the source code of solana-cli. Basically, it is working quite differently to other wallets (that's why it's generating different addresses) because it's not an HD-wallet (i.e. it's not deriving any children key, but only generating a master key). So, what solana-cli does is:

So, to replicate this behavior FromPrivateKey method shall be used for construction, by taking only the first 32-byte of the seed, e.g.:

mnemonic = "long mango angle near comic pave useful mandate loop language quantum cruise"
seed_bytes = Bip39SeedGenerator(mnemonic).Generate()
bip44_ctx = Bip44.FromPrivateKey(seed_bytes[:32], Bip44Coins.SOLANA)
# Same address of the one printed by solana-cli
print(bip44_ctx.PublicKey().ToAddress())
# Print master key, no need to derive any children key
print(bip44_ctx.PublicKey().RawCompressed().ToHex())
print(bip44_ctx.PrivateKey().Raw().ToHex())

Moreover, the data it saves in the JSON file is not the seed bytes but the key pair, so 32-byte of private key and 32-byte of public key:

priv_key_bytes = bip44_ctx.PrivateKey().Raw().ToBytes()
pub_key_bytes = bip44_ctx.PublicKey().RawCompressed().ToBytes()[1:]
key_pair = priv_key_bytes + pub_key_bytes
print(list(key_pair))

These are the same bytes you'll find in the json file.

aleixisp commented 3 years ago

Amazing, I was about to give you a brief on how solana-cli works, since I just started using bip_utils and i don't know how all methods/classes works, yet. Thanks for your fast and accurate feedback, worked flawlessly.

Keep in contact for future suggestions and questions