bitcoinlib-10-minutes


Learn BitcoinLib in 10 minutes

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

Keys

With the Key class you can create and manage private and public cryptographic keys.

Run the code below to create a random key, then show the class data with the info() method which is handy for developing and debugging your code.


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'))

HDKey

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())

A Mnemonic sentence is an easy way for us humans to read and exchange private keys. With bitcoinlib you can create Mnemonic phrases and use them to create keys or wallets.


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

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()

Service providers

The library can get transaction and blockchain information from various online or locally installed providers. So you can get check if a transaction output (UTXO) is spent already, update a wallet's address balance or push a new transaction to the network.


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)

Wallets

The HDWallet class combines all classes we've seen so far. The wallet class is used to store and manage keys, check for new utxo's, create, sign and send transactions.

Creating a bitcoin wallet is very easy, just use the create() method and pass a wallet name.


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)


Deposit to address tb1qxedjhe6ckzl4mf60adzknuh4336hazrea9rgjh to get started

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()


=== WALLET ===
 ID                             1
 Name                           bitcoinlib-testnet1
 Owner                          
 Scheme                         bip32
 Multisig                       False
 Witness type                   segwit
 Main network                   testnet
 Latest update                  2020-01-16 21:40:44.947327

= Wallet Master Key =
 ID                             1
 Private                        True
 Depth                          0

- NETWORK: testnet -
- - Keys
    6 m/84'/1'/0'/0/0              tb1q6qsl0gxys42tp85nuqs69q7ye23n7sslac8gqc    address index 0                     0.00000000 TBTC
    7 m/84'/1'/0'/0/1              tb1qxgjgla2dgw0ngheh89vjfs4etz8xl5m86xgupt    address index 1                     0.00000000 TBTC
    8 m/84'/1'/0'/0/2              tb1qppduulzc9wfwy9nexyr60q04ed0fr6vzv5jgut    address index 2                     0.00000000 TBTC
    9 m/84'/1'/0'/0/3              tb1q48cauqexy5zs20sesrdyduqavlj9jvghw7mmz0    address index 3                     0.00000000 TBTC
   10 m/84'/1'/0'/0/4              tb1qhdsccs0pav4gg5ssnncakd4w4uf94galt6yjkf    address index 4                     0.00000000 TBTC
   11 m/84'/1'/0'/0/5              tb1qxedjhe6ckzl4mf60adzknuh4336hazrea9rgjh    address index 5                     0.00010000 TBTC

- - Transactions Account 0 (1)
3963a36d204d5eb1913734ba83b9e82df24ec73f581ee4d24ee716edb822e3d1 tb1qxedjhe6ckzl4mf60adzknuh4336hazrea9rgjh        5         10000 U 

= Balance Totals (includes unconfirmed) =
testnet              (Account 0)               0.00010000 TBTC


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()


Transaction b8f3072a0768e2ae91407e4f2817008d41e7ba1dbb8ce911b320c13bec78d9c9
Date: None
Network: testnet
Version: 1
Witness type: segwit
Status: unconfirmed
Verified: True
Inputs
- tb1qxedjhe6ckzl4mf60adzknuh4336hazrea9rgjh 10000 3963a36d204d5eb1913734ba83b9e82df24ec73f581ee4d24ee716edb822e3d1 0
  segwit sig_pubkey; sigs: 1 (1-of-1) valid
Outputs
- tb1qxgjgla2dgw0ngheh89vjfs4etz8xl5m86xgupt 4000 p2wpkh U
- tb1q7tega57rca4lpa7p0x5nvgu892azrkq97t37qm 5000 p2wpkh U
Size: 141
Vsize: 141
Fee: 1000
Confirmations: 0
Pushed to network: True
Wallet: bitcoinlib-testnet1


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.