10000 wallet shows incorrect BIP32 derivation paths · Issue #663 · mimblewimble/grin-wallet · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

wallet shows incorrect BIP32 derivation paths #663

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

Open
marekyggdrasil opened this issue Jul 29, 2022 · 4 comments
Open

wallet shows incorrect BIP32 derivation paths #663

marekyggdrasil opened this issue Jul 29, 2022 · 4 comments

Comments

@marekyggdrasil
Copy link

Describe the bug

The key derivation path shown after running grin-wallet account is not correct.

To Reproduce
Steps to reproduce the behavior:

  1. Create bunch of accounts
  2. Run grin-wallet account to list them, note the derivation paths
  3. Derive the slatepack addresses from those exact paths using GRIN++ or mimblewimble-py
  4. 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.

@davidtavarez
Copy link
Contributor

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

@Anynomouss
Copy link
Contributor

@marekyggdrasil Is this issues still an open issue?
If not, can you close it.

@Anynomouss
Copy link
Contributor
Anynomouss commented Apr 11, 2025

This part of grins use of BIP32 is indeed confusing, I think more confusing than it needs to be due to lacking explicit documentation on how grin uses BIP32 key derivation.
The wallet output when running grin-wallet account says it shows the Parent BIP-32 Derivation Path , which is an incomplete description, its show the Parent BIP-32 Derivation Path for output blinding factors not the parent for address generation
alice | m/1/0
bob | m/2/0
default | m/0/0

Grin is both smart and tricky in that it uses BIP32 derived receive keys for outputs and derived change keys for SlatepackAddress generation.

As can be found in the RFC on SlatepackAddresses, two keys are derived from the account master key, one for the SlatepackAddress, one for blinding factors

Key Generation
Keys used in SlatepackAddresses are derived from a path from the master seed in a given wallet account. Currently the wallet uses separate derivation paths: one for the private bytes used for the blinding factor keys and one for the private bytes used to derive the ed25519 keys used to derive Tor onion addresses. ed25519 keys used for a SlatepackAddress are derived from this second derivation path of the master seed.

Source: https://github.com/j01tz/grin-rfcs/blob/slatepack/text/0015-slatepack.md

It would be best for to provide explicit example within this RFC as well as in the grin wiki documentation on key derivation both for output private-keys (blinding factors) and slatepacks. Currently the only explicit example can be found hidden in the the code:

Source: https://raw.githubusercontent.com/mimblewimble/grin-wallet/b1ffe86a55492d08f050ebdd8317272da5c78dd7/libwallet/src/address.rs

	// An output derivation for acct m/0
	// is m/0/0/0, m/0/0/1 (for instance), m/1 is m/1/0/0, m/1/0/1
	// Address generation path should be
	// for m/0: m/0/1/0, m/0/1/1
	// for m/1: m/1/1/0, m/1/1/1

If we would implement account specific SlatepackAddresses, it would make sense to change the output to show something like this

____ Wallet Accounts ____

Name | Output BIP-32 Parent Key | SlatepackAddress key | address
---------+--------------------------------------------------------------------------------------
default | m/0/0 | m/0/1/0: grin14kgku7l5x6te3arast3p59zk4rteznq2ug6kmmypf2d6z8md76eqg3su35

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants
0