Exemple #1
0
  def test_error_zero_iotas_transferred(self):
    """
    The bundle doesn't spend any IOTAs.

    This is considered an error case because
    :py:meth:`MultisigIota.prepare_multisig_transfer` is specialized
    for generating bundles that require multisig inputs.  Any bundle
    that doesn't require multisig functionality should be generated
    using :py:meth:`iota_async.api.Iota.prepare_transfer` instead.
    """
    with self.assertRaises(ValueError):
      self.command(
        transfers = [
          ProposedTransaction(
            address = Address(self.trytes_1),
            value   = 0,
          ),
        ],

        multisigInput =
          MultisigAddress(
            digests = [self.digest_1, self.digest_2],
            trytes  = self.trytes_2,
          ),
      )
    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)
Exemple #3
0
  def test_fail_changeAddress_wrong_type(self):
    """
    ``changeAddress`` is not a TrytesCompatible value.
    """
    self.assertFilterErrors(
      {
        'changeAddress': 42,

        'multisigInput':
          MultisigAddress(
            digests = [self.digest_1, self.digest_2],
            trytes  = self.trytes_2,
          ),

        'transfers':
          [
            ProposedTransaction(
              address = Address(self.trytes_3),
              value   = 42,
            ),
          ],
      },

      {
        'changeAddress': [f.Type.CODE_WRONG_TYPE],
      },
    )
Exemple #4
0
  def test_pass_happy_path(self):
    """
    Request is valid.
    """
    request = {
      'changeAddress':
        Address(self.trytes_1),

      'multisigInput':
        MultisigAddress(
          digests = [self.digest_1, self.digest_2],
          trytes  = self.trytes_2,
        ),

      'transfers':
        [
          ProposedTransaction(
            address = Address(self.trytes_3),
            value   = 42,
          ),
        ],
    }

    filter_ = self._filter(request)

    self.assertFilterPasses(filter_)
    self.assertDictEqual(filter_.cleaned_data, request)
Exemple #5
0
  def test_pass_changeAddress_multisig_address(self):
    """
    ``changeAddress`` is allowed to be a MultisigAddress.
    """
    change_addy =\
      MultisigAddress(
        digests = [self.digest_1, self.digest_2],
        trytes  = self.trytes_1
      )

    filter_ = self._filter({
      'changeAddress': change_addy,

      'multisigInput':
        MultisigAddress(
          digests = [self.digest_1, self.digest_2],
          trytes  = self.trytes_2,
        ),

      'transfers':
        [
          ProposedTransaction(
            address = Address(self.trytes_3),
            value   = 42,
          ),
        ],
    })

    self.assertFilterPasses(filter_)
    self.assertIs(filter_.cleaned_data['changeAddress'], change_addy)
Exemple #6
0
  def test_fail_multisigInput_wrong_type(self):
    """
    ``multisigInput`` is not a MultisigAddress.
    """
    self.assertFilterErrors(
      {
        'changeAddress':
          Address(self.trytes_1),

        # This value must be a MultisigAddress, so that we know the
        # total security level of the digests used to create it.
        'multisigInput':
          Address(self.trytes_2),

        'transfers':
          [
            ProposedTransaction(
              address = Address(self.trytes_3),
              value   = 42,
            ),
          ],
      },

      {
        'multisigInput': [f.Type.CODE_WRONG_TYPE],
      },
    )
Exemple #7
0
  def test_fail_transfers_wrong_type(self):
    """
    ``transfers`` is not an array.
    """
    self.assertFilterErrors(
      {
        'changeAddress':
          Address(self.trytes_1),

        'multisigInput':
          MultisigAddress(
            digests = [self.digest_1, self.digest_2],
            trytes  = self.trytes_2,
          ),

        # ``transfers`` must be an array, even if there's only one
        # transaction.
        'transfers':
          ProposedTransaction(
            address = Address(self.trytes_3),
            value   = 42,
          ),
      },

      {
        'transfers': [f.Array.CODE_WRONG_TYPE],
      },
    )
Exemple #8
0
    def test_fail_transfers_contents_invalid(self):
        """
    ``transfers`` is a non-empty array, but it contains invalid values.
    """
        self.assertFilterErrors(
            {
                'transfers': [
                    None,

                    # This value is valid; just adding it to make sure the filter
                    # doesn't cheat!
                    ProposedTransaction(address=Address(self.trytes2),
                                        value=42),
                    {
                        'address': Address(self.trytes2),
                        'value': 42
                    },
                ],
                'depth':
                100,
                'minWeightMagnitude':
                18,
                'seed':
                Seed(self.trytes1),
            },
            {
                'transfers.0': [f.Required.CODE_EMPTY],
                'transfers.2': [f.Type.CODE_WRONG_TYPE],
            },
        )
