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_trits = [0] * HASH_LENGTH # type: MutableSequence[int] sponge.squeeze(bundle_hash_trits) # Copy bundle hash to individual transactions. bundle_hash = BundleHash.from_trits(bundle_hash_trits) for txn in self: txn.bundle_hash = bundle_hash # Initialize signature/message fragment. txn.signature_message_fragment = Fragment(txn.message or b'')
def calc_hash(self, bundle): Trxn_HASH_Trytes = 81 HASH_LENGTH = Trxn_HASH_Trytes * 3 # Trits conversion # Generate bundle hash. (taken from python API client) while True: sponge = Kerl() last_index = len(bundle) - 1 for i, txn in enumerate(bundle): txn.current_index = i txn.last_index = last_index sponge.absorb(txn.get_bundle_essence_trits()) bundle_hash_trits = [0] * HASH_LENGTH sponge.squeeze(bundle_hash_trits) bundle_hash = BundleHash.from_trits( bundle_hash_trits) # Convert trits to ascii Trytes bundle.bundle_hash = bundle_hash # Check that we generated a secure bundle hash. # https://github.com/iotaledger/iota.py/issues/84 if any(13 in part for part in normalize(bundle_hash)): # Increment the legacy tag and try again. bundle.tail_transaction.increment_legacy_tag() else: break # Copy bundle hash to individual transactions. for txn in bundle: txn.bundle_hash = bundle_hash # Initialize signature/message fragment. if not txn.value_trxn: # Put dummy message in fragment. # txn.signature_message_fragment = Fragment('9' * 2187) # Fragment Length txn.signature_message_fragment = Fragment( TryteString.from_string( 'IOTA is cool! This is a meta transaction!') ) # Fragment Length else: # Generate signature for bundle transaction txn.signature_message_fragment = self.genSig( bundleHash=bundle_hash) bundle.data_payload = self.name + " (" + str( bundle.outputTrxn.value ) + ") ->" + bundle.outputTrxn.recName return self.conductPOW(bundle)
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_finalize_insecure_bundle(self): """ When finalizing, the bundle detects an insecure bundle hash. References: - https://github.com/iotaledger/iota.lib.py/issues/84 """ # noinspection SpellCheckingInspection bundle =\ ProposedBundle([ ProposedTransaction( address =\ Address( '9XV9RJGFJJZWITDPKSQXRTHCKJAIZZY9BYLBEQUX' 'UNCLITRQDR9CCD99AANMXYEKD9GLJGVB9HIAGRIBQ', ), tag = Tag('PPDIDNQDJZGUQKOWJ9JZRCKOVGP'), timestamp = 1509136296, value = 0, ), ]) bundle.finalize() # The resulting bundle hash is insecure (contains a [1, 1, 1]), so # the legacy tag is manipulated until a secure hash is generated. # noinspection SpellCheckingInspection self.assertEqual(bundle[0].legacy_tag, Tag('ZTDIDNQDJZGUQKOWJ9JZRCKOVGP')) # The proper tag is left alone, however. # noinspection SpellCheckingInspection self.assertEqual(bundle[0].tag, Tag('PPDIDNQDJZGUQKOWJ9JZRCKOVGP')) # The bundle hash takes the modified legacy tag into account. # noinspection SpellCheckingInspection self.assertEqual( bundle.hash, BundleHash( 'NYSJSEGCWESDAFLIFCNJFWGZ9PCYDOT9VCSALKBD' '9UUNKBJAJCB9KVMTHZDPRDDXC9UFJQBJBQFUPJKFC', ) )
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. while True: sponge = Kerl() 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_trits = [0] * HASH_LENGTH # type: MutableSequence[int] sponge.squeeze(bundle_hash_trits) bundle_hash = BundleHash.from_trits(bundle_hash_trits) # Check that we generated a secure bundle hash. # https://github.com/iotaledger/iota.lib.py/issues/84 if any(13 in part for part in normalize(bundle_hash)): # Increment the legacy tag and try again. tail_transaction = self.tail_transaction # type: ProposedTransaction tail_transaction.increment_legacy_tag() else: break # Copy bundle hash to individual transactions. for txn in self: txn.bundle_hash = bundle_hash # Initialize signature/message fragment. txn.signature_message_fragment = Fragment(txn.message or b'')
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]), )
def finalize(self): # type: () -> None """ Finalizes the bundle, preparing it to be attached to the Tangle. This operation includes checking if the bundle has zero balance, generating the bundle hash and updating the transactions with it, furthermore to initialize signature/message fragment fields. Once this method is invoked, no new transactions may be added to the bundle. :raises RuntimeError: if bundle is already finalized. :raises ValueError: - if bundle has no transactions. - if bundle has unspent inputs (there is no ``change_address`` attribute specified.) - if inputs are insufficient to cover bundle spend. """ 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. while True: sponge = Kerl() last_index = len(self) - 1 for i, txn in enumerate(self): txn.current_index = i txn.last_index = last_index sponge.absorb(txn.get_signature_validation_trytes().as_trits()) bundle_hash_trits = [0] * HASH_LENGTH sponge.squeeze(bundle_hash_trits) bundle_hash = BundleHash.from_trits(bundle_hash_trits) # Check that we generated a secure bundle hash. # https://github.com/iotaledger/iota.py/issues/84 if any(13 in part for part in normalize(bundle_hash)): # Increment the legacy tag and try again. tail_transaction = (self.tail_transaction ) # type: ProposedTransaction tail_transaction.increment_legacy_tag() else: break # Copy bundle hash to individual transactions. for txn in self: txn.bundle_hash = bundle_hash # Initialize signature/message fragment. txn.signature_message_fragment = Fragment(txn.message or b'')
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. while True: sponge = Kerl() 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_trits = [0] * HASH_LENGTH # type: MutableSequence[int] sponge.squeeze(bundle_hash_trits) bundle_hash = BundleHash.from_trits(bundle_hash_trits) # Check that we generated a secure bundle hash. # https://github.com/iotaledger/iota.lib.py/issues/84 if any(13 in part for part in normalize(bundle_hash)): # Increment the legacy tag and try again. tail_transaction = self.tail_transaction # type: ProposedTransaction tail_transaction.increment_legacy_tag() else: break # Copy bundle hash to individual transactions. for txn in self: txn.bundle_hash = bundle_hash # Initialize signature/message fragment. txn.signature_message_fragment = Fragment(txn.message or b'')