def create_tx(current_owner, new_owner, tx_input, operation, payload=None): """Create a new transaction A transaction in the bigchain is a transfer of a digital asset between two entities represented by public keys. Currently the bigchain supports two types of operations: `CREATE` - Only federation nodes are allowed to use this operation. In a create operation a federation node creates a digital asset in the bigchain and assigns that asset to a public key. The owner of the private key can then decided to transfer this digital asset by using the `transaction id` of the transaction as an input in a `TRANSFER` transaction. `TRANSFER` - A transfer operation allows for a transfer of the digital assets between entities. 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. operation (str): Either `CREATE` or `TRANSFER` operation. payload (Optional[dict]): dictionary with information about asset. Returns: dict: unsigned transaction. Raises: TypeError: if the optional ``payload`` argument is not a ``dict``. """ data = None if payload is not None: if isinstance(payload, dict): hash_payload = crypto.hash_data(serialize(payload)) data = {'hash': hash_payload, 'payload': payload} else: raise TypeError('`payload` must be an dict instance') hash_payload = crypto.hash_data(serialize(payload)) data = {'hash': hash_payload, 'payload': payload} tx = { 'current_owner': current_owner, 'new_owner': new_owner, 'input': tx_input, 'operation': operation, 'timestamp': timestamp(), 'data': data } # serialize and convert to bytes tx_serialized = serialize(tx) tx_hash = crypto.hash_data(tx_serialized) # create the transaction transaction = {'id': tx_hash, 'transaction': tx} return transaction
def test_invalid_transactions_in_block(self, b, user_public_key, ): # invalid transaction valid_input = b.get_owned_ids(user_public_key).pop() tx_invalid = b.create_transaction('a', 'b', valid_input, 'c') block = b.create_block([tx_invalid]) # create a block with invalid transactions block = { 'timestamp': util.timestamp(), 'transactions': [tx_invalid], 'node_pubkey': b.me, 'voters': b.federation_nodes } block_data = util.serialize(block) block_hash = crypto.hash_data(block_data) block_signature = crypto.SigningKey(b.me_private).sign(block_data) block = { 'id': block_hash, 'block': block, 'signature': block_signature, 'votes': [] } with pytest.raises(exceptions.TransactionOwnerError) as excinfo: b.validate_block(block) assert excinfo.value.args[0] == 'current_owner `a` does not own the input `{}`'.format(valid_input)
def create_block(self, validated_transactions): """Creates a block given a list of `validated_transactions`. Note that this method does not validate the transactions. Transactions should be validated before calling create_block. Args: validated_transactions (list): list of validated transactions. Returns: dict: created block. """ # Prevent the creation of empty blocks if len(validated_transactions) == 0: raise exceptions.OperationError("Empty block creation is not allowed") # Create the new block block = { "timestamp": util.timestamp(), "transactions": validated_transactions, "node_pubkey": self.me, "voters": self.nodes_except_me + [self.me], } # Calculate the hash of the new block block_data = util.serialize(block) block_hash = crypto.hash_data(block_data) block_signature = crypto.SigningKey(self.me_private).sign(block_data) block = {"id": block_hash, "block": block, "signature": block_signature} return block
def test_invalid_transactions_in_block(self, b): # invalid transaction create_inputs() valid_input = b.get_owned_ids(USER_PUBLIC_KEY).pop() tx_invalid = b.create_transaction('a', 'b', valid_input, 'c') block = b.create_block([tx_invalid]) assert invalid_transactions == [tx_invalid] # create a block with invalid transactions block = { 'timestamp': b.timestamp(), 'transactions': [tx_invalid], 'node_pubkey': b.me, 'voters': b.federation_nodes } block_data = b.serialize(block) block_hash = hash_data(block_data) block_signature = PrivateKey(b.me_private).sign(block_data) block = { 'id': block_hash, 'block': block, 'signature': block_signature, 'votes': [] } with pytest.raises(exceptions.TransactionOwnerError) as excinfo: b.validate_block(block) assert excinfo.value.args[0] == 'current_owner `a` does not own the input `{}`'.format(valid_input)
def validate_block(bigchain, block): """Validate a block. Args: bigchain (Bigchain): an instantiated bigchaindb.Bigchain object. block (dict): block to validate. Returns: The block if the block is valid else it raises an exception describing the reason why the block is invalid. Raises: InvalidHash: if the hash of the block is wrong. """ # Check if current hash is correct calculated_hash = crypto.hash_data(util.serialize(block['block'])) if calculated_hash != block['id']: raise exceptions.InvalidHash() # Check if the block was created by a federation node if block['block']['node_pubkey'] not in (bigchain.nodes_except_me + [bigchain.me]): raise exceptions.OperationError( 'Only federation nodes can create blocks') # Check if block signature is valid verifying_key = crypto.VerifyingKey(block['block']['node_pubkey']) if not verifying_key.verify(util.serialize(block['block']), block['signature']): raise exceptions.InvalidSignature('Invalid block signature') return block
def validate_block(bigchain, block): """Validate a block. Args: bigchain (Bigchain): an instantiated bigchaindb.Bigchain object. block (dict): block to validate. Returns: The block if the block is valid else it raises an exception describing the reason why the block is invalid. Raises: InvalidHash: if the hash of the block is wrong. """ # Check if current hash is correct calculated_hash = crypto.hash_data(util.serialize(block['block'])) if calculated_hash != block['id']: raise exceptions.InvalidHash() # Check if the block was created by a federation node if block['block']['node_pubkey'] not in (bigchain.federation_nodes + [bigchain.me]): raise exceptions.OperationError('Only federation nodes can create blocks') # Check if block signature is valid verifying_key = crypto.VerifyingKey(block['block']['node_pubkey']) if not verifying_key.verify(util.serialize(block['block']), block['signature']): raise exceptions.InvalidSignature('Invalid block signature') return block
def test_invalid_transactions_in_block(self, b, user_public_key, ): # invalid transaction valid_input = b.get_owned_ids(user_public_key).pop() tx_invalid = b.create_transaction('a', 'b', valid_input, 'c') block = b.create_block([tx_invalid]) assert invalid_transactions == [tx_invalid] # create a block with invalid transactions block = { 'timestamp': b.timestamp(), 'transactions': [tx_invalid], 'node_pubkey': b.me, 'voters': b.federation_nodes } block_data = b.serialize(block) block_hash = hash_data(block_data) block_signature = PrivateKey(b.me_private).sign(block_data) block = { 'id': block_hash, 'block': block, 'signature': block_signature, 'votes': [] } with pytest.raises(exceptions.TransactionOwnerError) as excinfo: b.validate_block(block) assert excinfo.value.args[0] == 'current_owner `a` does not own the input `{}`'.format(valid_input)
def exists(digital_asset_payload): payload_hash = crypto.hash_data(util.serialize(digital_asset_payload)) transactions = b.get_tx_by_payload_hash(payload_hash) result = transactions is not None and len(transactions) > 0 return result
def create_block(self, validated_transactions): """Creates a block given a list of `validated_transactions`. Note that this method does not validate the transactions. Transactions should be validated before calling create_block. Args: validated_transactions (list): list of validated transactions. Returns: dict: created block. """ # Create the new block block = { 'timestamp': util.timestamp(), 'transactions': validated_transactions, 'node_pubkey': self.me, 'voters': self.federation_nodes + [self.me] } # Calculate the hash of the new block block_data = util.serialize(block) block_hash = crypto.hash_data(block_data) block_signature = crypto.SigningKey(self.me_private).sign(block_data) block = { 'id': block_hash, 'block': block, 'signature': block_signature, 'votes': [] } return block
def create_block(self, validated_transactions): """Creates a block given a list of `validated_transactions`. Note that this method does not validate the transactions. Transactions should be validated before calling create_block. Args: validated_transactions (list): list of validated transactions. Returns: dict: created block. """ # Create the new block block = { 'timestamp': util.timestamp(), 'transactions': validated_transactions, 'node_pubkey': self.me, 'voters': self.federation_nodes + [self.me] } # Calculate the hash of the new block block_data = util.serialize(block) block_hash = crypto.hash_data(block_data) block_signature = crypto.SigningKey(self.me_private).sign(block_data).decode() block = { 'id': block_hash, 'block': block, 'signature': block_signature, 'votes': [] } return block
def check_hash_and_signature(transaction): # Check hash of the transaction calculated_hash = crypto.hash_data(serialize(transaction['transaction'])) if calculated_hash != transaction['id']: raise exceptions.InvalidHash() # Check signature if not verify_signature(transaction): raise exceptions.InvalidSignature()
def test_create_new_block(self, b): new_block = b.create_block([]) block_hash = hash_data(b.serialize(new_block['block'])) assert new_block['block']['voters'] == [b.me] assert new_block['block']['node_pubkey'] == b.me assert PublicKey(b.me).verify(b.serialize(new_block['block']), new_block['signature']) is True assert new_block['id'] == block_hash assert new_block['votes'] == []
def check_hash_and_signature(transaction): # Check hash of the transaction calculated_hash = hash_data(serialize(transaction['transaction'])) if calculated_hash != transaction['id']: raise exceptions.InvalidHash() # Check signature if not verify_signature(transaction): raise exceptions.InvalidSignature()
def test_create_new_block(self, b): new_block = b.create_block([]) block_hash = hash_data(b.serialize(new_block['block'])) assert new_block['block']['voters'] == [b.me] assert new_block['block']['node_pubkey'] == b.me assert PublicKey(b.me).verify(b.serialize(new_block['block']), new_block['signature']) == True assert new_block['id'] == block_hash assert new_block['votes'] == []
def test_create_new_block(self, b): new_block = b.create_block([]) block_hash = crypto.hash_data(util.serialize(new_block['block'])) assert new_block['block']['voters'] == [b.me] assert new_block['block']['node_pubkey'] == b.me assert crypto.VerifyingKey(b.me).verify(util.serialize(new_block['block']), new_block['signature']) is True assert new_block['id'] == block_hash assert new_block['votes'] == []
def test_transaction_hash(self, b): payload = {'cats': 'are awesome'} tx = b.create_transaction('a', 'b', 'c', 'd', payload) tx_calculated = { 'current_owner': 'a', 'new_owner': 'b', 'input': 'c', 'operation': 'd', 'timestamp': tx['transaction']['timestamp'], 'data': { 'hash': hash_data(b.serialize(payload)), 'payload': payload } } assert tx['transaction']['data'] == tx_calculated['data']
def get_hash_data(transaction): """ Get the hashed data that (should) correspond to the `transaction['id']` Args: transaction (dict): the transaction to be hashed Returns: str: the hash of the transaction """ tx = copy.deepcopy(transaction) if 'transaction' in tx: tx = tx['transaction'] # remove the fulfillment messages (signatures) for fulfillment in tx['fulfillments']: fulfillment['fulfillment'] = None return crypto.hash_data(serialize(tx))
def validate_block(bigchain, block): """Validate a block. Args: bigchain (Bigchain): an instantiated bigchaindb.Bigchain object. block (dict): block to validate. Returns: The block if the block is valid else it raises an exception describing the reason why the block is invalid. Raises: InvalidHash: if the hash of the block is wrong. """ # Check if current hash is correct calculated_hash = crypto.hash_data(util.serialize(block['block'])) if calculated_hash != block['id']: raise exceptions.InvalidHash() return block
def create_block(self, validated_transactions): """Creates a block given a list of `validated_transactions`. Note that this method does not validate the transactions. Transactions should be validated before calling create_block. Args: validated_transactions (list): list of validated transactions. Returns: dict: created block. """ # Prevent the creation of empty blocks if len(validated_transactions) == 0: raise exceptions.OperationError( 'Empty block creation is not allowed') # Create the new block block = { 'timestamp': util.timestamp(), 'transactions': validated_transactions, 'node_pubkey': self.me, 'voters': self.nodes_except_me + [self.me] } # Calculate the hash of the new block block_data = util.serialize(block) block_hash = crypto.hash_data(block_data) block_signature = crypto.SigningKey(self.me_private).sign(block_data) block = { 'id': block_hash, 'block': block, 'signature': block_signature, 'votes': [] } return block
def create_block(self, validated_transactions): """Creates a block given a list of `validated_transactions`. Note that this method does not validate the transactions. Transactions should be validated before calling create_block. Args: validated_transactions (list): list of validated transactions. Returns: dict: created block. """ # Prevent the creation of empty blocks if len(validated_transactions) == 0: raise exceptions.OperationError('Empty block creation is not allowed') # Create the new block block = { 'timestamp': util.timestamp(), 'transactions': validated_transactions, 'node_pubkey': self.me, 'voters': self.nodes_except_me + [self.me] } # Calculate the hash of the new block block_data = util.serialize(block) block_hash = crypto.hash_data(block_data) block_signature = crypto.SigningKey(self.me_private).sign(block_data) block = { 'id': block_hash, 'block': block, 'signature': block_signature, 'votes': [] } return block
def validate_block(self, block): """Validate a block. Args: block (dict): block to validate. Returns: The block if the block is valid else it raises and exception describing the reason why the block is invalid. """ # 1. Check if current hash is correct calculated_hash = crypto.hash_data(util.serialize(block['block'])) if calculated_hash != block['id']: raise exceptions.InvalidHash() # 2. Validate all transactions in the block for transaction in block['block']['transactions']: if not self.is_valid_transaction(transaction): # this will raise the exception self.validate_transaction(transaction) return block
def create_tx(current_owners, new_owners, inputs, operation, payload=None): """Create a new transaction A transaction in the bigchain is a transfer of a digital asset between two entities represented by public keys. Currently the bigchain supports two types of operations: `CREATE` - Only federation nodes are allowed to use this operation. In a create operation a federation node creates a digital asset in the bigchain and assigns that asset to a public key. The owner of the private key can then decided to transfer this digital asset by using the `transaction id` of the transaction as an input in a `TRANSFER` transaction. `TRANSFER` - A transfer operation allows for a transfer of the digital assets between entities. Args: current_owners (list): base58 encoded public key of the current owners of the asset. new_owners (list): base58 encoded public key of the new owners of the digital asset. 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. Returns: dict: unsigned transaction. Raises: TypeError: if the optional ``payload`` argument is not a ``dict``. Reference: { "id": "<sha3 hash>", "version": "transaction version number", "transaction": { "fulfillments": [ { "current_owners": ["list of <pub-keys>"], "input": { "txid": "<sha3 hash>", "cid": "condition index" }, "fulfillment": "fulfillement of condition cid", "fid": "fulfillment index" } ], "conditions": [ { "new_owners": ["list of <pub-keys>"], "condition": "condition to be met", "cid": "condition index (1-to-1 mapping with fid)" } ], "operation": "<string>", "timestamp": "<timestamp from client>", "data": { "hash": "<SHA3-256 hash hexdigest of payload>", "payload": { "title": "The Winds of Plast", "creator": "Johnathan Plunkett", "IPFS_key": "QmfQ5QAjvg4GtA3wg3adpnDJug8ktA1BxurVqBD8rtgVjP" } } }, } """ # validate arguments (owners and inputs should be lists or None) # The None case appears on fulfilling a hashlock if current_owners is None: current_owners = [] if not isinstance(current_owners, list): current_owners = [current_owners] # The None case appears on assigning a hashlock if new_owners is None: new_owners = [] if not isinstance(new_owners, list): new_owners = [new_owners] if not isinstance(inputs, list): inputs = [inputs] # handle payload data = None if payload is not None: if isinstance(payload, dict): hash_payload = crypto.hash_data(serialize(payload)) data = {'hash': hash_payload, 'payload': payload} else: raise TypeError('`payload` must be an dict instance') # handle inputs fulfillments = [] # transfer if inputs: for fid, tx_input in enumerate(inputs): fulfillments.append({ 'current_owners': current_owners, 'input': tx_input, 'fulfillment': None, 'fid': fid }) # create else: fulfillments.append({ 'current_owners': current_owners, 'input': None, 'fulfillment': None, 'fid': 0 }) # handle outputs conditions = [] for fulfillment in fulfillments: # threshold condition if len(new_owners) > 1: condition = cc.ThresholdSha256Fulfillment( threshold=len(new_owners)) for new_owner in new_owners: condition.add_subfulfillment( cc.Ed25519Fulfillment(public_key=new_owner)) # simple signature condition elif len(new_owners) == 1: condition = cc.Ed25519Fulfillment(public_key=new_owners[0]) # to be added later (hashlock conditions) else: condition = None if condition: conditions.append({ 'new_owners': new_owners, 'condition': { 'details': rapidjson.loads(condition.serialize_json()), 'uri': condition.condition_uri }, 'cid': fulfillment['fid'] }) tx = { 'fulfillments': fulfillments, 'conditions': conditions, 'operation': operation, 'timestamp': timestamp(), 'data': data } # serialize and convert to bytes tx_hash = get_hash_data(tx) # create the transaction transaction = {'id': tx_hash, 'version': 1, 'transaction': tx} return transaction
def create_tx(current_owners, new_owners, inputs, operation, payload=None): """Create a new transaction A transaction in the bigchain is a transfer of a digital asset between two entities represented by public keys. Currently the bigchain supports two types of operations: `CREATE` - Only federation nodes are allowed to use this operation. In a create operation a federation node creates a digital asset in the bigchain and assigns that asset to a public key. The owner of the private key can then decided to transfer this digital asset by using the `transaction id` of the transaction as an input in a `TRANSFER` transaction. `TRANSFER` - A transfer operation allows for a transfer of the digital assets between entities. Args: current_owners (list): base58 encoded public key of the current owners of the asset. new_owners (list): base58 encoded public key of the new owners of the digital asset. 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. Returns: dict: unsigned transaction. Raises: TypeError: if the optional ``payload`` argument is not a ``dict``. Reference: { "id": "<sha3 hash>", "version": "transaction version number", "transaction": { "fulfillments": [ { "current_owners": ["list of <pub-keys>"], "input": { "txid": "<sha3 hash>", "cid": "condition index" }, "fulfillment": "fulfillement of condition cid", "fid": "fulfillment index" } ], "conditions": [ { "new_owners": ["list of <pub-keys>"], "condition": "condition to be met", "cid": "condition index (1-to-1 mapping with fid)" } ], "operation": "<string>", "timestamp": "<timestamp from client>", "data": { "hash": "<SHA3-256 hash hexdigest of payload>", "payload": { "title": "The Winds of Plast", "creator": "Johnathan Plunkett", "IPFS_key": "QmfQ5QAjvg4GtA3wg3adpnDJug8ktA1BxurVqBD8rtgVjP" } } }, } """ # validate arguments (owners and inputs should be lists or None) # The None case appears on fulfilling a hashlock if current_owners is None: current_owners = [] if not isinstance(current_owners, list): current_owners = [current_owners] # The None case appears on assigning a hashlock if new_owners is None: new_owners = [] if not isinstance(new_owners, list): new_owners = [new_owners] if not isinstance(inputs, list): inputs = [inputs] # handle payload data = None if payload is not None: if isinstance(payload, dict): hash_payload = crypto.hash_data(serialize(payload)) data = { 'hash': hash_payload, 'payload': payload } else: raise TypeError('`payload` must be an dict instance') # handle inputs fulfillments = [] # transfer if inputs: for fid, tx_input in enumerate(inputs): fulfillments.append({ 'current_owners': current_owners, 'input': tx_input, 'fulfillment': None, 'fid': fid }) # create else: fulfillments.append({ 'current_owners': current_owners, 'input': None, 'fulfillment': None, 'fid': 0 }) # handle outputs conditions = [] for fulfillment in fulfillments: # threshold condition if len(new_owners) > 1: condition = cc.ThresholdSha256Fulfillment(threshold=len(new_owners)) for new_owner in new_owners: condition.add_subfulfillment(cc.Ed25519Fulfillment(public_key=new_owner)) # simple signature condition elif len(new_owners) == 1: condition = cc.Ed25519Fulfillment(public_key=new_owners[0]) # to be added later (hashlock conditions) else: condition = None if condition: conditions.append({ 'new_owners': new_owners, 'condition': { 'details': json.loads(condition.serialize_json()), 'uri': condition.condition.serialize_uri() }, 'cid': fulfillment['fid'] }) tx = { 'fulfillments': fulfillments, 'conditions': conditions, 'operation': operation, 'timestamp': timestamp(), 'data': data } # serialize and convert to bytes tx_hash = get_hash_data(tx) # create the transaction transaction = { 'id': tx_hash, 'version': 1, 'transaction': tx } return transaction
def validate_transaction(bigchain, transaction): """Validate a transaction. Args: bigchain (Bigchain): an instantiated bigchaindb.Bigchain object. transaction (dict): transaction to validate. Returns: The 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 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']: raise ValueError('A CREATE operation has no inputs') if transaction['transaction']['current_owner'] not in ( bigchain.federation_nodes + [bigchain.me]): raise exceptions.OperationError( 'Only federation nodes can use the operation `CREATE`') else: # check if the input exists, is owned by the current_owner if not transaction['transaction']['input']: raise ValueError( 'Only `CREATE` transactions can have null inputs') tx_input = bigchain.get_transaction( 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'])) # 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 hash of the transaction calculated_hash = crypto.hash_data( util.serialize(transaction['transaction'])) if calculated_hash != transaction['id']: raise exceptions.InvalidHash() # Check signature if not util.verify_signature(transaction): raise exceptions.InvalidSignature() return transaction
def validate_transaction(bigchain, transaction): """Validate a transaction. Args: bigchain (Bigchain): an instantiated bigchaindb.Bigchain object. transaction (dict): transaction to validate. Returns: The 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 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']: raise ValueError('A CREATE operation has no inputs') if transaction['transaction']['current_owner'] not in ( bigchain.federation_nodes + [bigchain.me]): raise exceptions.OperationError( 'Only federation nodes can use the operation `CREATE`') else: # check if the input exists, is owned by the current_owner if not transaction['transaction']['input']: raise ValueError( 'Only `CREATE` transactions can have null inputs') tx_input = bigchain.get_transaction( 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'])) # 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 hash of the transaction calculated_hash = crypto.hash_data(util.serialize( transaction['transaction'])) if calculated_hash != transaction['id']: raise exceptions.InvalidHash() # Check signature if not util.verify_signature(transaction): raise exceptions.InvalidSignature() return transaction
def create_tx(current_owner, new_owner, tx_input, operation, payload=None): """Create a new transaction A transaction in the bigchain is a transfer of a digital asset between two entities represented by public keys. Currently the bigchain supports two types of operations: `CREATE` - Only federation nodes are allowed to use this operation. In a create operation a federation node creates a digital asset in the bigchain and assigns that asset to a public key. The owner of the private key can then decided to transfer this digital asset by using the `transaction id` of the transaction as an input in a `TRANSFER` transaction. `TRANSFER` - A transfer operation allows for a transfer of the digital assets between entities. 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. operation (str): Either `CREATE` or `TRANSFER` operation. payload (Optional[dict]): dictionary with information about asset. Returns: dict: unsigned transaction. Raises: TypeError: if the optional ``payload`` argument is not a ``dict``. """ data = None if payload is not None: if isinstance(payload, dict): hash_payload = crypto.hash_data(serialize(payload)) data = { 'hash': hash_payload, 'payload': payload } else: raise TypeError('`payload` must be an dict instance') hash_payload = crypto.hash_data(serialize(payload)) data = { 'hash': hash_payload, 'payload': payload } tx = { 'current_owner': current_owner, 'new_owner': new_owner, 'input': tx_input, 'operation': operation, 'timestamp': timestamp(), 'data': data } # serialize and convert to bytes tx_serialized = serialize(tx) tx_hash = crypto.hash_data(tx_serialized) # create the transaction transaction = { 'id': tx_hash, 'transaction': tx } return transaction