A short walk through all the important BitcionLib classes: create keys, transactions and wallets using this library.
You can run and experiment with the code examples if you have installed Jupyter
In [ ]:
from bitcoinlib.keys import Key
k = Key()
k.info()
As you can see a private key, public key, address and WIF (Wallet Import Format) are all created as part of the Key representation.
You can also see a 'point x' and 'point y' as a private key is nothing more then a point on the Secp256k1 curve.
If you have already a private key WIF you can use this to create a Key class.
In [ ]:
from bitcoinlib.keys import Key
wif = 'KyAoQkmmoAgdC8YXamgpqFb2R8j6g5jiBnGdJo62aDJCxstboTqS'
k = Key(wif)
print(k.address())
You can use the bip32_encrypt method to create an encrypted key. In the example below this key is used to recreate the original unencrypted key.
In [ ]:
from bitcoinlib.keys import Key
k = Key()
print(k.private_hex)
encrypted_wif = k.bip38_encrypt('secret')
print(encrypted_wif)
k_import = Key(encrypted_wif, passphrase='secret')
print(k_import.private_hex)
And of course the Key class can also represent other networks and encodings, such as Litecoin or the bech32 encoding used in segwit.
In [ ]:
from bitcoinlib.keys import Key
k = Key(network='litecoin')
print(k.address())
print(k.address(encoding='bech32'))
The HDKey class, or hierarchical deterministic key class, is a child of the Key class and mainly adds a chain code. This chain code allows to create key structures or related which can be derived from a single master key.
The creation of a random HDKey is just as simple as a normal Key but it gives a range of extra possibilities.
When you run the code below and check the info() output near 'Extended Key' you can find the extra's the HDKey provides. First the chain code, which is also random generated just as the key. Next the depth and child index allowing to create levels of keys. And final the WIF representing all HDKey info in an exchangeable string.
In [ ]:
from bitcoinlib.keys import HDKey
k = HDKey(witness_type='segwit')
k.info()
So let's try to import a HDKey wif and create a private child key and the a public child key.
In [ ]:
from bitcoinlib.keys import HDKey
extended_wif = 'zprvAWgYBBk7JR8Gk4CexKBHgzfYiYXgV71Ybi2tJFU2yDn8RgiqsviqT4eYPE9LofWMdrSkYmWciMtiD7jqA5dccDLnJj' \
'DSMghhGRv41vHo9yx'
k = HDKey(extended_wif)
ck = k.child_private(10)
print("ck.private_hex: %s" % ck.private_hex)
print("ck.depth: %s" % ck.depth)
ck_pub = ck.child_public(0)
print("ck_pub.private_hex: %s" % ck_pub.private_hex)
print("ck_pub.public_hex: %s" % ck_pub.public_hex)
print("ck_pub.depth: %s" % ck_pub.depth)
As you can see the depth increases with 1 with every derived child key, and when you derive a public key the private key is not available anymore.
While this is all very interesting I think, please remember you not have to care of any of this if you just using the HDWallet class. The HDWallet class handles key derivation in the background for you.
The HDKey class has various methods to create key paths used in most modern wallet software today.
In [ ]:
from bitcoinlib.keys import HDKey
prv_masterkey = HDKey()
pub_masterkey = prv_masterkey.public_master()
print("Public masterkey to exchange (method 1): %s" % pub_masterkey.wif())
pub_masterkey = prv_masterkey.subkey_for_path("m/44'/0'/0'")
print("Public masterkey to exchange (method 2): %s" % pub_masterkey.wif())
In [ ]:
from bitcoinlib.mnemonic import Mnemonic
from bitcoinlib.keys import HDKey
phrase = Mnemonic().generate()
print(phrase)
k = HDKey.from_passphrase(phrase)
print(k.private_hex)
Use the address class to create an address or to analyse address information.
In [ ]:
from bitcoinlib.keys import Address
address = 'bc1q96zj7hv097x9u9f86azlk49ffxak7zltyfghld'
a = Address.import_address(address)
print(a.as_json())
Transactions represent the transaction of value and are stored on the Blockchain. A transaction has 1 or more inputs to unlock previous outputs and 1 or more outputs containing locking scripts. These locking script can be unlocked with a private key and then spent again in inputs of a new transaction.
Let's create a simple transaction.
In [ ]:
from bitcoinlib.transactions import Transaction
from bitcoinlib.keys import HDKey
t = Transaction()
prev_hash = '9c81f44c29ff0226f835cd0a8a2f2a7eca6db52a711f8211b566fd15d3e0e8d4'
t.add_input(prev_hash, output_n=0)
k = HDKey()
t.add_output(100000, k.address())
t.info()
There are many way to create transactions in most cases you will get them for the blockchain or create them with the HDWallet class.
Below is an example of how to parse a raw transaction.
In [ ]:
from bitcoinlib.transactions import Transaction
rawtx = "010000000001015f171218b2e273be55af4f1cf0a56c0499b48b098d16ebdc68c62db78c55765a0100000000ffffff000200e1f505" \
"0000000017a9140db01b8486f63ef80f02fe78bada7680c46c11ef8730f10801000000001600141dfba959940495c3a92cbb80b0b5" \
"0246cfe0f11702473044022004eb67e91dc04179a367d99c0d65617cda385c313e79d717f8ade695a5731b8c02207a273d8592d815" \
"9d6f587a0db993ab4b4a030fbfa390229b490d789a77b8c8540121029422dbe194e42bac01e94925cf8b619f0fd4aa5d0181633659" \
"e7deed79eb5b7a00000000"
t = Transaction.import_raw(rawtx)
t.info()
In [ ]:
from bitcoinlib.services.services import Service
srv = Service()
print("Estimated transaction fee: %s" % srv.estimatefee())
print("Latest block: %s" % srv.blockcount())
After a request with the Service class the results and errors are stored in the class. If you for instance request an unknown transaction, the request returns False, the Service().results is empty and the errors can be found in Service().errors.
In [ ]:
from bitcoinlib.services.services import Service
srv = Service()
unknown_txid = '9c81f44c29ff0226f835cd0a8a2f2a7eca6db52a711f8211b566fd15d3e0e8d4'
srv.gettransaction(unknown_txid)
print(srv.results)
print(srv.errors)
In [ ]:
from bitcoinlib.wallets import HDWallet
w = HDWallet.create('jupyter-test-wallet')
w.info()
The create() methods raises an error if the wallet already exists, so if you are not sure use the wallet_create_or_open method.
And now let's conclude with a Bitcoin segwit testnet wallet and a send a real transaction.
In [3]:
from bitcoinlib.wallets import wallet_create_or_open
w = wallet_create_or_open('bitcoinlib-testnet1', network='testnet', witness_type='segwit')
wk = w.new_key()
print("Deposit to address %s to get started" % wk.address)
Now go to a Bitcoin testnet faucet to get some coins.
Check if the testnet coins are successfully send, so we can start creating a transaction.
In [2]:
from bitcoinlib.wallets import wallet_create_or_open
w = wallet_create_or_open('bitcoinlib-testnet1', network='testnet', witness_type='segwit')
n_utxos = w.utxos_update()
if n_utxos:
print("Found new unspent outputs (UTXO's), we are ready to create a transaction")
w.info()
The utxos_update() method only checks for new unspent outputs for existing keys. Two other methods to receive transaction information for your wallet are the transactions_update() and scan() method. Transactions_update retrieves all transactions from the service providers for your wallet's key: incoming and outgoing.
The scan() method also generates new keys/addresses and updates all transactions.
Now we have a wallet with coins and can create a transaction and send some coins. The send_to() method creates a transactions and then broadcasts the signed transactions to the network.
In [3]:
from bitcoinlib.wallets import wallet_create_or_open
w = wallet_create_or_open('bitcoinlib-testnet1', network='testnet', witness_type='segwit')
t = w.send_to('tb1qprqnf4dqwuphxs9xqpzkjdgled6eeptn389nec', 4000, fee=1000)
t.info()
When you run this code you should receive a transaction ID and t.pushed equals True. You can also create an outgoing transactions with the HDWallet.send() method. To spent all available UTXO's and empty a wallet the sweep() method is used.
This concludes this brief overview of BitcoinLib. Check out the examples on GitHub or the documentation for further exploration.