Advanced Usage Examples¶
This section has examples of using the Python Driver for more advanced use cases such as escrow.
Todo
This is work in progress. More examples will gradually appear as issues like
are taken care of.
Getting Started¶
First, make sure you have RethinkDB and BigchainDB Server installed and running, e.g.:
$ rethinkdb --daemon
$ bigchaindb configure
$ bigchaindb start
Don’t shut them down!
Next, make sure you’ve installed the bigchaindb_driver Python package. Then, in a new terminal, run an IPython shell:
$ ipython
Make sure it’s Python 3.
Now we can import the BigchainDB
class and create
an instance:
In [1]: from bigchaindb_driver import BigchainDB
In [2]: bdb = BigchainDB()
This instantiates an object bdb
of class
BigchainDB
. When instantiating a
BigchainDB
object without arguments (as above), it
uses the default BigchainDB Root URL Endpoint http://localhost:9984
.
If you want to connect to something other than a BigchainDB node on localhost, see the page about other connection options.
Create a Digital Asset¶
At a high level, a “digital asset” is something which can be represented digitally and can be assigned to a user. In BigchainDB, users are identified by their public key, and the data payload in a digital asset is represented using a generic Python dict.
In BigchainDB, digital assets can be created by doing a special kind of
transaction: a CREATE
transaction.
In [3]: from bigchaindb_driver.crypto import generate_keypair
Create a test user: alice
In [4]: alice = generate_keypair()
Define a digital asset data payload
In [5]: digital_asset_payload = {'data': {'msg': 'Hello BigchainDB!'}}
In [6]: tx = bdb.transactions.prepare(operation='CREATE',
...: signers=alice.public_key,
...: asset=digital_asset_payload)
...:
All transactions need to be signed by the user creating the transaction.
In [7]: signed_tx = bdb.transactions.fulfill(tx, private_keys=alice.private_key)
In [8]: signed_tx
Out[8]:
{'asset': {'data': {'msg': 'Hello BigchainDB!'}},
'id': 'b5b9dfc981f6ae61fcebe95e99f9c1a452e398f89b2b5c453f83e9fd2af81d64',
'inputs': [{'fulfillment': 'cf:4:ALKIDJM7PnkGpLomYp_l4RNY6mwsEQEJ_pQv0XnobPVIqKsEP96N4LqIDJ8-rZbjlJmFyTuSYmv-vFajU6PLlMdNoN1J7ga-bjeanyO7x2CJqasAHe3cHsqxllh0ErsE',
'fulfills': None,
'owners_before': ['13itro2dJCVM8UaH3rokCMnP7WBSaFyHGpxp2EUVE6yi']}],
'metadata': None,
'operation': 'CREATE',
'outputs': [{'amount': 1,
'condition': {'details': {'bitmask': 32,
'public_key': '13itro2dJCVM8UaH3rokCMnP7WBSaFyHGpxp2EUVE6yi',
'signature': None,
'type': 'fulfillment',
'type_id': 4},
'uri': 'cc:4:20:ALKIDJM7PnkGpLomYp_l4RNY6mwsEQEJ_pQv0XnobPU:96'},
'public_keys': ['13itro2dJCVM8UaH3rokCMnP7WBSaFyHGpxp2EUVE6yi']}],
'version': '0.9'}
Write the transaction to BigchainDB. The transaction will be stored in a backlog where it will be validated before being included in a block.
>>> sent_tx = bdb.transactions.send(signed_tx)
Note that the transaction payload returned by the BigchainDB node is equivalent to the signed transaction payload.
>>> sent_tx == signed_tx
True
Read the Creation Transaction from the DB¶
After a couple of seconds, we can check if the transaction was validated in a block:
# Retrieve a validated transaction
>>> tx_retrieved = bdb.transactions.retrieve(tx['id'])
The new owner of the digital asset is now Alice (or more correctly, her public key):
In [9]: alice.public_key
Out[9]: '13itro2dJCVM8UaH3rokCMnP7WBSaFyHGpxp2EUVE6yi'
Transfer the Digital Asset¶
Now that alice
has a digital asset assigned to her, she can transfer it to
another person. Transfer transactions require an input. The input will be the
transaction id of a digital asset that was assigned to alice
, which in our
case is
In [10]: signed_tx['id']
Out[10]: 'b5b9dfc981f6ae61fcebe95e99f9c1a452e398f89b2b5c453f83e9fd2af81d64'
BigchainDB makes use of the crypto-conditions library
to cryptographically lock and unlock transactions. The locking script is
referred to as a condition
(put inside an “output”) and a corresponding
fulfillment
(put inside an “input”) unlocks the output condition of an
input_tx
.
Since a transaction can have multiple outputs each with their own
(crypto)condition, each transaction input is required to refer to the output
condition that they fulfill via fulfills['output']
.
In order to prepare a transfer transaction, Alice needs to provide at least three things:
inputs
– one or more fulfillments that fulfill a prior transaction’s output conditions.asset['id']
– the id of the asset being transferred.- Recipient
public_keys
– one or more public keys representing the new recipients(s).
To construct the input:
In [11]: output_index = 0
In [12]: output = tx['outputs'][output_index]
In [13]: input_ = {
....: 'fulfillment': output['condition']['details'],
....: 'fulfills': {
....: 'output': output_index,
....: 'txid': tx['id'],
....: },
....: 'owners_before': output['public_keys'],
....: }
....:
The asset in a TRANSFER
transaction must be a dictionary with an id
key
denoting the asset to transfer. This asset id is either the id of the
CREATE
transaction of the asset (as it is in this case), or is the
asset['id']
property in a TRANSFER
transaction (note that this value
simply points to the id of the asset’s CREATE
transaction):
In [14]: transfer_asset_id = tx['id']
In [15]: transfer_asset = {
....: 'id': transfer_asset_id,
....: }
....:
Create a second test user, bob
:
In [16]: bob = generate_keypair()
In [17]: bob.public_key
Out[17]: 'EapHr9a658oZf5MpZwKke3EKkDdDzAffH2DjGrGvFj5X'
And prepare the transfer transaction:
In [18]: tx_transfer = bdb.transactions.prepare(
....: operation='TRANSFER',
....: inputs=input_,
....: asset=transfer_asset,
....: recipients=bob.public_key,
....: )
....:
The tx_transfer
dictionary should look something like:
In [19]: tx_transfer
Out[19]:
{'asset': {'id': 'b5b9dfc981f6ae61fcebe95e99f9c1a452e398f89b2b5c453f83e9fd2af81d64'},
'id': 'b67b4b36cfd49409a5f6d17337037a7eb90c1d19733ad570da5456ef2dd462db',
'inputs': [{'fulfillment': {'bitmask': 32,
'public_key': '13itro2dJCVM8UaH3rokCMnP7WBSaFyHGpxp2EUVE6yi',
'signature': None,
'type': 'fulfillment',
'type_id': 4},
'fulfills': {'output': 0,
'txid': 'b5b9dfc981f6ae61fcebe95e99f9c1a452e398f89b2b5c453f83e9fd2af81d64'},
'owners_before': ['13itro2dJCVM8UaH3rokCMnP7WBSaFyHGpxp2EUVE6yi']}],
'metadata': None,
'operation': 'TRANSFER',
'outputs': [{'amount': 1,
'condition': {'details': {'bitmask': 32,
'public_key': 'EapHr9a658oZf5MpZwKke3EKkDdDzAffH2DjGrGvFj5X',
'signature': None,
'type': 'fulfillment',
'type_id': 4},
'uri': 'cc:4:20:ydGReMeiCAA196grv0491mZB5WFEIuEvihFPwVY-RY4:96'},
'public_keys': ['EapHr9a658oZf5MpZwKke3EKkDdDzAffH2DjGrGvFj5X']}],
'version': '0.9'}
Notice, bob
‘s public key, appearing in the above dict
.
In [20]: tx_transfer['outputs'][0]['public_keys'][0]
Out[20]: 'EapHr9a658oZf5MpZwKke3EKkDdDzAffH2DjGrGvFj5X'
In [21]: bob.public_key