def __init__(self, name, initBal=100): self.name = name self.bal = initBal self.seed = iota.crypto.types.Seed.random( ) # Generates official random 81 tryte seed generator = AddressGenerator( self.seed, security_level=1) # Generates IOTA address from seed self.address = generator.get_addresses(start=0) self.address = self.address[0].address keyGen = KeyGenerator(self.seed) self.privKey = keyGen.get_key( index=0, iterations=1) # Generate IOTA key from seed
def _execute(self, request): count = request['count'] # type: Optional[int] index = request['index'] # type: int seed = request['seed'] # type: Seed security_level = request['securityLevel'] # type: int generator = KeyGenerator(seed) return { 'keys': generator.get_keys(start=index, count=count, iterations=security_level), }
async def _execute(self, request: dict) -> dict: count: Optional[int] = request['count'] index: int = request['index'] seed: Seed = request['seed'] security_level: int = request['securityLevel'] generator = KeyGenerator(seed) return { 'keys': generator.get_keys( start=index, count=count, iterations=security_level, ), }
def create_iterator(self, start=0, step=1): # type: (int, int) -> Generator[Address] """ Creates an iterator that can be used to progressively generate new addresses. :param start: Starting index. Warning: This method may take awhile to reset if ``start`` is a large number! :param step: Number of indexes to advance after each address. Warning: The generator may take awhile to advance between iterations if ``step`` is a large number! """ key_iterator = (KeyGenerator(self.seed).create_iterator( start, step, iterations=self.DIGEST_ITERATIONS)) while True: if self.cache: with self.cache.acquire_lock(): address = self.cache.get(self.seed, key_iterator.current) if not address: address = self._generate_address(key_iterator) self.cache.set(self.seed, address.key_index, address) else: address = self._generate_address(key_iterator) yield address
def test_sign_input_at_error_index_not_input(self): """ The specified index references a transaction that is not an input. """ # Add a transaction so that we can finalize the bundle. # noinspection SpellCheckingInspection self.bundle.add_transaction(ProposedTransaction( address = Address( b'TESTVALUE9DONTUSEINPRODUCTION99999QARFLF' b'TDVATBVFTFCGEHLFJBMHPBOBOHFBSGAGWCM9PG9GX' ), value = 42, )) self.bundle.add_inputs([self.input_0_bal_eq_42]) self.bundle.finalize() private_key =\ KeyGenerator(self.seed).get_key_for(self.input_0_bal_eq_42) with self.assertRaises(ValueError): # You can't sign the spend transaction, silly! self.bundle.sign_input_at(0, private_key)
def create_iterator(self, start=0, step=1): # type: (int, int) -> Generator[Address, None, None] """ Creates an iterator that can be used to progressively generate new addresses. :param start: Starting index. Warning: This method may take awhile to reset if ``start`` is a large number! :param step: Number of indexes to advance after each address. Warning: The generator may take awhile to advance between iterations if ``step`` is a large number! """ key_iterator = (KeyGenerator(self.seed).create_iterator( start, step, self.security_level, )) while True: yield self._generate_address(key_iterator)
def test_sign_input_at_error_not_finalized(self): """ Cannot sign inputs because the bundle isn't finalized yet. """ # Add a transaction so that we can finalize the bundle. self.bundle.add_transaction(ProposedTransaction( address = Address( b'TESTVALUE9DONTUSEINPRODUCTION99999QARFLF' b'TDVATBVFTFCGEHLFJBMHPBOBOHFBSGAGWCM9PG9GX' ), value = 42, )) self.bundle.add_inputs([self.input_0_bal_eq_42]) # Oops; did we forget something? # self.bundle.finalize() private_key =\ KeyGenerator(self.seed).get_key_for(self.input_0_bal_eq_42) with self.assertRaises(RuntimeError): self.bundle.sign_input_at(1, private_key)
def test_sign_input_at_error_already_signed(self): """ Attempting to sign an input that is already signed. """ # Add a transaction so that we can finalize the bundle. # noinspection SpellCheckingInspection self.bundle.add_transaction( ProposedTransaction( address=Address(b'TESTVALUE9DONTUSEINPRODUCTION99999QARFLF' b'TDVATBVFTFCGEHLFJBMHPBOBOHFBSGAGWCM9PG9GX'), value=42, )) self.bundle.add_inputs([self.input_0_bal_eq_42]) self.bundle.finalize() # The existing signature fragment doesn't have to be valid; it just # has to be not empty. self.bundle[1].signature_message_fragment = Fragment(b'A') private_key =\ KeyGenerator(self.seed).get_key_for(self.input_0_bal_eq_42) with self.assertRaises(ValueError): self.bundle.sign_input_at(1, private_key)
def test_sign_inputs_security_level(self): """ You may include inputs with different security levels in the same bundle. """ # noinspection SpellCheckingInspection self.bundle.add_transaction( ProposedTransaction( address = Address( b'TESTVALUE9DONTUSEINPRODUCTION99999XE9IVG' b'EFNDOCQCMERGUATCIEGGOHPHGFIAQEZGNHQ9W99CH', ), value = 84, ), ) self.bundle.add_inputs([ self.input_4_bal_eq_42_sl_2, self.input_5_bal_eq_42_sl_3, ]) self.bundle.finalize() self.bundle.sign_inputs(KeyGenerator(self.seed)) # Quick sanity check. self.assertEqual(len(self.bundle), 6) # The spending transaction does not have a signature. self.assertEqual( self.bundle[0].signature_message_fragment, Fragment(b''), ) # The signature fragments are really long, and we already have unit # tests for the signature fragment generator, so to keep this test # focused, we are only interested in whether a signature fragment # gets applied. # # References: # - :py:class:`test.crypto.signing_test.SignatureFragmentGeneratorTestCase` for i in range(1, len(self.bundle)): if self.bundle[i].signature_message_fragment == Fragment(b''): self.fail( "Transaction {i}'s signature fragment is unexpectedly empty!".format( i = i, ), )
def test_sign_inputs(self): """ Signing inputs in a finalized bundle, using a key generator. """ # noinspection SpellCheckingInspection self.bundle.add_transaction(ProposedTransaction( address = Address( b'TESTVALUE9DONTUSEINPRODUCTION99999QARFLF' b'TDVATBVFTFCGEHLFJBMHPBOBOHFBSGAGWCM9PG9GX' ), value = 42, )) self.bundle.add_inputs([self.input_1_bal_eq_40, self.input_2_bal_eq_2]) self.bundle.finalize() self.bundle.sign_inputs(KeyGenerator(self.seed)) # Quick sanity check: # 1 spend + 2 inputs (security level 1) = 3 transactions. # Applying signatures should not introduce any new transactions # into the bundle. # # Note: we will see what happens when we use inputs with different # security levels in the next test. self.assertEqual(len(self.bundle), 3) # The spending transaction does not have a signature. self.assertEqual( self.bundle[0].signature_message_fragment, Fragment(b''), ) # The signature fragments are really long, and we already have unit # tests for the signature fragment generator, so to keep this test # focused, we are only interested in whether a signature fragment # gets applied. # # References: # - :py:class:`test.crypto.signing_test.SignatureFragmentGeneratorTestCase` for i in range(1, len(self.bundle)): if self.bundle[i].signature_message_fragment == Fragment(b''): self.fail( "Transaction {i}'s signature fragment is unexpectedly empty!".format( i = i, ), )
def test_sign_input_at_single_fragment(self): """ Signing an input at the specified index, only 1 fragment needed. """ # Add a transaction so that we can finalize the bundle. # noinspection SpellCheckingInspection self.bundle.add_transaction(ProposedTransaction( address = Address( b'TESTVALUE9DONTUSEINPRODUCTION99999QARFLF' b'TDVATBVFTFCGEHLFJBMHPBOBOHFBSGAGWCM9PG9GX' ), value = 42, )) self.bundle.add_inputs([self.input_0_bal_eq_42]) self.bundle.finalize() private_key =\ KeyGenerator(self.seed).get_key_for(self.input_0_bal_eq_42) self.bundle.sign_input_at(1, private_key) # Only 2 transactions are needed for this bundle: # 1 spend + 1 input (security level = 1). self.assertEqual(len(self.bundle), 2) # The spending transaction does not have a signature. self.assertEqual( self.bundle[0].signature_message_fragment, Fragment(b''), ) # The signature fragments are really long, and we already have unit # tests for the signature fragment generator, so to keep this test # focused, we are only interested in whether a signature fragment # gets applied. # # References: # - :py:class:`test.crypto.signing_test.SignatureFragmentGeneratorTestCase` for i in range(1, len(self.bundle)): if self.bundle[i].signature_message_fragment == Fragment(b''): self.fail( "Transaction {i}'s signature fragment is unexpectedly empty!".format( i = i, ), )
def _create_digest_generator(self, start, step): # type: (int, int) -> Generator[List[int]] """ Initializes a generator to create PrivateKey digests. Implemented as a separate method so that it can be mocked during unit tests. """ key_generator = ( KeyGenerator(self.seed) .create_generator(start, step, iterations=self.DIGEST_ITERATIONS) ) while True: signing_key = next(key_generator) # type: PrivateKey yield signing_key.get_digest_trits()
def test_sign_input_at_error_index_invalid(self): """ The specified index doesn't exist in the bundle. """ # Add a transaction so that we can finalize the bundle. # noinspection SpellCheckingInspection self.bundle.add_transaction( ProposedTransaction( address=Address(b'TESTVALUE9DONTUSEINPRODUCTION99999QARFLF' b'TDVATBVFTFCGEHLFJBMHPBOBOHFBSGAGWCM9PG9GX'), value=42, )) self.bundle.add_inputs([self.input_0_bal_eq_42]) self.bundle.finalize() private_key =\ KeyGenerator(self.seed).get_key_for(self.input_0_bal_eq_42) with self.assertRaises(IndexError): self.bundle.sign_input_at(2, private_key)
def test_sign_inputs_error_not_finalized(self): """ Attempting to sign inputs in a bundle that hasn't been finalized yet. """ # Add a transaction so that we can finalize the bundle. # noinspection SpellCheckingInspection self.bundle.add_transaction( ProposedTransaction( address=Address(b'TESTVALUE9DONTUSEINPRODUCTION99999QARFLF' b'TDVATBVFTFCGEHLFJBMHPBOBOHFBSGAGWCM9PG9GX'), value=42, )) self.bundle.add_inputs([self.input_0_bal_eq_42]) # Oops; did we forget something? # self.bundle.finalize() with self.assertRaises(RuntimeError): self.bundle.sign_inputs(KeyGenerator(b''))
def create_iterator(self, start: int = 0, step: int = 1) -> Generator[Address, None, None]: """ Creates an iterator that can be used to progressively generate new addresses. Returns an iterator that will create addresses endlessly. Use this if you have a feature that needs to generate addresses “on demand”. :param int start: Starting index. .. warning:: This method may take awhile to reset if ``start`` is a large number! :param int step: Number of indexes to advance after each address. .. warning:: The generator may take awhile to advance between iterations if ``step`` is a large number! :return: ``Generator[Address, None, None]`` object that you can iterate to generate addresses. """ key_iterator = (KeyGenerator(self.seed).create_iterator( start, step, self.security_level, )) while True: yield self._generate_address(key_iterator)
def _execute(self, request): # Required parameters. seed = request['seed'] # type: Seed bundle = ProposedBundle(request['transfers']) # Optional parameters. change_address = request.get( 'changeAddress') # type: Optional[Address] proposed_inputs = request.get( 'inputs') # type: Optional[List[Address]] want_to_spend = bundle.balance if want_to_spend > 0: # We are spending inputs, so we need to gather and sign them. if proposed_inputs is None: # No inputs provided. Scan addresses for unspent inputs. gi_response = GetInputsCommand(self.adapter)( seed=seed, threshold=want_to_spend, ) confirmed_inputs = gi_response['inputs'] else: # Inputs provided. Check to make sure we have sufficient # balance. available_to_spend = 0 confirmed_inputs = [] # type: List[Address] gb_response = GetBalancesCommand(self.adapter)( addresses=[i.address for i in proposed_inputs], ) for i, balance in enumerate(gb_response.get('balances') or []): input_ = proposed_inputs[i] if balance > 0: available_to_spend += balance # Update the address balance from the API response, just in # case somebody tried to cheat. input_.balance = balance confirmed_inputs.append(input_) if available_to_spend < want_to_spend: raise with_context( exc=BadApiResponse( 'Insufficient balance; found {found}, need {need} ' '(``exc.context`` has more info).'.format( found=available_to_spend, need=want_to_spend, ), ), context={ 'available_to_spend': available_to_spend, 'confirmed_inputs': confirmed_inputs, 'request': request, 'want_to_spend': want_to_spend, }, ) bundle.add_inputs(confirmed_inputs) if bundle.balance < 0: if not change_address: change_address =\ GetNewAddressesCommand(self.adapter)(seed=seed)['addresses'][0] bundle.send_unspent_inputs_to(change_address) bundle.finalize() if confirmed_inputs: bundle.sign_inputs(KeyGenerator(seed)) else: bundle.finalize() return { 'trytes': bundle.as_tryte_strings(), }
bundle.add_transaction(pt) addy = gna_result["addresses"][0] addy.balance = 0 addy.key_index = 0 bundle.add_inputs([ addy ]) bundle.send_unspent_inputs_to( gna_result["addresses"][0] ) bundle.finalize() bundle.sign_inputs(KeyGenerator(b'BXOM9LUNLPSEXBRJV9UUNLHSUHABEOGHQOGNBNBUEYSGOFZOEPYKEYRSFTXBOEJLUODUQXXGQ9NWQBSGH')) tips = json.loads(call_iota_api(getTips)) print("llsdldld") for x in tips['hashes']: getTransactionsToApprove = {"command": "getTransactionsToApprove", "depth": 15, "reference": x} result = json.loads(call_iota_api(getTransactionsToApprove)) if "exception" in result: print(result) continue elif "error" in result: print(result) continue else: print(result) trunk_hash = result['trunkTransaction']
def sign_inputs(self, key_generator: KeyGenerator) -> None: """ Sign inputs in a finalized bundle. Generates the necessary cryptographic signatures to authorize spending the inputs. .. note:: You do not need to invoke this method if the bundle does not contain any transactions that spend iotas. :param KeyGenerator key_generator: Generator to create private keys for signing. :raises RuntimeError: if bundle is not yet finalized. :raises ValueError: - if the input transaction specifies an address that doesn't have ``key_index`` attribute defined. - if the input transaction specifies an address that doesn't have ``security_level`` attribute defined. """ if not self.hash: raise RuntimeError('Cannot sign inputs until bundle is finalized.') # Use a counter for the loop so that we can skip ahead as we go. i = 0 while i < len(self): txn = self[i] if txn.value < 0: # In order to sign the input, we need to know the index # of the private key used to generate it. if txn.address.key_index is None: raise with_context( exc=ValueError( 'Unable to sign input {input}; ' '``key_index`` is None ' '(``exc.context`` has more info).'.format( input=txn.address, ), ), context={ 'transaction': txn, }, ) if txn.address.security_level is None: raise with_context( exc=ValueError( 'Unable to sign input {input}; ' '``security_level`` is None ' '(``exc.context`` has more info).'.format( input=txn.address, ), ), context={ 'transaction': txn, }, ) self.sign_input_at(i, key_generator.get_key_for(txn.address)) i += txn.address.security_level else: # No signature needed (nor even possible, in some # cases); skip this transaction. i += 1