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 get_spent(self, txid, output, current_transactions=[]): transactions = backend.query.get_spent(self.connection, txid, output) transactions = list(transactions) if transactions else [] if len(transactions) > 1: raise core_exceptions.CriticalDoubleSpend( '`{}` was spent more than once. There is a problem' ' with the chain'.format(txid)) current_spent_transactions = [] for ctxn in current_transactions: for ctxn_input in ctxn.inputs: if ctxn_input.fulfills and\ ctxn_input.fulfills.txid == txid and\ ctxn_input.fulfills.output == output: current_spent_transactions.append(ctxn) transaction = None if len(transactions) + len(current_spent_transactions) > 1: raise DoubleSpend('tx "{}" spends inputs twice'.format(txid)) elif transactions: transaction = Transaction.from_db(self, transactions[0]) elif current_spent_transactions: transaction = current_spent_transactions[0] return transaction
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