def _get_group_signature_error(group, sponge_type): # type: (List[Transaction], type) -> Optional[Text] """ Validates the signature fragments for a group of transactions using the specified sponge type. Note: this method assumes that the transactions in the group have already passed basic validation (see :py:meth:`_create_validator`). :return: - ``None``: Indicates that the signature fragments are valid. - ``Text``: Error message indicating the fragments are invalid. """ validate_group_signature =\ validate_signature_fragments( fragments = [txn.signature_message_fragment for txn in group], hash_ = group[0].bundle_hash, public_key = group[0].address, sponge_type = sponge_type, ) if validate_group_signature: return None return ( 'Transaction {i} has invalid signature ' '(using {fragments} fragments).'.format( fragments = len(group), i = group[0].current_index, ) )
def _create_validator(self): # type: () -> Generator[Text] """ Creates a generator that does all the work. """ bundle_hash = self.bundle.hash last_index = len(self.bundle) - 1 balance = 0 for (i, txn) in enumerate(self.bundle): # type: Tuple[int, Transaction] balance += txn.value if txn.bundle_hash != bundle_hash: yield 'Transaction {i} has invalid bundle hash.'.format(i=i, ) if txn.current_index != i: yield ('Transaction {i} has invalid current index value ' '(expected {i}, actual {actual}).'.format( actual=txn.current_index, i=i, )) if txn.last_index != last_index: yield ('Transaction {i} has invalid last index value ' '(expected {expected}, actual {actual}).'.format( actual=txn.last_index, expected=last_index, i=i, )) if balance != 0: yield ('Bundle has invalid balance (expected 0, actual {actual}).'. format(actual=balance, )) # Signature validation is only meaningful if the transactions are # otherwise valid. if not self._errors: i = 0 while i <= last_index: txn = self.bundle[i] if txn.value < 0: signature_fragments = [txn.signature_message_fragment] # The following transaction(s) should contain additional # fragments. fragments_valid = True j = 0 for j in range(1, AddressGenerator.DIGEST_ITERATIONS): i += 1 try: next_txn = self.bundle[i] except IndexError: yield ( 'Reached end of bundle while looking for ' 'signature fragment {j} for transaction {i}.'. format( i=txn.current_index, j=j + 1, )) fragments_valid = False break if next_txn.address != txn.address: yield ('Unable to find signature fragment {j} ' 'for transaction {i}.'.format( i=txn.current_index, j=j + 1, )) fragments_valid = False break if next_txn.value != 0: yield ('Transaction {i} has invalid amount ' '(expected 0, actual {actual}).'.format( actual=next_txn.value, i=next_txn.current_index, )) fragments_valid = False # Keep going, just in case there's another signature # fragment next (so that we skip it in the next iteration # of the outer loop). continue signature_fragments.append( next_txn.signature_message_fragment) if fragments_valid: signature_valid = validate_signature_fragments( fragments=signature_fragments, hash_=txn.bundle_hash, public_key=txn.address, ) if not signature_valid: yield ('Transaction {i} has invalid signature ' '(using {fragments} fragments).'.format( fragments=len(signature_fragments), i=txn.current_index, )) # Skip signature fragments in the next iteration. # Note that it's possible to have # ``j < AddressGenerator.DIGEST_ITERATIONS`` if the bundle is # badly malformed. i += j else: # No signature to validate; skip this transaction. i += 1
def _create_validator(self): # type: () -> Generator[Text] """ Creates a generator that does all the work. """ # Group transactions by address to make it easier to iterate over # inputs. grouped_transactions = self.bundle.group_transactions() # Define a few expected values. bundle_hash = self.bundle.hash last_index = len(self.bundle) - 1 # Track a few others as we go along. balance = 0 # Check indices and balance first. # Note that we use a counter to keep track of the current index, # since at this point we can't trust that the transactions have # correct ``current_index`` values. counter = 0 for group in grouped_transactions: for txn in group: balance += txn.value if txn.bundle_hash != bundle_hash: yield 'Transaction {i} has invalid bundle hash.'.format( i=counter, ) if txn.current_index != counter: yield ('Transaction {i} has invalid current index value ' '(expected {i}, actual {actual}).'.format( actual=txn.current_index, i=counter, )) if txn.last_index != last_index: yield ('Transaction {i} has invalid last index value ' '(expected {expected}, actual {actual}).'.format( actual=txn.last_index, expected=last_index, i=counter, )) counter += 1 # Bundle must be balanced (spends must match inputs). if balance != 0: yield ('Bundle has invalid balance (expected 0, actual {actual}).'. format(actual=balance, )) # Signature validation is only meaningful if the transactions are # otherwise valid. if not self._errors: for group in grouped_transactions: # Signature validation only applies to inputs. if group[0].value >= 0: continue signature_valid = True signature_fragments = [] for j, txn in enumerate( group): # type: Tuple[int, Transaction] if (j > 0) and (txn.value != 0): # Input is malformed; signature fragments after the first # should have zero value. yield ( 'Transaction {i} has invalid amount ' '(expected 0, actual {actual}).'.format( actual=txn.value, # If we get to this point, we know that the # ``current_index`` value for each transaction can be # trusted. i=txn.current_index, )) # We won't be able to validate the signature, but continue # anyway, so that we can check that the other transactions # in the group have the correct ``value``. signature_valid = False continue signature_fragments.append(txn.signature_message_fragment) # After collecting the signature fragment from each transaction # in the group, run it through the validator. if signature_valid: signature_valid = validate_signature_fragments( fragments=signature_fragments, hash_=txn.bundle_hash, public_key=txn.address, ) if not signature_valid: yield ( 'Transaction {i} has invalid signature ' '(using {fragments} fragments).'.format( fragments=len(signature_fragments), # If we get to this point, we know that the # ``current_index`` value for each transaction can be # trusted. i=group[0].current_index, ))