Skip to content
This repository was archived by the owner on Jan 13, 2023. It is now read-only.

Commit 540668b

Browse files
authored
Merge pull request #149 from iotaledger/release/2.0.4
Release/2.0.4
2 parents 9389d17 + c158888 commit 540668b

38 files changed

+1664
-179
lines changed

.travis.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,12 @@ python:
44
- '2.7'
55
- '3.5'
66
- '3.6'
7-
install: pip install .
8-
script: nosetests
7+
install:
8+
- pip install .
9+
- pip install docutils # Used to check package metadata.
10+
script:
11+
- python setup.py check --strict --metadata --restructuredtext
12+
- nosetests
913
deploy:
1014
on:
1115
python: '3.6'

README.rst

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Join the Discussion
1616
===================
1717
If you want to get involved in the community, need help with getting setup,
1818
have any issues related with the library or just want to discuss Blockchain,
19-
Distributed Ledgers and IoT with other people, feel free to join our `Slack`_.
19+
Distributed Ledgers and IoT with other people, feel free to join our `Discord`_.
2020

2121
You can also ask questions on our `dedicated forum`_.
2222

@@ -45,8 +45,6 @@ To install this extension, use the following command::
4545
pip install pyota[ccurl]
4646

4747

48-
.. _readme-installing-from-source:
49-
5048
Installing from Source
5149
======================
5250

@@ -60,17 +58,24 @@ To run unit tests after installing from source::
6058

6159
python setup.py test
6260

63-
PyOTA is also compatible with `tox`_::
61+
PyOTA is also compatible with `tox`_, which will run the unit tests in different
62+
virtual environments (one for each supported version of Python).
63+
64+
To run the unit tests, it is recommended that you use the `detox`_ library.
65+
detox speeds up the tests by running them in parallel.
66+
67+
Install PyOTA with the ``test-runner`` extra to set up the necessary
68+
dependencies, and then you can run the tests with the ``detox`` command::
6469

65-
pip install tox
66-
tox
70+
pip install -e .[test-runner]
71+
detox -v
6772

6873
=============
6974
Documentation
7075
=============
7176
PyOTA's documentation is available on `ReadTheDocs`_.
7277

73-
If you are :ref:`installing from source <readme-installing-from-source>`, you
78+
If you are installing from source (see above), you
7479
can also build the documentation locally:
7580

7681
#. Install extra dependencies (you only have to do this once)::
@@ -93,9 +98,10 @@ can also build the documentation locally:
9398
make html
9499

95100
.. _Create virtualenv: https://realpython.com/blog/python/python-virtual-environments-a-primer/
101+
.. _Discord: https://discordapp.com/invite/yxve4wu
96102
.. _PyOTA Bug Tracker: https://github.com/iotaledger/iota.lib.py/issues
97103
.. _ReadTheDocs: https://pyota.readthedocs.io/
98-
.. _Slack: https://slack.iota.org/
99104
.. _dedicated forum: https://forum.iota.org/
105+
.. _detox: https://pypi.python.org/pypi/detox
100106
.. _official API: https://iota.readme.io/
101107
.. _tox: https://tox.readthedocs.io/

docs/adapters.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,13 @@ HttpAdapter
4444
api = Iota('https://service.iotasupport.com:14265')
4545
api = Iota(HttpAdapter('https://service.iotasupport.com:14265'))
4646
47+
# Use HTTPS with basic authentication and 60 seconds timeout:
48+
api = Iota(
49+
HttpAdapter(
50+
'https://service.iotasupport.com:14265',
51+
authentication=('myusername', 'mypassword'),
52+
timeout=60))
53+
4754
``HttpAdapter`` uses the HTTP protocol to send requests to the node.
4855

4956
To configure an ``Iota`` instance to use ``HttpAdapter``, specify an

docs/api.rst

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
Standard API
2-
============
1+
Core API
2+
========
33

4-
The Standard API includes all of the core API calls that are made
4+
The Core API includes all of the core API calls that are made
55
available by the current `IOTA Reference
66
Implementation <https://github.com/iotaledger/iri>`__.
77

88
These methods are "low level" and generally do not need to be called
99
directly.
1010

