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.

Connect to Multiple Nodes

You can optionally configure multiple nodes to connect to.

In [1]: from bigchaindb_driver import BigchainDB

In [2]: first_node = 'https://first.example.com:9984'

In [3]: second_node = 'https://second.example.com:9984'

In [4]: headers = {'app_id': 'your_app_id', 'app_key': 'your_app_key'}

In [5]: bdb = BigchainDB(first_node, second_node, headers=headers)

Each node can have its specific headers in addition to headers specified for all nodes, if any.

In [6]: first_node = 'https://first.example.com:9984'

In [7]: second_node = 'https://second.example.com:9984'

In [8]: common_headers = {'app_id': 'your_app_id', 'app_key': 'your_app_key'}

In [9]: second_node_headers = {'node_header': 'node_header_value'}

In [10]: bdb = BigchainDB(first_node,
   ....:                  {'endpoint': second_node, 'headers': second_node_headers},
   ....:                  headers=common_headers)
   ....: 

The driver switches nodes on connection failures in a round robin fashion. Connection failures are intermittent network issues like DNS failures or “refused connection” errors.

The driver switches nodes and repeats requests until the specified timeout is expired. The default timeout is 20 seconds. When timeout expires, an instance of bigchaindb_driver.exceptions.TimeoutError is raised. Specify timeout=None to repeat connection errors indefinitely.

In [11]: bdb = BigchainDB(first_node, second_node, headers=headers, timeout=None)

Also, the driver takes care of the exponential backoff. If a connection error occurs, the driver ensures at least half of a second is passed before the request to the same node is repeated. The intervals increase exponentially when multiple connection errors occur in a row. The user-specified timeout is always respected.

Warning

Every node the driver communicates with is supposed to be trusted. The driver does not currently implement any light client protocols.

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 [12]: from bigchaindb_driver.crypto import generate_keypair

Create a test user: alice

In [13]: alice = generate_keypair()

Define a digital asset data payload

In [14]: digital_asset_payload = {'data': {'msg': 'Hello BigchainDB!'}}

In [15]: 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 [16]: signed_tx = bdb.transactions.fulfill(tx, private_keys=alice.private_key)

