Skip to content
This repository was archived by the owner on Jan 13, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -80,14 +80,14 @@ can also build the documentation locally:

#. Install extra dependencies (you only have to do this once)::

pip install '.[docs-builder]'
pip install .[docs-builder]

.. tip::

To install the CCurl extension and the documentation builder tools
together, use the following command::

pip install '.[ccurl,docs-builder]'
pip install .[ccurl,docs-builder]

#. Switch to the ``docs`` directory::

Expand Down
34 changes: 34 additions & 0 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,40 @@ This method returns a ``dict`` with the following items:
broadcast/stored. Should be the same as the value of the ``trytes``
parameter.


``find_transaction_objects``
----------------------------

A more extensive version of the core API ``find_transactions`` that returns
transaction objects instead of hashes.

Effectively, this is ``find_transactions`` + ``get_trytes`` + converting
the trytes into transaction objects. It accepts the same parameters
as ``find_transactions``

Find the transactions which match the specified input.
All input values are lists, for which a list of return values
(transaction hashes), in the same order, is returned for all
individual elements. Using multiple of these input fields returns the
intersection of the values.

Parameters
~~~~~~~~~~

- ``bundles: Optional[Iterable[BundleHash]]``: List of bundle IDs.
- ``addresses: Optional[Iterable[Address]]``: List of addresses.
- ``tags: Optional[Iterable[Tag]]``: List of tags.
- ``param: Optional[Iterable[TransactionHash]]``: List of approvee
transaction IDs.

Return
~~~~~~

This method returns a ``dict`` with the following items:

- ``transactions: List[Transaction]``: List of Transaction objects that
match the input

``get_account_data``
--------------------

Expand Down
44 changes: 44 additions & 0 deletions iota/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,50 @@ def broadcast_and_store(self, trytes):
"""
return extended.BroadcastAndStoreCommand(self.adapter)(trytes=trytes)

def find_transaction_objects(
self,
bundles=None, # type: Optional[Iterable[BundleHash]]
addresses=None, # type: Optional[Iterable[Address]]
tags=None, # type: Optional[Iterable[Tag]]
approvees=None, # type: Optional[Iterable[TransactionHash]]
):
# type: (...) -> dict
"""
A more extensive version of :py:meth:`find_transactions` that
returns transaction objects instead of hashes.

Effectively, this is ``find_transactions`` + ``get_trytes`` +
converting the trytes into transaction objects.

It accepts the same parameters as :py:meth:`find_transactions`

:param bundles:
List of bundle IDs.

:param addresses:
List of addresses.

:param tags:
List of tags.

:param approvees:
List of approvee transaction IDs.

:return:
Dict with the following structure::

{
'transactions': List[Transaction],
List of Transaction objects that match the input.
}
"""
return extended.FindTransactionObjectsCommand(self.adapter)(
bundles=bundles,
addresses=addresses,
tags=tags,
approvees=approvees,
)

def get_account_data(self, start=0, stop=None, inclusion_states=False, security_level=None):
# type: (int, Optional[int], bool, Optional[int]) -> dict
"""
Expand Down
1 change: 1 addition & 0 deletions iota/commands/extended/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
unicode_literals

from .broadcast_and_store import *
from .find_transaction_objects import *
from .get_account_data import *
from .get_bundles import *
from .get_inputs import *
Expand Down
55 changes: 55 additions & 0 deletions iota/commands/extended/find_transaction_objects.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# coding=utf-8
from __future__ import absolute_import, division, print_function, \
unicode_literals

from typing import Iterable, List, Optional

from iota import Address, BundleHash, Tag, Transaction, TransactionHash
from iota.commands.core import GetTrytesCommand, FindTransactionsCommand

__all__ = [
'FindTransactionObjectsCommand',
]


class FindTransactionObjectsCommand(FindTransactionsCommand):
"""
Executes `FindTransactionObjects` command.

See :py:meth:`iota.api.StrictIota.find_transaction_objects`.
"""
command = 'findTransactionObjects'

def get_response_filter(self):
pass

def _execute(self, request):
bundles = request\
.get('bundles') # type: Optional[Iterable[BundleHash]]
addresses = request\
.get('addresses') # type: Optional[Iterable[Address]]
tags = request\
.get('tags') # type: Optional[Iterable[Tag]]
approvees = request\
.get('approvees') # type: Optional[Iterable[TransactionHash]]

