I am implementing bip32 for my wallet as a project to learn Bitcoin.
I am following this documentation: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
Since I am not implementing bip39 yet, I am getting the master private key and code chain from the BIP39 Root Key from this web: https://iancoleman.io/bip39/#english
using a random set of words which are:
praise vague cruise stem test gesture twin long donate violin special hamster horror rough loan
which throws:
xprv9s21ZrQH143K2E7weJFDreP9A8dFkx18swFoAJ6DBdsVuSPoFBLzekLca8vEG1i6ikCcvzZb79joaEZAjeM8Nz1DZ549aCnv3YFXy33mLoW
Comparing this output with another web implementation here: https://iancoleman.io/bip39/
These two outputs match. However, after this point they don't. And my implementation doesn't match with any of those implementations!
Here is my python code where I deserialize the BIP32 extended key. This matches perfectly with the master private key and chain code from the second web app:
def deserialize_xprvk(s):
version = s.read(4)
depth = s.read(1)
fingerprint = s.read(4)
index= s.read(4)
chain_code = s.read(32)
s.read(1)
privkey = s.read(32)
return [version,depth, fingerprint,index,chain_code,privkey]
decoded =deserialize_xprvk( BytesIO(decode_base58_extended(xtprvk)))
printing "decoded" I get:
0x488ade4
0x0
0x0
0x0
0x10e85c06826fdcd006c244ff6c44200044c1418bdd82cc088c33e48ae7b4c2ca
0xf214c0afd9b311544fdb686cae5100f1ac0ac8aba2b6c55719ba41ac2d4f2d12
which matches perfectly with the 2nd implementation (the firs one doesn't show the master private key or chain code).
Then I try to follow the instructions from here: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki to derivate the child PRIVATE KEY with index 0 for this master key and chain code like this:
#The order of the ciclic group in the finite field of the ecliptic curve
N = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141
#creates a string to emulate the index in a 4-byte hex format.
def str_i(i):
i_str = hex(i)[2:]
while len(i_str)<8:
i_str="0"+i_str
return i_str
pub_key = PrivateKey(int.from_bytes(master_priv_key,"big")).point.sec()
#I put an extra "0" because every public key begins with a "02" or "03" but
#the hex() function commits this character.
#The str_i(0) means I am trying to get the index 0.
msg=("0" + hex(int.from_bytes(pub_key,"big"))[2:] + str_i(0)).encode()
I= hmac.new(
key = hex(int.from_bytes(chain_code,"big"))[2:].encode() ,
msg=msg ,
digestmod=sha512).digest()
I_L, I_R = I[:32], I[32:]
child_privkey_int = (int.from_bytes(I_L,"big") + int.from_bytes(master_priv_key,"big"))%N
child_privkey = PrivateKey(child_privkey_int).wif(compressed= True)
child_chain_code = I_R
If I run this code and try to get the child private key for path m\0\0\ I get:
"L29Rc5PyDkbUr2z1VcL2DuAhTQkNo3qwF7ENgRotS86ADUkiXPJF"
But in the first web app they get: "KwuPS3DGMnCc3jMJWtqVVzkCoMyhybxGZrf68fPBEBtFfujBdPGs"
And in the second one they get: "L1TcvgkPZFa6DyXhv9vYWceTXZhoAR7ZQMdt5rqV6LW3Zutpsqr8"
Is it ok to disagree on different implementations?? or am I doing something really wrong? I am starting to pull off my hair trying to find out where I am messing up.
Thanks.