def validate(self, bigchain): """Validate a block. Args: bigchain (Bigchain): an instantiated bigchaindb.Bigchain object. Returns: block (Block): The block as a `Block` object if it is valid. Else it raises an appropriate exception describing the reason of invalidity. Raises: OperationError: if a non-federation node signed the block. """ # First, make sure this node hasn't already voted on this block if bigchain.has_previous_vote(self.id, self.voters): return self # Check if the block was created by a federation node possible_voters = (bigchain.nodes_except_me + [bigchain.me]) if self.node_pubkey not in possible_voters: raise OperationError('Only federation nodes can create blocks') if not self.is_signature_valid(): raise InvalidSignature('Block signature invalid') # Finally: Tentative assumption that every blockchain will want to # validate all transactions in each block for tx in self.transactions: # NOTE: If a transaction is not valid, `is_valid` will throw an # an exception and block validation will be canceled. bigchain.validate_transaction(tx) return self
def from_dict(cls, block_body): block = block_body['block'] block_serialized = serialize(block) block_id = hash_data(block_serialized) verifying_key = VerifyingKey(block['node_pubkey']) try: signature = block_body['signature'] except KeyError: signature = None if block_id != block_body['id']: raise InvalidHash() if signature is not None: # NOTE: CC throws a `ValueError` on some wrong signatures # https://github.com/bigchaindb/cryptoconditions/issues/27 try: signature_valid = verifying_key\ .verify(block_serialized.encode(), signature) except ValueError: signature_valid = False if signature_valid is False: raise InvalidSignature('Invalid block signature') transactions = [ Transaction.from_dict(tx) for tx in block['transactions'] ] return cls(transactions, block['node_pubkey'], block['timestamp'], block['voters'], signature)
def validate(self, bigchain, current_transactions=[]): """Validate transaction spend Args: bigchain (BigchainDB): an instantiated bigchaindb.BigchainDB object. Returns: The transaction (Transaction) if the transaction is valid else it raises an exception describing the reason why the transaction is invalid. Raises: ValidationError: If the transaction is invalid """ input_conditions = [] if self.operation == Transaction.CREATE: duplicates = any(txn for txn in current_transactions if txn.id == self.id) if bigchain.is_committed(self.id) or duplicates: raise DuplicateTransaction( 'transaction `{}` already exists'.format(self.id)) if not self.inputs_valid(input_conditions): raise InvalidSignature('Transaction signature is invalid.') elif self.operation == Transaction.TRANSFER: self.validate_transfer_inputs(bigchain, current_transactions) return self
def from_dict(cls, ffill): """Transforms a Python dictionary to a Fulfillment object. Note: Optionally, this method can also serialize a Cryptoconditions- Fulfillment that is not yet signed. Args: ffill (dict): The Fulfillment to be transformed. Returns: :class:`~bigchaindb.common.transaction.Fulfillment` Raises: InvalidSignature: If a Fulfillment's URI couldn't be parsed. """ try: fulfillment = CCFulfillment.from_uri(ffill['fulfillment']) except ValueError: # TODO FOR CC: Throw an `InvalidSignature` error in this case. raise InvalidSignature("Fulfillment URI couldn't been parsed") except TypeError: # NOTE: See comment about this special case in # `Fulfillment.to_dict` fulfillment = CCFulfillment.from_dict(ffill['fulfillment']) input_ = TransactionLink.from_dict(ffill['input']) return cls(fulfillment, ffill['owners_before'], input_)
def from_dict(cls, data): """Transforms a Python dictionary to an Input object. Note: Optionally, this method can also serialize a Cryptoconditions- Fulfillment that is not yet signed. Args: data (dict): The Input to be transformed. Returns: :class:`~bigchaindb.common.transaction.Input` Raises: InvalidSignature: If an Input's URI couldn't be parsed. """ fulfillment = data['fulfillment'] if not isinstance(fulfillment, (Fulfillment, type(None))): try: fulfillment = Fulfillment.from_uri(data['fulfillment']) except ASN1DecodeError: # TODO Remove as it is legacy code, and simply fall back on # ASN1DecodeError raise InvalidSignature("Fulfillment URI couldn't been parsed") except TypeError: # NOTE: See comment about this special case in # `Input.to_dict` fulfillment = _fulfillment_from_details(data['fulfillment']) fulfills = TransactionLink.from_dict(data['fulfills']) return cls(fulfillment, data['owners_before'], fulfills)
def validate(self, bigchain, current_transactions=[]): """Validate election transaction For more details refer BEP-21: https://github.com/bigchaindb/BEPs/tree/master/21 NOTE: * A valid election is initiated by an existing validator. * A valid election is one where voters are validators and votes are alloacted according to the voting power of each validator node. Args: bigchain (BigchainDB): an instantiated bigchaindb.lib.BigchainDB object. Returns: ValidatorElection object Raises: ValidationError: If the election is invalid """ input_conditions = [] duplicates = any(txn for txn in current_transactions if txn.id == self.id) if bigchain.get_transaction(self.id) or duplicates: raise DuplicateTransaction( 'transaction `{}` already exists'.format(self.id)) if not self.inputs_valid(input_conditions): raise InvalidSignature('Transaction signature is invalid.') current_validators = self.get_validators(bigchain) # NOTE: Proposer should be a single node if len(self.inputs) != 1 or len(self.inputs[0].owners_before) != 1: raise MultipleInputsError( '`tx_signers` must be a list instance of length one') # NOTE: change more than 1/3 of the current power is not allowed if self.asset['data']['power'] >= (1 / 3) * sum( current_validators.values()): raise InvalidPowerChange( '`power` change must be less than 1/3 of total power') # NOTE: Check if the proposer is a validator. [election_initiator_node_pub_key] = self.inputs[0].owners_before if election_initiator_node_pub_key not in current_validators.keys(): raise InvalidProposer( 'Public key is not a part of the validator set') # NOTE: Check if all validators have been assigned votes equal to their voting power if not self.is_same_topology(current_validators, self.outputs): raise UnequalValidatorSet( 'Validator set much be exactly same to the outputs of election' ) return self
def validate_transfer_inputs(self, bigchain, current_transactions=[]): # store the inputs so that we can check if the asset ids match input_txs = [] input_conditions = [] for input_ in self.inputs: input_txid = input_.fulfills.txid input_tx = bigchain.get_transaction(input_txid) if input_tx is None: for ctxn in current_transactions: if ctxn.id == input_txid: input_tx = ctxn if input_tx is None: raise InputDoesNotExist( "input `{}` doesn't exist".format(input_txid)) spent = bigchain.get_spent(input_txid, input_.fulfills.output, current_transactions) if spent: raise DoubleSpend( 'input `{}` was already spent'.format(input_txid)) output = input_tx.outputs[input_.fulfills.output] input_conditions.append(output) input_txs.append(input_tx) # Validate that all inputs are distinct links = [i.fulfills.to_uri() for i in self.inputs] if len(links) != len(set(links)): raise DoubleSpend('tx "{}" spends inputs twice'.format(self.id)) # validate asset id asset_id = self.get_asset_id(input_txs) if asset_id != self.asset['id']: raise AssetIdMismatch(('The asset id of the input does not' ' match the asset id of the' ' transaction')) input_amount = sum( [input_condition.amount for input_condition in input_conditions]) output_amount = sum( [output_condition.amount for output_condition in self.outputs]) if output_amount != input_amount: raise AmountError( ('The amount used in the inputs `{}`' ' needs to be same as the amount used' ' in the outputs `{}`').format(input_amount, output_amount)) if not self.inputs_valid(input_conditions): raise InvalidSignature('Transaction signature is invalid.') return True
def validate(self, bigchain, current_transactions=[]): """Validate election transaction NOTE: * A valid election is initiated by an existing validator. * A valid election is one where voters are validators and votes are allocated according to the voting power of each validator node. Args: :param bigchain: (BigchainDB) an instantiated bigchaindb.lib.BigchainDB object. :param current_transactions: (list) A list of transactions to be validated along with the election Returns: Election: a Election object or an object of the derived Election subclass. Raises: ValidationError: If the election is invalid """ input_conditions = [] duplicates = any(txn for txn in current_transactions if txn.id == self.id) if bigchain.is_committed(self.id) or duplicates: raise DuplicateTransaction( 'transaction `{}` already exists'.format(self.id)) if not self.inputs_valid(input_conditions): raise InvalidSignature('Transaction signature is invalid.') current_validators = self.get_validators(bigchain) # NOTE: Proposer should be a single node if len(self.inputs) != 1 or len(self.inputs[0].owners_before) != 1: raise MultipleInputsError( '`tx_signers` must be a list instance of length one') # NOTE: Check if the proposer is a validator. [election_initiator_node_pub_key] = self.inputs[0].owners_before if election_initiator_node_pub_key not in current_validators.keys(): raise InvalidProposer( 'Public key is not a part of the validator set') # NOTE: Check if all validators have been assigned votes equal to their voting power if not self.is_same_topology(current_validators, self.outputs): raise UnequalValidatorSet( 'Validator set much be exactly same to the outputs of election' ) return self
def from_dict(cls, block_body): """Transform a Python dictionary to a Block object. Args: block_body (dict): A block dictionary to be transformed. Returns: :class:`~Block` Raises: InvalidHash: If the block's id is not corresponding to its data. InvalidSignature: If the block's signature is not corresponding to it's data or `node_pubkey`. """ # TODO: Reuse `is_signature_valid` method here. block = block_body['block'] block_serialized = serialize(block) block_id = hash_data(block_serialized) public_key = PublicKey(block['node_pubkey']) try: signature = block_body['signature'] except KeyError: signature = None if block_id != block_body['id']: raise InvalidHash() if signature is not None: # NOTE: CC throws a `ValueError` on some wrong signatures # https://github.com/bigchaindb/cryptoconditions/issues/27 try: signature_valid = public_key\ .verify(block_serialized.encode(), signature) except ValueError: signature_valid = False if signature_valid is False: raise InvalidSignature('Invalid block signature') transactions = [ Transaction.from_dict(tx) for tx in block['transactions'] ] return cls(transactions, block['node_pubkey'], block['timestamp'], block['voters'], signature)
def _validate_block(self, bigchain): """Validate the Block without validating the transactions. Args: bigchain (:class:`~bigchaindb.Bigchain`): An instantiated Bigchain object. Raises: ValidationError: If there is a problem with the block """ # Check if the block was created by a federation node if self.node_pubkey not in bigchain.federation: raise SybilError('Only federation nodes can create blocks') # Check that the signature is valid if not self.is_signature_valid(): raise InvalidSignature('Invalid block signature')
def _validate_block(self, bigchain): """Validate the Block without validating the transactions. Args: bigchain (:class:`~bigchaindb.Bigchain`): An instantiated Bigchain object. Raises: ValidationError: If there is a problem with the block """ # Check that the signature is valid if not self.is_signature_valid(): raise InvalidSignature('Invalid block signature') # Check that the block contains no duplicated transactions txids = [tx.id for tx in self.transactions] if len(txids) != len(set(txids)): raise DuplicateTransaction('Block has duplicate transaction')
def _validate_block(self, bigchain): """Validate the Block without validating the transactions. Args: bigchain (:class:`~bigchaindb.Bigchain`): An instantiated Bigchain object. Raises: OperationError: If a non-federation node signed the Block. InvalidSignature: If a Block's signature is invalid. """ # Check if the block was created by a federation node possible_voters = (bigchain.nodes_except_me + [bigchain.me]) if self.node_pubkey not in possible_voters: raise OperationError('Only federation nodes can create blocks') # Check that the signature is valid if not self.is_signature_valid(): raise InvalidSignature('Invalid block signature')
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 TransactionNotInValidBlock: if the input of the transaction is not in a valid block 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.inputs) == 0: raise ValueError('Transaction contains no inputs') input_conditions = [] inputs_defined = all([input_.fulfills for input_ in self.inputs]) # validate amounts if any(output.amount < 1 for output in self.outputs): raise AmountError('`amount` needs to be greater than zero') if self.operation in (Transaction.CREATE, Transaction.GENESIS): # validate asset if self.asset['data'] is not None and not isinstance( self.asset['data'], dict): raise TypeError(('`asset.data` must be a dict instance or ' 'None for `CREATE` transactions')) # validate inputs if inputs_defined: raise ValueError('A CREATE operation has no inputs') elif self.operation == Transaction.TRANSFER: # validate asset if not isinstance(self.asset['id'], str): raise ValueError(('`asset.id` must be a string for ' '`TRANSFER` transations')) # check inputs if not inputs_defined: raise ValueError('Only `CREATE` transactions can have null ' 'inputs') # store the inputs so that we can check if the asset ids match input_txs = [] for input_ in self.inputs: input_txid = input_.fulfills.txid 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 TransactionNotInValidBlock( 'input `{}` does not exist in a valid block'.format( input_txid)) 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)) output = input_tx.outputs[input_.fulfills.output] input_conditions.append(output) input_txs.append(input_tx) if output.amount < 1: raise AmountError('`amount` needs to be greater than zero') # Validate that all inputs are distinct links = [i.fulfills.to_uri() for i in self.inputs] if len(links) != len(set(links)): raise DoubleSpend('tx "{}" spends inputs twice'.format( self.id)) # validate asset id asset_id = Transaction.get_asset_id(input_txs) if asset_id != self.asset['id']: raise AssetIdMismatch(('The asset id of the input does not' ' match the asset id of the' ' transaction')) # validate the amounts for output in self.outputs: if output.amount < 1: raise AmountError('`amount` needs to be greater than zero') input_amount = sum([ input_condition.amount for input_condition in input_conditions ]) output_amount = sum( [output_condition.amount for output_condition in self.outputs]) if output_amount != input_amount: raise AmountError( ('The amount used in the inputs `{}`' ' needs to be same as the amount used' ' in the outputs `{}`').format(input_amount, output_amount)) else: allowed_operations = ', '.join(Transaction.ALLOWED_OPERATIONS) raise TypeError('`operation`: `{}` must be either {}.'.format( self.operation, allowed_operations)) if not self.inputs_valid(input_conditions): raise InvalidSignature('Transaction signature is invalid.') return self
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 validate(self, bigchain): """Validate transaction spend 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: ValidationError: If the transaction is invalid """ input_conditions = [] if self.operation == Transaction.TRANSFER: # store the inputs so that we can check if the asset ids match input_txs = [] for input_ in self.inputs: input_txid = input_.fulfills.txid input_tx, status = bigchain.\ get_transaction(input_txid, include_status=True) if input_tx is None: raise InputDoesNotExist( "input `{}` doesn't exist".format(input_txid)) if status != bigchain.TX_VALID: raise TransactionNotInValidBlock( 'input `{}` does not exist in a valid block'.format( input_txid)) 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)) output = input_tx.outputs[input_.fulfills.output] input_conditions.append(output) input_txs.append(input_tx) # Validate that all inputs are distinct links = [i.fulfills.to_uri() for i in self.inputs] if len(links) != len(set(links)): raise DoubleSpend('tx "{}" spends inputs twice'.format( self.id)) # validate asset id asset_id = Transaction.get_asset_id(input_txs) if asset_id != self.asset['id']: raise AssetIdMismatch(('The asset id of the input does not' ' match the asset id of the' ' transaction')) input_amount = sum([ input_condition.amount for input_condition in input_conditions ]) output_amount = sum( [output_condition.amount for output_condition in self.outputs]) if output_amount != input_amount: raise AmountError( ('The amount used in the inputs `{}`' ' needs to be same as the amount used' ' in the outputs `{}`').format(input_amount, output_amount)) if not self.inputs_valid(input_conditions): raise InvalidSignature('Transaction signature is invalid.') return self
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 """ # print(self.operation) if self.operation == Transaction.METADATA: return self if len( self.fulfillments ) == 0 and self.operation != Transaction.CONTRACT and self.operation != Transaction.INTERIM: # print(self.id) print('Transaction contains no fulfillments') raise ValueError('Transaction contains no fulfillments') # print("3") # print("self::",self) if len( self.conditions ) == 0 and self.operation != Transaction.CONTRACT and self.operation != Transaction.INTERIM: print('Transaction contains no conditions') raise ValueError('Transaction contains no conditions') input_conditions = [] inputs_defined = all([ffill.tx_input for ffill in self.fulfillments]) # print("4",inputs_defined) if self.operation in (Transaction.CREATE, Transaction.GENESIS): # print("5") # validate inputs if inputs_defined: raise ValueError('A CREATE operation has no inputs') # validate asset self.asset._validate_asset() elif self.operation in (Transaction.CONTRACT, Transaction.INTERIM): pass elif self.operation == Transaction.TRANSFER: if not inputs_defined: raise ValueError('Only `CREATE` transactions can have null ' 'inputs') # print("6") # 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)) # print("validate in=2========",self.operation) if self.operation in (Transaction.CONTRACT): # validate contract signature # 1.validate the contract users signture # print("7") ContractBody = deepcopy(self.Contract["ContractBody"]) contract_owners = ContractBody["ContractOwners"] contract_signatures = ContractBody["ContractSignatures"] ContractBody["ContractSignatures"] = None detail_serialized = serialize(ContractBody) if contract_owners != None and contract_signatures != None: if len(contract_owners) < len(contract_signatures): raise MutilContractOwner for index, contract_sign in enumerate(contract_signatures): owner_pubkey = contract_owners[index] signature = contract_sign["Signature"] if not self.is_signature_valid(detail_serialized, owner_pubkey, signature): print("Invalid contract Signature") raise InvalidSignature() return self else: # TODO 2.validate the contract votes? return self # print("validate in=3========",self.operation,"==",self.version) if self.version == 2: # 1.validate the nodes signature voters = self.Relation["Voters"] votes = self.Relation["Votes"] # print("taskid 146 --- ",self.Relation["TaskId"]) # tx_dict = deepcopy(self.to_dict()) # tx_dict["transaction"].pop('Relation') # tx_dict["transaction"].pop('Contract') # detail_serialized = serialize(tx_dict) if len(voters) < len(votes): raise MutilcontractNode for index, vote in enumerate(votes): # print(index) owner_pubkey = voters[index] signature = vote["Signature"] detail_serialized = self.id print(detail_serialized) # print(owner_pubkey) # print(signature) if not self.is_signature_valid(detail_serialized, owner_pubkey, signature): print("Invalid vote Signature") raise InvalidSignature() return self if not self.fulfillments_valid(input_conditions): raise InvalidSignature() else: return self