Exemple #9
0
  def test_error_insufficient_inputs(self):
    """
    The multisig input does not contain sufficient IOTAs to cover the
    spends.
    """
    self.adapter.seed_response(
      command = GetBalancesCommand.command,

      response = {
        'balances': [42],
        'duration': 86,
      },
    )

    with self.assertRaises(ValueError):
      self.command(
        transfers = [
          ProposedTransaction(
            address = Address(self.trytes_1),
            value   = 101,
          ),
        ],

        multisigInput =
          MultisigAddress(
            digests = [self.digest_1, self.digest_2],
            trytes  = self.trytes_2,
          ),
      )
    def test_add_inputs_security_level(self):
        """
    Each input's security level determines the number of transactions
    we will need in order to store the entire signature.
    """
        # 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()

        # Each input's security level determines how many transactions will
        # be needed to hold all of its signature fragments:
        # 1 spend + 2 fragments for input 0 + 3 fragments for input 1
        self.assertEqual(len(self.bundle), 6)
Exemple #11
0
  def test_fail_unexpected_parameters(self):
    """
    Request contains unexpected parameters.
    """
    self.assertFilterErrors(
      {
        'changeAddress':
          Address(self.trytes_1),

        'multisigInput':
          MultisigAddress(
            digests = [self.digest_1, self.digest_2],
            trytes  = self.trytes_2,
          ),

        'transfers':
          [
            ProposedTransaction(
              address = Address(self.trytes_3),
              value   = 42,
            ),
          ],

        # Oh come on!
        'foo': 'bar',
      },

      {
        'foo': [f.FilterMapper.CODE_EXTRA_KEY],
      },
    )
    def test_add_inputs_with_change(self):
        """
    Adding inputs to a bundle results in unspent inputs.
    """
        tag = Tag(b'CHANGE9TXN')

        # noinspection SpellCheckingInspection
        self.bundle.add_transaction(
            ProposedTransaction(
                address=Address(b'TESTVALUE9DONTUSEINPRODUCTION99999VELDTF'
                                b'QHDFTHIHFE9II9WFFDFHEATEI99GEDC9BAUH9EBGZ'),
                value=29,
            ))

        # noinspection SpellCheckingInspection
        self.bundle.add_transaction(
            ProposedTransaction(
                address=Address(b'TESTVALUE9DONTUSEINPRODUCTION99999OGVEEF'
                                b'BCYAM9ZEAADBGBHH9BPBOHFEGCFAM9DESCCHODZ9Y'),
                tag=tag,
                value=13,
            ))

        self.bundle.add_inputs([self.input_3_bal_eq_100])

        # noinspection SpellCheckingInspection
        change_address =\
          Address(
            b'TESTVALUE9DONTUSEINPRODUCTION99999KAFGVC'
            b'IBLHS9JBZCEFDELEGFDCZGIEGCPFEIQEYGA9UFPAE'
          )

        self.bundle.send_unspent_inputs_to(change_address)

        self.bundle.finalize()

        # 2 spends + 1 input (with security level 1) + 1 change
        self.assertEqual(len(self.bundle), 4)

        change_txn = self.bundle[-1]
        self.assertEqual(change_txn.address, change_address)
        self.assertEqual(change_txn.value, 58)
        self.assertEqual(change_txn.tag, tag)
    def test_add_transaction_error_already_finalized(self):
        """
    Attempting to add a transaction to a bundle that is already
    finalized.
    """
        # noinspection SpellCheckingInspection
        self.bundle.add_transaction(
            ProposedTransaction(
                address=Address(b'TESTVALUE9DONTUSEINPRODUCTION999999DCBIE'
                                b'U9AIE9H9BCKGMCVCUGYDKDLCAEOHOHZGW9KGS9VGH'),
                value=0,
            ))
        self.bundle.finalize()

        with self.assertRaises(RuntimeError):
            self.bundle.add_transaction(
                ProposedTransaction(
                    address=Address(b''),
                    value=0,
                ))
    def test_add_inputs_no_change(self):
        """
    Adding inputs to cover the exact amount of the bundle spend.
    """
        # noinspection SpellCheckingInspection
        self.bundle.add_transaction(
            ProposedTransaction(
                address=Address(b'TESTVALUE9DONTUSEINPRODUCTION99999VELDTF'
                                b'QHDFTHIHFE9II9WFFDFHEATEI99GEDC9BAUH9EBGZ'),
                value=29,
            ))

        # noinspection SpellCheckingInspection
        self.bundle.add_transaction(
            ProposedTransaction(
                address=Address(b'TESTVALUE9DONTUSEINPRODUCTION99999OGVEEF'
                                b'BCYAM9ZEAADBGBHH9BPBOHFEGCFAM9DESCCHODZ9Y'),
                value=13,
            ))

        self.bundle.add_inputs([
            self.input_1_bal_eq_40,
            self.input_2_bal_eq_2,
        ])

        # Just to be tricky, add an unnecessary change address, just to
        # make sure the bundle ignores it.
        # noinspection SpellCheckingInspection
        self.bundle.send_unspent_inputs_to(
            Address(b'TESTVALUE9DONTUSEINPRODUCTION99999FDCDFD'
                    b'VAF9NFLCSCSFFCLCW9KFL9TCAAO9IIHATCREAHGEA'), )

        self.bundle.finalize()

        # All of the addresses that we generate for this test case have
        # security level set to 1, so we only need 1 transaction per
        # input (4 total, including the spends).
        #
        # Also note: because the transaction is already balanced, no change
        # transaction is necessary.
        self.assertEqual(len(self.bundle), 4)
    def test_add_transaction_error_negative_value(self):
        """
    Attempting to add a transaction with a negative value to a bundle.

    Use :py:meth:`ProposedBundle.add_inputs` to add inputs to a bundle.
    """
        with self.assertRaises(ValueError):
            self.bundle.add_transaction(
                ProposedTransaction(
                    address=Address(b''),
                    value=-1,
                ))
    def test_create_tag_from_string(self):
        """
    Check if string value of tag is converted into a Tag object
    """

        transaction = ProposedTransaction(
            address=Address(b'TESTVALUE9DONTUSEINPRODUCTION99999QARFLF'
                            b'TDVATBVFTFCGEHLFJBMHPBOBOHFBSGAGWCM9PG9GX'),
            tag="AAAZZZZ999",
            value=42,
        )

        self.assertEqual(type(transaction.tag), type(Tag(b'')))
