def test_invalid_transaction_initialization(): from bigchaindb_common.transaction import Transaction, Asset with raises(ValueError): Transaction(operation='invalid operation', asset=Asset()) with raises(TypeError): Transaction(operation='CREATE', asset='invalid asset') with raises(TypeError): Transaction( operation='CREATE', asset=Asset(), conditions='invalid conditions' ) with raises(TypeError): Transaction( operation='CREATE', asset=Asset(), conditions=[], fulfillments='invalid fulfillments' ) with raises(TypeError): Transaction( operation='CREATE', asset=Asset(), conditions=[], fulfillments=[], metadata='invalid metadata' )
def test_create_default_asset_on_tx_initialization(): from bigchaindb_common.transaction import Transaction, Asset tx = Transaction(Transaction.CREATE, None) expected = Asset() asset = tx.asset expected.data_id = None asset.data_id = None assert asset == expected
def test_create_transfer_with_invalid_parameters(): from bigchaindb_common.transaction import Transaction, Asset with raises(TypeError): Transaction.transfer({}, [], Asset()) with raises(ValueError): Transaction.transfer([], [], Asset()) with raises(TypeError): Transaction.transfer(['fulfillment'], {}, Asset()) with raises(ValueError): Transaction.transfer(['fulfillment'], [], Asset())
def test_validate_asset(): from bigchaindb_common.transaction import Asset with raises(TypeError): Asset(divisible=1) with raises(TypeError): Asset(refillable=1) with raises(TypeError): Asset(updatable=1) with raises(TypeError): Asset(data='we need more lemon pledge')
def test_asset_invalid_asset_initialization(): from bigchaindb_common.transaction import Asset with raises(TypeError): Asset(data='some wrong type') with raises(TypeError): Asset(divisible=1) with raises(TypeError): Asset(refillable=1) with raises(TypeError): Asset(updatable=1)
def test_asset_serialization(data, data_id): from bigchaindb_common.transaction import Asset expected = { 'id': data_id, 'divisible': False, 'updatable': False, 'refillable': False, 'data': data, } asset = Asset(data, data_id) assert asset.to_dict() == expected
def test_add_fulfillment_to_tx(user_ffill): from bigchaindb_common.transaction import Transaction, Asset tx = Transaction(Transaction.CREATE, Asset(), [], []) tx.add_fulfillment(user_ffill) assert len(tx.fulfillments) == 1
def test_add_condition_to_tx(user_cond): from bigchaindb_common.transaction import Transaction, Asset tx = Transaction(Transaction.CREATE, Asset()) tx.add_condition(user_cond) assert len(tx.conditions) == 1
def test_validate_multiple_fulfillments(user_ffill, user_cond, user_priv): from copy import deepcopy from bigchaindb_common.crypto import SigningKey from bigchaindb_common.transaction import Transaction, Asset tx = Transaction(Transaction.CREATE, Asset(), [user_ffill, deepcopy(user_ffill)], [user_ffill, deepcopy(user_cond)]) expected_first = deepcopy(tx) expected_second = deepcopy(tx) expected_first.fulfillments = [expected_first.fulfillments[0]] expected_first.conditions = [expected_first.conditions[0]] expected_second.fulfillments = [expected_second.fulfillments[1]] expected_second.conditions = [expected_second.conditions[1]] expected_first_bytes = str(expected_first).encode() expected_first.fulfillments[0].fulfillment.sign(expected_first_bytes, SigningKey(user_priv)) expected_second_bytes = str(expected_second).encode() expected_second.fulfillments[0].fulfillment.sign(expected_second_bytes, SigningKey(user_priv)) tx.sign([user_priv]) assert tx.fulfillments[0].to_dict()['fulfillment'] == \ expected_first.fulfillments[0].fulfillment.serialize_uri() assert tx.fulfillments[1].to_dict()['fulfillment'] == \ expected_second.fulfillments[0].fulfillment.serialize_uri() assert tx.fulfillments_valid() is True
def test_validate_threshold_create_transaction(user_pub, user_priv, user2_pub, data): from bigchaindb_common.transaction import Transaction, Asset tx = Transaction.create([user_pub], [user_pub, user2_pub], data, Asset()) tx = tx.sign([user_priv]) assert tx.fulfillments_valid() is True
def test_multiple_fulfillment_validation_of_transfer_tx(user_ffill, user_cond, user_priv, user2_pub, user2_priv, user3_pub, user3_priv): from copy import deepcopy from bigchaindb_common.transaction import (Transaction, TransactionLink, Fulfillment, Condition, Asset) from cryptoconditions import Ed25519Fulfillment tx = Transaction(Transaction.CREATE, Asset(), [user_ffill, deepcopy(user_ffill)], [user_cond, deepcopy(user_cond)]) tx.sign([user_priv]) fulfillments = [Fulfillment(cond.fulfillment, cond.owners_after, TransactionLink(tx.id, index)) for index, cond in enumerate(tx.conditions)] conditions = [Condition(Ed25519Fulfillment(public_key=user3_pub), [user3_pub]), Condition(Ed25519Fulfillment(public_key=user3_pub), [user3_pub])] transfer_tx = Transaction('TRANSFER', tx.asset, fulfillments, conditions) transfer_tx = transfer_tx.sign([user_priv]) assert transfer_tx.fulfillments_valid(tx.conditions) is True
def transfer_utx(user_cond, user2_cond, utx): from bigchaindb_common.transaction import (Fulfillment, TransactionLink, Transaction, Asset) user_cond = user_cond.to_dict() ffill = Fulfillment(utx.conditions[0].fulfillment, user_cond['owners_after'], TransactionLink(utx.id, 0)) return Transaction('TRANSFER', Asset(), [ffill], [user2_cond])
def test_transaction_deserialization(user_ffill, user_cond, data, data_id): from bigchaindb_common.transaction import Transaction, Asset timestamp = '66666666666' expected_asset = Asset(data, data_id) expected = Transaction(Transaction.CREATE, expected_asset, [user_ffill], [user_cond], None, timestamp, Transaction.VERSION) tx = { 'version': Transaction.VERSION, 'transaction': { # NOTE: This test assumes that Fulfillments and Conditions can # successfully be serialized 'fulfillments': [user_ffill.to_dict()], 'conditions': [user_cond.to_dict()], 'operation': Transaction.CREATE, 'timestamp': timestamp, 'metadata': None, 'asset': { 'id': data_id, 'divisible': False, 'updatable': False, 'refillable': False, 'data': data, } } } tx_no_signatures = Transaction._remove_signatures(tx) tx['id'] = Transaction._to_hash(Transaction._to_str(tx_no_signatures)) tx = Transaction.from_dict(tx) assert tx == expected
def test_transaction_serialization(user_ffill, user_cond, data, data_id): from bigchaindb_common.transaction import Transaction, Asset tx_id = 'l0l' timestamp = '66666666666' expected = { 'id': tx_id, 'version': Transaction.VERSION, 'transaction': { # NOTE: This test assumes that Fulfillments and Conditions can # successfully be serialized 'fulfillments': [user_ffill.to_dict(0)], 'conditions': [user_cond.to_dict(0)], 'operation': Transaction.CREATE, 'timestamp': timestamp, 'metadata': None, 'asset': { 'id': data_id, 'divisible': False, 'updatable': False, 'refillable': False, 'data': data, } } } tx = Transaction(Transaction.CREATE, Asset(data, data_id), [user_ffill], [user_cond]) tx_dict = tx.to_dict() tx_dict['id'] = tx_id tx_dict['transaction']['asset']['id'] = data_id tx_dict['transaction']['timestamp'] = timestamp assert tx_dict == expected
def test_asset_default_values(): from bigchaindb_common.transaction import Asset asset = Asset() assert asset.data is None assert asset.data_id assert asset.divisible is False assert asset.updatable is False assert asset.refillable is False
def test_validate_tx_simple_create_signature(user_ffill, user_cond, user_priv): from copy import deepcopy from bigchaindb_common.crypto import SigningKey from bigchaindb_common.transaction import Transaction, Asset tx = Transaction(Transaction.CREATE, Asset(), [user_ffill], [user_cond]) expected = deepcopy(user_cond) expected.fulfillment.sign(str(tx).encode(), SigningKey(user_priv)) tx.sign([user_priv]) assert tx.fulfillments[0].to_dict()['fulfillment'] == \ expected.fulfillment.serialize_uri() assert tx.fulfillments_valid() is True
def test_create_transfer_transaction_single_io(tx, user_pub, user2_pub, user2_cond, user_priv, data_id): from copy import deepcopy from bigchaindb_common.crypto import SigningKey from bigchaindb_common.transaction import Transaction, Asset from bigchaindb_common.util import serialize expected = { 'transaction': { 'conditions': [user2_cond.to_dict(0)], 'metadata': None, 'asset': { 'id': data_id, }, 'fulfillments': [ { 'owners_before': [ user_pub ], 'fid': 0, 'fulfillment': None, 'input': { 'txid': tx.id, 'cid': 0 } } ], 'operation': 'TRANSFER', }, 'version': 1 } inputs = tx.to_inputs([0]) asset = Asset(None, data_id) transfer_tx = Transaction.transfer(inputs, [user2_pub], asset=asset) transfer_tx = transfer_tx.sign([user_priv]) transfer_tx = transfer_tx.to_dict() transfer_tx_body = transfer_tx['transaction'] expected_input = deepcopy(inputs[0]) expected['id'] = transfer_tx['id'] expected['transaction']['timestamp'] = transfer_tx_body['timestamp'] expected_input.fulfillment.sign(serialize(expected).encode(), SigningKey(user_priv)) expected_ffill = expected_input.fulfillment.serialize_uri() transfer_ffill = transfer_tx_body['fulfillments'][0]['fulfillment'] assert transfer_ffill == expected_ffill transfer_tx = Transaction.from_dict(transfer_tx) assert transfer_tx.fulfillments_valid([tx.conditions[0]]) is True
def test_create_create_transaction_hashlock(user_pub, data, data_id): from cryptoconditions import PreimageSha256Fulfillment from bigchaindb_common.transaction import Transaction, Condition, Asset secret = b'much secret, wow' hashlock = PreimageSha256Fulfillment(preimage=secret).condition_uri cond = Condition(hashlock) expected = { 'transaction': { 'conditions': [cond.to_dict(0)], 'metadata': { 'data': data, }, 'asset': { 'id': data_id, 'divisible': False, 'updatable': False, 'refillable': False, 'data': data, }, 'fulfillments': [ { 'owners_before': [ user_pub, ], 'fid': 0, 'fulfillment': None, 'input': None }, ], 'operation': 'CREATE', }, 'version': 1 } asset = Asset(data, data_id) tx = Transaction.create([user_pub], [], data, asset, secret).to_dict() tx.pop('id') tx['transaction']['metadata'].pop('id') tx['transaction'].pop('timestamp') tx['transaction']['fulfillments'][0]['fulfillment'] = None assert tx == expected
def test_create_create_transaction_threshold(user_pub, user2_pub, user3_pub, user_user2_threshold_cond, user_user2_threshold_ffill, data, data_id): from bigchaindb_common.transaction import Transaction, Asset expected = { 'transaction': { 'conditions': [user_user2_threshold_cond.to_dict(0)], 'metadata': { 'data': data, }, 'asset': { 'id': data_id, 'divisible': False, 'updatable': False, 'refillable': False, 'data': data, }, 'fulfillments': [ { 'owners_before': [ user_pub, ], 'fid': 0, 'fulfillment': None, 'input': None }, ], 'operation': 'CREATE', }, 'version': 1 } asset = Asset(data, data_id) tx = Transaction.create([user_pub], [user_pub, user2_pub], data, asset) tx_dict = tx.to_dict() tx_dict.pop('id') tx_dict['transaction']['metadata'].pop('id') tx_dict['transaction'].pop('timestamp') tx_dict['transaction']['fulfillments'][0]['fulfillment'] = None assert tx_dict == expected
def utx(user_ffill, user_cond): from bigchaindb_common.transaction import Transaction, Asset return Transaction(Transaction.CREATE, Asset(), [user_ffill], [user_cond])
def test_validate_hashlock_create_transaction(user_pub, user_priv, data): from bigchaindb_common.transaction import Transaction, Asset tx = Transaction.create([user_pub], [], data, Asset(), b'much secret, wow') tx = tx.sign([user_priv]) assert tx.fulfillments_valid() is True
def test_add_condition_to_tx_with_invalid_parameters(): from bigchaindb_common.transaction import Transaction, Asset tx = Transaction(Transaction.CREATE, Asset(), [], []) with raises(TypeError): tx.add_condition('somewronginput')
def test_invalid_asset_comparison(data, data_id): from bigchaindb_common.transaction import Asset assert Asset(data, data_id) != 'invalid comparison'
def validate(self, bigchain): """Validate a transaction. Args: bigchain (Bigchain): an instantiated bigchaindb.Bigchain object. Returns: The transaction (Transaction) if the transaction is valid else it raises an exception describing the reason why the transaction is invalid. Raises: OperationError: if the transaction operation is not supported TransactionDoesNotExist: if the input of the transaction is not found TransactionOwnerError: if the new transaction is using an input it doesn't own DoubleSpend: if the transaction is a double spend InvalidHash: if the hash of the transaction is wrong InvalidSignature: if the signature of the transaction is wrong """ if len(self.fulfillments) == 0: raise ValueError('Transaction contains no fulfillments') input_conditions = [] inputs_defined = all([ffill.tx_input for ffill in self.fulfillments]) if self.operation in (Transaction.CREATE, Transaction.GENESIS): # validate inputs if inputs_defined: raise ValueError('A CREATE operation has no inputs') # validate asset self.asset._validate_asset() elif self.operation == Transaction.TRANSFER: if not inputs_defined: raise ValueError('Only `CREATE` transactions can have null ' 'inputs') # check inputs # store the inputs so that we can check if the asset ids match input_txs = [] for ffill in self.fulfillments: input_txid = ffill.tx_input.txid input_cid = ffill.tx_input.cid input_tx, status = bigchain.\ get_transaction(input_txid, include_status=True) if input_tx is None: raise TransactionDoesNotExist( "input `{}` doesn't exist".format(input_txid)) if status != bigchain.TX_VALID: raise FulfillmentNotInValidBlock( 'input `{}` does not exist in a valid block'.format( input_txid)) spent = bigchain.get_spent(input_txid, ffill.tx_input.cid) if spent and spent.id != self.id: raise DoubleSpend( 'input `{}` was already spent'.format(input_txid)) input_conditions.append(input_tx.conditions[input_cid]) input_txs.append(input_tx) # validate asset id asset_id = Asset.get_asset_id(input_txs) if asset_id != self.asset.data_id: raise AssetIdMismatch( 'The asset id of the input does not match the asset id of the transaction' ) else: allowed_operations = ', '.join(Transaction.ALLOWED_OPERATIONS) raise TypeError('`operation`: `{}` must be either {}.'.format( self.operation, allowed_operations)) if not self.fulfillments_valid(input_conditions): raise InvalidSignature() else: return self
def test_asset_creation_with_data(data): from bigchaindb_common.transaction import Asset asset = Asset(data) assert asset.data == data