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_transaction(self, transaction): """Validate a transaction. Args: transaction (dict): transaction to validate. Returns: The transaction if the transaction is valid else it raises and 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 not (set(transaction['transaction']['current_owners']) <= set(self.federation_nodes + [self.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 = self.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_owners'] != transaction[ 'transaction']['current_owners']: raise exceptions.TransactionOwnerError( 'current_owner `{}` does not own the input `{}`'.format( transaction['transaction']['current_owners'], transaction['transaction']['input'])) # check if the input was already spent by a transaction other then this one. spent = self.get_spent(tx_input['id']) if spent: if spent['id'] != transaction['id']: raise exceptions.DoubleSpend( 'input `{}` was already spent'.format( transaction['transaction']['input'])) util.check_hash_and_signature(transaction) return transaction
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.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 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': # TODO: for now lets assume a CREATE transaction only has one fulfillment if transaction['transaction']['fulfillments'][0]['input']: raise ValueError('A CREATE operation has no inputs') # TODO: for now lets assume a CREATE transaction only has one current_owner if transaction['transaction']['fulfillments'][0]['current_owners'][ 0] not in (bigchain.nodes_except_me + [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']['fulfillments']: raise ValueError('Transaction contains no fulfillments') # check inputs for fulfillment in transaction['transaction']['fulfillments']: if not fulfillment['input']: raise ValueError( 'Only `CREATE` transactions can have null inputs') tx_input = bigchain.get_transaction( fulfillment['input']['txid']) if not tx_input: raise exceptions.TransactionDoesNotExist( 'input `{}` does not exist in the bigchain'.format( fulfillment['input']['txid'])) # TODO: check if current owners own tx_input (maybe checked by InvalidSignature) # check if the input was already spent by a transaction other than # this one. spent = bigchain.get_spent(fulfillment['input']) if spent and spent['id'] != transaction['id']: raise exceptions.DoubleSpend( 'input `{}` was already spent'.format( fulfillment['input'])) # Check hash of the transaction calculated_hash = util.get_hash_data(transaction) if calculated_hash != transaction['id']: raise exceptions.InvalidHash() # Check fulfillments if not util.validate_fulfillments(transaction): raise exceptions.InvalidSignature() return transaction