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
18 changes: 18 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,22 @@ To install this extension, use the following command::

pip install pyota[ccurl]

Optional Local Pow
==================
To perform proof-of-work locally without relying on a node,
you can install an extension module called `PyOTA-PoW`_ .

Specifiy the ``local_pow=True`` argument when creating an
api instance, that will redirect all ``attach_to_tangle``
API calls to an interface function in the ``pow`` package.

To install this extension, use the following command::

pip install pyota[pow]

Alternativley you can take a look on the repository
`Ccurl.interface.py`_ to install Pyota-PoW.
Follow the steps depicted in the repo's README file.

Installing from Source
======================
Expand Down Expand Up @@ -101,3 +117,5 @@ can also build the documentation locally:
.. _ReadTheDocs: https://pyota.readthedocs.io/
.. _official API: https://docs.iota.org/docs/node-software/0.1/iri/references/api-reference
.. _tox: https://tox.readthedocs.io/
.. _Ccurl.interface.py: https://github.com/iotaledger/ccurl.interface.py
.. _PyOTA-PoW: https://pypi.org/project/PyOTA-PoW/
16 changes: 16 additions & 0 deletions docs/adapters.rst
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,22 @@ depending on the command name.
For example, you could use this wrapper to direct all PoW requests to a
local node, while sending the other requests to a light wallet node.

.. note::

A common use case for ``RoutingWrapper`` is to perform proof-of-work on
a specific (local) node, but let all other requests go to another node.
Take care when you use ``RoutingWrapper`` adapter and ``local_pow``
parameter together in an API instance, because the behavior might not
be obvious.

``local_pow`` tells the API to perform proof-of-work (``attach_to_tangle``)
without relying on an actual node. It does this by calling an extension
package `PyOTA-PoW <https://pypi.org/project/PyOTA-PoW/>`_ that does the
job. In PyOTA, this means the request doesn't reach the adapter, it
is redirected before.
As a consequence, ``local_pow`` has precedence over the route that is
defined in ``RoutingWrapper``.

``RoutingWrapper`` must be initialized with a default URI/adapter. This
is the adapter that will be used for any command that doesn't have a
route associated with it.
Expand Down
18 changes: 17 additions & 1 deletion docs/getting_started.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Install PyOTA using `pip`:

.. code-block:: bash

pip install pyota[ccurl]
pip install pyota[ccurl,pow]

.. note::

Expand All @@ -15,6 +15,21 @@ Install PyOTA using `pip`:
This extension boosts the performance of certain crypto operations
significantly (speedups of 60x are common).

.. note::

The ``[pow]`` extra installs the optional `PyOTA-PoW extension`_.

This extension makes it possible to perform proof-of-work
(api call ``attach_to_tangle``) locally, without relying on an iota node.
Use the ``local_pow`` parameter at api instantiation:

.. code::

api = Iota('https://nodes.thetangle.org:443', local_pow=True)

Or the ``set_local_pow`` method of the api class to dynamically enable/disable
the local proof-of-work feature.

Getting Started
===============
In order to interact with the IOTA network, you will need access to a node.
Expand Down Expand Up @@ -78,6 +93,7 @@ your API requests so that they contain the necessary authentication metadata.
.. _forum: https://forum.iota.org/
.. _official api: https://docs.iota.org/docs/node-software/0.1/iri/references/api-reference
.. _pyota-ccurl extension: https://pypi.python.org/pypi/PyOTA-CCurl
.. _pyota-pow extension: https://pypi.org/project/PyOTA-PoW/
.. _run your own node.: http://iotasupport.com/headlessnode.shtml
.. _slack: http://slack.iota.org/
.. _use a light wallet node.: http://iotasupport.com/lightwallet.shtml
Expand Down
38 changes: 38 additions & 0 deletions examples/local_pow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from __future__ import absolute_import, division, print_function, \
unicode_literals

import iota
from pprint import pprint

# Generate a random seed.
myseed = iota.crypto.types.Seed.random()
# Get an address generator.
addres_generator = iota.crypto.addresses.AddressGenerator(myseed)

# Instantiate API. Note the `local_pow=True` argument.
# This will cause PyOTA to do proof-of-work locally,
# by using the pyota-pow extension package. (if installed)
# Find it at: https://pypi.org/project/PyOTA-PoW/
api = iota.Iota("https://nodes.thetangle.org:443",myseed,local_pow=True)

# Generate two addresses
addys = addres_generator.get_addresses(1, count=2)
pprint('Generated addresses are:')
pprint(addys)

# Preparing transactions
pt = iota.ProposedTransaction(address = iota.Address(addys[0]),
message = iota.TryteString.from_unicode('Tx1: The PoW for this transaction was done by Pyota-Pow.'),
tag = iota.Tag(b'LOCALATTACHINTERFACE99999'), # Up to 27 trytes
value = 0)

