How to generate Bitcoin keys/addresses from a seed in Python?

2024/10/7 10:25:59

I am trying to create a set of public/private keys from a mnemonic based on BIP0039. I am working in Python.

Here is the code I have so far:

from mnemonic import Mnemonic
mnemon = Mnemonic('english')
words = mnemon.generate(256)
mnemon.check(words)
seed = mnemon.to_seed(words)

In BIP0039, it is stated that you should be able to get to the Keys with a seed, but I haven't been able to figure it out in Python using bip32utils. Does anyone have an example of how to use bip32utils to convert a seed into private/public keys and their respective addresses?

Answer

The generation of the seed from the mnemonic is already implemented in the posted code.

The root key is derived from the seed with:

root_key = bip32utils.BIP32Key.fromEntropy(seed)

For BIP32 the child keys are created with:

child_key = root_key.ChildKey(0).ChildKey(0)

and for BIP44:

child_key = root_key.ChildKey(44 + bip32utils.BIP32_HARDEN).ChildKey(0 + bip32utils.BIP32_HARDEN).ChildKey(0 + bip32utils.BIP32_HARDEN).ChildKey(0).ChildKey(0) 

The BIP32Key class encapsulates the key and provides different methods to retrieve the keys in different formats.


Example for BIP32:

from mnemonic import Mnemonic
import bip32utilsmnemon = Mnemonic('english')
#words = mnemon.generate(256)
#print(words)
#mnemon.check(words)
#seed = mnemon.to_seed(words)
seed = mnemon.to_seed(b'lucky labor rally law toss orange weasel try surge meadow type crumble proud slide century')
print(f'BIP39 Seed: {seed.hex()}\n')root_key = bip32utils.BIP32Key.fromEntropy(seed)
root_address = root_key.Address()
root_public_hex = root_key.PublicKey().hex()
root_private_wif = root_key.WalletImportFormat()
print('Root key:')
print(f'\tAddress: {root_address}')
print(f'\tPublic : {root_public_hex}')
print(f'\tPrivate: {root_private_wif}\n')child_key = root_key.ChildKey(0).ChildKey(0)
child_address = child_key.Address()
child_public_hex = child_key.PublicKey().hex()
child_private_wif = child_key.WalletImportFormat()
print('Child key m/0/0:')
print(f'\tAddress: {child_address}')
print(f'\tPublic : {child_public_hex}')
print(f'\tPrivate: {child_private_wif}\n')

produces the output:

BIP39 Seed: 487a440fb26cb376168b6b88a2e46699cb9967bdc4a107fab571f6fdeaab02ea95d149073b3319735c5eace5acafd362edd1ad4c3ac3f655aaa6468973999500Root key:Address: 15Zpz6hJkSkAiw1A5Sm9UoemCVBCuW1SSPPublic : 036830d1cbcecf9e01ce1ddb154ccb754a6a765d06b3b48dced926861e03bd9485Private: KzKjSsprRaWBfVy3oPNwPJBAzVxLXU5AAT5Xe9EJh5pJjpJAqP7qChild key m/0/0:Address: 1AP5U7iDUrvH8B1m1qiarbkA31Ux7jX8YFPublic : 03a78bb2b1fb86280b4091e5cdffc5d8c87430f5c0988e84a7c5d972bb3f1a1b93Private: L47DQmmwwc88oZLPmyZr7CQWn1RzayBmH6gSpnFoMRCCTsR5yRpN

This can be verified using the website https://iancoleman.io/bip39/#english by entering the mnemonic used in the example above into the BIP39 Mnemonic field and selecting BIP32 as the derivation path.


The keys can be dumped in different formats using the BIP32Key#dump() method, e.g.:

root_key.dump()

provides the following output:

* Identifier* (hex):       b'3215de8b72f8c407682a6e9334ccd11ae17b1f9c'* (fpr):       b'3215de8b'* (main addr): 15Zpz6hJkSkAiw1A5Sm9UoemCVBCuW1SSP
* Secret key* (hex):       5c9c29e08d1ee9d3c8295ba2a931a9f0166e4282cc01702549664736a16b3a89* (wif):       KzKjSsprRaWBfVy3oPNwPJBAzVxLXU5AAT5Xe9EJh5pJjpJAqP7q
* Public key* (hex):       b'036830d1cbcecf9e01ce1ddb154ccb754a6a765d06b3b48dced926861e03bd9485'
* Chain code* (hex):       b'43088cf562e569922e1c1d0d689144ca2b171cb3cc3b2fedaa198f63be7ec130'
* Serialized* (pub hex):   b'0488b21e00000000000000000043088cf562e569922e1c1d0d689144ca2b171cb3cc3b2fedaa198f63be7ec130036830d1cbcecf9e01ce1ddb154ccb754a6a765d06b3b48dced926861e03bd9485'* (prv hex):   b'0488ade400000000000000000043088cf562e569922e1c1d0d689144ca2b171cb3cc3b2fedaa198f63be7ec130005c9c29e08d1ee9d3c8295ba2a931a9f0166e4282cc01702549664736a16b3a89'* (pub b58):   xpub661MyMwAqRbcFD9E5CavptgKf8JFbkynXnRui6zHDi7TveyV1vnebzqJ1UUDRbcWjBLNy29ABLUxgevE86Pmt3PNMDZFzLyRzQuebs5Kn1G* (prv b58):   xprv9s21ZrQH143K2j4kyB3vTkjb76TmCJFwAZWJuiaffNaV3reLUPUQ4CWpABQbzZoo1SvSbuykaZfwj241YvtCs9FVpeKMAFd9eXvQTZwxSNU

Btw, the source code of BIP32Key#dump() is also a nice description of which method returns which format.

Edit:
The mnemonic library provides the method Mnemonic#to_hd_master_key() which returns the extended private key Base58 encoded as defined e.g. in bitcon/bips, xprv.... If this is enough for you, then you do not need bip32utils.
However, as far as I can see, there is no support within mnemonic to derive the private key from this, the chain code, the public or extended public key. This is just implemented in the bip32utils class.
It is not clear to me which libraries you can use and which not. You may have to implement the missing functionalities yourself (with corresponding effort):
Chain code and private key can be determined from the extended private key. For this you need only knowledge about the format (see e.g. bitcon/bips). The public key can be derived from the private key if the curve is known (secp256k1 for Bitcoin) (ec arithmetic or an ec library is required for this) and thus the extended public key. bip32utils can serve as a blueprint for this.

https://en.xdnf.cn/q/70250.html

Related Q&A

NumPy - Set values in structured array based on other values in structured array

I have a structured NumPy array:a = numpy.zeros((10, 10), dtype=[("x", int),("y", str)])I want to set values in a["y"] to either "hello" if the corresponding val…

numpy.disutils.system_info.NotFoundError: no lapack/blas resources found

Problem: Linking numpy to correct Linear Algebra libraries. Process is so complicated that I might be looking for the solution 6th time and I have no idea whats going wrong. I am on Ubuntu 12.04.5. I …

Opencv: Jetmap or colormap to grayscale, reverse applyColorMap()

To convert to colormap, I doimport cv2 im = cv2.imread(test.jpg, cv2.IMREAD_GRAYSCALE) im_color = cv2.applyColorMap(im, cv2.COLORMAP_JET) cv2.imwrite(colormap.jpg, im_color)Then,cv2.imread(colormap.jpg…

Get file modification time to nanosecond precision

I need to get the full nanosecond-precision modified timestamp for each file in a Python 2 program that walks the filesystem tree. I want to do this in Python itself, because spawning a new subprocess …

`TypeError: argument 2 must be a connection, cursor or None` in Psycopg2

I have a heroku pipeline set up, and have just enabled review apps for it. It is using the same codebase as my staging and production apps, same settings files and everything.When the review app spins …

replace string if length is less than x

I have a dataframe below. a = {Id: [ants, bees, cows, snakes, horses], 2nd Attempts: [10, 12, 15, 14, 0],3rd Attempts: [10, 10, 9, 11, 10]} a = pd.DataFrame(a) print (a)I want to able add text (-s) to …

Convert ast node into python object

Given an ast node that can be evaluated by itself, but is not literal enough for ast.literal_eval e.g. a list comprehensionsrc = [i**2 for i in range(10)] a = ast.parse(src)Now a.body[0] is an ast.Expr…

Keras custom loss function (elastic net)

Im try to code Elastic-Net. Its look likes:And I want to use this loss function into Keras:def nn_weather_model():ip_weather = Input(shape = (30, 38, 5))x_weather = BatchNormalization(name=weather1)(ip…

Django Models / SQLAlchemy are bloated! Any truly Pythonic DB models out there?

"Make things as simple as possible, but no simpler."Can we find the solution/s that fix the Python database world?Update: A lustdb prototype has been written by Alex Martelli - if you know a…

Fabric Sudo No Password Solution

This question is about best practices. Im running a deployment script with Fabric. My deployment user deploy needs sudo to restart services. So I am using the sudo function from fabric to run these com…