Example #1
0
    def test_wireup(self):
        """
    Verify that the command is wired up correctly.

    The API method indeed calls the appropiate command.
    """
        with patch(
                'iota.commands.extended.promote_transaction.PromoteTransactionCommand.__call__',
                MagicMock(return_value='You found me!')) as mocked_command:

            api = Iota(self.adapter)

            # Don't need to call with proper args here.
            response = api.promote_transaction('transaction')

            self.assertTrue(mocked_command.called)

            self.assertEqual(response, 'You found me!')
Example #2
0
class Wallet:
    """docstring for Wallet"""
    def __init__(self, uri: str, seed: Optional[str] = None) -> None:
        self._iota_api = Iota(uri, seed)
        self._account: _Account

    @property
    def account(self) -> _Account:
        try:
            return self._account
        except AttributeError:
            # We get an attibute error if we check this property before ever
            # calling refresh_account.
            self.refresh_account()
            return self._account

    @property
    def addresses(self) -> List[Address]:
        return self.account.addresses

    @property
    def balance(self) -> int:
        return self.account.balance

    @property
    def bundles(self) -> Dict[str, Iterable[Bundle]]:
        return {
            'confirmed': self.account.confirmed_bundles,
            'unconfirmed': self.account.unconfirmed_bundles,
            'duplicate': self.account.duplicate_bundles,
        }

    def _is_above_max_depth(self, transaction: Transaction) -> bool:
        current_millis = time.time() * 1000
        max_age = 11 * 60 * 1000  # 11 minutes
        diff = current_millis - cast(float, transaction.attachment_timestamp)
        return (0 < diff < max_age)

    def _is_promotable(self, bundle: Bundle) -> bool:
        return (self._is_above_max_depth(bundle.tail_transaction)
                and self._iota_api.helpers.is_promotable(
                    bundle.tail_transaction.hash))

    def _promote(self, bundle: Bundle) -> Bundle:
        tail_hash = bundle.tail_transaction.hash
        response = self._iota_api.get_latest_inclusion([tail_hash])
        if response['states'][tail_hash]:
            raise BundleAlreadyPromoted()

        response = self._iota_api.promote_transaction(transaction=tail_hash,
                                                      depth=DEPTH)
        return response['bundle']

    def _reattach(self, bundle: Bundle) -> Bundle:
        response = self._iota_api.replay_bundle(
            bundle.tail_transaction.hash,
            DEPTH,
        )
        return Bundle.from_tryte_strings(response['trytes'])

    def create_new_address(self) -> Address:
        response = self._iota_api.get_new_addresses(count=None)
        address = response['addresses'][0]

        # Attach the address
        self._iota_api.send_transfer(
            depth=DEPTH,
            transfers=[ProposedTransaction(address, value=0)],
        )

        return address

    def refresh_account(self) -> None:
        response = self._iota_api.get_account_data(inclusion_states=True)
        addresses = response['addresses']
        balance = response['balance']
        bundles = response['bundles']

        self._account = _Account(addresses, balance, bundles)

    def retry_unconfirmed_bundles(self, *bundles: Bundle) -> None:
        if len(bundles) == 0:
            bundles = tuple(self.bundles['unconfirmed'])
        for bundle in bundles:
            print(f'Retrying bundle: {bundle.hash}')
            if not self._is_promotable(bundle):
                bundle = self._reattach(bundle)
                while True:
                    time.sleep(2)
                    if self._is_promotable(bundle):
                        break
            for attempt in range(5):
                try:
                    promote_bundle = self._promote(bundle)
                except BundleAlreadyPromoted:
                    break
                else:
                    print(
                        f'Promotion attempt ({attempt}): Bundle {promote_bundle.hash}'
                    )

    def send(self, address: str, value: int) -> None:
        print(f'Sending {value} iota to {address}...')
        response = self._iota_api.send_transfer(
            depth=DEPTH,
            transfers=[ProposedTransaction(Address(address), value=value)])
        bundle = response['bundle']
        print(f'Iota sent! Bundle hash: {bundle.hash}')