Exemple #17
0
    def setUp(self):
        super(SendTransferRequestFilterTestCase, self).setUp()

        # Define some tryte sequences that we can reuse between tests.
        self.trytes1 = (b'TESTVALUEONE9DONTUSEINPRODUCTION99999JBW'
                        b'GEC99GBXFFBCHAEJHLC9DX9EEPAI9ICVCKBX9FFII')

        self.trytes2 = (b'TESTVALUETWO9DONTUSEINPRODUCTION99999THZ'
                        b'BODYHZM99IR9KOXLZXVUOJM9LQKCQJBWMTY999999')

        self.trytes3 = (b'TESTVALUETHREE9DONTUSEINPRODUCTIONG99999'
                        b'GTQ9CSNUFPYW9MBQ9LFQJSORCF9LGTY9BWQFY9999')

        self.trytes4 = (b'TESTVALUEFOUR9DONTUSEINPRODUCTION99999ZQ'
                        b'HOGCBZCOTZVZRFBEHQKHENBIZWDTUQXTOVWEXRIK9')

        self.transfer1 =\
          ProposedTransaction(
            address =
              Address(
                b'TESTVALUEFIVE9DONTUSEINPRODUCTION99999MG'
                b'AAAHJDZ9BBG9U9R9XEOHCBVCLCWCCCCBQCQGG9WHK'
              ),

            value = 42,
          )

        self.transfer2 =\
          ProposedTransaction(
            address =
              Address(
                b'TESTVALUESIX9DONTUSEINPRODUCTION99999GGT'
                b'FODSHHELBDERDCDRBCINDCGQEI9NAWDJBC9TGPFME'
              ),

            value = 86,
          )
Exemple #18
0
  def test_add_inputs_error_already_finalized(self):
    """
    Attempting to add inputs to a bundle that is already finalized.
    """
    self.bundle.add_transaction(
      ProposedTransaction(
        address = Address(self.trytes_1),
        value   = 0,
      ),
    )

    self.bundle.finalize()

    with self.assertRaises(RuntimeError):
      self.bundle.add_inputs([])
    def test_add_transaction_short_message(self):
        """
    Adding a transaction to a bundle, with a message short enough to
    fit inside a single transaction.
    """
        # noinspection SpellCheckingInspection
        self.bundle.add_transaction(
            ProposedTransaction(
                address=Address(b'TESTVALUE9DONTUSEINPRODUCTION99999AETEXB'
                                b'D9YBTH9EMFKF9CAHJIAIKDBEPAMH99DEN9DAJETGN'),
                message=TryteString.from_unicode('Hello, IOTA!'),
                value=42,
            ))

        # We can fit the message inside a single fragment, so only one
        # transaction is necessary.
        self.assertEqual(len(self.bundle), 1)
