def _create_sponge(self, index): # type: (int) -> Curl """ Prepares the Curl sponge for the generator. """ seed = self.seed.as_trits() # type: MutableSequence[int] for i in range(index): # Treat ``seed`` like a really big number and add ``index``. # Note that addition works a little bit differently in balanced # ternary. for j in range(len(seed)): seed[j] += 1 if seed[j] > 1: seed[j] = -1 else: break sponge = Curl() sponge.absorb(seed) # Squeeze all of the trits out of the sponge and re-absorb them. # Note that Curl transforms several times per operation, so this # sequence is not as redundant as it looks at first glance. sponge.squeeze(seed) sponge.reset() sponge.absorb(seed) return sponge
def from_tryte_string(cls, trytes): # type: (TrytesCompatible) -> Transaction """ Creates a Transaction object from a sequence of trytes. """ tryte_string = TransactionTrytes(trytes) hash_ = [0] * HASH_LENGTH # type: MutableSequence[int] sponge = Curl() sponge.absorb(tryte_string.as_trits()) sponge.squeeze(hash_) return cls( hash_=TransactionHash.from_trits(hash_), signature_message_fragment=Fragment(tryte_string[0:2187]), address=Address(tryte_string[2187:2268]), value=int_from_trits(tryte_string[2268:2295].as_trits()), tag=Tag(tryte_string[2295:2322]), timestamp=int_from_trits(tryte_string[2322:2331].as_trits()), current_index=int_from_trits(tryte_string[2331:2340].as_trits()), last_index=int_from_trits(tryte_string[2340:2349].as_trits()), bundle_hash=BundleHash(tryte_string[2349:2430]), trunk_transaction_hash=TransactionHash(tryte_string[2430:2511]), branch_transaction_hash=TransactionHash(tryte_string[2511:2592]), nonce=Hash(tryte_string[2592:2673]), )
def finalize(self): # type: () -> None """ Finalizes the bundle, preparing it to be attached to the Tangle. """ if self.hash: raise RuntimeError('Bundle is already finalized.') if not self: raise ValueError('Bundle has no transactions.') # Quick validation. balance = self.balance if balance < 0: if self.change_address: self.add_transaction( ProposedTransaction( address=self.change_address, value=-balance, tag=self.tag, )) else: raise ValueError( 'Bundle has unspent inputs (balance: {balance}); ' 'use ``send_unspent_inputs_to`` to create ' 'change transaction.'.format(balance=balance, ), ) elif balance > 0: raise ValueError( 'Inputs are insufficient to cover bundle spend ' '(balance: {balance}).'.format(balance=balance, ), ) # Generate bundle hash. sponge = Curl() last_index = len(self) - 1 for (i, txn) in enumerate(self): # type: Tuple[int, ProposedTransaction] txn.current_index = i txn.last_index = last_index sponge.absorb(txn.get_signature_validation_trytes().as_trits()) bundle_hash = [0] * HASH_LENGTH # type: MutableSequence[int] sponge.squeeze(bundle_hash) self.hash = Hash.from_trits(bundle_hash) # Copy bundle hash to individual transactions. for txn in self: txn.bundle_hash = self.hash # Initialize signature/message fragment. txn.signature_message_fragment = Fragment(txn.message or b'')
def _generate_checksum(self): # type: () -> TryteString """ Generates the correct checksum for this address. """ checksum_trits = [] # type: MutableSequence[int] sponge = Curl() sponge.absorb(self.address.as_trits()) sponge.squeeze(checksum_trits) checksum_length = AddressChecksum.LEN * TRITS_PER_TRYTE return TryteString.from_trits(checksum_trits[:checksum_length])
def get_digest(self): # type: () -> Digest """ Generates the digest used to do the actual signing. Signing keys can have variable length and tend to be quite long, which makes them not-well-suited for use in crypto algorithms. The digest is essentially the result of running the signing key through a PBKDF, yielding a constant-length hash that can be used for crypto. """ hashes_per_fragment = FRAGMENT_LENGTH // Hash.LEN key_fragments = self.iter_chunks(FRAGMENT_LENGTH) # The digest will contain one hash per key fragment. digest = [0] * HASH_LENGTH * len(key_fragments) for (i, fragment ) in enumerate(key_fragments): # type: Tuple[int, TryteString] fragment_trits = fragment.as_trits() key_fragment = [0] * FRAGMENT_LENGTH hash_trits = [] for j in range(hashes_per_fragment): hash_start = j * HASH_LENGTH hash_end = hash_start + HASH_LENGTH hash_trits = fragment_trits[ hash_start:hash_end] # type: MutableSequence[int] for k in range(26): sponge = Curl() sponge.absorb(hash_trits) sponge.squeeze(hash_trits) key_fragment[hash_start:hash_end] = hash_trits sponge = Curl() sponge.absorb(key_fragment) sponge.squeeze(hash_trits) fragment_start = i * FRAGMENT_LENGTH fragment_end = fragment_start + FRAGMENT_LENGTH digest[fragment_start:fragment_end] = hash_trits return Digest(TryteString.from_trits(digest), self.key_index)
def address_from_digest(digest): # type: (Digest) -> Address """ Generates an address from a private key digest. """ address_trits = [0] * (Address.LEN * TRITS_PER_TRYTE ) # type: MutableSequence[int] sponge = Curl() sponge.absorb(digest.as_trits()) sponge.squeeze(address_trits) address = Address.from_trits(address_trits) address.key_index = digest.key_index return address
def validate_signature_fragments(fragments, hash_, public_key): # type: (Sequence[TryteString], Hash, TryteString) -> bool """ Returns whether a sequence of signature fragments is valid. :param fragments: Sequence of signature fragments (usually :py:class:`cornode.transaction.Fragment` instances). :param hash_: Hash used to generate the signature fragments (usually a :py:class:`cornode.transaction.BundleHash` instance). :param public_key: The public key value used to verify the signature digest (usually a :py:class:`cornode.types.Address` instance). """ checksum = [0] * (HASH_LENGTH * len(fragments)) normalized_hash = normalize(hash_) for (i, fragment) in enumerate(fragments): # type: Tuple[int, TryteString] outer_sponge = Curl() # If there are more than 3 iterations, loop back around to the # start. normalized_chunk = normalized_hash[i % len(normalized_hash)] buffer = [] for (j, hash_trytes) in enumerate(fragment.iter_chunks( Hash.LEN)): # type: Tuple[int, TryteString] buffer = hash_trytes.as_trits() # type: MutableSequence[int] inner_sponge = Curl() # Note the sign flip compared to ``SignatureFragmentGenerator``. for _ in range(13 + normalized_chunk[j]): inner_sponge.reset() inner_sponge.absorb(buffer) inner_sponge.squeeze(buffer) outer_sponge.absorb(buffer) outer_sponge.squeeze(buffer) checksum[i * HASH_LENGTH:(i + 1) * HASH_LENGTH] = buffer actual_public_key = [0] * HASH_LENGTH # type: MutableSequence[int] addy_sponge = Curl() addy_sponge.absorb(checksum) addy_sponge.squeeze(actual_public_key) return actual_public_key == public_key.as_trits()
class SignatureFragmentGenerator(Iterator[TryteString]): """ Used to generate signature fragments progressively. Each instance can generate 1 signature per fragment in the private key. """ def __init__(self, private_key, hash_): # type: (PrivateKey, TryteString) -> None super(SignatureFragmentGenerator, self).__init__() self._key_chunks = private_key.iter_chunks(FRAGMENT_LENGTH) self._iteration = -1 self._normalized_hash = normalize(hash_) self._sponge = Curl() def __iter__(self): # type: () -> SignatureFragmentGenerator return self def __len__(self): # type: () -> int """ Returns the number of fragments this generator can create. Note: This method always returns the same result, no matter how many iterations have been completed. """ return len(self._key_chunks) def __next__(self): # type: () -> TryteString """ Returns the next signature fragment. """ key_trytes = next(self._key_chunks) # type: TryteString self._iteration += 1 # If the key is long enough, loop back around to the start. normalized_chunk =\ self._normalized_hash[self._iteration % len(self._normalized_hash)] signature_fragment = key_trytes.as_trits() # Build the signature, one hash at a time. for i in range(key_trytes.count_chunks(Hash.LEN)): hash_start = i * HASH_LENGTH hash_end = hash_start + HASH_LENGTH buffer = signature_fragment[hash_start: hash_end] # type: MutableSequence[int] for _ in range(13 - normalized_chunk[i]): self._sponge.reset() self._sponge.absorb(buffer) self._sponge.squeeze(buffer) signature_fragment[hash_start:hash_end] = buffer return TryteString.from_trits(signature_fragment) if PY2: next = __next__