diff --git a/bigchaindb/backend/rethinkdb/query.py b/bigchaindb/backend/rethinkdb/query.py index e3428fc5..3bbaaeb2 100644 --- a/bigchaindb/backend/rethinkdb/query.py +++ b/bigchaindb/backend/rethinkdb/query.py @@ -95,13 +95,13 @@ def get_asset_by_id(connection, asset_id): @register_query(RethinkDBConnection) -def get_spent(connection, transaction_id, output_id): +def get_spent(connection, transaction_id, output): # TODO: use index! return connection.run( r.table('bigchain', read_mode=READ_MODE) .concat_map(lambda doc: doc['block']['transactions']) .filter(lambda transaction: transaction['inputs'].contains( - lambda input: input['fulfills'] == {'txid': transaction_id, 'idx': output_id}))) + lambda input: input['fulfills'] == {'txid': transaction_id, 'output': output}))) @register_query(RethinkDBConnection) diff --git a/bigchaindb/common/schema/transaction.yaml b/bigchaindb/common/schema/transaction.yaml index 41778801..af3eadf7 100644 --- a/bigchaindb/common/schema/transaction.yaml +++ b/bigchaindb/common/schema/transaction.yaml @@ -214,10 +214,10 @@ definitions: Reference to the output that is being spent. additionalProperties: false required: - - idx + - output - txid properties: - idx: + output: "$ref": "#/definitions/offset" description: | Index of the output containing the condition being fulfilled diff --git a/bigchaindb/common/transaction.py b/bigchaindb/common/transaction.py index 1c844c5c..fd7c289b 100644 --- a/bigchaindb/common/transaction.py +++ b/bigchaindb/common/transaction.py @@ -132,11 +132,11 @@ class TransactionLink(object): Attributes: txid (str, optional): A Transaction to link to. - idx (int, optional): An output's index in a Transaction with id + output (int, optional): An output's index in a Transaction with id `txid`. """ - def __init__(self, txid=None, idx=None): + def __init__(self, txid=None, output=None): """Create an instance of a :class:`~.TransactionLink`. Note: @@ -144,18 +144,18 @@ class TransactionLink(object): as an IPLD link can simply point to an object, as well as an objects properties. So instead of having a (de)serializable class, we can have a simple IPLD link of the form: - `//transaction/outputs//`. + `//transaction/outputs//`. Args: txid (str, optional): A Transaction to link to. - idx (int, optional): An Outputs's index in a Transaction with + output (int, optional): An Outputs's index in a Transaction with id `txid`. """ self.txid = txid - self.idx = idx + self.output = output def __bool__(self): - return self.txid is not None and self.idx is not None + return self.txid is not None and self.output is not None def __eq__(self, other): # TODO: If `other !== TransactionLink` return `False` @@ -172,7 +172,7 @@ class TransactionLink(object): :class:`~bigchaindb.common.transaction.TransactionLink` """ try: - return cls(link['txid'], link['idx']) + return cls(link['txid'], link['output']) except TypeError: return cls() @@ -182,12 +182,12 @@ class TransactionLink(object): Returns: (dict|None): The link as an alternative serialization format. """ - if self.txid is None and self.idx is None: + if self.txid is None and self.output is None: return None else: return { 'txid': self.txid, - 'idx': self.idx, + 'output': self.output, } def to_uri(self, path=''): diff --git a/bigchaindb/core.py b/bigchaindb/core.py index 564cfb7a..cd0846b2 100644 --- a/bigchaindb/core.py +++ b/bigchaindb/core.py @@ -356,7 +356,7 @@ class Bigchain(object): if cursor: return Asset.from_dict(cursor[0]['asset']) - def get_spent(self, txid, idx): + def get_spent(self, txid, output): """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 @@ -364,7 +364,7 @@ class Bigchain(object): Args: txid (str): The id of the transaction - idx (num): the index of the output in the respective transaction + output (num): the index of the output in the respective transaction Returns: The transaction (Transaction) that used the `txid` as an input else @@ -372,8 +372,8 @@ class Bigchain(object): """ # checks if an input was already spent # checks if the bigchain has any transaction with input {'txid': ..., - # 'idx': ...} - transactions = list(backend.query.get_spent(self.connection, txid, idx)) + # 'output': ...} + transactions = list(backend.query.get_spent(self.connection, txid, output)) # a transaction_id should have been spent at most one time if transactions: @@ -405,7 +405,7 @@ class Bigchain(object): owner (str): base58 encoded public key. Returns: - :obj:`list` of TransactionLink: list of ``txid`` s and ``idx`` s + :obj:`list` of TransactionLink: list of ``txid`` s and ``output`` s pointing to another transaction's condition """ @@ -437,7 +437,7 @@ class Bigchain(object): if util.condition_details_has_owner(output['condition']['details'], owner): tx_link = TransactionLink(tx['id'], index) # check if input was already spent - if not self.get_spent(tx_link.txid, tx_link.idx): + if not self.get_spent(tx_link.txid, tx_link.output): owned.append(tx_link) return owned diff --git a/bigchaindb/models.py b/bigchaindb/models.py index ef8f54ea..7a81cad8 100644 --- a/bigchaindb/models.py +++ b/bigchaindb/models.py @@ -56,7 +56,6 @@ class Transaction(Transaction): input_amount = 0 for input in self.inputs: input_txid = input.fulfills.txid - input_idx = input.fulfills.idx input_tx, status = bigchain.\ get_transaction(input_txid, include_status=True) @@ -69,16 +68,17 @@ class Transaction(Transaction): 'input `{}` does not exist in a valid block'.format( input_txid)) - spent = bigchain.get_spent(input_txid, input_idx) + spent = bigchain.get_spent(input_txid, input.fulfills.output) if spent and spent.id != self.id: raise DoubleSpend('input `{}` was already spent' .format(input_txid)) - input_conditions.append(input_tx.outputs[input_idx]) + output = input_tx.outputs[input.fulfills.output] + input_conditions.append(output) input_txs.append(input_tx) - if input_tx.outputs[input_idx].amount < 1: + if output.amount < 1: raise AmountError('`amount` needs to be greater than zero') - input_amount += input_tx.outputs[input_idx].amount + input_amount += output.amount # validate asset id asset_id = Asset.get_asset_id(input_txs) diff --git a/tests/common/test_transaction.py b/tests/common/test_transaction.py index a45dd863..5e01c9f6 100644 --- a/tests/common/test_transaction.py +++ b/tests/common/test_transaction.py @@ -387,7 +387,7 @@ def test_transaction_link_serialization(): tx_id = 'a transaction id' expected = { 'txid': tx_id, - 'idx': 0, + 'output': 0, } tx_link = TransactionLink(tx_id, 0) @@ -410,7 +410,7 @@ def test_transaction_link_deserialization(): expected = TransactionLink(tx_id, 0) tx_link = { 'txid': tx_id, - 'idx': 0, + 'output': 0, } tx_link = TransactionLink.from_dict(tx_link) @@ -896,7 +896,7 @@ def test_outputs_to_inputs(tx): assert input.owners_before == tx.outputs[0].public_keys assert input.fulfillment == tx.outputs[0].fulfillment assert input.fulfills.txid == tx.id - assert input.fulfills.idx == 0 + assert input.fulfills.output == 0 def test_create_transfer_transaction_single_io(tx, user_pub, user2_pub, @@ -921,7 +921,7 @@ def test_create_transfer_transaction_single_io(tx, user_pub, user2_pub, 'fulfillment': None, 'fulfills': { 'txid': tx.id, - 'idx': 0 + 'output': 0 } } ], @@ -970,7 +970,7 @@ def test_create_transfer_transaction_multiple_io(user_pub, user_priv, 'fulfillment': None, 'fulfills': { 'txid': tx.id, - 'idx': 0 + 'output': 0 } }, { 'owners_before': [ @@ -979,7 +979,7 @@ def test_create_transfer_transaction_multiple_io(user_pub, user_priv, 'fulfillment': None, 'fulfills': { 'txid': tx.id, - 'idx': 1 + 'output': 1 } } ], diff --git a/tests/db/test_bigchain_api.py b/tests/db/test_bigchain_api.py index a4c31133..a0af611d 100644 --- a/tests/db/test_bigchain_api.py +++ b/tests/db/test_bigchain_api.py @@ -1025,7 +1025,7 @@ class TestMultipleInputs(object): # check spents input_txid = owned_inputs_user1.txid - input_idx = owned_inputs_user1.idx + input_idx = owned_inputs_user1.output spent_inputs_user1 = b.get_spent(input_txid, input_idx) assert spent_inputs_user1 is None @@ -1061,7 +1061,7 @@ class TestMultipleInputs(object): # check spents input_txid = owned_inputs_user1.txid - input_idx = owned_inputs_user1.idx + input_idx = owned_inputs_user1.output spent_inputs_user1 = b.get_spent(input_txid, input_idx) assert spent_inputs_user1 is None @@ -1105,7 +1105,7 @@ class TestMultipleInputs(object): # check spents for input_tx in owned_inputs_user1: - assert b.get_spent(input_tx.txid, input_tx.idx) is None + assert b.get_spent(input_tx.txid, input_tx.output) is None # transfer the first 2 inputs tx_transfer = Transaction.transfer(tx_create.to_inputs()[:2], @@ -1117,7 +1117,7 @@ class TestMultipleInputs(object): # check that used inputs are marked as spent for ffill in tx_create.to_inputs()[:2]: - spent_tx = b.get_spent(ffill.fulfills.txid, ffill.fulfills.idx) + spent_tx = b.get_spent(ffill.fulfills.txid, ffill.fulfills.output) assert spent_tx == tx_transfer_signed # check if remaining transaction that was unspent is also perceived @@ -1147,7 +1147,7 @@ class TestMultipleInputs(object): # check spents for input_tx in owned_inputs_user1: - assert b.get_spent(input_tx.txid, input_tx.idx) is None + assert b.get_spent(input_tx.txid, input_tx.output) is None # create a transaction tx = Transaction.transfer(transactions[0].to_inputs(),