Input
Conceptually, an input is a pointer to an output of a previous transaction. It specifies to whom an asset belonged before and it provides a proof that the conditions required to transfer the ownership of that asset (e.g., a person needs to sign) are fulfilled. In a CREATE transaction, there is no previous owner, so an input in a CREATE transaction simply specifies who the person is that is registering the object (this is usually the same as the initial owner of the asset). In a TRANSFER transaction, an input contains a proof that the user is authorized to spend (transfer or update) this particular output. In practical terms, this means that with the input, a user is stating which asset (e.g., the bike) should be transferred. He also demonstrates that he or she is authorized to do the transfer of that asset.
Output
A transaction output specifies the conditions that need to be fulfilled to change the ownership of a specific asset. For instance: to transfer a bicycle, a person needs to sign the transaction with his or her private key. This also implicitly contains the information that the public key associated with that private key is the current owner of the asset.
The transaction can also have multiple outputs. These are called divisible assets. The output can also contain complex conditions (e.g., multiple signatures of multiple people) to acquire ownership.
Metadata
The metadata field allows users to add additional data to a transaction. This can be any type of data, like the age of a bicycle or the kilometers driven [11]. The good thing about the metadata is that it can be updated with every transaction. In contrast to the data in the asset field, the metadata field allows to add new information to every transaction.
Transaction ID
The ID of a transaction is a unique hash that identifies a transaction. It contains all the information about the transaction in a hashed way.
Getting Started
Begin by creating an object of class BigchainDB:
In [1]: frombigchaindb_driverimportBigchainDB
In [2]: bdb_root_url='https://example.com:9984'#Use YOUR
BigchainDB Root URL here
If the BigchainDB node or cluster doesn’t require authentication tokens, you can do: In [3]: bdb=BigchainDB(bdb_root_url)
If it does require authentication tokens, you can do put them in a diet like so:
In [4]: tokens={'app_id':'your_app_id','app_key':'your_app_key'} In [5]: bdb=BigchainDB(bdb_root_url, headers=tokens)
Digital Asset Definition
As an example, let’s consider the creation and transfer of a digital asset that represents a bicycle:
In [6]: bicycle={
...: 1 data':{
...: 1 bicycle':{
...: 1serial_number':'abcdl234',
...: 'manufacturer':'bkfab',
Suppose a bike belongs to Alice and that it will be transferred to Bob. In general, use any dictionary for the data property.
Metadata Definition (Optional)
You can optionally add metadata to a transaction. Any dictionary is accepted.
For example:
In [7]: metadata={'planetearth'}
Asset Creation
Create the digital asset. First, prepare the transaction:
In [10]: prepared_creation_tx=bdb.transactions.prepare(
....: operation=1 CREATE 1,
....: signers=alice,public_key,
....: asset=bicycle,
....: metadata=metadata,
----:)
The prepared_creation_tx dictionary should be similar to
In [11]: prepared_creation_tx Out[11]:
{'asset': {'data': {'bicycle': {'manufacturer': 'bkfab',
'serial_number': 'abcdl234'}}},
'id': None,
'inputs': [{'fulfillment': {'public_key':
' 7rr59L64LwwMKr8dtieotdugkl3oHkosFFoTuXCj zz6 ',
'type': 'ed255l9-sha-256'},
'fulfills': None,
'owners_before': ['7rr59L64LvvwMKr8dtieotdugkl3oHkosFFoTuXCj
zz6 ' ] }] ,
'metadata': {'planet': 'earth'},
'operation': 'CREATE',
'outputs': [{'amount': '1',
'condition': {'details': {'public_key': '7rr59L64LwwMKr8
dtieotdugkl3oHkosFFoTuXCjzz6' ,
'type': 'ed255l9-sha-256'},
'uri' : 'ni:///sha-56;e2COOfzLhNG_nLwWVmZOyGbIQ61qDCduYZy7HKPr2 3c?fpt=ed255l9-sha-256&cost=13l072'},
'public_keys': ['7rr59L64LwwMKr8dtieotdugkl3oHkosFFoTuXCj
ZZ6'1 }] ,
'version': '2.0'}
The transaction now needs to be fulfilled by signing it with Alice’s private key:
In [12]: fulfilled_creation_tx=bdb.transactions.fulfill(
....: prepared_creation_tx, private_keys=alice.private_key)
In [13]: fulfilled_creation_tx
Out[13]:
{'asset': {'data': {'bicycle': {'manufacturer': 'bkfab',
'serial_number': 'abcdl234'}}},
'id': 'bfe679e84f528410270fe67d309fl9d03afe09970650140621d
aedfe6f546598',
1 inputs 1: [{1 fulfillment1: 1pGSAIGXrEQNtYgyzOBswGHnoWgbU
WFWVE89Kez 25zti91PTzgUDY8sy8E9FtHw09-nab89wBspemhBCshbbRR JqLlqilTQ7Hb JZYMfr8_mawfTDre3lpJEgLSrqFU-clmJjkrwUO1,
- 1 fulfills 1: None,
- 1owners_before1: [17rr59L64LwwMKr8dtieotdugkl3oHkosFFoTuXCj zz6 1 ] }] ,
'metadata': {'planet': 'earth'},
'operation' : 'CREATE',
'outputs': [{'amount': '1',
'condition': {'details': {'public_key':
' 7rr59L64LwwMKr8dtieotdugkl3oHkosFFoTuXCj zz6 ',
'type': 'ed255l9-sha-256'},
'uri' : 'ni:///sha-2 56;e2C00f zLhNG_nLwWVmZOyGbIQ61qDCduYZy7HK
Pr23c?fpt=ed25519-sha-256&cost=131072'},
'public_keys': ['7rr59L64LwwMKr8dtieotdugkl3oHkosFFo
Tuxcjzz6' ] }] ,
'version': '2.0'}
And sent over to a BigchainDB node:
>>>sent_creation_tx=bdb.transactions.send_commit (fulfilled_creation_tx)
Note that the response from the node should be the same as that which was sent:
>>>sent_creation_tx==fulf illed_creation_tx True
Notice the transaction id:
In [14]: txid=fulfilled_creation_tx['id']
In [15] : txid
Out [15] : 'bfe679e84f528410270fe67d3 0 9fl9d03afe09970650140621
daedfe6f546598'
Check if the Transaction was sent successfully
After a couple of seconds, we can check if the transaction was included in a block.
# Retrieve the block height
>>>block_height=bdb.blocks.get(txid=signed_tx['id'])
This will return the block height containing the transaction. If the transaction is not in any block then None is returned. If it is None, it can have different reasons for example the transaction was not valid or is still in the queue and you can try again later. If the transaction was invalid or could not be sent an exception is raised.
If we want to see the whole block we can use the block height to retrieve the block itself.
# Retrieve the block that contains the transaction >>>block=bdb.blocks.retrieve(str(block_height))
Asset Transfer
Imagine some time goes by, during which Alice is happy with her bicycle, and one day she meets Bob, who is interested in acquiring her bicycle. The timing is good for Alice as she wanted to get a new bicycle [12].
To transfer the bicycle (asset) to Bob, Alice must consume the transaction in which the Bicycle asset was created.
Alice could retrieve the transaction:
>>>creation_tx=bdb.transactions.retrieve(txid)
or simply use fulfilled_creation_tx :
In [16]:creation_tx=fulfilled_creation_tx
In order to prepare the transfer transaction, we first need to know the id of the asset we’ll be transferring. Here, because Alice is consuming a |CREATE| transaction, we have a special case in that the asset id is NOT found on the asset | itself, but is simply the |CREATE| transaction’s id:
In [17]:asset_id=creation_tx['id']
In [18]:transfer_asset={
....: 'id1:asset_id,
....:}
Let’s now prepare the transfer transaction:
In [19]: output_index=0
In [20]: output=creation_tx['outputs'][output_index]
In [21]: transfer_input={
....: 'fulfillment':output['condition'] ['details 1],
. . . . : 'fulfills' : {
....: 1output_index1:output_index,
....: 1transaction_id1:creation_tx['id'],
. . . . : 'owners_before':output['public_keys ' ],
....:}
In [22]: prepared_transfer_tx=bdb.transactions.prepare(
....: operation='TRANSFER',
....: asset=transfer_asset,
....: inputs=transfer_input,
....: recipients=bob.public_key,
----:)
fulfill it:
In [23]: fulfilled_transfer_tx=bdb.transactions.fulfill(
....: prepared_transfer_tx,
....: private_keys=alice.private_key,
----:)
The fulfilled_transfer_tx dictionary should look something like:
In [24] : fulfilled_transfer_tx Out[24]:
{'asset': {'id': 'bfe679e84f5284l0270fe67d309fI9d03afe09
97065014062ldaedfe6f546598'} ,
'id': 'fb745cf6b645f5a20a82a4ladef835ce66913cc6ldd0lb2830085d
7776855520',
'inputs': [{'fulfillment': 'pGSAIGXrEQNtYgyzOBswGHno
WgbUWFWVE 89Kez25zti9lPTzgUBuHqMbJO 6C2vm9aBcaz90 CBXpg PZ-FQPrFLeGURN77an7Ej YXGvLGeUhf-fTB9uZvpYs6utzhZPt 24rdSJJXQC',
'fulfills': {'output_index': 0,
'transaction_id1:
'bfe679e84f528410270fe67d309fl9d03afe09970 650140621 daedfe6f546598 '},
1owners_before': ['7rr59L64LwwMKr8dtieotdugkl3oHkos
FFoTuXCj ZZ6'1 }] ,
'metadata': None,
'operation': 'TRANSFER',
'outputs': [{'amount': '1',
'condition': {'details': {'public_key': 'SRaeQAZEEQMFCLBEDr Ktk5p Lue4SuVkdw9Xb55ucpvQ6',
'type': 'ed255l9-sha-2561},
'uri': 'ni:///sha-256;UKI3FbYGLQhOeu3QkZNuoWrjxGugzG4q7LxnhV qevHU? fpt=ed255l9-sha-256&cost=13l072' },
1public_keys1: ['5RaeQAZEEQMFCLBEDrKtk5pLue4SuVkdw9Xb55
ucpvQ6']}],
'version': '2.0'}
and finally, send it to the connected BigchainDB node:
>>>sent_transfer_tx=bdb.transactions.
send_commit(fulfilled_transfer_tx)
>>>sent_transfer_tx==fulfilled_transfer_tx
True
Bob is the new owner:
In [25]: fulfilled_transfer_tx['outputs'][0]['public_keys']
[0]==bob.public_key Out[25]:True
Alice is the former owner:
In [26]: fulfilled_transfer_tx['inputs'][0] ['owners_before']
[0]==alice,public_key Out[26]:True
Note:
Obtaining asset ids:
You might have noticed that we considered Alice’s case of consuming a | CREATE transaction as a special case. In order to obtain the asset id of a | CREATE] transaction, we had to use the CREATE] transaction’s id:
transfer_asset_id=create_tx['id']
If you instead wanted to consume TRANSFER] transactions (e.g., fulfilled_transfer_tx|), you could obtain the asset id to transfer from the asset[‘id’] property:
transfer_asset_id=transfer_tx['asset']['id']