From 194bf8c6bdc13cde734b4b9feab3c1fc95a52fe8 Mon Sep 17 00:00:00 2001 From: Rodolphe Marques Date: Tue, 22 Mar 2016 19:30:53 +0100 Subject: [PATCH] initial implementation of multi input support --- bigchaindb/consensus.py | 42 +++++++++++++++++------------------ bigchaindb/core.py | 2 +- bigchaindb/util.py | 10 ++++++--- tests/db/test_bigchain_api.py | 13 +++++++++++ 4 files changed, 42 insertions(+), 25 deletions(-) diff --git a/bigchaindb/consensus.py b/bigchaindb/consensus.py index b65ab9ad..f0ee601e 100644 --- a/bigchaindb/consensus.py +++ b/bigchaindb/consensus.py @@ -119,7 +119,7 @@ class BaseConsensusRules(AbstractConsensusRules): # If the operation is CREATE the transaction should have no inputs and # should be signed by a federation node if transaction['transaction']['operation'] == 'CREATE': - if transaction['transaction']['input']: + if transaction['transaction']['inputs']: raise ValueError('A CREATE operation has no inputs') if transaction['transaction']['current_owner'] not in ( bigchain.federation_nodes + [bigchain.me]): @@ -128,32 +128,32 @@ class BaseConsensusRules(AbstractConsensusRules): else: # check if the input exists, is owned by the current_owner - if not transaction['transaction']['input']: + if not transaction['transaction']['inputs']: raise ValueError( 'Only `CREATE` transactions can have null inputs') - tx_input = bigchain.get_transaction( - transaction['transaction']['input']) + # check inputs + for inp in transaction['transaction']['inputs']: + tx_input = bigchain.get_transaction(inp) - if not tx_input: - raise exceptions.TransactionDoesNotExist( - 'input `{}` does not exist in the bigchain'.format( - transaction['transaction']['input'])) + if not tx_input: + raise exceptions.TransactionDoesNotExist( + 'input `{}` does not exist in the bigchain'.format( + transaction['transaction']['input'])) - if (tx_input['transaction']['new_owner'] != - transaction['transaction']['current_owner']): - raise exceptions.TransactionOwnerError( - 'current_owner `{}` does not own the input `{}`'.format( - transaction['transaction']['current_owner'], - transaction['transaction']['input'])) + if (tx_input['transaction']['new_owner'] != + transaction['transaction']['current_owner']): + raise exceptions.TransactionOwnerError( + 'current_owner `{}` does not own the input `{}`'.format( + transaction['transaction']['current_owner'], + transaction['transaction']['input'])) - # check if the input was already spent by a transaction other than - # this one. - spent = bigchain.get_spent(tx_input['id']) - if spent and spent['id'] != transaction['id']: - raise exceptions.DoubleSpend( - 'input `{}` was already spent'.format( - transaction['transaction']['input'])) + # check if the input was already spent by a transaction other than + # this one. + spent = bigchain.get_spent(tx_input['id']) + if spent and spent['id'] != transaction['id']: + raise exceptions.DoubleSpend( + 'input `{}` was already spent'.format(inp)) # Check hash of the transaction calculated_hash = hash_data(util.serialize( diff --git a/bigchaindb/core.py b/bigchaindb/core.py index f76dd2d6..99131055 100644 --- a/bigchaindb/core.py +++ b/bigchaindb/core.py @@ -203,7 +203,7 @@ class Bigchain(object): # checks if an input was already spent # checks if the bigchain has any transaction with input `transaction_id` response = r.table('bigchain').concat_map(lambda doc: doc['block']['transactions'])\ - .filter(lambda transaction: transaction['transaction']['input'] == txid).run(self.conn) + .filter(lambda transaction: transaction['transaction']['inputs'].contains(txid)).run(self.conn) # a transaction_id should have been spent at most one time transactions = list(response) diff --git a/bigchaindb/util.py b/bigchaindb/util.py index d8ddc9ce..999805fe 100644 --- a/bigchaindb/util.py +++ b/bigchaindb/util.py @@ -76,7 +76,7 @@ def timestamp(): return "{0:.6f}".format(time.mktime(dt.timetuple()) + dt.microsecond / 1e6) -def create_tx(current_owner, new_owner, tx_input, operation, payload=None): +def create_tx(current_owner, new_owner, inputs, operation, payload=None): """Create a new transaction A transaction in the bigchain is a transfer of a digital asset between two entities represented @@ -94,7 +94,7 @@ def create_tx(current_owner, new_owner, tx_input, operation, payload=None): Args: current_owner (str): base58 encoded public key of the current owner of the asset. new_owner (str): base58 encoded public key of the new owner of the digital asset. - tx_input (str): id of the transaction to use as input. + inputs (list): id of the transaction to use as input. operation (str): Either `CREATE` or `TRANSFER` operation. payload (Optional[dict]): dictionary with information about asset. @@ -106,6 +106,7 @@ def create_tx(current_owner, new_owner, tx_input, operation, payload=None): TypeError: if the optional ``payload`` argument is not a ``dict``. """ + # handle payload data = None if payload is not None: if isinstance(payload, dict): @@ -123,10 +124,13 @@ def create_tx(current_owner, new_owner, tx_input, operation, payload=None): 'payload': payload } + if inputs == []: + inputs = None + tx = { 'current_owner': current_owner, 'new_owner': new_owner, - 'input': tx_input, + 'inputs': inputs, 'operation': operation, 'timestamp': timestamp(), 'data': data diff --git a/tests/db/test_bigchain_api.py b/tests/db/test_bigchain_api.py index 1fed8800..6349a824 100644 --- a/tests/db/test_bigchain_api.py +++ b/tests/db/test_bigchain_api.py @@ -756,3 +756,16 @@ class TestBigchainBlock(object): def test_duplicated_transactions(self): pytest.skip('We may have duplicates in the initial_results and changefeed') + + +class TestMultipleInputs(object): + + def test_transfer_transaction_multiple(self, b): + pass + + def test_transfer_single_input_from_multi_input(self, b): + pass + + def test_get_spent(self, b): + pass +