embit
is a bitcoin library designed to run either with Python 3 on a PC or with MicroPython on embedded devices.
For cryptography it uses libsecp256k1 library maintained by Bitcoin Core team for elliptic, and everything else is implemented in python.
- BIP-39, BIP-32 key derivation (API docs: bip39, bip32)
- parsing and signing PSBT transactions - both version 1 and 2 (API docs)
- signing with custom SIGHASH flags
- Descriptors and miniscript support (API docs)
- SLIP-39 Shamir Secret Sharing scheme (experimental, API docs)
- Liquid network support (experimental, API docs)
- Taproot support (in progress, experimental)
Python 3:
pip3 install embit
Micropython: requires custom build with C bindings to hashlib and secp256k1. Docs TBD, see examples for now: stm32, RiscV, esp32
This script generates bip39 recovery phrase, converts it to the root key, derives native segwit xpub, prints first 5 receiving addresses, parses PSBT transaction and signs it.
We use hexlify
and %s
formatting to keep it compatible with MicroPython, if you use Python3 you can use .hex()
and f-strings.
For more details check out the API docs
from embit import bip32, bip39
from embit.psbt import PSBT
from embit.descriptor import Descriptor
from binascii import hexlify
# Generate mnemonic from 16 bytes of entropy (use real entropy here!):
mnemonic = bip39.mnemonic_from_bytes(b"128 bits is fine")
# >>> couple mushroom amount shadow nuclear define like common call crew fortune slice
# Generate root privkey, password can be omitted if you don't want it
seed = bip39.mnemonic_to_seed(mnemonic, password="my bip39 password")
root = bip32.HDKey.from_seed(seed)
# Derive and convert to pubkey
xpub = root.derive("m/84h/0h/0h").to_public()
# Generate native segwit descriptors.
# You can use {0,1} for combined receive and change descriptors
desc = Descriptor.from_string("wpkh([%s/84h/0h/0h]%s/{0,1}/*)" % (hexlify(root.my_fingerprint).decode(), xpub))
# >>> wpkh([67c32a74/84h/0h/0h]xpub6CH26VtYLqm5nw8UKA2qH8doMrvGZUpeQst1JkrmyGo9LYRoKVnyfgdvjcVQoK4XSWUwyZEcupk8wBh6a2mLJ82ouUo9x2n1Y3zeoEcRSYr/{0,1}/*)
# Print first 5 addresses
for i in range(5):
print(desc.derive(i).address())
# parse base64-encoded PSBT transaction
psbt = PSBT.from_string("cHNidP8BAHECAAAAAaW9Cd1X07XEcA/D0XmE5dwI2AEQr4aTTTwBqopD1mxAAAAAAAD9////AvJJXQUAAAAAFgAUUa2Cs4u5XOmDFhwNxl/szK5L9beAlpgAAAAAABYAFCwSoUTerJLG437IpfbWF8DgWx6kAAAAAAABAHECAAAAATVenbXof59P6l5N+BxpXQytbyWp29JfJDyT+OwohRWKAAAAAAD+////AgDh9QUAAAAAFgAUgmkBPePxvl4jTWsuNNnypKngm824IKMwAAAAABYAFOiPQIZGLU3UZ8JugMpHcCwxmUK2zQEAAAEBHwDh9QUAAAAAFgAUgmkBPePxvl4jTWsuNNnypKngm80iBgPHS/KrcrFXnxQ0/kvZeBkmEsQGjBLEc5JRUjzP9yVXVhhnwyp0VAAAgAAAAIAAAACAAAAAAAAAAAAAIgIC9jzRiRyPDoZ5F2xMV/QfW6qma/6i0PtyELYn8YR5PjsYZ8MqdFQAAIAAAACAAAAAgAEAAAAAAAAAAAA=")
# only print outputs that are not change
for out in psbt.outputs:
if not desc.owns(out):
print("Send %d to %s" % (out.value, out.script_pubkey.address()))
# print fee
print("fee: %d" % psbt.fee())
# sign psbt and print it
psbt.sign_with(root)
print(psbt)
- Specter-DIY - airgapped hardware wallet on STM32F469NI-Discovery board (STM32)
- SeedSigner - airgapped hardware wallet on Raspberry Pi Zero
- krux - airgapped hardware wallet on M5StickV developer board (RiscV)
- Specter-Desktop
- LNBits watch only extension