Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1#!/usr/bin/env python32# Copyright (c) 2026-present The Bitcoin Core developers3# Distributed under the MIT software license, see the accompanying4# file COPYING or http://www.opensource.org/licenses/mit-license.php.5"""Base classes for creating dynamic xprvs and xpubs. Only basic6functionality of BIP32 is provided here. These classes work over7the ECKey and ECPubKey classes in the key.py."""9import hashlib10import hmac11import unittest13from test_framework.address import byte_to_base5814from test_framework.crypto import secp256k115from test_framework.key import (16ECKey,17ECPubKey,18generate_privkey,19ORDER,20)21from test_framework.script import hash16023BIP32_HARDENED = 0x8000000025def get_version(private=False, mainnet=False):26if mainnet:27if private:28return bytes.fromhex("0488ADE4")29return bytes.fromhex("0488B21E")31if private:32return bytes.fromhex("04358394")33return bytes.fromhex("043587CF")35def hardened(index):36assert 0 <= index < BIP32_HARDENED37return index | BIP32_HARDENED39def derive_path(key, path, path_idx_parser):40if path in ("", "m"):41return key42parts = path.split("/")43if parts[0] == "m":44parts = parts[1:]45for part in parts:46index = path_idx_parser(part)47key = key._derive(index)49return key51class ExtendedPrivateKey:52def __init__(self, key, chaincode, depth=0, parent_fingerprint_bytes=b"\x00\x00\x00\x00", child_num=0):53self.key = key54self.chaincode = chaincode55self.depth = depth56self.parent_fingerprint_bytes = parent_fingerprint_bytes57self.child_num = child_num59@classmethod60def from_seed(cls, seed):61I = hmac.new(b"Bitcoin seed", seed, hashlib.sha512).digest()63secret = I[:32]64chaincode = I[32:]66secret_int = int.from_bytes(secret, "big")67if secret_int == 0 or secret_int >= ORDER:68raise ValueError("Invalid master key")70key = ECKey()71key.set(secret, compressed=True)73return cls(key, chaincode)75@classmethod76def generate(cls):77return cls.from_seed(generate_privkey())79def _fingerprint(self):80return hash160(self.key.get_pubkey().get_bytes())[:4]82def _derive(self, index):83if index >= BIP32_HARDENED:84data = (b"\x00" + self.key.get_bytes() + index.to_bytes(4, "big"))85else:86data = (self.key.get_pubkey().get_bytes() + index.to_bytes(4, "big"))88I = hmac.new(self.chaincode, data, hashlib.sha512).digest()89IL = I[:32]90IR = I[32:]92IL_int = int.from_bytes(IL, "big")93child_secret = (IL_int + int.from_bytes(self.key.get_bytes(), "big")) % ORDER94# Per BIP32, if IL >= n or the child key is 0, the key is invalid. This is95# astronomically unlikely (~1 in 2^127), so reject rather than retrying the next index.96if IL_int >= ORDER or child_secret == 0:97raise ValueError("Invalid BIP32 child key")99child = ECKey()100child.set(child_secret.to_bytes(32, 'big'), compressed=True)102return ExtendedPrivateKey(child, IR, self.depth + 1, self._fingerprint(), index)104def _serialize(self):105return (bytes([self.depth]) + self.parent_fingerprint_bytes + self.child_num.to_bytes(4, "big") + self.chaincode + b"\x00" + self.key.get_bytes())107def pubkey(self):108return ExtendedPublicKey(self.key.get_pubkey(), self.chaincode, self.depth, self.parent_fingerprint_bytes, self.child_num)110def derive_path(self, path):111def path_idx_parser(part):112if part.endswith(("h", "'")):113return hardened(int(part[:-1]))114return int(part)116return derive_path(self, path, path_idx_parser)118def to_string(self, mainnet=False):119return byte_to_base58(self._serialize(), get_version(private=True, mainnet=mainnet))121class ExtendedPublicKey:122def __init__(self, pubkey, chaincode, depth, parent_fingerprint_bytes, child_num):123self.pubkey = pubkey124self.chaincode = chaincode125self.depth = depth126self.parent_fingerprint_bytes = parent_fingerprint_bytes127self.child_num = child_num129def _fingerprint(self):130return hash160(self.pubkey.get_bytes())[:4]132def _derive(self, index):133if index >= BIP32_HARDENED:134raise ValueError("Cannot derive hardened child from xpub")136data = (self.pubkey.get_bytes() + index.to_bytes(4, "big"))137I = hmac.new(self.chaincode, data, hashlib.sha512).digest()138IL = I[:32]139IR = I[32:]141IL_int = int.from_bytes(IL, "big")142child_point = IL_int * secp256k1.G + self.pubkey.p143# Per BIP32, if IL >= n or the resulting point is infinity, the key is invalid.144if IL_int >= ORDER or child_point.infinity:145raise ValueError("Invalid BIP32 child key")147child_pubkey = ECPubKey()148child_pubkey.set(child_point.to_bytes_compressed())150return ExtendedPublicKey(child_pubkey, IR, self.depth + 1, self._fingerprint(), index)152def _serialize(self):153return (bytes([self.depth]) + self.parent_fingerprint_bytes + self.child_num.to_bytes(4, "big") + self.chaincode + self.pubkey.get_bytes())155def derive_path(self, path):156return derive_path(self, path, lambda x: int(x))158def to_string(self, mainnet=False):159return byte_to_base58(self._serialize(), get_version(private=False, mainnet=mainnet))161class TestFrameworkExtendedKey(unittest.TestCase):162def test_bip32_vectors(self):163vectors = [164[165"000102030405060708090a0b0c0d0e0f",166[167["m", "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi", "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8"],168["m/0h", "xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7", "xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw"],169["m/0h/1", "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs", "xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ"],170["m/0h/1/2h", "xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM", "xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5"],171["m/0h/1/2h/2", "xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334", "xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV"],172["m/0h/1/2h/2/1000000000", "xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76", "xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy"],173]174],175[176"fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542",177[178["m", "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U", "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB"],179["m/0", "xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt", "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH"],180["m/0/2147483647h", "xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9", "xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a"],181["m/0/2147483647h/1", "xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef", "xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon"],182["m/0/2147483647h/1/2147483646h", "xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc", "xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL"],183["m/0/2147483647h/1/2147483646h/2", "xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j", "xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt"]184]185]186]188for vector in vectors:189seed = bytes.fromhex(vector[0])190xprv = ExtendedPrivateKey.from_seed(seed)191for seed_vector in vector[1]:192path = seed_vector[0]193derivedxprv = xprv.derive_path(path)194self.assertEqual(derivedxprv.to_string(mainnet=True), seed_vector[1])195self.assertEqual(derivedxprv.pubkey().to_string(mainnet=True), seed_vector[2])
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.