11-
For the full documentation of all the Standard API calls, please refer
11+
For the full documentation of all the Core API calls, please refer
1212
to the `official documentation <https://iota.readme.io/>`__.
1313

1414
Extended API

docs/multisig.rst

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
Multisignature
22
==============
33

4-
Multisignature transactions are transactions which require multiple signatures before execution. In simplest example it means that, if there is token wallet which require 5 signatures from different parties, all 5 parties must sign spent transaction, before it will be processed.
4+
Multisignature transactions are transactions which require multiple signatures before execution. In simplest example it means that, if there is token wallet which require 5 signatures from different parties, all 5 parties must sign spent transaction, before it will be processed.
55

66
It is standard functionality in blockchain systems and it is also implemented in IOTA
77

88
.. note::
99

10-
You can read more about IOTA multisignature on the `wiki`_.
10+
You can read more about IOTA multisignature on the `wiki`_.
1111

1212
Generating multisignature address
1313
---------------------------------
1414

15-
In order to use multisignature functionality, a special multisignature address must be created. It is done by adding each key digest in agreed order into digests list. At the end, last participant is converting digests list (Curl state trits) into multisignature address.
15+
In order to use multisignature functionality, a special multisignature address must be created. It is done by adding each key digest in agreed order into digests list. At the end, last participant is converting digests list (Curl state trits) into multisignature address.
1616

1717
.. note::
1818

@@ -45,8 +45,8 @@ And here is example where digests are converted into multisignature address:
4545
.. code-block:: python
4646
4747
cma_result =\
48-
api_1.create_multisig_address(digests=[digest_1,
49-
digest_2,
48+
api_1.create_multisig_address(digests=[digest_1,
49+
digest_2,
5050
digest_3])
5151
5252
# For consistency, every API command returns a dict, even if it only
@@ -90,7 +90,7 @@ First signer for multisignature wallet is defining address where tokens should b
9090
# If you'd like, you may include an optional tag and/or
9191
# message.
9292
tag = Tag(b'KITTEHS'),
93-
message = TryteString.from_string('thanx fur cheezburgers'),
93+
message = TryteString.from_unicode('thanx fur cheezburgers'),
9494
),
9595
],
9696
@@ -142,9 +142,9 @@ When trytes are prepared, round of signing must be performed. Order of signing m
142142
.. note::
143143

144144
After creation, bundle can be optionally validated:
145-
145+
146146
.. code-block:: python
147-
147+
148148
validator = BundleValidator(bundle)
149149
if not validator.is_valid():
150150
raise ValueError(
@@ -181,11 +181,11 @@ Full code `example`_.
181181

182182
In order to enable a 2 of 3 multisig, the cosigners need to share their private keys with the other parties in such a way that no single party can sign inputs alone, but that still enables an M-of-N multsig. In our example, the sharing of the private keys would look as follows:
183183

184-
Alice -> Bob
184+
Alice -> Bob
185185

186-
Bob -> Carol
186+
Bob -> Carol
187187

188-
Carol -> Alice
188+
Carol -> Alice
189189

190190
Now, each participant holds two private keys that he/she can use to collude with another party to successfully sign the inputs and make a transaction. But no single party holds enough keys (3 of 3) to be able to independently make the transaction.
191191

docs/types.rst

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,21 +65,21 @@ Encoding
6565
6666
from iota import TryteString
6767
68-
message_trytes = TryteString.from_string('Hello, IOTA!')
68+
message_trytes = TryteString.from_unicode('Hello, IOTA!')
6969
7070
To encode character data into trytes, use the
71-
``TryteString.from_string`` method.
71+
``TryteString.from_unicode`` method.
7272

7373
You can also convert a tryte sequence into characters using
74-
``TryteString.as_string``. Note that not every tryte sequence can be
74+
``TryteString.decode``. Note that not every tryte sequence can be
7575
converted; garbage in, garbage out!
7676

7777
.. code:: python
7878
7979
from iota import TryteString
8080
8181
trytes = TryteString(b'RBTC9D9DCDQAEASBYBCCKBFA')
82-
message = trytes.as_string()
82+
message = trytes.decode()
8383
8484
.. note::
8585

@@ -158,6 +158,9 @@ Each ``Transaction`` has the following attributes:
158158
- ``address: Address``: The address associated with this transaction.
159159
Depending on the transaction's ``value``, this address may be a
160160
sender or a recipient.
161+
- ``attachment_timestamp: int``: Estimated epoch time of the attachment to the tangle.
162+
- ``attachment_time_lower_bound: int``: The lowest possible epoch time of the attachment to the tangle.
163+
- ``attachment_time_upper_bound: int``: The highest possible epoch time of the attachment to the tangle.
161164
- ``branch_transaction_hash: TransactionHash``: An unrelated
162165
transaction that this transaction "approves". Refer to the Basic
163166
Concepts section for more information.
@@ -179,6 +182,7 @@ Each ``Transaction`` has the following attributes:
179182
- ``last_index: int``: The index of the final transaction in the
180183
bundle. This value is attached to every transaction to make it easier
181184
to traverse and verify bundles.
185+
- ``legacy_tag: Tag``: A short message attached to the transaction. Deprecated, use ``tag`` instead.
182186
- ``nonce: Hash``: This is the product of the PoW process.
183187
- ``signature_message_fragment: Fragment``: Additional data attached to
184188
the transaction:
@@ -222,7 +226,7 @@ hasn't been broadcast yet.
222226
b'EFNDOCQCMERGUATCIEGGOHPHGFIAQEZGNHQ9W99CH'
223227
),
224228
225-
message = TryteString.from_string('thx fur cheezburgers'),
229+
message = TryteString.from_unicode('thx fur cheezburgers'),
226230
tag = Tag(b'KITTEHS'),
227231
value = 42,
228232
)

