Handcrafting Transactions¶
For those who wish to assemble transaction payloads “by hand”, with examples in Python.
Overview¶
Submitting a transaction to a BigchainDB node consists of three main steps:
- Preparing the transaction payload;
- Fulfilling the prepared transaction payload; and
- Sending the transaction payload via HTTPS.
Step 1 and 2 can be performed offline on the client. That is, they do not require any connection to any BigchainDB node.
For convenience’s sake, some utilites are provided to prepare and fulfill a
transaction via the BigchainDB
class, and via the
offchain
module. For an introduction on using these
utilities, see the Basic Usage Examples or Advanced Usage Examples sections.
The rest of this document will guide you through completing steps 1 and 2 manually by revisiting some of the examples provided in the usage sections. We will:
- provide all values, including the default ones;
- generate the transaction id;
- learn to use crypto-conditions to generate a condition that locks the transaction, hence protecting it from being consumed by an unauthorized user;
- learn to use crypto-conditions to generate a fulfillment that unlocks the transaction asset, and consequently enact an ownership transfer.
In order to perform all of the above, we’ll use the following Python libraries:
json
: to serialize the transaction dictionary into a JSON formatted string;- sha3: to hash the serialized transaction; and
- cryptoconditions: to create conditions and fulfillments
High-level view of a transaction in Python¶
For detailled documentation on the transaction schema, please consult The Transaction Model and The Transaction Schema.
From the point of view of Python, a transaction is simply a dictionary:
{
'operation': 'CREATE',
'asset': {
'data': {
'bicycle': {
'manufacturer': 'bkfab',
'serial_number': 'abcd1234'
}
}
},
'version': '1.0',
'outputs': [
{
'condition': {
'details': {
'public_key': '2GoYB8cMZQrUBZzx9BH9Bq92eGWXBy3oanDXbRK3YRpW',
'signature': None,
'type': 'ed25519-sha-256'
},
'uri': 'ni:///sha-256;1hBHivh6Nxhgi2b1ndUbP55ZlyUFdLC9BipPUBWth7U?fpt=ed25519-sha-256&cost=131072'
},
'public_keys': [
'2GoYB8cMZQrUBZzx9BH9Bq92eGWXBy3oanDXbRK3YRpW'
],
'amount': '1'
}
],
'inputs': [
{
'fulfills': None,
'owners_before': [
'2GoYB8cMZQrUBZzx9BH9Bq92eGWXBy3oanDXbRK3YRpW'
],
'fulfillment': {
'public_key': '2GoYB8cMZQrUBZzx9BH9Bq92eGWXBy3oanDXbRK3YRpW',
'signature': None,
'type': 'ed25519-sha-256'
}
}
],
'id': '52f8ec4d56b4187be256231ef11017038f316d16ef0a0d250e235d39e901f4d1',
'metadata': {
'planet': 'earth'
}
}
Because a transaction must be signed before being sent, the id
and
fulfillment
must be provided by the client.
Important
Implications of Signed Payloads
Because BigchainDB relies on cryptographic signatures, the payloads need to be fully prepared and signed on the client side. This prevents the server(s) from tampering with the provided data.
This enhanced security puts more work on the clients, as various values that could traditionally be generated on the server side need to be generated on the client side.
Bicycle Asset Creation Revisited¶
The Prepared Transaction¶
Recall that in order to prepare a transaction, we had to do something similar to:
In [1]: from bigchaindb_driver.crypto import generate_keypair
In [2]: from bigchaindb_driver.offchain import prepare_transaction
In [3]: alice = generate_keypair()
In [4]: bicycle = {
...: 'data': {
...: 'bicycle': {
...: 'serial_number': 'abcd1234',
...: 'manufacturer': 'bkfab',
...: },
...: },
...: }
...:
In [5]: metadata = {'planet': 'earth'}
In [6]: prepared_creation_tx = prepare_transaction(
...: operation='CREATE',
...: signers=alice.public_key,
...: asset=bicycle,
...: metadata=metadata,
...: )
...:
and the payload of the prepared transaction looked similar to:
In [7]: prepared_creation_tx
Out[7]:
{'asset': {'data': {'bicycle': {'manufacturer': 'bkfab',
'serial_number': 'abcd1234'}}},
'id': 'a39c16cbbedb8fd6603c81ede65433980ab50a1873534d9ed9e8afdc234513a7',
'inputs': [{'fulfillment': {'public_key': 'CpSvS5ChPYVeNMaZjyLqoL61wTYnaFnUVdbHYT4oMKkY',
'signature': None,
'type': 'ed25519-sha-256'},
'fulfills': None,
'owners_before': ['CpSvS5ChPYVeNMaZjyLqoL61wTYnaFnUVdbHYT4oMKkY']}],
'metadata': {'planet': 'earth'},
'operation': 'CREATE',
'outputs': [{'amount': '1',
'condition': {'details': {'public_key': 'CpSvS5ChPYVeNMaZjyLqoL61wTYnaFnUVdbHYT4oMKkY',
'signature': None,
'type': 'ed25519-sha-256'},
'uri': 'ni:///sha-256;TXer72LAWeE5WucyTk2X8peoRShB-kgXlKY-ULJmZAM?fpt=ed25519-sha-256&cost=131072'},
'public_keys': ['CpSvS5ChPYVeNMaZjyLqoL61wTYnaFnUVdbHYT4oMKkY']}],
'version': '1.0'}
Note alice
‘s public key is listed in the public keys of outputs
:
In [8]: alice.public_key
Out[8]: 'CpSvS5ChPYVeNMaZjyLqoL61wTYnaFnUVdbHYT4oMKkY'
In [9]: prepared_creation_tx['outputs'][0]['public_keys'][0] == alice.public_key