def test_happy_path(self): """ Typical use case. """ # noinspection SpellCheckingInspection input_ = ( 'EMIDYNHBWMBCXVDEFOFWINXTERALUKYYPPHKP9JJ' 'FGJEIUY9MUDVNFZHMMWZUYUSWAIOWEVTHNWMHANBH' ) trits = TryteString(input_).as_trits() curl = Curl() curl.absorb(trits) trits_out = [] curl.squeeze(trits_out) trits_out = TryteString.from_trits(trits_out) # print('trits_out: ', trits_out) # # AQBOPUMJMGVHFOXSMUAGZNACKUTISDPBSILMRAGIGRXXS9JJTLIKZUW9BCJWKSTFBDSBLNVEEGVGAMSSM # noinspection SpellCheckingInspection self.assertEqual( trits_out, 'AQBOPUMJMGVHFOXSMUAGZNACKUTISDPBSILMRAGI' 'GRXXS9JJTLIKZUW9BCJWKSTFBDSBLNVEEGVGAMSSM', )
def test_length_greater_than_243(self): """ The input is longer than 1 hash. """ # noinspection SpellCheckingInspection input_ = ( 'G9JYBOMPUXHYHKSNRNMMSSZCSHOFYOYNZRSZMAAYWDYEIMVVOGKPJB' 'VBM9TDPULSFUNMTVXRKFIDOHUXXVYDLFSZYZTWQYTE9SPYYWYTXJYQ' '9IFGYOLZXWZBKWZN9QOOTBQMWMUBLEWUEEASRHRTNIQWJQNDWRYLCA' ) trits = TryteString(input_).as_trits() curl = Curl() curl.absorb(trits) trits_out = [] curl.squeeze(trits_out) trits_out = TryteString.from_trits(trits_out) # noinspection SpellCheckingInspection self.assertEqual( trits_out, 'RWCBOLRFANOAYQWXXTFQJYQFAUTEEBSZWTIRSSDR' 'EYGCNFRLHQVDZXYXSJKCQFQLJMMRHYAZKRRLQZDKR', )
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 test_squeeze_multiple_hashes(self): """ Squeezing more than 1 hash from the sponge. """ # noinspection SpellCheckingInspection input_ = ('EMIDYNHBWMBCXVDEFOFWINXTERALUKYYPPHKP9JJ' 'FGJEIUY9MUDVNFZHMMWZUYUSWAIOWEVTHNWMHANBH') trits = TryteString(input_).as_trits() curl = Curl() curl.absorb(trits) trits_out = [] curl.squeeze(trits_out, length=486) trits_out = TryteString.from_trits(trits_out) # noinspection SpellCheckingInspection self.assertEqual( trits_out, 'AQBOPUMJMGVHFOXSMUAGZNACKUTISDPBSILMRAGIG' 'RXXS9JJTLIKZUW9BCJWKSTFBDSBLNVEEGVGAMSSMQ' 'GSJWCCFQRHWKTSMVPWWCEGOMCNWFYWDZBEDBLXIFB' 'HOTCKUMCANLSXXTNKSYNBMOSDDEYFTDOYIKDRJM', )
def test_length(self): """ Specifying different values for the ``length`` argument. """ # noinspection SpellCheckingInspection input_ = ('G9JYBOMPUXHYHKSNRNMMSSZCSHOFYOYNZRSZMAAYWDYEIMVVOGKPJB' 'VBM9TDPULSFUNMTVXRKFIDOHUXXVYDLFSZYZTWQYTE9SPYYWYTXJYQ' '9IFGYOLZXWZBKWZN9QOOTBQMWMUBLEWUEEASRHRTNIQWJQNDWRYLCA') trits = TryteString(input_).as_trits() curl = Curl() curl.absorb(trits, offset=0, length=486) curl.absorb(trits, offset=0, length=243) trits_out = [] curl.squeeze(trits_out) trits_out = TryteString.from_trits(trits_out) # noinspection SpellCheckingInspection self.assertEqual( trits_out, 'OTYHXEXJLCSMEY9LYCC9ASJXMORTLAYQEHRS9DAH' '9NR9DXLXYDGOVOBEL9LWRITLWPHPYPZDKXVPAPKUA', )
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 test_absorb_offset(self): """ Passing an ``offset`` argument to :py:meth:`Curl.absorb`. """ # noinspection SpellCheckingInspection input_ = ('G9JYBOMPUXHYHKSNRNMMSSZCSHOFYOYNZRSZMAAYWDYEIMVVOGKPJB' 'VBM9TDPULSFUNMTVXRKFIDOHUXXVYDLFSZYZTWQYTE9SPYYWYTXJYQ' '9IFGYOLZXWZBKWZN9QOOTBQMWMUBLEWUEEASRHRTNIQWJQNDWRYLCA') trits = TryteString(input_).as_trits() curl = Curl() curl.absorb(trits, offset=243, length=486) curl.absorb(trits, offset=0, length=243) trits_out = [] curl.squeeze(trits_out) trits_out = TryteString.from_trits(trits_out) # noinspection SpellCheckingInspection self.assertEqual( trits_out, 'ZWNF9YOCAKC9CXQFYZDKXSSAZOCAZLEVEB9OZDJQ' 'GWEULHUDY9RAWAT9GIUXTTUSYJEGNGQDVJCGTQLN9', )
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 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) # Iterate over each fragment in the key. for (i, fragment) in enumerate(key_fragments): # type: Tuple[int, TryteString] fragment_trits = fragment.as_trits() key_fragment = [0] * FRAGMENT_LENGTH hash_trits = [] # Within each fragment, iterate over one hash at a time. 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 # # After processing all of the hashes in the fragment, generate a # final hash and append it to the digest. # # Note that we will do this once per fragment in the key, so the # longer the key is, the longer the digest will be. # 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 _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 address_from_digest(digest_trits, key_index): # type: (Iterable[int], int) -> 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_trits) sponge.squeeze(address_trits) address = Address.from_trits(address_trits) address.key_index = key_index return address
def get_digest_trits(self): # type: () -> List[int] """ 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_chunks = self.iter_chunks(FRAGMENT_LENGTH) # The digest will contain one hash per key fragment. digest = [0] * HASH_LENGTH * len(key_chunks) for (i, fragment ) in enumerate(key_chunks): # 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
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) return Address.from_trits( trits=address_trits, key_index=digest.key_index, security_level=digest.security_level, )
def from_tryte_string(cls, trytes, hash_=None): # type: (TrytesCompatible, Optional[TransactionHash]) -> Transaction """ Creates a Transaction object from a sequence of trytes. :param trytes: Raw trytes. Should be exactly 2673 trytes long. :param hash_: The transaction hash, if available. If not provided, it will be computed from the transaction trytes. """ tryte_string = TransactionTrytes(trytes) if not hash_: hash_trits = [0] * HASH_LENGTH # type: MutableSequence[int] sponge = Curl() sponge.absorb(tryte_string.as_trits()) sponge.squeeze(hash_trits) hash_ = TransactionHash.from_trits(hash_trits) return cls( hash_=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()), legacy_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]), tag=Tag(tryte_string[2592:2619]), attachment_timestamp=int_from_trits( tryte_string[2619:2628].as_trits()), attachment_timestamp_lower_bound=int_from_trits( tryte_string[2628:2637].as_trits()), attachment_timestamp_upper_bound=int_from_trits( tryte_string[2637:2646].as_trits()), nonce=Nonce(tryte_string[2646:2673]), )
def test_squeeze_offset(self): """ Passing an ``offset`` argument to :py:meth:`Curl.squeeze`. Example use case: https://github.com/iotaledger/iri/blob/v1.4.1.6/src/main/java/com/iota/iri/hash/ISS.java#L83 """ # noinspection SpellCheckingInspection input_ = ( 'CDLFODMOGMQAWXDURDXTUAOO9BFESHYGZLBUWIIHPTLNZCUNHZAAXSUPUIBW' 'IRLOVKCVWJSWEKRJQZUVRDZGZRNANUNCSGANCJWVHMZMVNJVUAZNFZKDAIVV' 'LSMIM9SVGUHYECTGGIXTAMXXO9FIXUMQFZCGRQWAOWJPBTXNNQIRSTZEEAJV' 'FSXWTHWBQJCWQNYYMHSPCYRA99ITVILYJPMFGOGOUOZUVABK9HMGABSORCVD' 'FNGLMPJ9NFKBWCZMFPIWEAGRWPRNLLG9VYUUVLCTEWKGWQIRIJKERZWC9LVR' 'XJEXNHBNUGEGGLMWGERKYFB9YEZCLXLKKMCGLRKQOGASDOUDYEDJLMV9BHPG' 'GCXQIUVUOFFXKEIIINLVWLRYHHLKXPLSTWKIKNEJWEDFQQFXQVEHGRCIJC9T' 'GVQNPPKGCFGPJNWSCPQZDDSIGAVZEIVYJDVPUOCTEMKTZFGXNGPQCOIBD9MX' 'YTHJTX' ) trits = TryteString(input_).as_trits() curl = Curl() trits_out = [0] * 243 for i in range(6): curl.reset() curl.absorb(trits, i * 243, (i + 1) * 243) curl.squeeze(trits, i * 243) curl.reset() curl.absorb(trits) curl.squeeze(trits_out) trits_out = TryteString.from_trits(trits_out) # noinspection SpellCheckingInspection self.assertEqual( trits_out, 'TAWDGNSEAD9ZRGBBVRVEKQYYVDOKHYQ9KEIYJKFT' 'BQEYZDWZVMRFJQQGTMPHBZOGPIJCCVWLZVDKLAQVI', )
def from_tryte_string(cls, trytes, hash_=None): # type: (TrytesCompatible, Optional[TransactionHash]) -> Transaction """ Creates a Transaction object from a sequence of trytes. :param trytes: Raw trytes. Should be exactly 2673 trytes long. :param hash_: The transaction hash, if available. If not provided, it will be computed from the transaction trytes. """ tryte_string = TransactionTrytes(trytes) if not hash_: hash_trits = [0] * HASH_LENGTH # type: MutableSequence[int] sponge = Curl() sponge.absorb(tryte_string.as_trits()) sponge.squeeze(hash_trits) hash_ = TransactionHash.from_trits(hash_trits) return cls( hash_ = 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()), legacy_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]), tag = Tag(tryte_string[2592:2619]), attachment_timestamp = int_from_trits(tryte_string[2619:2628].as_trits()), attachment_timestamp_lower_bound = int_from_trits(tryte_string[2628:2637].as_trits()), attachment_timestamp_upper_bound = int_from_trits(tryte_string[2637:2646].as_trits()), nonce = Nonce(tryte_string[2646:2673]), )
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:`iota.transaction.Fragment` instances). :param hash_: Hash used to generate the signature fragments (usually a :py:class:`iota.transaction.BundleHash` instance). :param public_key: The public key value used to verify the signature digest (usually a :py:class:`iota.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 MultisigAddressBuilder(object): """ Creates multisig addresses. Note that this class generates a single address from multiple inputs, (digests) unlike :py:class:`iota.crypto.addresses.AddressGenerator` which generates multiple addresses from a single input (seed). """ def __init__(self): super(MultisigAddressBuilder, self).__init__() self._digests = [] # type: List[Digest] """ Keeps track of digests that were added, so that we can attach them to the final :py:class:`MultisigAddress` object. """ self._address = None # type: Optional[MultisigAddress] """ Caches the generated address. Generating the address modifies the internal state of the curl sponge, so each :py:class:`MultisigAddressBuilder` instance can only generate a single address. """ self._sponge = Curl() def add_digest(self, digest): # type: (Digest) -> None """ Absorbs a digest into the sponge. IMPORTANT: Keep track of the order that digests are added! To spend inputs from a multisig address, you must provide the private keys in the same order! References: - https://github.com/iotaledger/wiki/blob/master/multisigs.md#spending-inputs """ if self._address: raise ValueError( 'Cannot add digests once an address is extracted.') self._sponge.absorb(digest.as_trits()) self._digests.append(digest) def get_address(self): # type: () -> MultisigAddress """ Returns the new multisig address. Note that you can continue to add digests after extracting an address; the next address will use *all* of the digests that have been added so far. """ if not self._digests: raise ValueError( 'Must call ``add_digest`` at least once ' 'before calling ``get_address``.', ) if not self._address: address_trits = [0] * HASH_LENGTH self._sponge.squeeze(address_trits) self._address =\ MultisigAddress.from_trits(address_trits, digests=self._digests[:]) return self._address
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__
def hash(trytes): curl = Curl() curl.absorb(trytes.as_trits()) trits_out = [] curl.squeeze(trits_out) return TryteString.from_trits(trits_out)
def conductPOW(self, bundle): HASH_LENGTH = 243 mwm = 14 # minimum weight on mainnet tangle (expected 1 minute for iot device at this level) trailing_zeros = [0] * mwm # For checking transaction hash previoustx = None max_iter = 5 i = 0 branch_transaction_hash = bundle.branchObj.bundle_hash trunk_transaction_hash = bundle.trunkObj.bundle_hash for trxn in reversed(bundle.trxns): startTime = time.time() trxn.attachment_timestamp = int(round(time.time() * 1000)) while i != max_iter: if (not previoustx): if trxn.current_index == trxn.last_index: trxn.branch_transaction_hash = branch_transaction_hash trxn.trunk_transaction_hash = trunk_transaction_hash else: raise ValueError( 'Head transaction is inconsistent in bundle') else: # It is not the head transaction (For clarity, see bundle figure in report) trxn.branch_transaction_hash = trunk_transaction_hash trxn.trunk_transaction_hash = previoustx # Let's do the pow locally trxn_string = trxn.as_tryte_string().__str__() # returns a python unicode string powed_trytes = _libccurl.ccurl_pow(trxn_string.encode('utf-8'), mwm) # construct trytestring from python string powed_trytes_bytes = powed_trytes[:2673] # Let's decode into unicode powed_txn_trytes = powed_trytes_bytes.decode('utf-8') powed_txn_trytes = TryteString(powed_txn_trytes) # Create powed txn hash hash_trits: MutableSequence[int] = [0] * HASH_LENGTH sponge = Curl() sponge.absorb(powed_txn_trytes.as_trits()) sponge.squeeze(hash_trits) hash = TransactionHash.from_trits(hash_trits) trxn.hash = hash previoustx = hash if hash_trits[-mwm:] == trailing_zeros: # We are good to go, exit from while loop totalTime = time.time() - startTime logger.info("Successfully conducted PoW for trxn " + str(trxn.current_index) + "! Time: {:.4g}".format(totalTime) + " sec") break else: i = i + 1 logger.info( 'Oops, wrong hash detected in try' ' #{rounds}. Recalculating PoW... '.format(rounds=i)) # Tail transactions represent bundles in the tangle return bundle.tail_transaction.hash
def from_tryte_string(cls: Type[T], trytes: TrytesCompatible, hash_: Optional[TransactionHash] = None) -> T: """ Creates a Transaction object from a sequence of trytes. :param TrytesCompatible trytes: Raw trytes. Should be exactly 2673 trytes long. :param Optional[TransactionHash] hash_: The transaction hash, if available. If not provided, it will be computed from the transaction trytes. :return: :py:class:`Transaction` object. Example usage:: from iota import Transaction txn =\\ Transaction.from_tryte_string( b'GYPRVHBEZOOFXSHQBLCYW9ICTCISLHDBNMMVYD9JJHQMPQCTIQAQTJNNNJ9IDXLRCC' b'OYOXYPCLR9PBEY9ORZIEPPDNTI9CQWYZUOTAVBXPSBOFEQAPFLWXSWUIUSJMSJIIIZ' b'WIKIRH9GCOEVZFKNXEVCUCIIWZQCQEUVRZOCMEL9AMGXJNMLJCIA9UWGRPPHCEOPTS' b'VPKPPPCMQXYBHMSODTWUOABPKWFFFQJHCBVYXLHEWPD9YUDFTGNCYAKQKVEZYRBQRB' b'XIAUX9SVEDUKGMTWQIYXRGSWYRK9SRONVGTW9YGHSZRIXWGPCCUCDRMAXBPDFVHSRY' b'WHGB9DQSQFQKSNICGPIPTRZINYRXQAFSWSEWIFRMSBMGTNYPRWFSOIIWWT9IDSELM9' b'JUOOWFNCCSHUSMGNROBFJX9JQ9XT9PKEGQYQAWAFPRVRRVQPUQBHLSNTEFCDKBWRCD' b'X9EYOBB9KPMTLNNQLADBDLZPRVBCKVCYQEOLARJYAGTBFR9QLPKZBOYWZQOVKCVYRG' b'YI9ZEFIQRKYXLJBZJDBJDJVQZCGYQMROVHNDBLGNLQODPUXFNTADDVYNZJUVPGB9LV' b'PJIYLAPBOEHPMRWUIAJXVQOEM9ROEYUOTNLXVVQEYRQWDTQGDLEYFIYNDPRAIXOZEB' b'CS9P99AZTQQLKEILEVXMSHBIDHLXKUOMMNFKPYHONKEYDCHMUNTTNRYVMMEYHPGASP' b'ZXASKRUPWQSHDMU9VPS99ZZ9SJJYFUJFFMFORBYDILBXCAVJDPDFHTTTIYOVGLRDYR' b'TKHXJORJVYRPTDH9ZCPZ9ZADXZFRSFPIQKWLBRNTWJHXTOAUOL9FVGTUMMPYGYICJD' b'XMOESEVDJWLMCVTJLPIEKBE9JTHDQWV9MRMEWFLPWGJFLUXI9BXPSVWCMUWLZSEWHB' b'DZKXOLYNOZAPOYLQVZAQMOHGTTQEUAOVKVRRGAHNGPUEKHFVPVCOYSJAWHZU9DRROH' b'BETBAFTATVAUGOEGCAYUXACLSSHHVYDHMDGJP9AUCLWLNTFEVGQGHQXSKEMVOVSKQE' b'EWHWZUDTYOBGCURRZSJZLFVQQAAYQO9TRLFFN9HTDQXBSPPJYXMNGLLBHOMNVXNOWE' b'IDMJVCLLDFHBDONQJCJVLBLCSMDOUQCKKCQJMGTSTHBXPXAMLMSXRIPUBMBAWBFNLH' b'LUJTRJLDERLZFUBUSMF999XNHLEEXEENQJNOFFPNPQ9PQICHSATPLZVMVIWLRTKYPI' b'XNFGYWOJSQDAXGFHKZPFLPXQEHCYEAGTIWIJEZTAVLNUMAFWGGLXMBNUQTOFCNLJTC' b'DMWVVZGVBSEBCPFSM99FLOIDTCLUGPSEDLOKZUAEVBLWNMODGZBWOVQT9DPFOTSKRA' b'BQAVOQ9RXWBMAKFYNDCZOJGTCIDMQSQQSODKDXTPFLNOKSIZEOY9HFUTLQRXQMEPGO' b'XQGLLPNSXAUCYPGZMNWMQWSWCKAQYKXJTWINSGPPZG9HLDLEAWUWEVCTVRCBDFOXKU' b'ROXH9HXXAXVPEJFRSLOGRVGYZASTEBAQNXJJROCYRTDPYFUIQJVDHAKEG9YACV9HCP' b'JUEUKOYFNWDXCCJBIFQKYOXGRDHVTHEQUMHO999999999999999999999999999999' b'999999999999999999999999999999999999999999999999999999999999999999' b'999999999999999999999999999999999999999999999999999999999999999999' b'999999999999999999999999999999999999999999999999999999999999999999' b'999999999999999999999999999999999999999999999999999999999999999999' b'999999999999999999999999999999999999999999999999999999999999999999' b'999999999999999999999999999999999999999999999999999999999999999999' b'999999999999999999999999999999999999999999999999999999999999999999' b'999999999999999999999999999999999999999999999999999999999999999999' b'999999999999999999999999999999999999999999999999999999999999999999' b'999999999999999999999999999999999999999999999999999999999999999999' b'999999999999RKWEEVD99A99999999A99999999NFDPEEZCWVYLKZGSLCQNOFUSENI' b'XRHWWTZFBXMPSQHEDFWZULBZFEOMNLRNIDQKDNNIELAOXOVMYEI9PGTKORV9IKTJZQ' b'UBQAWTKBKZ9NEZHBFIMCLV9TTNJNQZUIJDFPTTCTKBJRHAITVSKUCUEMD9M9SQJ999' b'999TKORV9IKTJZQUBQAWTKBKZ9NEZHBFIMCLV9TTNJNQZUIJDFPTTCTKBJRHAITVSK' b'UCUEMD9M9SQJ999999999999999999999999999999999999999999999999999999' b'999999999999999999999999999999999' ) """ tryte_string = TransactionTrytes(trytes) if not hash_: hash_trits: MutableSequence[int] = [0] * HASH_LENGTH sponge = Curl() sponge.absorb(tryte_string.as_trits()) sponge.squeeze(hash_trits) hash_ = TransactionHash.from_trits(hash_trits) return cls( hash_=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()), legacy_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]), tag=Tag(tryte_string[2592:2619]), attachment_timestamp=int_from_trits( tryte_string[2619:2628].as_trits()), attachment_timestamp_lower_bound=int_from_trits( tryte_string[2628:2637].as_trits()), attachment_timestamp_upper_bound=int_from_trits( tryte_string[2637:2646].as_trits()), nonce=Nonce(tryte_string[2646:2673]), )