examples/send_transfer.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
"""
55
from iota import *
66

7+
SEED1 = b"THESEEDOFTHEWALLETSENDINGGOESHERE999999999999999999999999999999999999999999999999"
8+
ADDRESS_WITH_CHECKSUM_SECURITY_LEVEL_2 = b"RECEIVINGWALLETADDRESSGOESHERE9WITHCHECKSUMANDSECURITYLEVEL2999999999999999999999999999999"
79

810
# Create the API instance.
911
api =\
@@ -12,7 +14,7 @@
1214
'http://localhost:14265/',
1315

1416
# Seed used for cryptographic functions.
15-
seed = b'SEED9GOES9HERE'
17+
seed = SEED1
1618
)
1719

1820
# For more information, see :py:meth:`Iota.send_transfer`.
@@ -26,8 +28,7 @@
2628
# Recipient of the transfer.
2729
address =
2830
Address(
29-
b'TESTVALUE9DONTUSEINPRODUCTION99999FBFFTG'
30-
b'QFWEHEL9KCAFXBJBXGE9HID9XCOHFIDABHDG9AHDR'
31+
ADDRESS_WITH_CHECKSUM_SECURITY_LEVEL_2,
3132
),
3233

3334
# Amount of IOTA to transfer.

iota/adapter/__init__.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from socket import getdefaulttimeout as get_default_timeout
1111
from typing import Container, Dict, List, Optional, Text, Tuple, Union
1212

13-
from requests import Response, codes, request
13+
from requests import Response, codes, request, auth
1414
from six import PY2, binary_type, iteritems, moves as compat, text_type, \
1515
with_metaclass
1616

@@ -223,10 +223,13 @@ class HttpAdapter(BaseAdapter):
223223
in the ``headers`` kwarg.
224224
"""
225225

226-
def __init__(self, uri):
227-
# type: (Union[Text, SplitResult]) -> None
226+
def __init__(self, uri, timeout=None, authentication=None):
227+
# type: (Union[Text, SplitResult], Optional[int]) -> None
228228
super(HttpAdapter, self).__init__()
229229

230+
self.timeout = timeout
231+
self.authentication = authentication
232+
230233
if isinstance(uri, text_type):
231234
uri = compat.urllib_parse.urlsplit(uri) # type: SplitResult
232235