pt2 = iota.ProposedTransaction(address = iota.Address(addys[1]),
message = iota.TryteString.from_unicode('Tx2: The PoW for this transaction was done by Pyota-Pow.'),
tag = iota.Tag(b'LOCALATTACHINTERFACE99999'), # Up to 27 trytes
value = 0)

# `send_transfer` will take care of the rest
response = api.send_transfer([pt,pt2])

pprint('Broadcasted bundle:')
pprint(response['bundle'].as_json_compatible())
10 changes: 10 additions & 0 deletions iota/adapter/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ def __init__(self):
super(BaseAdapter, self).__init__()

self._logger = None # type: Logger
self.local_pow = False # type: boolean

@abstract_method
def get_uri(self):
Expand Down Expand Up @@ -209,6 +210,15 @@ def _log(self, level, message, context=None):
if self._logger:
self._logger.log(level, message, extra={'context': context or {}})

def set_local_pow(self, local_pow):
# type: (bool) -> None
"""
Sets the local_pow attribute of the adapter. If it is true,
attach_to_tangle command calls external interface to perform
pow, instead of sending the request to a node.
By default, it is set to false.
"""
self.local_pow = local_pow

class HttpAdapter(BaseAdapter):
"""
Expand Down
33 changes: 28 additions & 5 deletions iota/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ class StrictIota(object):
"""
commands = discover_commands('iota.commands.core')

def __init__(self, adapter, testnet=False):
# type: (AdapterSpec, bool) -> None
def __init__(self, adapter, testnet=False, local_pow=False):
# type: (AdapterSpec, bool, bool) -> None
"""
:param adapter:
URI string or BaseAdapter instance.
Expand All @@ -82,6 +82,17 @@ def __init__(self, adapter, testnet=False):
adapter = resolve_adapter(adapter)

self.adapter = adapter # type: BaseAdapter
# Note that the `local_pow` parameter is passed to adapter,
# the api class has no notion about it. The reason being,
# that this parameter is used in `AttachToTangeCommand` calls,
# that is called from various api calls (`attach_to_tangle`,
# `send_trytes` or `send_transfer`). Inside `AttachToTangeCommand`,
# we no longer have access to the attributes of the API class, therefore
# `local_pow` needs to be associated with the adapter.
# Logically, `local_pow` will decide if the api call does pow
# via pyota-pow extension, or sends the request to a node.
# But technically, the parameter belongs to the adapter.
self.adapter.set_local_pow(local_pow)
self.testnet = testnet

def __getattr__(self, command):
Expand Down Expand Up @@ -139,6 +150,18 @@ def create_command(self, command):
"""
return CustomCommand(self.adapter, command)

def set_local_pow(self, local_pow):
# type: (bool) -> None
"""
Sets the local_pow attribute of the adapter of the api instance.
If it is true, attach_to_tangle command calls external interface
to perform pow, instead of sending the request to a node.
By default, it is set to false.
This particular method is needed if one wants to change
local_pow behavior dynamically.
"""
self.adapter.set_local_pow(local_pow)

@property
def default_min_weight_magnitude(self):
# type: () -> int
Expand Down Expand Up @@ -527,8 +550,8 @@ class Iota(StrictIota):
"""
commands = discover_commands('iota.commands.extended')

def __init__(self, adapter, seed=None, testnet=False):
# type: (AdapterSpec, Optional[TrytesCompatible], bool) -> None
def __init__(self, adapter, seed=None, testnet=False, local_pow=False):
# type: (AdapterSpec, Optional[TrytesCompatible], bool, bool) -> None
"""
:param seed:
Seed used to generate new addresses.
Expand All @@ -537,7 +560,7 @@ def __init__(self, adapter, seed=None, testnet=False):
.. note::
This value is never transferred to the node/network.
"""
super(Iota, self).__init__(adapter, testnet)
super(Iota, self).__init__(adapter, testnet, local_pow)

self.seed = Seed(seed) if seed else Seed.random()
self.helpers = Helpers(self)
Expand Down
12 changes: 12 additions & 0 deletions iota/commands/core/attach_to_tangle.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,18 @@ def get_request_filter(self):
def get_response_filter(self):
return AttachToTangleResponseFilter()

def _execute(self, request):
if self.adapter.local_pow is True:
from pow import ccurl_interface
powed_trytes = ccurl_interface.attach_to_tangle(
request['trytes'],
request['branchTransaction'],
request['trunkTransaction'],
request['minWeightMagnitude']
)
return {'trytes': powed_trytes}
else:
return super(FilterCommand, self)._execute(request)

class AttachToTangleRequestFilter(RequestFilter):
def __init__(self):
Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@

extras_require={
'ccurl': ['pyota-ccurl'],
'pow': ['pyota-pow >= 1.0.1'],
'docs-builder': ['sphinx', 'sphinx_rtd_theme'],
# tox is able to run the tests in parallel since version 3.7
'test-runner': ['tox >= 3.7'] + tests_require,
Expand Down
Loading