From 35fc0b5dad9450579f44794b8861039d795f34fd Mon Sep 17 00:00:00 2001 From: Rodolphe Marques Date: Mon, 18 Apr 2016 14:50:49 +0200 Subject: [PATCH 1/4] remove unnecessary sorts --- tests/db/test_bigchain_api.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/db/test_bigchain_api.py b/tests/db/test_bigchain_api.py index 1e2c3a85..9632e218 100644 --- a/tests/db/test_bigchain_api.py +++ b/tests/db/test_bigchain_api.py @@ -27,8 +27,8 @@ class TestBigchainApi(object): def test_create_transaction_create(self, b, user_sk): tx = b.create_transaction(b.me, user_sk, None, 'CREATE') - assert sorted(tx) == sorted(['id', 'transaction', 'version']) - assert sorted(tx['transaction']) == sorted(['conditions', 'data', 'fulfillments', 'operation', 'timestamp']) + assert sorted(tx) == ['id', 'transaction', 'version'] + assert sorted(tx['transaction']) == ['conditions', 'data', 'fulfillments', 'operation', 'timestamp'] def test_create_transaction_with_unsupported_payload_raises(self, b): with pytest.raises(TypeError): @@ -41,8 +41,8 @@ class TestBigchainApi(object): tx = b.create_transaction(user_vk, b.me, input_tx, 'TRANSFER') - assert sorted(tx) == sorted(['id', 'transaction', 'version']) - assert sorted(tx['transaction']) == sorted(['conditions', 'data', 'fulfillments', 'operation', 'timestamp']) + assert sorted(tx) == ['id', 'transaction', 'version'] + assert sorted(tx['transaction']) == ['conditions', 'data', 'fulfillments', 'operation', 'timestamp'] tx_signed = b.sign_transaction(tx, user_sk) From 7a75dc1700877b8b1938de40353793d03c7e248b Mon Sep 17 00:00:00 2001 From: Rodolphe Marques Date: Mon, 18 Apr 2016 14:55:56 +0200 Subject: [PATCH 2/4] replace `inp` with `tx_input` --- bigchaindb/core.py | 8 ++++---- bigchaindb/util.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/bigchaindb/core.py b/bigchaindb/core.py index 93a36b65..477fab9b 100644 --- a/bigchaindb/core.py +++ b/bigchaindb/core.py @@ -185,14 +185,14 @@ class Bigchain(object): transactions = list(cursor) return transactions - def get_spent(self, inp): + def get_spent(self, tx_input): """Check if a `txid` was already used as an input. A transaction can be used as an input for another transaction. Bigchain needs to make sure that a given `txid` is only used once. Args: - inp (dict): Input of a transaction in the form `{'txid': 'transaction id', 'cid': 'condition id'}` + tx_input (dict): Input of a transaction in the form `{'txid': 'transaction id', 'cid': 'condition id'}` Returns: The transaction that used the `txid` as an input if it exists else it returns `None` @@ -201,7 +201,7 @@ class Bigchain(object): # checks if the bigchain has any transaction with input {'txid': ..., 'cid': ...} response = r.table('bigchain').concat_map(lambda doc: doc['block']['transactions'])\ .filter(lambda transaction: transaction['transaction']['fulfillments'] - .contains(lambda fulfillment: fulfillment['input'] == inp))\ + .contains(lambda fulfillment: fulfillment['input'] == tx_input))\ .run(self.conn) # a transaction_id should have been spent at most one time @@ -209,7 +209,7 @@ class Bigchain(object): if transactions: if len(transactions) != 1: raise Exception('`{}` was spent more then once. There is a problem with the chain'.format( - inp['txid'])) + tx_input['txid'])) else: return transactions[0] else: diff --git a/bigchaindb/util.py b/bigchaindb/util.py index 3cfb4048..e43716bd 100644 --- a/bigchaindb/util.py +++ b/bigchaindb/util.py @@ -173,10 +173,10 @@ def create_tx(current_owners, new_owners, inputs, operation, payload=None): # transfer if inputs: - for fid, inp in enumerate(inputs): + for fid, tx_input in enumerate(inputs): fulfillments.append({ 'current_owners': current_owners, - 'input': inp, + 'input': tx_input, 'fulfillment': None, 'fid': fid }) From b8aeb228e801ba4fed4f421b5aa18ca127a7f67c Mon Sep 17 00:00:00 2001 From: Rodolphe Marques Date: Mon, 18 Apr 2016 15:24:24 +0200 Subject: [PATCH 3/4] documented the new transaction structure --- docs/source/models.md | 91 +++++++++++++++++++++++++++++++++---------- 1 file changed, 71 insertions(+), 20 deletions(-) diff --git a/docs/source/models.md b/docs/source/models.md index a7b0ae4e..9186cdf1 100644 --- a/docs/source/models.md +++ b/docs/source/models.md @@ -7,37 +7,88 @@ Transactions, blocks and votes are represented using JSON documents with the fol ```json { "id": "", + "version": "", "transaction": { - "current_owner": "", - "new_owner": "", - "input": "", + "fulfillments": [""], + "conditions": [""], "operation": "", "timestamp": "", "data": { "hash": "", - "payload": { - "title": "The Winds of Plast", - "creator": "Johnathan Plunkett", - "IPFS_key": "QmfQ5QAjvg4GtA3wg3adpnDJug8ktA1BxurVqBD8rtgVjP" - } + "payload": "" } - }, - "signature": "" + } } ``` A transaction is an operation between the `current_owner` and the `new_owner` over the digital content described by `hash`. For example if could be a transfer of ownership of the digital content `hash` -- `id`: sha3 hash of the transaction. The `id` is also the DB primary key. -- `current_owner`: Public key of the current owner of the digital content with hash `hash` -- `new_owner`: Public key of the new owner of the digital content with hash `hash` -- `input`: id (sha3 hash) of the transaction in which the content was transfered to the user (similar to input in the blockchain). Right now we will assume that there is only one input per transaction to simplify the prototype. This can be changed in the future to allow multiple inputs per transaction. -- `operation`: String representation of the operation being performed (REGISTER, TRANSFER, ...) this will define how -the transactions should be validated -- `timestamp`: Time of creation of the transaction in UTC. It's provided by the client. -- `data`: JSON object describing the asset (digital content). It contains at least the field `hash` which is a -sha3 hash of the digital content. -- `signature`: Signature of the transaction with the `current_owner` private key +- **Transaction header**: + - `id`: sha3 hash of the transaction. The `id` is also the DB primary key. + - `version`: Version of the transaction. For future compability with changes in the transaction model. +- **Transaction body**: + - `fulfillments`: List of fulfillments. Each _fulfillment_ contains a pointer to a unspent digital asset + and a _crypto fulfillment_ that satisfies a spending condition set on the unspent digital asset. A _fulfillment_ + is usually a signature proving the ownership of the digital asset. + See [conditions and fulfillments](models.md#conditions-and-fulfillments) + - `conditions`: List of conditions. Each _condition_ a _crypto condition_ that needs to be fulfilled by the + new owner in order to spend the digital asset. + See [conditions and fulfillments](models.md#conditions-and-fulfillments) + - `operation`: String representation of the operation being performed (CREATE, TRANSFER, ...) this will define how + the transactions should be validated + - `timestamp`: Time of creation of the transaction in UTC. It's provided by the client. + - `data`: JSON object describing the asset (digital content). It contains at least the field `hash` which is a + sha3 hash of the digital content. + +## Conditions and Fulfillments + +### Conditions + +```json +{ + "cid": "", + "condition": { + "details": { + "bitmask": "", + "public_key": "", + "signature": null, + "type": "fulfillment", + "type_id": "" + }, + "uri": "" + }, + "new_owners": [""] +} +``` + +- **Condition header**: + - `cid`: Condition index so that we can reference this output as an input to another transaction. It also matches + the input `fid`, making this the condition to fulfill in order to spend the digital asset used as input with `fid` + - `new_owners`: List of public keys of the new owners. +- **Condition body**: + - `bitmask`: + - `public_key`: + - `signature`: + - `type`: + - `type_id`: + - `uri`: + +- Simple signatures +- Multisignatures + +### Fulfillments + +```json +{ + "current_owners": ["5nRNzUc2QoGaTKDPjpZV9p9qWKJaB5vowQWVEmXkEC6k"], + "fid": 0, + "fulfillment": "cf:4:RxFzIE679tFBk8zwEgizhmTuciAylvTUwy6EL6ehddHFJOhK5F4IjwQ1xLu2oQK9iyRCZJdfWAefZVjTt3DeG5j2exqxpGliOPYseNkRAWEakqJ_UrCwgnj92dnFRAEE", + "input": { + "cid": 0, + "txid": "11b3e7d893cc5fdfcf1a1706809c7def290a3b10b0bef6525d10b024649c42d3" + } +} +``` ## The Block Model From b64a39d59a0b0445816d5b7639cd9cb6a41d3c37 Mon Sep 17 00:00:00 2001 From: Rodolphe Marques Date: Mon, 18 Apr 2016 16:00:51 +0200 Subject: [PATCH 4/4] updated documentation --- docs/source/models.md | 66 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 3 deletions(-) diff --git a/docs/source/models.md b/docs/source/models.md index 9186cdf1..940e7731 100644 --- a/docs/source/models.md +++ b/docs/source/models.md @@ -44,6 +44,10 @@ A transaction is an operation between the `current_owner` and the `new_owner` ov ### Conditions +##### Simple Signature + +If there is only one _new owner_ the condition will be a single signature condition. + ```json { "cid": "", @@ -73,14 +77,62 @@ A transaction is an operation between the `current_owner` and the `new_owner` ov - `type_id`: - `uri`: -- Simple signatures -- Multisignatures +##### Multi Signature + +If there are multiple _new owners_ by default we create a condition requiring a signature from each new owner in order +to spend the digital asset. + +Example of a condition with two _new owners_: +```json +{ + "cid": "", + "condition": { + "details": { + "bitmask": 41, + "subfulfillments": [ + { + "bitmask": 32, + "public_key": "", + "signature": null, + "type": "fulfillment", + "type_id": 4, + "weight": 1 + }, + { + "bitmask": 32, + "public_key": "", + "signature": null, + "type": "fulfillment", + "type_id": 4, + "weight": 1 + } + ], + "threshold": 2, + "type": "fulfillment", + "type_id": 2 + }, + "uri": "cc:2:29:ytNK3X6-bZsbF-nCGDTuopUIMi1HCyCkyPewm6oLI3o:206"}, + "new_owners": [ + "", + "" + ] +} +``` + +- `subfulfillments`: + - `weight`: +- `threshold`: + ### Fulfillments +##### Simple Signature + +If there is only one _current owner_ the fulfillment will be a single signature fulfillment. + ```json { - "current_owners": ["5nRNzUc2QoGaTKDPjpZV9p9qWKJaB5vowQWVEmXkEC6k"], + "current_owners": [""], "fid": 0, "fulfillment": "cf:4:RxFzIE679tFBk8zwEgizhmTuciAylvTUwy6EL6ehddHFJOhK5F4IjwQ1xLu2oQK9iyRCZJdfWAefZVjTt3DeG5j2exqxpGliOPYseNkRAWEakqJ_UrCwgnj92dnFRAEE", "input": { @@ -90,6 +142,14 @@ A transaction is an operation between the `current_owner` and the `new_owner` ov } ``` +- `fid`: Fulfillment index. It matches a `cid` in the conditions with a new _crypto condition_ that the new owner(s) +need to fulfill to spend this digital asset +- `current_owners`: Public key of the current owner(s) +- `fulfillment`: +- `input`: Pointer to the digital asset and condition of a previous transaction + - `cid`: Condition index + - `txid`: Transaction id + ## The Block Model ```json