Exemple #20
0
  def test_pass_compatible_types(self):
    """
    Request contains values that can be converted to the expected
    types.
    """
    txn =\
      ProposedTransaction(
        address = Address(self.trytes_3),
        value   = 42,
      )

    filter_ =\
      self._filter({
        # ``changeAddress`` can be any value that resolves to an
        # :py:class:`Address`.
        'changeAddress': self.trytes_1,

        # ``multisigInput`` must be a :py:class:`MultisigInput` object.
        'multisigInput':
          MultisigAddress(
            digests = [self.digest_1, self.digest_2],
            trytes  = self.trytes_2,
          ),

        # ``transfers`` must contain an array of
        # :py:class:`ProposedTransaction` objects.
        'transfers': [txn],
      })

    self.assertFilterPasses(filter_)

    self.assertDictEqual(
      filter_.cleaned_data,

      {
        'changeAddress': Address(self.trytes_1),

        'multisigInput':
          MultisigAddress(
            digests = [self.digest_1, self.digest_2],
            trytes  = self.trytes_2,
          ),

        'transfers': [txn],
      },
    )
    def test_finalize_error_already_finalized(self):
        """
    Attempting to finalize a bundle that is already finalized.
    """
        # Add 1 transaction so that we can finalize the bundle.
        # noinspection SpellCheckingInspection
        self.bundle.add_transaction(
            ProposedTransaction(
                address=Address(b'TESTVALUE9DONTUSEINPRODUCTION99999XE9IVG'
                                b'EFNDOCQCMERGUATCIEGGOHPHGFIAQEZGNHQ9W99CH'),
                value=0,
            ))

        self.bundle.finalize()

        with self.assertRaises(RuntimeError):
            self.bundle.finalize()
Exemple #22
0
  def test_pass_optional_parameters_excluded(self):
    """
    Request omits optional parameters.
    """
    txn =\
      ProposedTransaction(
        address = Address(self.trytes_3),
        value   = 42,
      )

    filter_ =\
      self._filter({
        # ``changeAddress`` is optional.
        # Technically, it's required if there are unspent inputs, but
        # the filter has no way to know whether this is the case.
        # 'changeAddress': self.trytes_1,

        # These parameters are required.
        'multisigInput':
          MultisigAddress(
            digests = [self.digest_1, self.digest_2],
            trytes  = self.trytes_2,
          ),

        'transfers': [txn],
      })

    self.assertFilterPasses(filter_)

    self.assertDictEqual(
      filter_.cleaned_data,

      {
        'changeAddress': None,

        'multisigInput':
          MultisigAddress(
            digests = [self.digest_1, self.digest_2],
            trytes  = self.trytes_2,
          ),

        'transfers': [txn],
      },
    )
    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 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 test_finalize_error_negative_balance(self):
        """
    Attempting to finalize a bundle with unspent inputs.
    """
        # noinspection SpellCheckingInspection
        self.bundle.add_transaction(
            ProposedTransaction(
                address=Address(b'TESTVALUE9DONTUSEINPRODUCTION99999IGEFUG'
                                b'LIHIJGJGZ9CGRENCRHF9XFEAWD9ILFWEJFKDLITCC'),
                value=42,
            ))

        self.bundle.add_inputs([self.input_0_bal_eq_42, self.input_2_bal_eq_2])

        # Bundle spends 42 IOTAs, but inputs total 44 IOTAs.
        self.assertEqual(self.bundle.balance, -2)

        # In order to finalize this bundle, we need to specify a change
        # address.
        with self.assertRaises(ValueError):
            self.bundle.finalize()
    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 test_finalize_error_positive_balance(self):
        """
    Attempting to finalize a bundle with insufficient inputs.
    """
        # noinspection SpellCheckingInspection
        self.bundle.add_transaction(
            ProposedTransaction(
                address=Address(b'TESTVALUE9DONTUSEINPRODUCTION99999IGEFUG'
                                b'LIHIJGJGZ9CGRENCRHF9XFEAWD9ILFWEJFKDLITCC'),
                value=42,
            ))

        self.bundle.add_inputs([self.input_1_bal_eq_40])

        # Bundle spends 42 IOTAs, but inputs total only 40 IOTAs.
        self.assertEqual(self.bundle.balance, 2)

        # In order to finalize this bundle, we need to provide additional
        # inputs.
        with self.assertRaises(ValueError):
            self.bundle.finalize()