This is some working code for calculating a mum or dad prolonged non-public key from a grasp prolonged public key and baby prolonged non-public key.
#!/usr/bin/env python3
# pip set up hdwallet
# pip set up bitcoin-utils
import hmac
import hashlib
import struct
from binascii import unhexlify
from base58check import b58encode
from hdwallet import HDWallet as HDWallet
from hdwallet.symbols import BTC as SYMBOL
# --------------------
# Offered info
# --------------------
# Grasp Prolonged Public Key (m)
MASTER_EXTENDED_PUBLIC_KEY: str = "xpub661MyMwAqRbcF2s3gyqdcUgw6dU7DjLGHCwkM9jndzGT8WXJjMxZiYnvNmfuzsEXC13FUn9BTeefy3uYf4Cj5k86iHUFzLS1yatpXN9hq4u"
# Youngster Prolonged Non-public Key (m/44/0/0/1/404)
CHILD_EXTENDED_PRIVATE_KEY: str = "xprvA3WvYTa5Xv4kSXuL9mAFMimc5psTTB1Dq8x4q9HB7R4EmpufsXFuhi9SpVVYXsmyjM5dvoGowU3W9hDuYRrgkQiiE1d881TZrFLhLNxBs7g"
# ------
# Methodology
# ------
# Initialize a pockets
hdwallet: HDWallet = HDWallet(image=SYMBOL)
# Create a HDWallet from the grasp prolonged public key
hdwallet.from_xpublic_key(xpublic_key=MASTER_EXTENDED_PUBLIC_KEY, strict=True)
# Get the mum or dad prolonged public key at m/44/0/0/1
# We'd like this in order that we will calculate the prolonged non-public key at m/44/0/0/1 (the extent above the kid non-public key that has been revealed)
PARENT_EXTENDED_PUBLIC_KEY = hdwallet.from_path("m/44/0/0/1")
PARENT_PUBLIC_KEY: str = PARENT_EXTENDED_PUBLIC_KEY.public_key()
PARENT_CHAIN_CODE: str = PARENT_EXTENDED_PUBLIC_KEY.chain_code()
print("Father or mother Prolonged Public Key (m/44/0/0/1):", PARENT_EXTENDED_PUBLIC_KEY.xpublic_key())
print(" Public Key:", PARENT_PUBLIC_KEY)
print(" Chain Code:", PARENT_CHAIN_CODE)
print("")
# Extract the uncooked non-public key from the kid prolonged non-public key m/44/0/0/1/404
# We'd like this in order that we will calculate the mum or dad prolonged non-public key
baby: HDWallet = HDWallet(image=SYMBOL)
baby.from_xprivate_key(xprivate_key=CHILD_EXTENDED_PRIVATE_KEY, strict=False) # False permits you to initialize from a non-root prolonged key
CHILD_PRIVATE_KEY: str = baby.private_key()
print("Youngster Non-public Key:", CHILD_PRIVATE_KEY)
print("")
# Calculate the mum or dad prolonged non-public key for m/44/0/0/1 (that is the mathematical bit)
# Put the chain code and (mum or dad public key + index) by HMAC-SHA512
# This step is utilized in HD Wallets when deriving non-hardened youngsters for each non-public and public prolonged keys
index = struct.pack(">L", 404) # Use the identical index quantity because the baby prolonged non-public key that we all know
information = unhexlify(PARENT_PUBLIC_KEY) + index
hmac_result = hmac.new(unhexlify(PARENT_CHAIN_CODE), information, hashlib.sha512).hexdigest()
# Get the left facet of the hmac digest (the left 32 bytes)
hmac_result_left = hmac_result[:64]
# Calculate the mum or dad non-public key (baby non-public key - hmac left)
# Word: All non-public keys have to be modulo the variety of factors on the Secp256k1 curve
secp256k1_n = 115792089237316195423570985008687907852837564279074904382605163141518161494337
PARENT_PRIVATE_KEY: str = ((int(CHILD_PRIVATE_KEY, 16) - int(hmac_result_left, 16)) % secp256k1_n).to_bytes(32, "massive").hex()
print("Father or mother Non-public Key:", PARENT_PRIVATE_KEY)
# Create an tackle for the mum or dad prolonged non-public key
# That is in order that we will intialize a brand new HD Pockets utilizing the prolonged non-public key and use it to derive youngsters simply utilizing the library
# Get the fingerprint, which could be discovered from the mum or dad of this mum or dad prolonged public key
parent_parent_extended_public_key: HDWallet = HDWallet(image=SYMBOL)
parent_parent_extended_public_key.from_xpublic_key(xpublic_key=MASTER_EXTENDED_PUBLIC_KEY, strict=True)
parent_fingerprint = parent_parent_extended_public_key.from_path("m/44/0/0").finger_print()
# Serialize all the information for the mum or dad prolonged non-public key
model = "0488ade4" # xprv (BIP)
depth = "04" # what number of ranges deep this prolonged secret is from grasp, so m/44/0/0/1 = 4
index = "00000001" # the index variety of this prolonged key from its mum or dad, which is the final variety of m/44/0/0/1
chain_code = PARENT_CHAIN_CODE
information = "00" + PARENT_PRIVATE_KEY
uncooked = (model + depth + parent_fingerprint + index + chain_code + information)
# Create a checksum for this serialized information (first 4 bytes of the hash256 of the uncooked information above)
checksum = hashlib.sha256(hashlib.sha256(unhexlify(uncooked)).digest()).hexdigest()[0:8]
# Create tackle by changing to base58
PARENT_EXTENDED_PRIVATE_KEY = b58encode(unhexlify(uncooked + checksum)).decode("utf-8")
print("Father or mother Prolonged Non-public Key (m/44/0/0/1):", PARENT_EXTENDED_PRIVATE_KEY)
print("")
# -----
# Utilization
# -----
# Now that we have calculated the prolonged non-public key for m/44/0/0/1, we will calculate the entire baby keys under it.
# For instance, we will calculate the prolonged non-public key at m/44/0/0/1/402
# Initialize a brand new pockets from the tackle for this mum or dad prolonged non-public key at m/44/0/0/1
mum or dad: HDWallet = HDWallet(image=SYMBOL)
mum or dad.from_xprivate_key(xprivate_key=PARENT_EXTENDED_PRIVATE_KEY, strict=False) # False permits you to initialize from a non-root prolonged key
# Derive the prolonged non-public key at m/44/0/0/1/402
instance = mum or dad.from_path("m/402") # Word: That is m/44/0/0/1/402, as a result of we're ranging from m/44/0/0/1
print("Instance (m/44/0/0/1/402):", instance.xprivate_key())
# ------
# Checks
# ------
# I already knew the grasp prolonged non-public key for this pockets, so we will use it to verify that it derives the identical prolonged non-public key at m/44/0/0/1/402 because the mum or dad prolonged non-public key we calculated.
MASTER_EXTENDED_PRIVATE_KEY: str = "xprv9s21ZrQH143K2YnaaxJdFLkCYbdcpGcQuz29YmLB5ejUFiCABpeKAkUSXVuqysESjMJhk8yTfhxR3BT6Ag7Z5fYJ1jFgidmrGgfZYNCLnNB"
verify: HDWallet = HDWallet(image=SYMBOL)
verify.from_xprivate_key(xprivate_key=MASTER_EXTENDED_PRIVATE_KEY, strict=True) # False permits you to initialize from a non-root prolonged key
verify.from_path("m/44/0/0/1/402")
print("Test (m/44/0/0/1/402):", verify.xprivate_key())
Firstly, two caveats:
- I am not excellent at Python, and I’ve not used the
hdwallet
orbitcoin-utils
Python libraries earlier than. - There are not any built-in strategies in these libraries for this, so I labored with what was accessible. I needed to carry out some steps manually (e.g. changing a uncooked non-public key in to an tackle).
So mainly the code could be very tough, and I simply did the naked minimal to get it working.
Secondly, I’ve solely labored out the prolonged non-public key on the m/44/0/0/1
degree of the tree. This lets you work out all of the baby keys under that degree solely.
Due to this fact, in the event you needed to calculate a toddler prolonged non-public key at m/44/0/0/0/402
for instance, you would want to work backwards once more to calculate the prolonged non-public key at m/44/0/0
first. This may be achieved by calculating the prolonged public key at m/44/0/0
(from the grasp prolonged public key), then utilizing this together with the newly-calculated prolonged non-public key at m/44/0/0/1
to calculate the mum or dad prolonged non-public key above it at m/44/0/0
. From right here you may then derive all the way down to m/44/0/0/0/402
.
The above could be achieved by repeating the related components of code.
I have not given the precise resolution to your problem, as I did not need take away from the training expertise, and this incorporates the code it’s essential get began.
In equity, this can be a pretty complicated problem that requires understanding of how HD Wallets work, and it isn’t one thing that you’d be capable of resolve utilizing the default features discovered inside a library. It might most likely helpful to write down your individual library for this sort of stuff.
Nonetheless, hopefully this code helps to get you heading in the right direction.
This web page explains a few of the particulars: https://learnmeabitcoin.com/technical/extended-keys
Previous reply
Utilizing a modified model of the instance code from right here: https://github.com/meherett/python-hdwallet/blob/grasp/examples/from_xprivate_key.py
#!/usr/bin/env python3
from hdwallet import HDWallet as HDWallet
from hdwallet.symbols import BTC as SYMBOL
# Bitcoin root xprivate key
XPRIVATE_KEY: str = "xprv9s21ZrQH143K2YnaaxJdFLkCYbdcpGcQuz29YmLB5ejUFiCABpeKAkUSXVuqysESjMJhk8yTfhxR3BT6Ag7Z5fYJ1jFgidmrGgfZYNCLnNB"
# Initialize Bitcoin mainnet HDWallet
hdwallet: HDWallet = HDWallet(image=SYMBOL)
# Get Bitcoin HDWallet from xprivate key
hdwallet.from_xprivate_key(xprivate_key=XPRIVATE_KEY, strict=True)
# Derivation from path
hdwallet.from_path("m/44/0/0/1/404")
# Print outcomes
print("Root XPrivate Key:", hdwallet.root_xprivate_key())
print("XPrivate Key:", hdwallet.xprivate_key())
You will want to interchange the grasp prolonged non-public key with your individual. This code simply incorporates an instance for testing.
I have not used this library earlier than, so I can not clarify an excessive amount of about how one can use it. However this instance ought to get you going.
EDIT:
Sorry I simply realized your asking to derive an prolonged non-public key from a grasp prolonged public key.
It is not attainable to derive non-public keys from a grasp prolonged public key in a HD pockets. Are you certain that is what you are being requested to do with this problem?