BigchainDB Python Driver¶
BigchainDB Python Driver¶
- Free software: Apache Software License 2.0
- Documentation: https://docs.bigchaindb.com/projects/py-driver/
Features¶
- Support for preparing, fulfilling, and sending transactions to a BigchainDB node.
- Retrieval of transactions by id.
- Getting status of a transaction by id.
Compatibility Matrix¶
BigchainDB Server | BigchainDB Driver |
---|---|
>= 0.8.2 |
>= 0.1.3 |
>= 0.9.1 |
0.2.x |
== 1.0.0rc1 |
0.3.x |
>= 1.0.0 |
0.4.x |
Although we do our best to keep the master branches in sync, there may be occasional delays.
Credits¶
This package was initially created using Cookiecutter and the audreyr/cookiecutter-pypackage project template. Many BigchainDB developers have contributed since then.
Quickstart / Installation¶
The BigchainDB Python Driver depends on:
libffi/ffi.h
libssl-dev
- Python 3.5+
- A recent Python 3 version of
pip
- A recent Python 3 version of
setuptools
If you’re missing one of those, then see below. Otherwise, you can install the BigchainDB Python Driver (bigchaindb_driver
) using:
$ pip3 install bigchaindb_driver
Next: determine the BigchainDB Root URL of the BigchainDB node or cluster you want to connect to.
How to Install the Dependencies¶
Dependency 1: ffi.h¶
BigchainDB (server and driver) depends on cryptoconditions,
which depends on PyNaCl (Networking and Cryptography library),
which depends on ffi.h
.
Hence, depending on your setup, you may need to install the
development files for libffi
.
On Ubuntu 14.04 and 16.04, this works:
$ sudo apt-get update
$ sudo apt-get install libffi-dev
On Fedora 23 and 24, this works:
$ sudo dnf update
$ sudo dnf install libffi-devel
For other operating systems, just do some web searches for “ffi.h” with the name of your OS.
Dependency 2: libssl-dev¶
BigchainDB (server and driver) also depends on cryptography, which in turn depends on libssl AND libcrypto. Hence, depending on your setup you need to install the libssl-dev (Ubuntu) OR openssl-devel (RHEL) package, which installs the development libraries and header files for libssl and libcrypto.
On Ubuntu 14.04 and 16.04:
$ sudo apt-get install libssl-dev
On Fedora 23 and 24:
$ sudo dnf install openssl-devel
Dependency 3: Python 3.5+¶
The BigchainDB Python Driver uses Python 3.5+. You can check your version of Python using:
$ python --version
OR
$ python3 --version
An easy way to install a specific version of Python, and to switch between versions of Python, is to use virtualenv. Another option is conda.
Dependency 4: pip¶
You also need to get a recent, Python 3 version of pip
, the Python package manager.
If you’re using virtualenv or conda, then each virtual environment should include an appropriate version of pip
.
You can check your version of pip
using:
$ pip --version
OR
$ pip3 --version
pip
was at version 9.0.0 as of November 2016.
If you need to upgrade your version of pip
,
then see the pip documentation
or our page about that in the BigchainDB Server docs.
Dependency 5: setuptools¶
Once you have a recent Python 3 version of pip
, you should be able to upgrade setuptools
using:
$ pip install --upgrade setuptools
OR
$ pip3 install --upgrade setuptools
Installing the Driver¶
Now you can install the BigchainDB Python Driver (bigchaindb_driver
) using:
$ pip install bigchaindb_driver
OR
$ pip3 install bigchaindb_driver
Next: determine the BigchainDB Root URL of the BigchainDB node or cluster you want to connect to.
Advanced Installation Options¶
See the Advanced Installation Options page.
Determine the BigchainDB Root URL¶
If you want to use the BigchainDB Python Driver to communicate with a BigchainDB node or cluster, then you will need its BigchainDB Root URL. This page is to help you determine it.
Case 1: BigchainDB on localhost¶
If a BigchainDB node is running locally
(and the BIGCHAINDB_SERVER_BIND
setting wasn’t changed
from the default localhost:9984
),
then the BigchainDB Root URL is:
bdb_root_url = 'http://localhost:9984'
Case 2: A Cluster Hosted by Someone Else¶
If you’re connecting to a BigchainDB cluster hosted by someone else, then they’ll tell you their BigchaindB Root URL. It can take many forms. It can use HTTP or HTTPS. It can use a hostname or an IP address. The port might not be 9984. Here are some examples:
bdb_root_url = 'http://example.com:9984'
bdb_root_url = 'http://api.example.com:9984'
bdb_root_url = 'http://example.com:1234'
bdb_root_url = 'http://example.com' # http is port 80 by default
bdb_root_url = 'https://example.com' # https is port 443 by default
bdb_root_url = 'http://12.34.56.123:9984'
bdb_root_url = 'http://12.34.56.123:5000'
Case 3: Docker Container on localhost¶
If you are running the Docker-based dev setup that comes along with the
bigchaindb_driver repository (see Development Environment with Docker for more
information), and wish to connect to it from the bdb-driver
linked
(container) service, use:
bdb_root_url = 'http://bdb-server:9984'
Alternatively, you may connect to the containerized BigchainDB node from “outside”, in which case you need to know the port binding:
$ docker-compose port bdb-server 9984
0.0.0.0:32780
bdb_root_url = 'http://0.0.0.0:32780'
Next, try some of the basic usage examples.
Basic Usage Examples¶
For the examples on this page, we assume you’re using a Python 3 version of IPython (or similar), you’ve installed the bigchaindb_driver Python package, and you have determined the BigchainDB Root URL of the node or cluster you want to connect to.
Getting Started¶
We begin by creating an object of class BigchainDB:
In [1]: from bigchaindb_driver import BigchainDB
In [1]: 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 [1]: bdb = BigchainDB(bdb_root_url)
If it does require authentication tokens, you can do put them in a dict like so:
In [1]: tokens = {'app_id': 'your_app_id', 'app_key': 'your_app_key'}
In [1]: 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 [1]: bicycle = {
...: 'data': {
...: 'bicycle': {
...: 'serial_number': 'abcd1234',
...: 'manufacturer': 'bkfab',
...: },
...: },
...: }
...:
We’ll suppose that the bike belongs to Alice, and that it will be transferred to Bob.
In general, you may 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 [1]: metadata = {'planet': 'earth'}
Cryptographic Identities Generation¶
Alice and Bob are represented by public/private key pairs. The private key is used to sign transactions, meanwhile the public key is used to verify that a signed transaction was indeed signed by the one who claims to be the signee.
In [1]: from bigchaindb_driver.crypto import generate_keypair
In [1]: alice, bob = generate_keypair(), generate_keypair()
Asset Creation¶
We’re now ready to create the digital asset. First, let’s prepare the transaction:
In [1]: prepared_creation_tx = bdb.transactions.prepare(
...: operation='CREATE',
...: signers=alice.public_key,
...: asset=bicycle,
...: metadata=metadata,
...: )
...:
The prepared_creation_tx
dictionary should be similar to:
In [1]: prepared_creation_tx
The transaction now needs to be fulfilled by signing it with Alice’s private key:
In [1]: fulfilled_creation_tx = bdb.transactions.fulfill(
...: prepared_creation_tx, private_keys=alice.private_key)
...:
In [1]: fulfilled_creation_tx
And sent over to a BigchainDB node:
>>> sent_creation_tx = bdb.transactions.send(fulfilled_creation_tx)
Note that the response from the node should be the same as that which was sent:
>>> sent_creation_tx == fulfilled_creation_tx
True
Notice the transaction id
:
In [1]: txid = fulfilled_creation_tx['id']
In [1]: txid
To check the status of the transaction:
>>> bdb.transactions.status(txid)
Note
It may take a small amount of time before a BigchainDB cluster confirms a transaction as being valid.
Here’s some code that keeps checking the status of the transaction until it is valid:
>>> trials = 0
>>> while trials < 100:
... try:
... if bdb.transactions.status(txid).get('status') == 'valid':
... break
... except bigchaindb_driver.exceptions.NotFoundError:
... trials += 1
>>> bdb.transactions.status(txid)
{'status': 'valid'}
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 had been wanting to get a new bicycle.
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 [1]: 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 [1]: asset_id = creation_tx['id']
In [1]: transfer_asset = {
...: 'id': asset_id,
...: }
...:
Let’s now prepare the transfer transaction:
In [1]: output_index = 0
In [1]: output = creation_tx['outputs'][output_index]
In [1]: transfer_input = {
...: 'fulfillment': output['condition']['details'],
...: 'fulfills': {
...: 'output_index': output_index,
...: 'transaction_id': creation_tx['id'],
...: },
...: 'owners_before': output['public_keys'],
...: }
...:
In [1]: prepared_transfer_tx = bdb.transactions.prepare(
...: operation='TRANSFER',
...: asset=transfer_asset,
...: inputs=transfer_input,
...: recipients=bob.public_key,
...: )
...:
fulfill it:
In [1]: fulfilled_transfer_tx = bdb.transactions.fulfill(
...: prepared_transfer_tx,
...: private_keys=alice.private_key,
...: )
...:
and finally send it to the connected BigchainDB node:
>>> sent_transfer_tx = bdb.transactions.send(fulfilled_transfer_tx)
>>> sent_transfer_tx == fulfilled_transfer_tx
True
The fulfilled_transfer_tx
dictionary should look something like:
In [1]: fulfilled_transfer_tx
Bob is the new owner:
In [1]: fulfilled_transfer_tx['outputs'][0]['public_keys'][0] == bob.public_key
Alice is the former owner:
In [1]: fulfilled_transfer_tx['inputs'][0]['owners_before'][0] == alice.public_key
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 (for example,
fulfilled_transfer_tx
), you could obtain the asset id to transfer from
the asset['id']
property:
transfer_asset_id = transfer_tx['asset']['id']
Recap: Asset Creation & Transfer¶
from bigchaindb_driver import BigchainDB
from bigchaindb_driver.crypto import generate_keypair
from time import sleep
from sys import exit
alice, bob = generate_keypair(), generate_keypair()
bdb_root_url = 'https://example.com:9984' # Use YOUR BigchainDB Root URL here
bdb = BigchainDB(bdb_root_url)
bicycle_asset = {
'data': {
'bicycle': {
'serial_number': 'abcd1234',
'manufacturer': 'bkfab'
},
},
}
bicycle_asset_metadata = {
'planet': 'earth'
}
prepared_creation_tx = bdb.transactions.prepare(
operation='CREATE',
signers=alice.public_key,
asset=bicycle_asset,
metadata=bicycle_asset_metadata
)
fulfilled_creation_tx = bdb.transactions.fulfill(
prepared_creation_tx,
private_keys=alice.private_key
)
sent_creation_tx = bdb.transactions.send(fulfilled_creation_tx)
txid = fulfilled_creation_tx['id']
trials = 0
while trials < 60:
try:
if bdb.transactions.status(txid).get('status') == 'valid':
print('Tx valid in:', trials, 'secs')
break
except bigchaindb_driver.exceptions.NotFoundError:
trials += 1
sleep(1)
if trials == 60:
print('Tx is still being processed... Bye!')
exit(0)
asset_id = txid
transfer_asset = {
'id': asset_id
}
output_index = 0
output = fulfilled_creation_tx['outputs'][output_index]
transfer_input = {
'fulfillment': output['condition']['details'],
'fulfills': {
'output_index': output_index,
'transaction_id': fulfilled_creation_tx['id']
},
'owners_before': output['public_keys']
}
prepared_transfer_tx = bdb.transactions.prepare(
operation='TRANSFER',
asset=transfer_asset,
inputs=transfer_input,
recipients=bob.public_key,
)
fulfilled_transfer_tx = bdb.transactions.fulfill(
prepared_transfer_tx,
private_keys=alice.private_key,
)
sent_transfer_tx = bdb.transactions.send(fulfilled_transfer_tx)
print("Is Bob the owner?",
sent_transfer_tx['outputs'][0]['public_keys'][0] == bob.public_key)
print("Was Alice the previous owner?",
fulfilled_transfer_tx['inputs'][0]['owners_before'][0] == alice.public_key)
Transaction Status¶
Using the id
of a transaction, its status can be obtained:
>>> bdb.transactions.status(creation_tx['id'])
{'status': 'valid'}
Handling cases for which the transaction id
may not be found:
import logging
from bigchaindb_driver import BigchainDB
from bigchaindb_driver.exceptions import NotFoundError
logger = logging.getLogger(__name__)
logging.basicConfig(format='%(asctime)-15s %(status)-3s %(message)s')
bdb_root_url = 'https://example.com:9984' # Use YOUR BigchainDB Root URL here
bdb = BigchainDB(bdb_root_url)
txid = '12345'
try:
status = bdb.transactions.status(txid)
except NotFoundError as e:
logger.error('Transaction "%s" was not found.',
txid,
extra={'status': e.status_code})
Running the above code should give something similar to:
2016-09-29 15:06:30,606 404 Transaction "12345" was not found.
Divisible Assets¶
All assets in BigchainDB become implicitly divisible if a transaction contains more than one of that asset (we’ll see how this happens shortly).
Let’s continue with the bicycle example. Bob is now the proud owner of the bicycle and he decides he wants to rent the bicycle. Bob starts by creating a time sharing token in which one token corresponds to one hour of riding time:
In [1]: bicycle_token = {
...: 'data': {
...: 'token_for': {
...: 'bicycle': {
...: 'serial_number': 'abcd1234',
...: 'manufacturer': 'bkfab'
...: }
...: },
...: 'description': 'Time share token. Each token equals one hour of riding.',
...: },
...: }
...:
Bob has now decided to issue 10 tokens and assigns them to Carly. Notice how we
denote Carly as receiving 10 tokens by using a tuple:
([carly.public_key], 10)
.
In [1]: bob, carly = generate_keypair(), generate_keypair()
In [1]: prepared_token_tx = bdb.transactions.prepare(
...: operation='CREATE',
...: signers=bob.public_key,
...: recipients=[([carly.public_key], 10)],
...: asset=bicycle_token,
...: )
...:
In [1]: fulfilled_token_tx = bdb.transactions.fulfill(
...: prepared_token_tx, private_keys=bob.private_key)
...:
Sending the transaction:
>>> sent_token_tx = bdb.transactions.send(fulfilled_token_tx)
>>> sent_token_tx == fulfilled_token_tx
True
Note
Defining recipients
:
To create divisible assets, we need to specify an amount >1
together
with the public keys. The way we do this is by passing a list
of
tuples
in recipients
where each tuple
corresponds to an output.
For instance, instead of creating a transaction with one output containing
amount=10
we could have created a transaction with two outputs each
holding amount=5
:
recipients=[([carly.public_key], 5), ([carly.public_key], 5)]
The reason why the addresses are contained in lists
is because each
output can have multiple recipients. For instance, we can create an
output with amount=10
in which both Carly and Alice are recipients
(of the same asset):
recipients=[([carly.public_key, alice.public_key], 10)]
The fulfilled_token_tx
dictionary should look something like:
In [1]: fulfilled_token_tx
Bob is the issuer:
In [1]: fulfilled_token_tx['inputs'][0]['owners_before'][0] == bob.public_key
Carly is the owner of 10 tokens:
In [1]: fulfilled_token_tx['outputs'][0]['public_keys'][0] == carly.public_key
In [1]: fulfilled_token_tx['outputs'][0]['amount'] == '10'
Now in possession of the tokens, Carly wants to ride the bicycle for two hours. To do so, she needs to send two tokens to Bob:
In [1]: output_index = 0
In [1]: output = prepared_token_tx['outputs'][output_index]
In [1]: transfer_input = {
...: 'fulfillment': output['condition']['details'],
...: 'fulfills': {
...: 'output_index': output_index,
...: 'transaction_id': prepared_token_tx['id'],
...: },
...: 'owners_before': output['public_keys'],
...: }
...:
In [1]: transfer_asset = {
...: 'id': prepared_token_tx['id'],
...: }
...:
In [1]: prepared_transfer_tx = bdb.transactions.prepare(
...: operation='TRANSFER',
...: asset=transfer_asset,
...: inputs=transfer_input,
...: recipients=[([bob.public_key], 2), ([carly.public_key], 8)]
...: )
...:
In [1]: fulfilled_transfer_tx = bdb.transactions.fulfill(
...: prepared_transfer_tx, private_keys=carly.private_key)
...:
>>> sent_transfer_tx = bdb.transactions.send(fulfilled_transfer_tx)
>>> sent_transfer_tx == fulfilled_transfer_tx
True
Notice how Carly needs to reassign the remaining eight tokens to herself if she wants to only transfer two tokens (out of the available 10) to Bob. BigchainDB ensures that the amount being consumed in each transaction (with divisible assets) is the same as the amount being output. This ensures that no amounts are lost.
Also note how, because we were consuming a TRANSFER
transaction, we were
able to directly use the TRANSFER
transaction’s asset
as the new
transaction’s asset
because it already contained the asset’s id.
The fulfilled_transfer_tx
dictionary should have two outputs, one with
amount='2'
and the other with amount='8'
:
In [1]: fulfilled_transfer_tx
Querying for Assets¶
BigchainDB allows you to query for assets using simple text search. This search is applied to all the strings inside the asset payload and returns all the assets that match a given text search string.
Let’s assume that we created 3 assets that look like this:
In [1]: assets = [
...: {'data': {'msg': 'Hello BigchainDB 1!'}},
...: {'data': {'msg': 'Hello BigchainDB 2!'}},
...: {'data': {'msg': 'Hello BigchainDB 3!'}}
...: ]
...:
Let’s perform a text search for all assets that contain the word bigchaindb
:
>> bdb.assets.get(search='bigchaindb')
[
{
'data': {'msg': 'Hello BigchainDB 1!'},
'id': '7582d7a81652d0230fefb47dafc360ff09b2c2566b68f05c3a004d57e7fe7610'
},
{
'data': {'msg': 'Hello BigchainDB 2!'},
'id': 'e40f4b6ac70b9c1b3b237ec13f4174384fd4d54d36dfde25520171577c49caa4'
},
{
'data': {'msg': 'Hello BigchainDB 3!'},
'id': '748f6c30daaf771c9020d84db9ad8ac4d1f7c8de7013db55e16f10ba090f7013'
}
]
This call returns all the assets that match the string bigchaindb
, sorted
by text score,
as well as the asset id
. This is the same id
of the transaction that
created the asset.
It’s also possible to limit the amount of returned results using the limit
argument:
>> bdb.assets.get(search='bigchaindb', limit=2)
[
{
'data': {'msg': 'Hello BigchainDB 1!'},
'id': '7582d7a81652d0230fefb47dafc360ff09b2c2566b68f05c3a004d57e7fe7610'
},
{
'data': {'msg': 'Hello BigchainDB 2!'},
'id': 'e40f4b6ac70b9c1b3b237ec13f4174384fd4d54d36dfde25520171577c49caa4'
}
]
Advanced Usage Examples¶
This page 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.
For the examples on this page, we assume you’re using a Python 3 version of IPython (or similar), you’ve installed the bigchaindb_driver Python package, and you have determined the BigchainDB Root URL of the node or cluster you want to connect to.
Getting Started¶
We begin by creating an object of class BigchainDB:
In [1]: from bigchaindb_driver import BigchainDB
In [2]: bdb_root_url = 'https://example.com:9984' # Use YOUR BigchainDB Root URL here
In [3]: bdb = BigchainDB(bdb_root_url)
That last command instantiates an object bdb
of class
BigchainDB
. When instantiating a
BigchainDB
object without arguments, it
uses the default BigchainDB Root URL http://localhost:9984
.
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 [4]: from bigchaindb_driver.crypto import generate_keypair
Create a test user: alice
In [5]: alice = generate_keypair()
Define a digital asset data payload
In [6]: digital_asset_payload = {'data': {'msg': 'Hello BigchainDB!'}}
In [7]: 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 [8]: signed_tx = bdb.transactions.fulfill(tx, private_keys=alice.private_key)
In [9]: signed_tx
Out[9]:
{'asset': {'data': {'msg': 'Hello BigchainDB!'}},
'id': '5fc62d1a2d9bf86abcf3da084c54e53696f01d3299092e018e8655f94fc3bb1b',
'inputs': [{'fulfillment': 'pGSAIA8EdrlpQuGB-xYLwnDOchlGAjSbtd7w22TqPwJDdud_gUDGXGeKXCL6_TUstmZy_vX-VbjDcbXwepcuwiiQnG7v4zdqOg485GVxWDCrqcibNIH3AAKECTjecaYi6CvHNtAK',
'fulfills': None,
'owners_before': ['21d4fkiZKfEERaejKt7kCueXnM4Tc8fA3VvHH3oq4wYi']}],
'metadata': None,
'operation': 'CREATE',
'outputs': [{'amount': '1',
'condition': {'details': {'public_key': '21d4fkiZKfEERaejKt7kCueXnM4Tc8fA3VvHH3oq4wYi',
'type': 'ed25519-sha-256'},
'uri': 'ni:///sha-256;xfVu6qB3f2_O4lEwzeWkmAbeJf1Qsrk4feiYZ_tA0fY?fpt=ed25519-sha-256&cost=131072'},
'public_keys': ['21d4fkiZKfEERaejKt7kCueXnM4Tc8fA3VvHH3oq4wYi']}],
'version': '1.0'}
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 [10]: alice.public_key
Out[10]: '21d4fkiZKfEERaejKt7kCueXnM4Tc8fA3VvHH3oq4wYi'
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 [11]: signed_tx['id']
Out[11]: '5fc62d1a2d9bf86abcf3da084c54e53696f01d3299092e018e8655f94fc3bb1b'
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_index']
.

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 [12]: output_index = 0
In [13]: output = tx['outputs'][output_index]
In [14]: input_ = {
....: 'fulfillment': output['condition']['details'],
....: 'fulfills': {
....: 'output_index': output_index,
....: 'transaction_id': 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 [15]: transfer_asset_id = tx['id']
In [16]: transfer_asset = {
....: 'id': transfer_asset_id,
....: }
....:
Create a second test user, bob
:
In [17]: bob = generate_keypair()
In [18]: bob.public_key
Out[18]: 'BFUNhaXmUHPZwub7MKCK68t7HckeBtDmEaBXwsXQwcez'
And prepare the transfer transaction:
In [19]: 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 [20]: tx_transfer
Out[20]:
{'asset': {'id': '5fc62d1a2d9bf86abcf3da084c54e53696f01d3299092e018e8655f94fc3bb1b'},
'id': '97e4cf8fac9291f04f906718a9a10b1a0f75bb122067dcee9227c12ee7acebb2',
'inputs': [{'fulfillment': {'public_key': '21d4fkiZKfEERaejKt7kCueXnM4Tc8fA3VvHH3oq4wYi',
'type': 'ed25519-sha-256'},
'fulfills': {'output_index': 0,
'transaction_id': '5fc62d1a2d9bf86abcf3da084c54e53696f01d3299092e018e8655f94fc3bb1b'},
'owners_before': ['21d4fkiZKfEERaejKt7kCueXnM4Tc8fA3VvHH3oq4wYi']}],
'metadata': None,
'operation': 'TRANSFER',
'outputs': [{'amount': '1',
'condition': {'details': {'public_key': 'BFUNhaXmUHPZwub7MKCK68t7HckeBtDmEaBXwsXQwcez',
'type': 'ed25519-sha-256'},
'uri': 'ni:///sha-256;4-lC0t6UQmPyDiQ7OSxTqNksS4duitNu4Cq0A3gWchc?fpt=ed25519-sha-256&cost=131072'},
'public_keys': ['BFUNhaXmUHPZwub7MKCK68t7HckeBtDmEaBXwsXQwcez']}],
'version': '1.0'}
Notice, bob
‘s public key, appearing in the above dict
.
In [21]: tx_transfer['outputs'][0]['public_keys'][0]
Out[21]: 'BFUNhaXmUHPZwub7MKCK68t7HckeBtDmEaBXwsXQwcez'
In [22]: bob.public_key