grin-wallet
grin-wallet copied to clipboard
wallet shows incorrect BIP32 derivation paths
Describe the bug
The key derivation path shown after running grin-wallet account is not correct.
To Reproduce Steps to reproduce the behavior:
- Create bunch of accounts
- Run
grin-wallet accountto list them, note the derivation paths - Derive the slatepack addresses from those exact paths using GRIN++ or mimblewimble-py
- Observe they do not match
Expected behavior
When I run
$ grin-wallet account
Password:
____ Wallet Accounts ____
Name | Parent BIP-32 Derivation Path
---------+-------------------------------
alice | m/1/0
bob | m/2/0
default | m/0/0
Command 'account' completed successfully
correct paths should be
$ grin-wallet account
Password:
____ Wallet Accounts ____
Name | Parent BIP-32 Derivation Path
---------+-------------------------------
alice | m/1/1/0
bob | m/2/1/0
default | m/0/1/0
Command 'account' completed successfully
You can get the test data here
Screenshots If applicable, add screenshots to help explain your problem.
Desktop (please complete the following information):
- OS: [e.g. iOS]
- Version [e.g. 22]
$ grin-wallet -V
grin-wallet 5.0.3
Additional context Add any other context about the problem here.
Here is some python code:
import hmac
from hashlib import blake2b, sha512
import bip32
from bip32 import BIP32
from bip_utils import Bech32Encoder
from mnemonic import Mnemonic
from nacl import bindings
mnemo = Mnemonic("english")
words = "sign interest obtain raw window monster jump bring nice crunch toward grunt prosper recycle sphere battle mother fold reject velvet emotion similar romance govern"
seed = mnemo.to_seed(mnemonic=words, passphrase="")
# I AM VOLDEMORT
m = hmac.new("IamVoldemort".encode("utf8"), digestmod=sha512)
m.update(mnemo.to_entropy(words))
secret = m.digest()
bip32 = BIP32(chaincode=secret[32:], privkey=secret[:32])
# get the m/0/1/0 key
chain, sk_der = bip32.get_extended_privkey_from_path("m/0/1/0")
# compute the blake2 hash of that key and that is ed25519 private key
sk_der_blake = blake2b(sk_der, digest_size=32).digest()
# get the ed25519 public key from it
pk, sk = bindings.crypto_sign_seed_keypair(sk_der_blake)
# compute the slatepack address
slatepack_address = Bech32Encoder.Encode("grin", pk)
print(f"slatepack at m/0/1/0:\t{slatepack_address}")
assert (
slatepack_address
== "grin14kgku7l5x6te3arast3p59zk4rteznq2ug6kmmypf2d6z8md76eqg3su35"
)
# get the m/0/2/0 key
chain, sk_der = bip32.get_extended_privkey_from_path("m/1/1/0")
# compute the blake2 hash of that key and that is ed25519 private key
sk_der_blake = blake2b(sk_der, digest_size=32).digest()
# get the ed25519 public key from it
pk, sk = bindings.crypto_sign_seed_keypair(sk_der_blake)
# compute the slatepack address
slatepack_address = Bech32Encoder.Encode("grin", pk)
print(f"slatepack at m/1/1/0:\t{slatepack_address}")
assert (
slatepack_address
== "grin1uqan8sf49yf0369ezef9jhl25jll9fc8xc5wjkcg0w6nv6v85v2sp4wgwy"
)
# get the m/0/3/0 key
chain, sk_der = bip32.get_extended_privkey_from_path("m/2/1/0")
# compute the blake2 hash of that key and that is ed25519 private key
sk_der_blake = blake2b(sk_der, digest_size=32).digest()
# get the ed25519 public key from it
pk, sk = bindings.crypto_sign_seed_keypair(sk_der_blake)
# compute the slatepack address
slatepack_address = Bech32Encoder.Encode("grin", pk)
print(f"slatepack at m/2/1/0:\t{slatepack_address}")
assert (
slatepack_address
== "grin1guszgjsjlt9vrppu42l03xx080epzzvse5nev3nvdh632explc0sj8ylja"
)
Output:
slatepack at m/0/1/0: grin14kgku7l5x6te3arast3p59zk4rteznq2ug6kmmypf2d6z8md76eqg3su35
slatepack at m/1/1/0: grin1uqan8sf49yf0369ezef9jhl25jll9fc8xc5wjkcg0w6nv6v85v2sp4wgwy
slatepack at m/2/1/0: grin1guszgjsjlt9vrppu42l03xx080epzzvse5nev3nvdh632explc0sj8ylja