ft_response = FindTransactionsCommand(adapter=self.adapter)(
bundles=bundles,
addresses=addresses,
tags=tags,
approvees=approvees,
)

hashes = ft_response['hashes']
transactions = []
if hashes:
gt_response = GetTrytesCommand(adapter=self.adapter)(hashes=hashes)

transactions = list(map(
Transaction.from_tryte_string,
gt_response.get('trytes') or [],
)) # type: List[Transaction]

return {
'transactions': transactions,
}
9 changes: 4 additions & 5 deletions iota/commands/extended/is_reattachable.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@

from iota import Address
from iota.commands import FilterCommand, RequestFilter, ResponseFilter
from iota.commands.extended import GetLatestInclusionCommand
from iota.commands.extended.utils import find_transaction_objects
from iota.commands.extended import FindTransactionObjectsCommand, \
GetLatestInclusionCommand
from iota.filters import Trytes

__all__ = [
Expand All @@ -33,10 +33,9 @@ def _execute(self, request):
addresses = request['addresses'] # type: List[Address]

# fetch full transaction objects
transactions = find_transaction_objects(
adapter=self.adapter,
transactions = FindTransactionObjectsCommand(adapter=self.adapter)(
addresses=addresses,
)
)['transactions']

# Map and filter transactions which have zero value.
# If multiple transactions for the same address are returned,
Expand Down
27 changes: 3 additions & 24 deletions iota/commands/extended/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,34 +9,14 @@
from iota.adapter import BaseAdapter
from iota.commands.core.find_transactions import FindTransactionsCommand
from iota.commands.core.get_trytes import GetTrytesCommand
from iota.commands.extended import FindTransactionObjectsCommand
from iota.commands.extended.get_bundles import GetBundlesCommand
from iota.commands.extended.get_latest_inclusion import \
GetLatestInclusionCommand
from iota.crypto.addresses import AddressGenerator
from iota.crypto.types import Seed


def find_transaction_objects(adapter, **kwargs):
# type: (BaseAdapter, **Iterable) -> List[Transaction]
"""
Finds transactions matching the specified criteria, fetches the
corresponding trytes and converts them into Transaction objects.
"""
ft_response = FindTransactionsCommand(adapter)(**kwargs)

hashes = ft_response['hashes']

if hashes:
gt_response = GetTrytesCommand(adapter)(hashes=hashes)

return list(map(
Transaction.from_tryte_string,
gt_response.get('trytes') or [],
)) # type: List[Transaction]

return []


def iter_used_addresses(
adapter, # type: BaseAdapter
seed, # type: Seed
Expand Down Expand Up @@ -103,10 +83,9 @@ def get_bundles_from_transaction_hashes(
non_tail_bundle_hashes.add(txn.bundle_hash)

if non_tail_bundle_hashes:
for txn in find_transaction_objects(
adapter=adapter,
for txn in FindTransactionObjectsCommand(adapter=adapter)(
bundles=list(non_tail_bundle_hashes),
):
)['transactions']:
if txn.is_tail:
if txn.hash not in tail_transaction_hashes:
all_transactions.append(txn)
Expand Down
118 changes: 118 additions & 0 deletions test/commands/extended/find_transaction_objects.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
# coding=utf-8
from __future__ import absolute_import, division, print_function, \
unicode_literals

from unittest import TestCase

import mock

from iota import Iota, MockAdapter, Transaction
from iota.commands.extended import FindTransactionObjectsCommand


class FindTransactionObjectsCommandTestCase(TestCase):
# noinspection SpellCheckingInspection
def setUp(self):
super(FindTransactionObjectsCommandTestCase, self).setUp()

self.adapter = MockAdapter()
self.command = FindTransactionObjectsCommand(self.adapter)

# Define values that we can reuse across tests.
self.address = 'A' * 81
self.transaction_hash = \
b'BROTOVRCAEMFLRWGPVWDPDTBRAMLHVCHQDEHXLCWH' \
b'KKXLVDFCPIJEUZTPPFMPQQ9KOHAEUAMMVJN99999'
self.trytes = \
b'99999999999999999999999999999999999999999999999999999999999999' \
b'99999999999999999999999999999999999999999999999999999999999999' \
b'99999999999999999999999999999999999999999999999999999999999999' \
b'99999999999999999999999999999999999999999999999999999999999999' \
b'99999999999999999999999999999999999999999999999999999999999999' \
b'99999999999999999999999999999999999999999999999999999999999999' \
b'99999999999999999999999999999999999999999999999999999999999999' \
b'99999999999999999999999999999999999999999999999999999999999999' \
b'99999999999999999999999999999999999999999999999999999999999999' \
b'99999999999999999999999999999999999999999999999999999999999999' \
b'99999999999999999999999999999999999999999999999999999999999999' \
b'99999999999999999999999999999999999999999999999999999999999999' \
b'99999999999999999999999999999999999999999999999999999999999999' \
b'99999999999999999999999999999999999999999999999999999999999999' \
b'99999999999999999999999999999999999999999999999999999999999999' \
b'99999999999999999999999999999999999999999999999999999999999999' \
b'99999999999999999999999999999999999999999999999999999999999999' \
b'99999999999999999999999999999999999999999999999999999999999999' \
b'99999999999999999999999999999999999999999999999999999999999999' \
b'99999999999999999999999999999999999999999999999999999999999999' \
b'99999999999999999999999999999999999999999999999999999999999999' \
b'99999999999999999999999999999999999999999999999999999999999999' \
b'99999999999999999999999999999999999999999999999999999999999999' \
b'99999999999999999999999999999999999999999999999999999999999999' \
b'99999999999999999999999999999999999999999999999999999999999999' \
b'99999999999999999999999999999999999999999999999999999999999999' \
b'99999999999999999999999999999999999999999999999999999999999999' \
b'99999999999999999999999999999999999999999999999999999999999999' \
b'99999999999999999999999999999999999999999999999999999999999999' \
b'99999999999999999999999999999999999999999999999999999999999999' \
b'99999999999999999999999999999999999999999999999999999999999999' \
b'99999999999999999999999999999999999999999999999999999999999999' \
b'99999999999999999999999999999999999999999999999999999999999999' \
b'99999999999999999999999999999999999999999999999999999999999999' \
b'99999999999999999999999999999999999999999999999999999999999999' \
b'99999999999999999AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' \
b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA99999999999999999999999999' \
b'9QC9999999999999999999999999PQYJHAD99999999999999999999WHIUDFV' \
b'IFXNBJVEHYPLDADIDINGAWMHYIJNPYUDWXCAWL9GSKTUIZLJGGFIXEIYTJEDQZ' \
b'TIYRXHC9PBWBDSOTEJTQTYYSZLVTFLDQMZSGLHKLYVJOLMXIJJRTGS9RYBXLAT' \
b'ZJXBVBCPUGWRUKZJYLBGPKRKWIA9999FPYHMFFWMMKOHTSAPMMATZQLWXJSPMT' \
b'JSRQIPMDCQXFFMXMHCYDKVJCFSRECAVALCOFIYCJLNRZZZ9999999999999999' \
b'999999999999999KITCXNZOF999999999MMMMMMMMMEA9999F9999999999999' \
b'9999999'

def test_wireup(self):
"""
Verify that the command is wired up correctly.
"""
self.assertIsInstance(
Iota(self.adapter).findTransactionObjects,
FindTransactionObjectsCommand,
)

def test_transaction_found(self):
"""
A transaction is found with the inputs. A transaction object is
returned
"""
with mock.patch(
'iota.commands.core.find_transactions.FindTransactionsCommand.'
'_execute',
mock.Mock(return_value={'hashes': [self.transaction_hash, ]}),
):
with mock.patch(
'iota.commands.core.get_trytes.GetTrytesCommand._execute',
mock.Mock(return_value={'trytes': [self.trytes, ]}),
):
response = self.command(addresses=[self.address])

self.assertEqual(len(response['transactions']), 1)
transaction = response['transactions'][0]
self.assertIsInstance(transaction, Transaction)
self.assertEqual(transaction.address, self.address)

def test_no_transactions_fround(self):
"""
No transaction is found with the inputs. An empty list is returned
"""
with mock.patch(
'iota.commands.core.find_transactions.FindTransactionsCommand.'
'_execute',
mock.Mock(return_value={'hashes': []}),
):
response = self.command(addresses=[self.address])

self.assertDictEqual(
response,
{
'transactions': [],
},
)