@@ -308,7 +311,11 @@ def _send_http_request(self, url, payload, method='post', **kwargs):
308311
Split into its own method so that it can be mocked during unit
309312
tests.
310313
"""
311-
kwargs.setdefault('timeout', get_default_timeout())
314+
315+
default_timeout = self.timeout if self.timeout else get_default_timeout()
316+
kwargs.setdefault('timeout', default_timeout)
317+
if self.authentication:
318+
kwargs.setdefault('auth', auth.HTTPBasicAuth(*self.authentication))
312319

313320
self._log(
314321
level = DEBUG,

iota/api.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from iota.adapter import BaseAdapter, resolve_adapter
1010
from iota.commands import BaseCommand, CustomCommand, core, \
1111
discover_commands, extended
12+
from iota.commands.extended.helpers import Helpers
1213
from iota.crypto.addresses import AddressGenerator
1314
from iota.crypto.types import Seed
1415
from six import with_metaclass
@@ -440,6 +441,7 @@ def __init__(self, adapter, seed=None, testnet=False):
440441
super(Iota, self).__init__(adapter, testnet)
441442

442443
self.seed = Seed(seed) if seed else Seed.random()
444+
self.helpers = Helpers(self)
443445

444446
def broadcast_and_store(self, trytes):
445447
# type: (Iterable[TransactionTrytes]) -> dict
@@ -773,6 +775,33 @@ def prepare_transfer(self, transfers, inputs=None, change_address=None):
773775
changeAddress = change_address,
774776
)
775777

778+
def promote_transaction(
779+
self,
780+
transaction,
781+
depth,
782+
min_weight_magnitude = None,
783+
):
784+
# type: (TransactionHash, int, Optional[int]) -> dict
785+
"""
786+
Promotes a transaction by adding spam on top of it.
787+
788+
:return:
789+
Dict containing the following values::
790+
791+
{
792+
'bundle': Bundle,
793+
The newly-published bundle.
794+
}
795+
"""
796+
if min_weight_magnitude is None:
797+
min_weight_magnitude = self.default_min_weight_magnitude
798+
799+
return extended.PromoteTransactionCommand(self.adapter)(
800+
transaction = transaction,
801+
depth = depth,
802+
minWeightMagnitude = min_weight_magnitude,
803+
)
804+
776805
def replay_bundle(
777806
self,
778807
transaction,
@@ -914,3 +943,27 @@ def send_trytes(self, trytes, depth, min_weight_magnitude=None):
914943
depth = depth,
915944
minWeightMagnitude = min_weight_magnitude,
916945
)
946+
947+
def is_reattachable(self, addresses):
948+
# type: (Iterable[Address]) -> dict
949+
"""
950+
This API function helps you to determine whether you should replay a
951+
transaction or make a completely new transaction with a different seed.
952+
What this function does, is it takes one or more input addresses (i.e. from spent transactions)
953+
as input and then checks whether any transactions with a value transferred are confirmed.
954+
If yes, it means that this input address has already been successfully used in a different
955+
transaction and as such you should no longer replay the transaction.
956+
957+
:param addresses:
958+
List of addresses.
959+
960+
:return:
961+
Dict containing the following values::
962+
{
963+
'reattachable': List[bool],
964+
Always a list, even if only one address was queried.
965+
}
966+
"""
967+
return extended.IsReattachableCommand(self.adapter)(
968+
addresses=addresses
969+
)

iota/commands/__init__.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,12 @@
1818

1919
__all__ = [
2020
'BaseCommand',
21-
'CustomCommand',
2221
'command_registry',
22+
'discover_commands',
23+
'CustomCommand',
24+
'FilterCommand',
25+
'RequestFilter',
26+
'ResponseFilter',
2327
]
2428

2529
command_registry = {} # type: Dict[Text, CommandMeta]
@@ -91,7 +95,7 @@ class BaseCommand(with_metaclass(CommandMeta)):
9195
command = None # Text
9296

9397
def __init__(self, adapter):
94-
# type: (BaseAdapter, bool) -> None
98+
# type: (BaseAdapter) -> None
9599
"""
96100
:param adapter:
97101
Adapter that will send request payloads to the node.

0 commit comments

Comments
 (0)