In [17]: signed_tx
Out[17]: 
{'asset': {'data': {'msg': 'Hello BigchainDB!'}},
 'id': 'b65bad7bd737d0a2b7a76d42d86e9216d14ef4df72293ad290714e35de5c5030',
 'inputs': [{'fulfillment': 'pGSAINtydsVkq5RlnTmBbZLjZgbTiDmORTOXyiUvLui1qqSogUAniIhBfK6wHZxxYWZGrHs9fsoWm70csT5vSY2-EQMjiZPekyIz2_RJA1ff33eeymqGV8gNm8aeczqUaPYiKPIB',
   'fulfills': None,
   'owners_before': ['FmdX4atvL1K6cAVTwm4NQHuQSQtn4bGQjiKwxD3YXTH1']}],
 'metadata': None,
 'operation': 'CREATE',
 'outputs': [{'amount': '1',
   'condition': {'details': {'public_key': 'FmdX4atvL1K6cAVTwm4NQHuQSQtn4bGQjiKwxD3YXTH1',
     'type': 'ed25519-sha-256'},
    'uri': 'ni:///sha-256;4WvzJrfqw7H8O-56Zr4hnei3aDKFaErgEgGAAEY-yCE?fpt=ed25519-sha-256&cost=131072'},
   'public_keys': ['FmdX4atvL1K6cAVTwm4NQHuQSQtn4bGQjiKwxD3YXTH1']}],
 'version': '2.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_commit(signed_tx)

Warning

The method .send will be deprecated in the next release of the driver, please use .send_commit, .send_sync, or .send_async instead. More info

Note that the transaction payload returned by the BigchainDB node is equivalent to the signed transaction payload.

>>> sent_tx == signed_tx
True

Recap: Asset Creation

from bigchaindb_driver import BigchainDB
from bigchaindb_driver.crypto import generate_keypair

bdb_root_url = 'https://example.com:9984'  # Use YOUR BigchainDB Root URL here
bdb = BigchainDB(bdb_root_url)

alice = generate_keypair()

digital_asset_payload = {'data': {'msg': 'Hello BigchainDB!'}}
tx = bdb.transactions.prepare(operation='CREATE',
                          signers=alice.public_key,
                          asset=digital_asset_payload)

signed_tx = bdb.transactions.fulfill(tx, private_keys=alice.private_key)
sent_tx = bdb.transactions.send_commit(signed_tx)
sent_tx == signed_tx

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

The new owner of the digital asset is now Alice (or more correctly, her public key):

In [18]: alice.public_key
Out[18]: 'FmdX4atvL1K6cAVTwm4NQHuQSQtn4bGQjiKwxD3YXTH1'

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 [19]: signed_tx['id']
Out[19]: 'b65bad7bd737d0a2b7a76d42d86e9216d14ef4df72293ad290714e35de5c5030'

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'].

_images/tx_single_condition_single_fulfillment_v1.png

In order to prepare a transfer transaction, Alice needs to provide at least three things:

  1. inputs – one or more fulfillments that fulfill a prior transaction’s output conditions.
  2. asset['id'] – the id of the asset being transferred.
  3. Recipient public_keys – one or more public keys representing the new recipients(s).

To construct the input:

In [20]: output_index = 0

In [21]: output = signed_tx['outputs'][output_index]

In [22]: input_ = {
   ....:     'fulfillment': output['condition']['details'],
   ....:     'fulfills': {
   ....:         'output_index': output_index,
   ....:         'transaction_id': signed_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 [23]: transfer_asset_id = signed_tx['id']

In [24]: transfer_asset = {
   ....:     'id': transfer_asset_id,
   ....: }
   ....: 

Create a second test user, bob:

In [25]: bob = generate_keypair()

In [26]: bob.public_key
Out[26]: 'GTT7caxUSjcjWuDPfVmGRA8U6hgaurxkQzLRhqpsy65z'

And prepare the transfer transaction:

In [27]: 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 [28]: tx_transfer
Out[28]: 
{'asset': {'id': 'b65bad7bd737d0a2b7a76d42d86e9216d14ef4df72293ad290714e35de5c5030'},
 'id': None,
 'inputs': [{'fulfillment': {'public_key': 'FmdX4atvL1K6cAVTwm4NQHuQSQtn4bGQjiKwxD3YXTH1',
    'type': 'ed25519-sha-256'},
   'fulfills': {'output_index': 0,
    'transaction_id': 'b65bad7bd737d0a2b7a76d42d86e9216d14ef4df72293ad290714e35de5c5030'},
   'owners_before': ['FmdX4atvL1K6cAVTwm4NQHuQSQtn4bGQjiKwxD3YXTH1']}],
 'metadata': None,
 'operation': 'TRANSFER',
 'outputs': [{'amount': '1',
   'condition': {'details': {'public_key': 'GTT7caxUSjcjWuDPfVmGRA8U6hgaurxkQzLRhqpsy65z',
     'type': 'ed25519-sha-256'},
    'uri': 'ni:///sha-256;b9RUoiqwS-9lmASG8ULmy9aBCnbKbHZ0ssvKbMRbTV8?fpt=ed25519-sha-256&cost=131072'},
   'public_keys': ['GTT7caxUSjcjWuDPfVmGRA8U6hgaurxkQzLRhqpsy65z']}],
 'version': '2.0'}

Notice, bob’s public key, appearing in the above dict.

In [29]: tx_transfer['outputs'][0]['public_keys'][0]
Out[29]: 'GTT7caxUSjcjWuDPfVmGRA8U6hgaurxkQzLRhqpsy65z'

In [30]: bob.public_key
Out[30]: 'GTT7caxUSjcjWuDPfVmGRA8U6hgaurxkQzLRhqpsy65z'

The transaction now needs to be fulfilled by alice:

In [31]: signed_tx_transfer = bdb.transactions.fulfill(
   ....:     tx_transfer,
   ....:     private_keys=alice.private_key,
   ....: )
   ....: 

If you look at the content of signed_tx_transfer you should see the added fulfilment uri, holding the signature:

In [32]: signed_tx_transfer
Out[32]: 
{'asset': {'id': 'b65bad7bd737d0a2b7a76d42d86e9216d14ef4df72293ad290714e35de5c5030'},
 'id': '8b3c718033014f150762f5e3171128d6b1bc308690617f250a1f30f5b467a6bc',
 'inputs': [{'fulfillment': 'pGSAINtydsVkq5RlnTmBbZLjZgbTiDmORTOXyiUvLui1qqSogUAIwNNg5wGLl5EarY3Vav1-jEl6sjwI54FHjnuwN6egA-Q-CugRc78F80mdZSq93AgLBJ0Cr1XdQP0eN8tUuisF',
   'fulfills': {'output_index': 0,
    'transaction_id': 'b65bad7bd737d0a2b7a76d42d86e9216d14ef4df72293ad290714e35de5c5030'},
   'owners_before': ['FmdX4atvL1K6cAVTwm4NQHuQSQtn4bGQjiKwxD3YXTH1']}],
 'metadata': None,
 'operation': 'TRANSFER',
 'outputs': [{'amount': '1',
   'condition': {'details': {'public_key': 'GTT7caxUSjcjWuDPfVmGRA8U6hgaurxkQzLRhqpsy65z',
     'type': 'ed25519-sha-256'},
    'uri': 'ni:///sha-256;b9RUoiqwS-9lmASG8ULmy9aBCnbKbHZ0ssvKbMRbTV8?fpt=ed25519-sha-256&cost=131072'},
   'public_keys': ['GTT7caxUSjcjWuDPfVmGRA8U6hgaurxkQzLRhqpsy65z']}],
 'version': '2.0'}

More precisely:

In [33]: signed_tx_transfer['inputs'][0]['fulfillment']
Out[33]: 'pGSAINtydsVkq5RlnTmBbZLjZgbTiDmORTOXyiUvLui1qqSogUAIwNNg5wGLl5EarY3Vav1-jEl6sjwI54FHjnuwN6egA-Q-CugRc78F80mdZSq93AgLBJ0Cr1XdQP0eN8tUuisF'

We have yet to send the transaction over to a BigchainDB node, as both preparing and fulfilling a transaction are done “offchain,” that is, without the need to have a connection to a BigchainDB federation.

>>> sent_tx_transfer = bdb.transactions.send_commit(signed_tx_transfer)

Warning

The method .send will be deprecated in the next release of the driver, please use .send_commit, .send_sync, or .send_async instead. More info

Again, as with the 'CREATE' transaction, notice how the payload returned by the server is equal to the signed one.

>>> sent_tx_transfer == signed_tx_transfer
True

Recap: Asset Transfer

output_index = 0
output = signed_tx['outputs'][output_index]
input_ = {
    'fulfillment': output['condition']['details'],
    'fulfills': {
        'output_index': output_index,
        'transaction_id': signed_tx['id'],
    },
    'owners_before': output['public_keys'],
}
transfer_asset_id = signed_tx['id']
transfer_asset = {
    'id': transfer_asset_id,
}
bob = generate_keypair()
tx_transfer = bdb.transactions.prepare(
    operation='TRANSFER',
    inputs=input_,
    asset=transfer_asset,
    recipients=bob.public_key,
)
signed_tx_transfer = bdb.transactions.fulfill(
    tx_transfer,
    private_keys=alice.private_key,
)
sent_tx_transfer = bdb.transactions.send_commit(signed_tx_transfer)

Double Spends

BigchainDB makes sure that a user can’t transfer the same digital asset two or more times (i.e. it prevents double spends).

If we try to create another transaction with the same input as before, the transaction will be marked invalid and the validation will throw a double spend exception.

Let’s suppose that Alice tries to re-send the asset back to her “secret” account.

In [34]: alice_secret_stash = generate_keypair()

Create another transfer transaction with the same input

In [35]: tx_transfer_2 = bdb.transactions.prepare(
   ....:     operation='TRANSFER',
   ....:     inputs=input_,
   ....:     asset=transfer_asset,
   ....:     recipients=alice_secret_stash.public_key,
   ....: )
   ....: 

Fulfill the transaction

In [36]: fulfilled_tx_transfer_2 = bdb.transactions.fulfill(
   ....:     tx_transfer_2,
   ....:     private_keys=alice.private_key,
   ....: )
   ....: 

Send the transaction over to the node and an error will occur, informing the user of the double spend and specifying the matching asset_id.

>>> from bigchaindb_driver.exceptions import BigchaindbException
>>> try:
...     bdb.transactions.send_commit(fulfilled_tx_transfer_2)
... except BigchaindbException as e:
...     print(e.info)

{'message': 'Invalid transaction (DoubleSpend): input `20401005e1ad1745cdb3716715749475dce3c8358189af37d1a6676a52733e16` was already spent', 'status': 400}

Multiple Owners

Say alice and bob own a car together:

from bigchaindb_driver import BigchainDB
from bigchaindb_driver.crypto import generate_keypair

bdb_root_url = 'https://example.com:9984' # Use YOUR BigchainDB Root URL here
bdb = BigchainDB(bdb_root_url)

alice, bob = generate_keypair(), generate_keypair()
In [37]: car_asset = {
   ....:     'data': {
   ....:         'car': {
   ....:             'vin': '5YJRE11B781000196'
   ....:         }
   ....:     }
   ....: }
   ....: 

and they agree that alice will be the one issuing the asset. To create a new digital asset with multiple owners, one can simply provide a list or tuple of recipients:

In [38]: car_creation_tx = bdb.transactions.prepare(
   ....:     operation='CREATE',
   ....:     signers=alice.public_key,
   ....:     recipients=(alice.public_key, bob.public_key),
   ....:     asset=car_asset,
   ....: )
   ....: 

In [39]: signed_car_creation_tx = bdb.transactions.fulfill(
   ....:     car_creation_tx,
   ....:     private_keys=alice.private_key,
   ....: )
   ....: 
>>> sent_car_tx = bdb.transactions.send_commit(signed_car_creation_tx)

>>> sent_car_tx == signed_car_creation_tx
True

Warning

The method .send will be deprecated in the next release of the driver, please use .send_commit, .send_sync, or .send_async instead. More info

Let’s see how the example looks like when alice and bob are the issuers:

from bigchaindb_driver import BigchainDB
from bigchaindb_driver.crypto import generate_keypair

bdb_root_url = 'https://example.com:9984'
bdb = BigchainDB(bdb_root_url)

alice, bob = generate_keypair(), generate_keypair()

car_asset = {
    'data': {
        'car': {
            'vin': '5YJRE11B781000196'
        }
    }
}
car_creation_tx = bdb.transactions.prepare(
    operation='CREATE',
    signers=(alice.public_key, bob.public_key),
    recipients=(alice.public_key, bob.public_key),
    asset=car_asset,
)
signed_car_creation_tx = bdb.transactions.fulfill(
    car_creation_tx,
    private_keys=[alice.private_key, bob.private_key],
)
sent_car_tx = bdb.transactions.send_commit(signed_car_creation_tx)

One day, alice and bob, having figured out how to teleport themselves, and realizing they no longer need their car, wish to transfer the ownership of their car over to carol:

In [40]: carol = generate_keypair()

In order to prepare the transfer transaction, alice and bob need the input:

In [41]: output_index = 0

In [42]: output = signed_car_creation_tx['outputs'][output_index]

In [43]: input_ = {
   ....:     'fulfillment': output['condition']['details'],
   ....:     'fulfills': {
   ....:         'output_index': output_index,
   ....:         'transaction_id': signed_car_creation_tx['id'],
   ....:     },
   ....:     'owners_before': output['public_keys'],
   ....: }
   ....: 

Let’s take a moment to contemplate what this input_ is:

In [44]: input_
Out[44]: 
{'fulfillment': {'subconditions': [{'public_key': 'FmdX4atvL1K6cAVTwm4NQHuQSQtn4bGQjiKwxD3YXTH1',
    'type': 'ed25519-sha-256'},
   {'public_key': 'GTT7caxUSjcjWuDPfVmGRA8U6hgaurxkQzLRhqpsy65z',
    'type': 'ed25519-sha-256'}],
  'threshold': 2,
  'type': 'threshold-sha-256'},
 'fulfills': {'output_index': 0,
  'transaction_id': '674220fbf1876012b2bffb3b9f78e09c105852f25307e4d4a7b7ee7aee2bbfa0'},
 'owners_before': ['FmdX4atvL1K6cAVTwm4NQHuQSQtn4bGQjiKwxD3YXTH1',
  'GTT7caxUSjcjWuDPfVmGRA8U6hgaurxkQzLRhqpsy65z']}

and the asset (because it’s a CREATE transaction):

In [45]: transfer_asset = {
   ....:     'id': signed_car_creation_tx['id'],
   ....: }
   ....: 

then alice can prepare the transfer:

In [46]: car_transfer_tx = bdb.transactions.prepare(
   ....:     operation='TRANSFER',
   ....:     recipients=carol.public_key,
   ....:     asset=transfer_asset,
   ....:     inputs=input_,
   ....: )
   ....: 

The asset can be transfered as soon as each of the original transaction’s signers fulfills the transaction, that is alice and bob.

To do so, simply provide a list of all private keys to the fulfill method.

In [47]: signed_car_transfer_tx = bdb.transactions.fulfill(
   ....:     car_transfer_tx, private_keys=[alice.private_key, bob.private_key]
   ....: )
   ....: 

Danger

We are currently working to support partial fulfillments, such that not all keys of all parties involved need to be supplied at once. The issue bigchaindb/bigchaindb/issues/729 addresses the current limitation. Your feedback is welcome!

Note, that if one of the private keys is missing, the fulfillment will fail. If we omit bob:

In [48]: from bigchaindb_driver.exceptions import MissingPrivateKeyError

In [49]: try:
   ....:     signed_car_transfer_tx = bdb.transactions.fulfill(
   ....:         car_transfer_tx,
   ....:         private_keys=alice.private_key,
   ....:     )
   ....: except MissingPrivateKeyError as e:
   ....:     print(e, e.__cause__, sep='\n')
   ....: 
A private key is missing!
Public key GTT7caxUSjcjWuDPfVmGRA8U6hgaurxkQzLRhqpsy65z is not a pair to any of the private keys

Notice bob’s public key in the above message:

In [50]:  bob.public_key
Out[50]: 'GTT7caxUSjcjWuDPfVmGRA8U6hgaurxkQzLRhqpsy65z'

And the same goes for alice. Try it!

Sending the transaction over to a BigchainDB node:

sent_car_transfer_tx = bdb.transactions.send_commit(signed_car_transfer_tx)

Warning

The method .send will be deprecated in the next release of the driver, please use .send_commit, .send_sync, or .send_async instead. More info

Done!

Happy, alice and bob have successfully transferred the ownership of their car to carol, and can go on exploring the countless galaxies of the universe using their new teleportation skills.

Crypto-Conditions (Advanced)

Introduction

Crypto-conditions provide a mechanism to describe a signed message such that multiple actors in a distributed system can all verify the same signed message and agree on whether it matches the description.

This provides a useful primitive for event-based systems that are distributed on the Internet since we can describe events in a standard deterministic manner (represented by signed messages) and therefore define generic authenticated event handlers.

Crypto-conditions are part of the Interledger protocol and the full specification can be found here.

Implementations of the crypto-conditions are available in Python, JavaScript, and Java.

Threshold Conditions

Threshold conditions introduce multi-signatures, m-of-n signatures, or even more complex binary Merkle trees to BigchainDB.

Setting up a generic threshold condition is a bit more elaborate than regular transaction signing but allows for flexible signing between multiple parties or groups.

The basic workflow for creating a more complex cryptocondition is the following:

  1. Create a transaction template that includes the public key of all (nested) parties (signers) in the output’s public_keys
  2. Set up the threshold condition using the cryptocondition library
  3. Update the output’s condition and hash in the transaction template

We’ll illustrate this with a threshold condition where 2 out of 3 of the signers need to sign the transaction:

Todo

Stay tuned. Will soon be documented once

is taken care of.

The transaction can now be transfered by fulfilling the threshold condition.

The fulfillment involves:

  1. Create a transaction template that includes the public key of all (nested) parties (signers) in the inputs’s owners_before
  2. Parsing the threshold condition into a fulfillment using the cryptocondition library
  3. Signing all necessary subfulfillments and updating the inputs of the transaction

Todo

Stay tuned. Will soon be documented once

are taken care of.

Hash-locked Conditions

A hash-lock condition on an asset is like a password condition: anyone with the secret preimage (i.e. a password) can fulfill the hash-lock condition and transfer the asset to themselves.

Under the hood, fulfilling a hash-lock condition amounts to finding a string (a “preimage”) which, when hashed, results in a given value. It’s easy to verify that a given preimage hashes to the given value, but it’s computationally difficult to find a string which hashes to the given value. The only practical way to get a valid preimage is to get it from the original creator (possibly via intermediaries).

One possible use case is to distribute preimages as “digital vouchers.” The first person to redeem a voucher will get the associated asset.

A federation node can create an asset with a hash-lock condition and no owners_after. Anyone who can fullfill the hash-lock condition can transfer the asset to themselves.

Todo

Stay tuned. Will soon be documented once

are taken care of.

In order to redeem the asset, one needs to create a fulfillment with the correct secret:

Todo

Stay tuned. Will soon be documented once

are taken care of.

Timeout Conditions

Timeout conditions allow assets to expire after a certain time. The primary use case of timeout conditions is to enable Escrow.

The condition can only be fulfilled before the expiry time. Once expired, the asset is lost and cannot be fulfilled by anyone.

Note

The timeout conditions are BigchainDB-specific and not (yet) supported by the ILP standard.

Important

Caveat: The times between nodes in a BigchainDB federation may (and will) differ slightly. In this case, the majority of the nodes will decide.

Todo

Stay tuned. Will soon be documented once

are taken care of.

The following demonstrates that the transaction invalidates once the timeout occurs:

Todo

Stay tuned. Will soon be documented once

are taken care of.

If you were fast enough, you should see the following output:

Todo

Stay tuned. Will soon be documented once

are taken care of.

Escrow

Escrow is a mechanism for conditional release of assets.

This means that the assets are locked up by a trusted party until an execute condition is presented. In order not to tie up the assets forever, the escrow foresees an abort condition, which is typically an expiry time.

BigchainDB and cryptoconditions provides escrow out-of-the-box, without the need of a trusted party.

A threshold condition is used to represent the escrow, since BigchainDB transactions cannot have a pending state.

_images/tx_escrow_execute_abort.png

The logic for switching between execute and abort conditions is conceptually simple:

if timeout_condition.validate(utcnow()):
    execute_fulfillment.validate(msg) == True
    abort_fulfillment.validate(msg) == False
else:
    execute_fulfillment.validate(msg) == False
    abort_fulfillment.validate(msg) == True

The above switch can be implemented as follows using threshold cryptoconditions:

_images/cc_escrow_execute_abort.png

The inverted timeout is denoted by a -1 threshold, which negates the output of the fulfillment.

inverted_fulfillment.validate(msg) == not fulfillment.validate(msg)

Note

inverted thresholds are BigchainDB-specific and not supported by the ILP standard. The main reason is that it’s difficult to tell whether the fulfillment was negated, or just omitted.

The following code snippet shows how to create an escrow condition:

In the case of bob, we create the abort fulfillment:

The following demonstrates that the transaction validation switches once the timeout occurs:

If you execute in a timely fashion, you should see the following:

Of course, when the execute transaction was accepted in-time by bigchaindb, then writing the abort transaction after expiry will yield a Doublespend error.