Installation¶
PyOTA is compatible with Python 3.7 and 3.6.
Install PyOTA using pip:
pip install pyota[ccurl,pow]
Note
The [ccurl]
extra installs the optional PyOTA-CCurl extension.
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:
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.
You can:
Note that light wallet nodes often disable certain features like PoW for security reasons.
Once you’ve gotten access to an IOTA node, initialize an iota.Iota
object with the URI of the node, and optional seed:
from iota import Iota
# Generate a random seed.
api = Iota('http://localhost:14265')
# Specify seed.
api = Iota('http://localhost:14265', 'SEED9GOES9HERE')
Test your connection to the server by sending a getNodeInfo
command:
print(api.get_node_info())
You are now ready to send commands to your IOTA node!
Basic Concepts¶
Before diving into the API, it’s important to understand the fundamental data types of IOTA.
The official IOTA documentation site gives a good and in-depth explanation of the concepts used in IOTA. PyOTA documentation will try to give references to the official site wherever possible.
Ternary¶
IOTA uses the ternary numerical system to represent data. The smallest unit of
information is a trit
, that can have a value of -1, 0 or 1 in a balanced
ternary system. A combination of 3 trits
equals one tryte
, therefore a
tryte
can have 3 * 3 * 3 = 27 different values.
To represent a tryte
, IOTA encodes these 27 values into characters based on
the tryte alphabet.
In PyOTA, trits
are represented as a sequence of numerical values (List[int]
)
while trytes have their own class called TryteString
.
IOTA token¶
The IOTA token is a unit of value that can be transferred over an IOTA network through transfer bundles.
The IOTA token was launched on the Mainnet in June 2017. At this point, the nodes in the network were hard-coded with a total supply of 2,779,530,283 277,761. This large supply allows each of the billions of devices, which are expected to be a part of the Internet of Things, to have its own wallet and transact micropayments with other devices.
Seed¶
Seed in IOTA is your unique password. It is the digital key that unlocks your safe that holds your tokens, or proves the ownership of messages.
Seeds in PyOTA are always 81 trytes long and may only contain characters from the tryte alphabet.
Warning
Treat your seed(s) the same as you would the password for any other financial service.
Never share your seed with anyone, and never use online generators to create a seed.
The library can help you to create your own locally and it does not require
internet connection: iota.crypto.Seed.random()
.
For PyOTA-specific implementation details on seeds, see crypto.Seed
.
Address¶
To send or receive any transaction (let them be zero-value or value transacitons) in IOTA, you will need to specify an address. An address is like a physical mailbox on your entrance door: anyone can drop things in your mailbox (send you messages or tokens), but only you can empty it (withdraw tokens).
Warning
Addresses should not be re-used once they are spent from. You can receive as many transactions to an address as you wish, but only spend from that address once.
Addresses are generated from your seed through cryptographic functions. There
are 957 different addresses that one might generate from a seed,
which is quite a lot. Given your seed
, the index
and security level
parameters specify which address will be generated from it. The process is
deterministic, meaning that same input paramteres always generate the same address.
Addresses are 81 trytes long and may contain extra 9 trytes for checksum. The checksum may be used to verify that an address is in fact a valid IOTA address.
For-PyOTA specific implementation details on addresses, see Address
.
Transaction¶
A transaction is a single transfer instruction that can either withdraw IOTA tokens from an address, deposit them into an address, or have zero-value (contain data, a message, or a signature). If you want to send anything to an IOTA network, you must send it to a node as a transaction.
—from the official IOTA documentation site
Transactions are always 2673 trytes long and their structure is defined by the protocol. They can be classified into three categories:
Input transaction: A transaction that withdraws tokens from an address.
Output transaction: A transaction that deposits tokens to an address.
Zero-value transaction: A transaction that has 0 value and might carry messages or signatures.
Depending on the type of the transaction, different fields are required to be filled.
A transaction’s unique identifier in IOTA is the TransactionHash
,
that is generated from the trytes of the transaction. If any trytes change
in the transaction, the returning transaction hash would alter. This way, transaction
hashes ensure the immutability of the Tangle.
To become accepted by the network, a transaction has to be attached to the Tangle. The attachment process means that the transaction should reference two unconfirmed transactions (tips) in the Tangle and do a small proof-of-work. This process might be performed by a node, or by using the local proof-of-work feature of the client libraries.
For PyOTA-specific implementation details on transactions, see Transaction
and ProposedTransaction
.
Bundle¶
A bundle is a group of transactions that rely on each other’s validity. For example, a transaction that deposits IOTA tokens into an address relies on another transaction to withdraw those IOTA tokens from another address. Therefore, those transactions must be in the same bundle.
—from the official IOTA documentation site
In other words, a bundle is collection of transactions, treated as an atomic unit when attached to the Tangle.
Note
Unlike a block in a blockchain, bundles are not first-class citizens in IOTA; only transactions get stored in the Tangle.
Instead, bundles must be inferred by following linked transactions with the same bundle hash.
Transactions in the bundle are linked together through their trunkTransaction
fields, furthermore they are indexed within the bundle and contain a bundleHash
field that is a unique identifier for the bundle.

Structure of a bundle with four transactions. Numbers in brackets denote
(currentIndex
, lastIndex
) fields. Head of the bundle has index 3,
while tail has index 0.¶
Read more about how bundles are structured.
Bundles can be classified into two categories:
Transfer bundles: Bundles that contain input and output transactions. A bundle always has to be balanced, meaning that input transaction values should equal to output transaction values.
Zero-value bundles: Bundles that contain only zero-value transactions.
For PyOTA-specific implementation details on bundles, see Bundle
and ProposedBundle
.
Now that you are familiar with some basic IOTA concepts, it is time to explore how PyOTA implements these and how you can work with them.
PyOTA Types¶
PyOTA defines a few types that will make it easy for you to model objects like Transactions and Bundles in your own code.
Since everything in IOTA is represented as a sequence of trits
and trytes
,
let us take a look on how you can work with them in PyOTA.
TryteString¶
-
class
iota.
TryteString
(trytes: Union[AnyStr, bytearray, TryteString], pad: Optional[int] = None)¶ A string representation of a sequence of trytes.
A
TryteString
is an ASCII representation of a sequence of trytes. In many respects, it is similar to a Pythonbytes
object (which is an ASCII representation of a sequence of bytes).In fact, the two objects behave very similarly; they support concatenation, comparison, can be used as dict keys, etc.
However, unlike
bytes
, aTryteString
can only contain uppercase letters and the number 9 (as a regular expression:^[A-Z9]*$
).Important
A TryteString does not represent a numeric value!
- Parameters
trytes (TrytesCompatible) – Byte string or bytearray.
pad (Optional[int]) –
Ensure at least this many trytes.
If there are too few, null trytes will be appended to the TryteString.
Note
If the TryteString is too long, it will not be truncated!
Example usage:
from iota import TryteString
# Create a TryteString object from bytes.
trytes_1 = TryteString(b'RBTC9D9DCDQAEASBYBCCKBFA')
# Ensure the created object is 81 trytes long by padding it with zeros.
# The value zero is represented with character '9' in trytes.
trytes_1 = TryteString(b'RBTC9D9DCDQAEASBYBCCKBFA', pad=81)
# Create a TryteString object from text type.
# Note that this will throw error if text contains unsupported characters.
trytes_2 = TryteString('LH9GYEMHCF9GWHZFEELHVFOEOHNEEEWHZFUD')
# Comparison and concatenation:
if trytes_1 != trytes_2:
trytes_combined = trytes_1 + trytes_2
# As dictionary keys:
index = {
trytes_1: 42,
trytes_2: 86,
}
As you go through the API documentation, you will see many references to
TryteString
and its subclasses:
Fragment
: A signature or message fragment inside a transaction. Fragments are always 2187 trytes long.Hash
: An object identifier. Hashes are always 81 trytes long. There are many different types of hashes:Address
: Identifies an address on the Tangle.BundleHash
: Identifies a bundle on the Tangle.TransactionHash
: Identifies a transaction on the Tangle.Seed
: A TryteString that is used for crypto functions such as generating addresses, signing inputs, etc. Seeds can be any length, but 81 trytes offers the best security.Tag
: A tag used to classify a transaction. Tags are always 27 trytes long.TransactionTrytes
: A TryteString representation of a transaction on the Tangle.TransactionTrytes
are always 2673 trytes long.
Let’s explore the capabilities of the TryteString
base class.
Encoding¶
You may use classmethods to create a TryteString
from bytes
,
unicode string
or from a list of trits
.
from_bytes¶
-
classmethod
TryteString.
from_bytes
(bytes_: Union[bytes, bytearray], codec: str = 'trytes_ascii', *args: Any, **kwargs: Any) → T¶ Creates a TryteString from a sequence of bytes.
- Parameters
bytes_ (Union[bytes,bytearray]) – Source bytes. ASCII representation of a sequence of bytes. Note that only tryte alphabet supported!
codec (str) – Reserved for future use. Currently supports only the ‘trytes_ascii’ codec. See https://github.com/iotaledger/iota.py/issues/62 for more information.
args – Additional positional arguments to pass to the initializer.
kwargs – Additional keyword arguments to pass to the initializer.
- Returns
TryteString
object.
Example usage:
from iota import TryteString message_trytes = TryteString.from_bytes(b'HELLO999IOTA')
from_unicode¶
-
classmethod
TryteString.
from_unicode
(string: str, *args: Any, **kwargs: Any) → T¶ Creates a TryteString from a Unicode string.
- Parameters
string (str) – Source Unicode string.
args – Additional positional arguments to pass to the initializer.
kwargs – Additional keyword arguments to pass to the initializer.
- Returns
TryteString
object.
Example usage:
from iota import TryteString message_trytes = TryteString.from_unicode('Hello, IOTA!')
Note
PyOTA also supports encoding non-ASCII characters, but this functionality is experimental and has not yet been evaluated by the IOTA community!
Until this feature has been standardized, it is recommended that you only
use ASCII characters when generating TryteString
objects from
character strings.
from_trits¶
-
classmethod
TryteString.
from_trits
(trits: Iterable[int], *args: Any, **kwargs: Any) → T¶ Creates a TryteString from a sequence of trits.
- Parameters
trits (Iterable[int]) – Iterable of trit values (-1, 0, 1).
args – Additional positional arguments to pass to the initializer.
kwargs – Additional keyword arguments to pass to the initializer.
- Returns
TryteString
object.
Example usage:
from iota import TryteString message_trytes = TryteString.from_trits( [1, 0, -1, -1, 1, 0, 1, -1, 0, -1, 1, 0, 0, 1, 0, 0, 1, 0, -1, 1, 1, -1, 1, 0] )
References:
int_from_trits()
from_trytes¶
-
classmethod
TryteString.
from_trytes
(trytes: Iterable[Iterable[int]], *args: Any, **kwargs: Any) → T¶ Creates a TryteString from a sequence of trytes.
- Parameters
trytes (Iterable[Iterable[int]]) –
Iterable of tryte values.
In this context, a tryte is defined as a list containing 3 trits.
args – Additional positional arguments to pass to the initializer.
kwargs – Additional keyword arguments to pass to the initializer.
- Returns
TryteString
object.
Example usage:
from iota import TryteString message_trytes = TryteString.from_trytes( [ [1, 0, -1], [-1, 1, 0], [1, -1, 0], [-1, 1, 0], [0, 1, 0], [0, 1, 0], [-1, 1, 1], [-1, 1, 0], ] )
References:
Additionally, you can encode a TryteString
into a lower-level
primitive (usually bytes). This might be useful when the TryteString
contains ASCII encoded characters but you need it as bytes
. See the example
below:
encode¶
-
TryteString.
encode
(errors: str = 'strict', codec: str = 'trytes_ascii') → bytes¶ Encodes the TryteString into a lower-level primitive (usually bytes).
- Parameters
errors (str) –
How to handle trytes that can’t be converted:
- ’strict’
raise an exception (recommended).
- ’replace’
replace with ‘?’.
- ’ignore’
omit the tryte from the result.
codec (str) –
Reserved for future use.
See https://github.com/iotaledger/iota.py/issues/62 for more information.
- Raises
iota.codecs.TrytesDecodeError
if the trytes cannot be decoded into bytes.
- Returns
Python
bytes
object.
Example usage:
from iota import TryteString # Message payload as unicode string message = 'Hello, iota!' # Create TryteString message_trytes = TryteString.from_unicode(message) # Encode TryteString into bytes encoded_message_bytes = message_trytes.encode() # This will be b'Hello, iota' print(encoded_message_bytes) # Get the original message decoded = encoded_message_bytes.decode() print(decoded == message)
Decoding¶
You can also convert a tryte sequence into characters using
TryteString.decode()
. Note that not every tryte sequence can be
converted; garbage in, garbage out!
decode¶
-
TryteString.
decode
(errors: str = 'strict', strip_padding: bool = True) → str¶ Decodes the TryteString into a higher-level abstraction (usually Unicode characters).
- Parameters
errors (str) –
How to handle trytes that can’t be converted, or bytes that can’t be decoded using UTF-8:
- ’strict’
raise an exception (recommended).
- ’replace’
replace with a placeholder character.
- ’ignore’
omit the invalid tryte/byte sequence.
strip_padding (bool) – Whether to strip trailing null trytes before converting.
- Raises
iota.codecs.TrytesDecodeError
if the trytes cannot be decoded into bytes.UnicodeDecodeError
if the resulting bytes cannot be decoded using UTF-8.
- Returns
Unicode string
object.
Example usage:
from iota import TryteString trytes = TryteString(b'RBTC9D9DCDQAEASBYBCCKBFA') message = trytes.decode()
as_json_compatible¶
-
TryteString.
as_json_compatible
() → str¶ Returns a JSON-compatible representation of the object.
References:
iota.json.JsonEncoder
.
- Returns
JSON-compatible representation of the object (string).
Example usage:
from iota import TryteString trytes = TryteString(b'RBTC9D9DCDQAEASBYBCCKBFA') json_payload = trytes.as_json_compatible()
as_integers¶
-
TryteString.
as_integers
() → List[int]¶ Converts the TryteString into a sequence of integers.
Each integer is a value between -13 and 13.
See the tryte alphabet for more info.
- Returns
List[int]
Example usage:
from iota import TryteString trytes = TryteString(b'RBTC9D9DCDQAEASBYBCCKBFA') tryte_ints = trytes.as_integers()
as_trytes¶
-
TryteString.
as_trytes
() → List[List[int]]¶ Converts the TryteString into a sequence of trytes.
Each tryte is represented as a list with 3 trit values.
See
as_trits()
for more info.Important
TryteString
is not a numeric type, so the result of this method should not be interpreted as an integer!- Returns
List[List[int]]
Example usage:
from iota import TryteString trytes = TryteString(b'RBTC9D9DCDQAEASBYBCCKBFA') tryte_list = trytes.as_trytes()
as_trits¶
-
TryteString.
as_trits
() → List[int]¶ Converts the TryteString into a sequence of trit values.
A trit may have value 1, 0, or -1.
References:
Important
TryteString
is not a numeric type, so the result of this method should not be interpreted as an integer!- Returns
List[int]
Example usage:
from iota import TryteString trytes = TryteString(b'RBTC9D9DCDQAEASBYBCCKBFA') trits = trytes.as_trits()
Generation¶
random¶
-
classmethod
TryteString.
random
(length: Optional[int] = None) → T¶ Generates a random sequence of trytes.
- Parameters
length (Optional[int]) – Number of trytes to generate.
- Returns
TryteString
object.- Raises
TypeError –
if
length
is negative,if
length
is not defined, and the class doesn’t haveLEN
attribute.
Seed¶
-
class
iota.
Seed
(trytes: Union[AnyStr, bytearray, TryteString, None] = None)¶ An
iota.TryteString
that acts as a seed for crypto functions.Note: This class is identical to
iota.TryteString
, but it has a distinct type so that seeds can be identified in Python code.IMPORTANT: For maximum security, a seed must be EXACTLY 81 trytes!
- Parameters
trytes (TrytesCompatible) – Byte string or bytearray.
- Raises
Warning – if
trytes
are longer than 81 trytes in length.
References:
random¶
-
classmethod
Seed.
random
(length: int = 81) → iota.crypto.types.Seed¶ Generates a random seed using a CSPRNG.
- Parameters
length (int) –
Length of seed, in trytes.
For maximum security, this should always be set to 81, but you can change it if you’re 110% sure you know what you’re doing.
See https://iota.stackexchange.com/q/249 for more info.
- Returns
iota.Seed
object.
Example usage:
from iota import Seed my_seed = Seed.random() print(my_seed)
Address¶
-
class
iota.
Address
(trytes: Union[AnyStr, bytearray, TryteString], balance: Optional[int] = None, key_index: Optional[int] = None, security_level: Optional[int] = None)¶ A
TryteString
that acts as an address, with support for generating and validating checksums.- Parameters
trytes (TrytesCompatible) – Object to construct the address from.
balance (Optional[int]) – Known balance of the address.
key_index (Optional[int]) – Index of the address that was used during address generation. Must be greater than zero.
security_level (Optional[int]) – Security level that was used during address generation. Might be 1, 2 or 3.
- :raises
ValueError: if
trytes
is longer than 81 trytes, unless it is exactly 90 trytes long (address + checksum).
-
address
: TryteString = None¶ Address trytes without the checksum.
-
balance
= None¶ Balance owned by this address. Defaults to
None
; usually set via thegetInputs
command.References:
-
key_index
= None¶ Index of the key used to generate this address. Defaults to
None
; usually set viaAddressGenerator
.References:
-
security_level
= None¶ Number of hashes in the digest that was used to generate this address.
as_json_compatible¶
-
Address.
as_json_compatible
() → Dict[str, Union[str, int]]¶ Returns a JSON-compatible representation of the Address.
- Returns
dict
with the following structure:{ 'trytes': str, 'balance': int, 'key_index': int, 'security_level': int, }
Example usage:
from iota import Address # Example address only, do not use in your code! addy = Address( b'LVHHIXQNYKWQMGXGLFOKOCDFHPKXAUKWMSZVDRAT' b'TICUZXFACM9DNJELJGMLMK99KDVVOOWLINVBZIGWZ' ) print(addy.as_json_compatible())
is_checksum_valid¶
-
Address.
is_checksum_valid
() → bool¶ Returns whether this address has a valid checksum.
- Returns
bool
Example usage:
from iota import Address # Example address only, do not use in your code! addy = Address( b'LVHHIXQNYKWQMGXGLFOKOCDFHPKXAUKWMSZVDRAT' b'TICUZXFACM9DNJELJGMLMK99KDVVOOWLINVBZIGWZ' ) # Should be ``False`` print(addy.is_checksum_valid()) addy.add_checksum() # Should be ``True`` print(addy.is_checksum_valid())
with_valid_checksum¶
-
Address.
with_valid_checksum
() → iota.types.Address¶ Returns the address with a valid checksum attached.
- Returns
Address
object.
Example usage:
from iota import Address # Example address only, do not use in your code! addy = Address( b'LVHHIXQNYKWQMGXGLFOKOCDFHPKXAUKWMSZVDRAT' b'TICUZXFACM9DNJELJGMLMK99KDVVOOWLINVBZIGWZ' ) addy_with_checksum = addy.with_valid_checksum() print(addy_with_checksum) # Should be ``True`` print(addy_with_checksum.is_checksum_valid())
add_checksum¶
-
Address.
add_checksum
() → None¶ Adds checksum to
Address
object.- Returns
None
Example usage:
from iota import Address # Example address only, do not use in your code! addy = Address( b'LVHHIXQNYKWQMGXGLFOKOCDFHPKXAUKWMSZVDRAT' b'TICUZXFACM9DNJELJGMLMK99KDVVOOWLINVBZIGWZ' ) # Should be ``False`` print(addy.is_checksum_valid()) print(addy.checksum) addy.add_checksum() # Should be ``True`` print(addy.is_checksum_valid()) print(addy.checksum)
remove_checksum¶
-
Address.
remove_checksum
() → None¶ Removes checksum from
Address
object.- Returns
None
Example usage:
from iota import Address # Example address only, do not use in your code! addy = Address( b'LVHHIXQNYKWQMGXGLFOKOCDFHPKXAUKWMSZVDRAT' b'TICUZXFACM9DNJELJGMLMK99KDVVOOWLINVBZIGWZ' b'AACAMCWUW' # 9 checksum trytes ) # Should be ``True`` print(addy.is_checksum_valid()) print(addy.checksum) addy.remove_checksum() # Should be ``False`` print(addy.is_checksum_valid()) print(addy.checksum)
AddressChecksum¶
-
class
iota.
AddressChecksum
(trytes: Union[AnyStr, bytearray, TryteString])¶ A
TryteString
that acts as an address checksum.- Parameters
trytes (TrytesCompatible) – Checksum trytes.
- Raises
ValueError – if
trytes
is not exactly 9 trytes in length.
-
LEN
= 9¶ Length of an address checksum.
Hash¶
-
class
iota.
Hash
(trytes: Union[AnyStr, bytearray, TryteString])¶ A
TryteString
that is exactly one hash long.- Parameters
trytes (TrytesCompatible) – Object to construct the hash from.
- Raises
ValueError – if
trytes
is longer than 81 trytes.
-
LEN
= 81¶ Length is always 81 trytes long.
TransactionHash¶
-
class
iota.
TransactionHash
(trytes: Union[AnyStr, bytearray, TryteString])¶ An
TryteString
(Hash
) that acts as a transaction hash.
BundleHash¶
-
class
iota.
BundleHash
(trytes: Union[AnyStr, bytearray, TryteString])¶ An
TryteString
(Hash
) that acts as a bundle hash.
TransactionTrytes¶
-
class
iota.
TransactionTrytes
(trytes: Union[AnyStr, bytearray, TryteString])¶ An
TryteString
representation of a Transaction.- Raises
ValueError – if
trytes
is longer than 2673 trytes in length.
-
LEN
= 2673¶ Length of a transaction in trytes.
Fragment¶
-
class
iota.
Fragment
(trytes: Union[AnyStr, bytearray, TryteString])¶ An
TryteString
representation of a signature/message fragment in a transaction.- Raises
ValueError – if
trytes
is longer than 2187 trytes in length.
-
LEN
= 2187¶ Length of a fragment in trytes.
Nonce¶
-
class
iota.
Nonce
(trytes: Union[AnyStr, bytearray, TryteString])¶ An
TryteString
that acts as a transaction nonce.- Raises
ValueError – if
trytes
is longer than 27 trytes in length.
-
LEN
= 27¶ Length of a nonce in trytes.
Tag¶
Transaction Types¶
PyOTA defines two different types used to represent transactions:
Transaction
for transactions that have already been attached to the Tangle. Generally, you will never need to createTransaction
objects; the API will build them for you, as the result of various API methods.
ProposedTransaction
for transactions that have been created locally and have not been broadcast yet.
Transaction¶
Each Transaction
object has several instance attributes that you
may manipulate and properties you can use to extract their values as trytes.
See the class documentation below:
-
class
iota.
Transaction
(hash_: Optional[iota.transaction.types.TransactionHash], signature_message_fragment: Optional[iota.transaction.types.Fragment], address: iota.types.Address, value: int, timestamp: int, current_index: Optional[int], last_index: Optional[int], bundle_hash: Optional[iota.transaction.types.BundleHash], trunk_transaction_hash: Optional[iota.transaction.types.TransactionHash], branch_transaction_hash: Optional[iota.transaction.types.TransactionHash], tag: Optional[iota.types.Tag], attachment_timestamp: Optional[int], attachment_timestamp_lower_bound: Optional[int], attachment_timestamp_upper_bound: Optional[int], nonce: Optional[iota.transaction.types.Nonce], legacy_tag: Optional[iota.types.Tag] = None)¶ A transaction that has been attached to the Tangle.
- Parameters
hash (Optional[TransactionHash]) – Transaction ID
signature_message_fragment (Optional[Fragment]) – Signature or message fragment.
address (Address) – The address associated with this transaction.
value (int) – Value of the transaction in iotas. Can be negative as well (spending from address).
timestamp (int) – Unix timestamp in seconds.
current_index (Optional[int]) – Index of the transaction within the bundle.
last_index (Optional[int]) – Index of head transaction in the bundle.
bundle_hash (Optional[BundleHash]) – Bundle hash of the bundle containing the transaction.
trunk_transaction_hash (Optional[TransactionHash]) – Hash of trunk transaction.
branch_transaction_hash (Optional[TransactionHash]) – Hash of branch transaction.
tag (Optional[Tag]) – Optional classification tag applied to this transaction.
attachment_timestamp (Optional[int]) – Unix timestamp in milliseconds, decribes when the proof-of-work for this transaction was done.
attachment_timestamp_lower_bound (Optional[int]) – Unix timestamp in milliseconds, lower bound of attachment.
attachment_timestamp_upper_bound (Optional[int]) – Unix timestamp in milliseconds, upper bound of attachment.
nonce (Optional[Nonce]) – Unique value used to increase security of the transaction hash. Result of the proof-of-work aglorithm.
legacy_tag (Optional[Tag]) – Optional classification legacy_tag applied to this transaction.
- Returns
Transaction
object.
-
address
: Address = None¶ The address associated with this transaction.
Depending on the transaction’s
value
, this address may be a sender or a recipient. Ifvalue
is != 0, the associated address’ balance is adjusted as a result of this transaction.- Type
-
attachment_timestamp
: Optional[int] = None¶ Estimated epoch time of the attachment to the tangle.
Decribes when the proof-of-work for this transaction was done.
- Type
int
, unix timestamp in milliseconds,
-
property
attachment_timestamp_as_trytes
¶ Returns a TryteString representation of the transaction’s
attachment_timestamp
.
-
attachment_timestamp_lower_bound
: Optional[int] = None¶ The lowest possible epoch time of the attachment to the tangle.
- Type
int
, unix timestamp in milliseconds.
-
property
attachment_timestamp_lower_bound_as_trytes
¶ Returns a TryteString representation of the transaction’s
attachment_timestamp_lower_bound
.
-
attachment_timestamp_upper_bound
: Optional[int] = None¶ The highest possible epoch time of the attachment to the tangle.
- Type
int
, unix timestamp in milliseconds.
-
property
attachment_timestamp_upper_bound_as_trytes
¶ Returns a TryteString representation of the transaction’s
attachment_timestamp_upper_bound
.
-
branch_transaction_hash
: Optional[TransactionHash] = None¶ An unrelated transaction that this transaction “approves”.
In order to add a transaction to the Tangle, the client must perform PoW to “approve” two existing transactions, called the “trunk” and “branch” transactions.
The branch transaction may be selected strategically to maximize the bundle’s chances of getting confirmed; otherwise it usually has no significance.
- Type
-
bundle_hash
: Optional[BundleHash] = None¶ The bundle hash, used to identify transactions that are part of the same bundle.
This value is generated by taking a hash of the metadata from all transactions in the bundle.
- Type
-
current_index
: Optional[int] = None¶ The position of the transaction inside the bundle.
If the
current_index
value is 0, then this is the “head transaction”.If it is equal to
last_index
, then this is the “tail transaction”.
For value transfers, the “spend” transaction is generally in the 0th position, followed by inputs, and the “change” transaction is last.
- Type
int
-
property
current_index_as_trytes
¶ Returns a TryteString representation of the transaction’s
current_index
.
-
hash
: TransactionHash = None¶ The transaction hash, used to uniquely identify the transaction on the Tangle.
This value is generated by taking a hash of the raw transaction trits.
- Type
-
is_confirmed
: bool = None¶ Whether this transaction has been confirmed by neighbor nodes. Must be set manually via the
getInclusionStates
API command.- Type
Optional[bool]
References:
-
property
is_tail
¶ Returns whether this transaction is a tail (first one in the bundle).
Because of the way the Tangle is organized, the tail transaction is generally the last one in the bundle that gets attached, even though it occupies the first logical position inside the bundle.
-
last_index
: Optional[int] = None¶ The index of the final transaction in the bundle.
This value is attached to every transaction to make it easier to traverse and verify bundles.
- Type
int
-
property
last_index_as_trytes
¶ Returns a TryteString representation of the transaction’s
last_index
.
-
property
legacy_tag
¶ Return the legacy tag of the transaction. If no legacy tag was set, returns the tag instead.
-
nonce
: Optional[Nonce] = None¶ Unique value used to increase security of the transaction hash.
This is the product of the PoW process.
- Type
-
signature_message_fragment
: Optional[Fragment] = None¶ “Signature/Message Fragment” (note the slash):
For inputs, this contains a fragment of the cryptographic signature, used to verify the transaction (depending on the security level of the corresponding address, the entire signature is usually too large to fit into a single transaction, so it is split across multiple transactions instead).
For other transactions, this contains a fragment of the message attached to the transaction (if any). This can be pretty much any value. Like signatures, the message may be split across multiple transactions if it is too large to fit inside a single transaction.
- Type
-
tag
: Optional[Tag] = None¶ Optional classification tag applied to this transaction.
Many transactions have empty tags (
Tag(b'999999999999999999999999999')
).- Type
-
timestamp
: int = None¶ Timestamp used to increase the security of the transaction hash.
Describes when the transaction was created.
Important
This value is easy to forge! Do not rely on it when resolving conflicts!
- Type
int
, unix timestamp in seconds.
-
trunk_transaction_hash
: Optional[TransactionHash] = None¶ The transaction hash of the next transaction in the bundle.
In order to add a transaction to the Tangle, the client must perform PoW to “approve” two existing transactions, called the “trunk” and “branch” transactions.
The trunk transaction is generally used to link transactions within a bundle.
- Type
-
value
: int = None¶ The number of iotas being transferred in this transaction:
If this value is negative, then the
address
is spending iotas.If it is positive, then the
address
is receiving iotas.If it is zero, then this transaction is being used to carry metadata (such as a signature fragment or a message) instead of transferring iotas.
- Type
int
as_json_compatible¶
-
Transaction.
as_json_compatible
() → dict¶ Returns a JSON-compatible representation of the object.
- Returns
dict
with the following structure:{ 'hash_': TransactionHash, 'signature_message_fragment': Fragment, 'address': Address, 'value': int, 'legacy_tag': Tag, 'timestamp': int, 'current_index': int, 'last_index': int, 'bundle_hash': BundleHash, 'trunk_transaction_hash': TransactionHash, 'branch_transaction_hash': TransactionHash, 'tag': Tag, 'attachment_timestamp': int, 'attachment_timestamp_lower_bound': int, 'attachment_timestamp_upper_bound': int, 'nonce': Nonce, }
References:
iota.json.JsonEncoder
.
as_tryte_string¶
-
Transaction.
as_tryte_string
() → iota.transaction.types.TransactionTrytes¶ Returns a TryteString representation of the transaction.
- Returns
TryteString
object.
from_tryte_string¶
-
classmethod
Transaction.
from_tryte_string
(trytes: Union[AnyStr, bytearray, TryteString], hash_: Optional[iota.transaction.types.TransactionHash] = None) → T¶ Creates a Transaction object from a sequence of trytes.
- Parameters
trytes (TrytesCompatible) – Raw trytes. Should be exactly 2673 trytes long.
hash (Optional[TransactionHash]) –
The transaction hash, if available.
If not provided, it will be computed from the transaction trytes.
- Returns
Transaction
object.
Example usage:
from iota import Transaction txn =\ Transaction.from_tryte_string( b'GYPRVHBEZOOFXSHQBLCYW9ICTCISLHDBNMMVYD9JJHQMPQCTIQAQTJNNNJ9IDXLRCC' b'OYOXYPCLR9PBEY9ORZIEPPDNTI9CQWYZUOTAVBXPSBOFEQAPFLWXSWUIUSJMSJIIIZ' b'WIKIRH9GCOEVZFKNXEVCUCIIWZQCQEUVRZOCMEL9AMGXJNMLJCIA9UWGRPPHCEOPTS' b'VPKPPPCMQXYBHMSODTWUOABPKWFFFQJHCBVYXLHEWPD9YUDFTGNCYAKQKVEZYRBQRB' b'XIAUX9SVEDUKGMTWQIYXRGSWYRK9SRONVGTW9YGHSZRIXWGPCCUCDRMAXBPDFVHSRY' b'WHGB9DQSQFQKSNICGPIPTRZINYRXQAFSWSEWIFRMSBMGTNYPRWFSOIIWWT9IDSELM9' b'JUOOWFNCCSHUSMGNROBFJX9JQ9XT9PKEGQYQAWAFPRVRRVQPUQBHLSNTEFCDKBWRCD' b'X9EYOBB9KPMTLNNQLADBDLZPRVBCKVCYQEOLARJYAGTBFR9QLPKZBOYWZQOVKCVYRG' b'YI9ZEFIQRKYXLJBZJDBJDJVQZCGYQMROVHNDBLGNLQODPUXFNTADDVYNZJUVPGB9LV' b'PJIYLAPBOEHPMRWUIAJXVQOEM9ROEYUOTNLXVVQEYRQWDTQGDLEYFIYNDPRAIXOZEB' b'CS9P99AZTQQLKEILEVXMSHBIDHLXKUOMMNFKPYHONKEYDCHMUNTTNRYVMMEYHPGASP' b'ZXASKRUPWQSHDMU9VPS99ZZ9SJJYFUJFFMFORBYDILBXCAVJDPDFHTTTIYOVGLRDYR' b'TKHXJORJVYRPTDH9ZCPZ9ZADXZFRSFPIQKWLBRNTWJHXTOAUOL9FVGTUMMPYGYICJD' b'XMOESEVDJWLMCVTJLPIEKBE9JTHDQWV9MRMEWFLPWGJFLUXI9BXPSVWCMUWLZSEWHB' b'DZKXOLYNOZAPOYLQVZAQMOHGTTQEUAOVKVRRGAHNGPUEKHFVPVCOYSJAWHZU9DRROH' b'BETBAFTATVAUGOEGCAYUXACLSSHHVYDHMDGJP9AUCLWLNTFEVGQGHQXSKEMVOVSKQE' b'EWHWZUDTYOBGCURRZSJZLFVQQAAYQO9TRLFFN9HTDQXBSPPJYXMNGLLBHOMNVXNOWE' b'IDMJVCLLDFHBDONQJCJVLBLCSMDOUQCKKCQJMGTSTHBXPXAMLMSXRIPUBMBAWBFNLH' b'LUJTRJLDERLZFUBUSMF999XNHLEEXEENQJNOFFPNPQ9PQICHSATPLZVMVIWLRTKYPI' b'XNFGYWOJSQDAXGFHKZPFLPXQEHCYEAGTIWIJEZTAVLNUMAFWGGLXMBNUQTOFCNLJTC' b'DMWVVZGVBSEBCPFSM99FLOIDTCLUGPSEDLOKZUAEVBLWNMODGZBWOVQT9DPFOTSKRA' b'BQAVOQ9RXWBMAKFYNDCZOJGTCIDMQSQQSODKDXTPFLNOKSIZEOY9HFUTLQRXQMEPGO' b'XQGLLPNSXAUCYPGZMNWMQWSWCKAQYKXJTWINSGPPZG9HLDLEAWUWEVCTVRCBDFOXKU' b'ROXH9HXXAXVPEJFRSLOGRVGYZASTEBAQNXJJROCYRTDPYFUIQJVDHAKEG9YACV9HCP' b'JUEUKOYFNWDXCCJBIFQKYOXGRDHVTHEQUMHO999999999999999999999999999999' b'999999999999999999999999999999999999999999999999999999999999999999' b'999999999999999999999999999999999999999999999999999999999999999999' b'999999999999999999999999999999999999999999999999999999999999999999' b'999999999999999999999999999999999999999999999999999999999999999999' b'999999999999999999999999999999999999999999999999999999999999999999' b'999999999999999999999999999999999999999999999999999999999999999999' b'999999999999999999999999999999999999999999999999999999999999999999' b'999999999999999999999999999999999999999999999999999999999999999999' b'999999999999999999999999999999999999999999999999999999999999999999' b'999999999999999999999999999999999999999999999999999999999999999999' b'999999999999RKWEEVD99A99999999A99999999NFDPEEZCWVYLKZGSLCQNOFUSENI' b'XRHWWTZFBXMPSQHEDFWZULBZFEOMNLRNIDQKDNNIELAOXOVMYEI9PGTKORV9IKTJZQ' b'UBQAWTKBKZ9NEZHBFIMCLV9TTNJNQZUIJDFPTTCTKBJRHAITVSKUCUEMD9M9SQJ999' b'999TKORV9IKTJZQUBQAWTKBKZ9NEZHBFIMCLV9TTNJNQZUIJDFPTTCTKBJRHAITVSK' b'UCUEMD9M9SQJ999999999999999999999999999999999999999999999999999999' b'999999999999999999999999999999999' )
get_bundle_essence_trytes¶
-
Transaction.
get_bundle_essence_trytes
() → iota.types.TryteString¶ Returns the values needed for calculating bundle hash. The bundle hash is the hash of the bundle essence, which is itself the hash of the following fields of transactions in the bundle:
address
,value
,legacy_tag
,current_index
,last_index
,and
timestamp
.
The transaction’s
signature_message_fragment
field contains the signature generated by signing the bundle hash with the address’s private key.- Returns
TryteString
object.
ProposedTransaction¶
-
class
iota.
ProposedTransaction
(address: iota.types.Address, value: int, tag: Optional[iota.types.Tag] = None, message: Optional[iota.types.TryteString] = None, timestamp: Optional[int] = None)¶ A transaction that has not yet been attached to the Tangle.
Proposed transactions are created locally. Note that for creation, only a small subset of the
Transaction
attributes is needed.Provide to
Iota.send_transfer()
to attach to tangle and publish/store.Note
In order to follow naming convention of other libs, you may use the name
Transfer
interchangeably withProposedTransaction
. See https://github.com/iotaledger/iota.py/issues/72 for more info.- Parameters
address (Address) – Address associated with the transaction.
value (int) – Transaction value.
tag (Optional[Tag]) – Optional classification tag applied to this transaction.
message (Optional[TryteString]) – Message to be included in
transaction.Transaction.signature_or_message_fragment
field of the transaction. Should not be longer thantransaction.Fragment.LEN
.timestamp (Optional[int]) – Timestamp of transaction creation. If not supplied, the library will generate it.
- Returns
iota.ProposedTransaction
object.
Example usage:
txn=\ ProposedTransaction( address = Address( b'TESTVALUE9DONTUSEINPRODUCTION99999XE9IVG' b'EFNDOCQCMERGUATCIEGGOHPHGFIAQEZGNHQ9W99CH' ), message = TryteString.from_unicode('thx fur cheezburgers'), tag = Tag(b'KITTENS'), value = 42, )
as_tryte_string¶
-
ProposedTransaction.
as_tryte_string
() → iota.types.TryteString¶ Returns a TryteString representation of the transaction.
- Returns
TryteString
object.- Raises
RuntimeError – if the transaction doesn’t have a bundle hash field, meaning that the bundle containing the transaction hasn’t been finalized yet.
Bundle Types¶
As with transactions, PyOTA defines two different types to represent bundles:
Bundle
for bundles that have already been broadcast to the Tangle. Generally, you will never need to createBundle
objects; the API will build them for you, as the result of various API methods.
ProposedBundle
for bundles that have been created locally and have not been broadcast yet.
Bundle¶
-
class
iota.
Bundle
(transactions: Optional[Iterable[iota.transaction.base.Transaction]] = None)¶ A collection of transactions, treated as an atomic unit when attached to the Tangle.
Note: unlike a block in a blockchain, bundles are not first-class citizens in IOTA; only transactions get stored in the Tangle.
Instead, Bundles must be inferred by following linked transactions with the same bundle hash.
- Parameters
transactions (Optional[Iterable[Transaction]]) – Transactions in the bundle. Note that transactions will be sorted into ascending order based on their
current_index
.- Returns
Bundle
object.
References:
-
property
hash
¶ Returns the hash of the bundle.
This value is determined by inspecting the bundle’s tail transaction, so in a few edge cases, it may be incorrect.
- Returns
BundleHash
object, orIf the bundle has no transactions, this method returns
None
.
-
property
is_confirmed
¶ Returns whether this bundle has been confirmed by neighbor nodes.
This attribute must be set manually.
- Returns
bool
References:
-
property
tail_transaction
¶ Returns the tail transaction of the bundle.
- Returns
-
transactions
: List[Transaction] = None¶ List of
Transaction
objects that are in the bundle.
as_json_compatible¶
-
Bundle.
as_json_compatible
() → List[dict]¶ Returns a JSON-compatible representation of the object.
- Returns
List[dict]
. Thedict
list elements contain individual transactions as inTransaction.as_json_compatible()
.
References:
iota.json.JsonEncoder
.
as_tryte_strings¶
-
Bundle.
as_tryte_strings
(head_to_tail: bool = False) → List[iota.transaction.types.TransactionTrytes]¶ Returns TryteString representations of the transactions in this bundle.
- Parameters
head_to_tail (bool) –
Determines the order of the transactions:
True
: head txn first, tail txn last.False
(default): tail txn first, head txn last.
Note that the order is reversed by default, as this is the way bundles are typically broadcast to the Tangle.
- Returns
List[TransactionTrytes]
from_tryte_strings¶
-
classmethod
Bundle.
from_tryte_strings
(trytes: Iterable[iota.types.TryteString]) → B¶ Creates a Bundle object from a list of tryte values.
Note, that this is effectively calling
Transaction.from_tryte_string()
on the iterbale elements and constructing the bundle from the created transactions.- Parameters
trytes (Iterable[TryteString]) – List of raw transaction trytes.
- Returns
Bundle
object.
Example usage:
from iota import Bundle bundle = Bundle.from_tryte_strings([ b'GYPRVHBEZOOFXSHQBLCYW9ICTCISLHDBNMMVYD9JJHQMPQCTIQAQTJNNNJ9IDXLRCC...', b'OYOXYPCLR9PBEY9ORZIEPPDNTI9CQWYZUOTAVBXPSBOFEQAPFLWXSWUIUSJMSJIIIZ...', # etc. ])
get_messages¶
-
Bundle.
get_messages
(errors: str = 'drop') → List[str]¶ Attempts to decipher encoded messages from the transactions in the bundle.
- Parameters
errors (str) –
How to handle trytes that can’t be converted, or bytes that can’t be decoded using UTF-8:
- ’drop’
Drop the trytes from the result.
- ’strict’
Raise an exception.
- ’replace’
Replace with a placeholder character.
- ’ignore’
Omit the invalid tryte/byte sequence.
- Returns
List[str]
ProposedBundle¶
Note
This section contains information about how PyOTA works “under the hood”.
The Iota.prepare_transfer()
API method encapsulates this
functionality for you; it is not necessary to understand how
ProposedBundle
works in order to use PyOTA.
-
class
iota.
ProposedBundle
(transactions: Optional[Iterable[iota.transaction.creation.ProposedTransaction]] = None, inputs: Optional[Iterable[iota.types.Address]] = None, change_address: Optional[iota.types.Address] = None)¶ A collection of proposed transactions, to be treated as an atomic unit when attached to the Tangle.
- Parameters
transactions (Optional[Iterable[ProposedTransaction]]) – Proposed transactions that should be put into the proposed bundle.
inputs (Optional[Iterable[Address]]) –
Addresses that hold iotas to fund outgoing transactions in the bundle. If provided, the library will create and sign withdrawing transactions from these addresses.
See
Iota.get_inputs()
for more info.change_address (Optional[Address]) – Due to the signatures scheme of IOTA, you can only spend once from an address. Therefore the library will always deduct the full available amount from an input address. The unused tokens will be sent to
change_address
if provided, or to a newly-generated and unused address if not.
- Returns
-
property
balance
¶ Returns the bundle balance. In order for a bundle to be valid, its balance must be 0:
A positive balance means that there aren’t enough inputs to cover the spent amount; add more inputs using
add_inputs()
.A negative balance means that there are unspent inputs; use
send_unspent_inputs_to()
to send the unspent inputs to a “change” address.
- Returns
bool
-
property
tag
¶ Determines the most relevant tag for the bundle.
- Returns
transaction.Tag
ProposedBundle
provides a convenient interface for creating new
bundles, listed in the order that they should be invoked:
add_transaction¶
-
ProposedBundle.
add_transaction
(transaction: iota.transaction.creation.ProposedTransaction) → None¶ Adds a transaction to the bundle.
If the transaction message is too long, it will be split automatically into multiple transactions.
- Parameters
transaction (ProposedTransaction) – The transaction to be added.
- Raises
RuntimeError – if bundle is already finalized
ValueError – if trying to add a spending transaction. Use
add_inputs()
instead.
add_inputs¶
-
ProposedBundle.
add_inputs
(inputs: Iterable[iota.types.Address]) → None¶ Specifies inputs that can be used to fund transactions that spend iotas.
The
ProposedBundle
will use these to create the necessary input transactions.Note that each input may require multiple transactions, in order to hold the entire signature.
- Parameters
inputs (Iterable[Address]) –
Addresses to use as the inputs for this bundle.
Important
Must have
balance
andkey_index
attributes! UseIota.get_inputs()
to prepare inputs.- Raises
RuntimeError – if bundle is already finalized.
ValueError –
if input address has no
balance
.if input address has no
key_index
.
send_unspent_inputs_to¶
-
ProposedBundle.
send_unspent_inputs_to
(address: iota.types.Address) → None¶ Specifies the address that will receive unspent iotas.
The
ProposedBundle
will use this to create the necessary change transaction, if necessary.If the bundle has no unspent inputs, this method does nothing.
- Parameters
address (Address) – Address to send unspent inputs to.
- Raises
RuntimeError – if bundle is already finalized.
add_signature_or_message¶
-
ProposedBundle.
add_signature_or_message
(fragments: Iterable[iota.transaction.types.Fragment], start_index: Optional[int] = 0) → None¶ Adds signature/message fragments to transactions in the bundle starting at start_index. If a transaction already has a fragment, it will be overwritten.
- Parameters
fragments (Iterable[Fragment]) – List of fragments to add. Use [Fragment(…),Fragment(…),…] to create this argument. Fragment() accepts any TryteString compatible type, or types that can be converted to TryteStrings (bytearray, unicode string, etc.). If the payload is less than
FRAGMENT_LENGTH
, it will pad it with 9s.start_index (int) – Index of transaction in bundle from where addition shoudl start.
- Raises
RuntimeError – if bundle is already finalized.
ValueError –
if empty list is provided for
fragments
if wrong
start_index
is provided.if
fragments
is too long and does’t fit into the bundle.
TypeError –
if
fragments
is not anIterable
if
fragments
contains other types thanFragment
.
finalize¶
-
ProposedBundle.
finalize
() → None¶ Finalizes the bundle, preparing it to be attached to the Tangle.
This operation includes checking if the bundle has zero balance, generating the bundle hash and updating the transactions with it, furthermore to initialize signature/message fragment fields.
Once this method is invoked, no new transactions may be added to the bundle.
- Raises
RuntimeError – if bundle is already finalized.
ValueError –
if bundle has no transactions.
if bundle has unspent inputs (there is no
change_address
attribute specified.)if inputs are insufficient to cover bundle spend.
sign_inputs¶
-
ProposedBundle.
sign_inputs
(key_generator: iota.crypto.signing.KeyGenerator) → None¶ Sign inputs in a finalized bundle.
Generates the necessary cryptographic signatures to authorize spending the inputs.
Note
You do not need to invoke this method if the bundle does not contain any transactions that spend iotas.
- Parameters
key_generator (KeyGenerator) – Generator to create private keys for signing.
- Raises
RuntimeError – if bundle is not yet finalized.
ValueError –
if the input transaction specifies an address that doesn’t have
key_index
attribute defined.if the input transaction specifies an address that doesn’t have
security_level
attribute defined.
sign_input_at¶
-
ProposedBundle.
sign_input_at
(start_index: int, private_key: iota.crypto.types.PrivateKey) → None¶ Signs the input at the specified index.
- Parameters
start_index (int) –
The index of the first input transaction.
If necessary, the resulting signature will be split across multiple transactions automatically (i.e., if an input has
security_level=2
, you still only need to callsign_input_at()
once).private_key (PrivateKey) –
The private key that will be used to generate the signature.
Important
Be sure that the private key was generated using the correct seed, or the resulting signature will be invalid!
- Raises
RuntimeError – if bundle is not yet finalized.
as_json_compatible¶
-
ProposedBundle.
as_json_compatible
() → List[dict]¶ Returns a JSON-compatible representation of the object.
- Returns
List[dict]
. Thedict
list elements contain individual transactions as inProposedTransaction.as_json_compatible()
.
References:
iota.json.JsonEncoder
.
Example usage¶
from iota import Address, ProposedBundle, ProposedTransaction
from iota.crypto.signing import KeyGenerator
bundle = ProposedBundle()
bundle.add_transaction(ProposedTransaction(...))
bundle.add_transaction(ProposedTransaction(...))
bundle.add_transaction(ProposedTransaction(...))
bundle.add_inputs([
Address(
address =
b'TESTVALUE9DONTUSEINPRODUCTION99999HAA9UA'
b'MHCGKEUGYFUBIARAXBFASGLCHCBEVGTBDCSAEBTBM',
balance = 86,
key_index = 0,
),
])
bundle.send_unspent_inputs_to(
Address(
b'TESTVALUE9DONTUSEINPRODUCTION99999D99HEA'
b'M9XADCPFJDFANCIHR9OBDHTAGGE9TGCI9EO9ZCRBN'
),
)
bundle.finalize()
bundle.sign_inputs(KeyGenerator(b'SEED9GOES9HERE'))
Once the ProposedBundle
has been finalized (and inputs signed, if
necessary), invoke its ProposedBundle.as_tryte_strings()
method to
generate the raw trytes that should be included in an
Iota.attach_to_tangle()
API request.
Adapters and Wrappers¶
The Iota
class defines the API methods that are available for
interacting with the node, but it delegates the actual interaction to
another set of classes: Adapters and Wrappers.
The API instance’s methods contain the logic and handle PyOTA-specific types, construct and translate objects, while the API instance’s adapter deals with the networking, communicating with a node.
You can choose and configure the available adapters to be used with the API:
HttpAdapter,
MockAdapter.
AdapterSpec¶
In a few places in the PyOTA codebase, you may see references to a
meta-type called AdapterSpec
.
-
iota.adapter.
AdapterSpec
= typing.Union[str, ForwardRef('BaseAdapter')]¶ Placeholder that means “URI or adapter instance”.
Will be resolved to a correctly-configured adapter instance upon API instance creation.
For example, when creating an Iota
object, the first argument
of Iota.__init__()
is an AdapterSpec
. This means that you can
initialize an Iota
object using either a node URI, or an adapter
instance:
Node URI:
api = Iota('http://localhost:14265')
Adapter instance:
api = Iota(HttpAdapter('http://localhost:14265'))
Adapters¶
Adapters are responsible for sending requests to the node and returning the response.
PyOTA ships with a few adapters:
HttpAdapter¶
from iota import Iota, HttpAdapter
# Use HTTP:
api = Iota('http://localhost:14265')
api = Iota(HttpAdapter('http://localhost:14265'))
# Use HTTPS:
api = Iota('https://nodes.thetangle.org:443')
api = Iota(HttpAdapter('https://nodes.thetangle.org:443'))
# Use HTTPS with basic authentication and 60 seconds timeout:
api = Iota(
HttpAdapter(
'https://nodes.thetangle.org:443',
authentication=('myusername', 'mypassword'),
timeout=60))
-
class
iota.
HttpAdapter
(uri: Union[str, urllib.parse.SplitResult], timeout: Optional[int] = None, authentication: Optional[Tuple[str, str]] = None)¶ Sends standard HTTP(S) requests to the node.
- Parameters
uri (AdapterSpec) –
URI or adapter instance.
If
uri
is astr
, it is parsed to extractscheme
,hostname
andport
.timeout (Optional[int]) – Connection timeout in seconds.
authentication (Optional[Tuple(str,str)]) – Credetentials for basic authentication with the node.
- Returns
HttpAdapter
object.- Raises
InvalidUri –
if protocol is unsupported.
if hostname is empty.
if non-numeric port is supplied.
To configure an Iota
instance to use HttpAdapter
,
specify an http://
or https://
URI, or provide an
HttpAdapter
instance.
The HttpAdapter
raises a BadApiResponse
exception if the server
sends back an error response (due to invalid request parameters, for
example).
Debugging HTTP Requests¶
To see all HTTP requests and responses as they happen, attach a
logging.Logger
instance to the adapter via its set_logger
method.
Any time the HttpAdapter
sends a request or receives a response, it
will first generate a log message. Note: if the response is an error
response (e.g., due to invalid request parameters), the HttpAdapter
will log the request before raising BadApiResponse
.
Note
HttpAdapter
generates log messages with DEBUG
level, so make
sure that your logger’s level
attribute is set low enough that it
doesn’t filter these messages!
Logging to console with default format
from logging import getLogger, basicConfig, DEBUG
from iota import Iota
api = Iota("https://nodes.thetangle.org:443")
# Sets the logging level for the root logger (and for its handlers)
basicConfig(level=DEBUG)
# Get a new logger derived from the root logger
logger = getLogger(__name__)
# Attach the logger to the adapter
api.adapter.set_logger(logger)
# Execute a command that sends request to the node
api.get_node_info()
# Log messages should be printed to console
Logging to a file with custom format
from logging import getLogger, DEBUG, FileHandler, Formatter
from iota import Iota
# Create a custom logger
logger = getLogger(__name__)
# Set logging level to DEBUG
logger.setLevel(DEBUG)
# Create handler to write to a log file
f_handler = FileHandler(filename='pyota.log',mode='a')
f_handler.setLevel(DEBUG)
# Create formatter and add it to handler
f_format = Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
f_handler.setFormatter(f_format)
# Add handler to the logger
logger.addHandler(f_handler)
# Create API instance
api = Iota("https://nodes.thetangle.org:443")
# Add logger to the adapter of the API instance
api.adapter.set_logger(logger)
# Sends a request to the node
api.get_node_info()
# Open 'pyota.log' file and observe the logs
Logging to console with custom format
from logging import getLogger, DEBUG, StreamHandler, Formatter
from iota import Iota
# Create a custom logger
logger = getLogger(__name__)
# Set logging level to DEBUG
logger.setLevel(DEBUG)
# Create handler to write to sys.stderr
s_handler = StreamHandler()
s_handler.setLevel(DEBUG)
# Create formatter and add it to handler
s_format = Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
s_handler.setFormatter(s_format)
# Add handler to the logger
logger.addHandler(s_handler)
# Create API instance
api = Iota("https://nodes.thetangle.org:443")
# Add logger to the adapter of the API instance
api.adapter.set_logger(logger)
# Sends a request to the node
api.get_node_info()
# Observe log messages in console
MockAdapter¶
from iota import Iota, MockAdapter
# Inject a mock adapter.
api = Iota('mock://')
api = Iota(MockAdapter())
# Seed responses from the node.
api.adapter.seed_response('getNodeInfo', {'message': 'Hello, world!'})
api.adapter.seed_response('getNodeInfo', {'message': 'Hello, IOTA!'})
# Invoke API commands, using the adapter.
print(api.get_node_info()) # {'message': 'Hello, world!'}
print(api.get_node_info()) # {'message': 'Hello, IOTA!'}
print(api.get_node_info()) # raises BadApiResponse exception
-
class
iota.
MockAdapter
¶ A mock adapter used for simulating API responses without actually sending any requests to the node.
This is particularly useful in unit and functional tests where you want to verify that your code works correctly in specific scenarios, without having to engineer your own subtangle.
To use this adapter, you must first “seed” the responses that the adapter should return for each request. The adapter will then return the appropriate seeded response each time it “sends” a request.
- Parameters
None – To construct a
MockAdapter
, you don’t need to supply any arguments.- Returns
MockAdapter
object.
To configure an
Iota
instance to useMockAdapter
, specifymock://
as the node URI, or provide aMockAdapter
instance.Example usage:
from iota import Iota, MockAdapter # Create API with a mock adapter. api = Iota('mock://') api = Iota(MockAdapter())
To use MockAdapter
, you must first seed the responses that you want
it to return by calling its MockAdapter.seed_response()
method.
seed_response¶
-
MockAdapter.
seed_response
(command: str, response: dict) → iota.adapter.MockAdapter¶ Sets the response that the adapter will return for the specified command.
You can seed multiple responses per command; the adapter will put them into a FIFO queue. When a request comes in, the adapter will pop the corresponding response off of the queue.
Note that you have to call
seed_response()
once for each request you expect it to process. IfMockAdapter
does not have a seeded response for a particular command, it will raise aBadApiResponse
exception (simulates a 404 response).- Parameters
command (str) – The name of the command. Note that this is the camelCase version of the command name (e.g.,
getNodeInfo
, notget_node_info
).response (dict) – The response that the adapter will return.
Example usage:
adapter.seed_response('sayHello', {'message': 'Hi!'}) adapter.seed_response('sayHello', {'message': 'Hello!'}) adapter.send_request({'command': 'sayHello'}) # {'message': 'Hi!'} adapter.send_request({'command': 'sayHello'}) # {'message': 'Hello!'}
Wrappers¶
Wrappers act like decorators for adapters; they are used to enhance or otherwise modify the behavior of adapters.
RoutingWrapper¶
-
class
iota.adapter.wrappers.
RoutingWrapper
(default_adapter: Union[str, BaseAdapter])¶ Routes commands (API requests) to different nodes depending on the command name.
This allows you to, for example, send POW requests to a local node, while routing all other requests to a remote one.
Once you’ve initialized the
RoutingWrapper
, invoke itsadd_route()
method to specify a different adapter to use for a particular command.- Parameters
default_adapter (AdapterSpec) – 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.
- Returns
RoutingWrapper
object.
Example usage:
from iota import Iota from iota.adapter.wrappers import RoutingWrapper # Route POW to localhost, everything else to 'https://nodes.thetangle.org:443'. api = Iota( RoutingWrapper('https://nodes.thetangle.org:443.'') .add_route('attachToTangle', 'http://localhost:14265') .add_route('interruptAttachingToTangle', 'http://localhost:14265') )
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 useRoutingWrapper
adapter andlocal_pow
parameter together in an API instance (seeiota.Iota
), because the behavior might not be obvious.local_pow
tells the API to perform proof-of-work (iota.Iota.attach_to_tangle()
) without relying on an actual node. It does this by calling an extension package 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 inRoutingWrapper
.
add_route¶
-
RoutingWrapper.
add_route
(command: str, adapter: Union[str, BaseAdapter]) → iota.adapter.wrappers.RoutingWrapper¶ Adds a route to the wrapper.
- Parameters
command (str) – The name of the command. Note that this is the camelCase version of the command name (e.g.,
attachToTangle
, notattach_to_tangle
).adapter (AdapterSpec) – The adapter object or URI to route requests to.
- Returns
The
RoutingWrapper
object it was called on. Useful for chaining the operation of adding routes in code.
See
RoutingWrapper
for example usage.
PyOTA API Classes¶
PyOTA offers you the Python API to interact with the IOTA network. The available methods can be grouped into two categories:
Core API |
Extended API |
---|---|
API commands for direct
interaction with a node.
|
Builds on top of the Core API to
perform more complex operations,
and abstract away low-level IOTA
specific procedures.
|
PyOTA supports both synchronous and asynchronous communication with the network, therefore the Core and Extended API classes are available in synchronous and asynchronous versions.
To use the API in your Python application or script, declare an API instance of any of the API classes. Since the Extended API incorporates the Core API, usually you end up only using the Extended API, but if for some reason you need only the core functionality, the library is there to help you.
1 2 3 4 5 6 7 8 | # Synchronous API classes
from iota import Iota, StrictIota
# This is how you declare a sync Extended API, use the methods of this object.
api = Iota('adapter-specification')
# This is how you declare a sync Core API, use the methods of this object.
api = StrictIota('adapter-specification')
|
The PyOTA speific StrictIota
class implements the Core API,
while Iota
implements the Extended API. From a Python
implementation point of view, Iota
is a subclass of
StrictIota
, therefore it inherits every method and attribute
the latter has.
To use the functionally same, but asynchronous API classes, you can do the following:
1 2 3 4 5 6 7 8 | # Asynchronous API classes
from iota import AsyncIota, AsyncStrictIota
# This is how you declare an async Extended API, use the methods of this object.
api = AsyncIota('adapter-specification')
# This is how you declare an async Core API, use the methods of this object.
api = AsyncStrictIota('adapter-specification')
|
Take a look on the class definitions and notice that Iota
and
AsyncIota
have a Seed
attribute. This is because the
Extended API is able to generate private keys, addresses and signatures from
your seed. Your seed never leaves the library and your machine!
Core API Classes¶
Synchronous¶
-
class
iota.
StrictIota
(adapter: Union[str, BaseAdapter], devnet: bool = False, local_pow: bool = False)¶ Synchronous API to send HTTP requests for communicating with an IOTA node.
This implementation only exposes the “core” API methods. For a more feature-complete implementation, use
Iota
instead.References:
- Parameters
adapter (AdapterSpec) – URI string or BaseAdapter instance.
devnet (Optional[bool]) – Whether to use devnet settings for this instance. On the devnet, minimum weight magnitude is set to 9, on mainnet it is 14 by default.
local_pow (Optional[bool]) –
Whether to perform proof-of-work locally by redirecting all calls to
attach_to_tangle()
to ccurl pow interface.See Optional Local Pow for more info and find out how to use it.
-
set_local_pow
(local_pow: bool) → None¶ Sets the
local_pow
attribute of the adapter of the api instance. If it isTrue
,attach_to_tangle()
command calls external interface to perform proof of work, instead of sending the request to a node.By default,
local_pow
is set toFalse
. This particular method is needed if one wants to change local_pow behavior dynamically.- Parameters
local_pow (bool) – Whether to perform pow locally.
- Returns
None
Asynchronous¶
-
class
iota.
AsyncStrictIota
(adapter: Union[str, BaseAdapter], devnet: bool = False, local_pow: bool = False)¶ Asynchronous API to send HTTP requests for communicating with an IOTA node.
This implementation only exposes the “core” API methods. For a more feature-complete implementation, use
AsyncIota
instead.References:
- Parameters
adapter (AdapterSpec) – URI string or BaseAdapter instance.
devnet (Optional[bool]) – Whether to use devnet settings for this instance. On the devnet, minimum weight magnitude is set to 9, on mainnet it is 1 by default.
local_pow (Optional[bool]) –
Whether to perform proof-of-work locally by redirecting all calls to
attach_to_tangle()
to ccurl pow interface.See Optional Local Pow for more info and find out how to use it.
-
set_local_pow
(local_pow: bool) → None¶ Sets the
local_pow
attribute of the adapter of the api instance. If it isTrue
,attach_to_tangle()
command calls external interface to perform proof of work, instead of sending the request to a node.By default,
local_pow
is set toFalse
. This particular method is needed if one wants to change local_pow behavior dynamically.- Parameters
local_pow (bool) – Whether to perform pow locally.
- Returns
None
Extended API Classes¶
Synchronous¶
-
class
iota.
Iota
(adapter: Union[str, BaseAdapter], seed: Union[AnyStr, bytearray, TryteString, None] = None, devnet: bool = False, local_pow: bool = False)¶ Implements the synchronous core API, plus additional synchronous wrapper methods for common operations.
- Parameters
adapter (AdapterSpec) – URI string or BaseAdapter instance.
seed (Optional[Seed]) –
Seed used to generate new addresses. If not provided, a random one will be generated.
Note
This value is never transferred to the node/network.
devnet (Optional[bool]) –
Whether to use devnet settings for this instance. On the devnet, minimum weight magnitude is decreased, on mainnet it is 14 by default.
For more info on the Mainnet and the Devnet, visit the official docs site<https://docs.iota.org/docs/getting-started/0.1/network/iota-networks/>.
local_pow (Optional[bool]) –
Whether to perform proof-of-work locally by redirecting all calls to
attach_to_tangle()
to ccurl pow interface.See Optional Local Pow for more info and find out how to use it.
References:
https://docs.iota.org/docs/node-software/0.1/iri/references/api-reference
https://github.com/iotaledger/wiki/blob/master/api-proposal.md
-
set_local_pow
(local_pow: bool) → None¶ Sets the
local_pow
attribute of the adapter of the api instance. If it isTrue
,attach_to_tangle()
command calls external interface to perform proof of work, instead of sending the request to a node.By default,
local_pow
is set toFalse
. This particular method is needed if one wants to change local_pow behavior dynamically.- Parameters
local_pow (bool) – Whether to perform pow locally.
- Returns
None
Asynchronous¶
-
class
iota.
AsyncIota
(adapter: Union[str, BaseAdapter], seed: Union[AnyStr, bytearray, TryteString, None] = None, devnet: bool = False, local_pow: bool = False)¶ Implements the async core API, plus additional async wrapper methods for common operations.
- Parameters
adapter (AdapterSpec) – URI string or BaseAdapter instance.
seed (Optional[Seed]) –
Seed used to generate new addresses. If not provided, a random one will be generated.
Note
This value is never transferred to the node/network.
devnet (Optional[bool]) –
Whether to use devnet settings for this instance. On the devnet, minimum weight magnitude is decreased, on mainnet it is 14 by default.
For more info on the Mainnet and the Devnet, visit the official docs site<https://docs.iota.org/docs/getting-started/0.1/network/iota-networks/>.
local_pow (Optional[bool]) –
Whether to perform proof-of-work locally by redirecting all calls to
attach_to_tangle()
to ccurl pow interface.See Optional Local Pow for more info and find out how to use it.
References:
https://docs.iota.org/docs/node-software/0.1/iri/references/api-reference
https://github.com/iotaledger/wiki/blob/master/api-proposal.md
-
set_local_pow
(local_pow: bool) → None¶ Sets the
local_pow
attribute of the adapter of the api instance. If it isTrue
,attach_to_tangle()
command calls external interface to perform proof of work, instead of sending the request to a node.By default,
local_pow
is set toFalse
. This particular method is needed if one wants to change local_pow behavior dynamically.- Parameters
local_pow (bool) – Whether to perform pow locally.
- Returns
None
Core API Methods¶
The Core API includes all of the core API calls that are made available by the current IOTA Reference Implementation.
These methods are “low level” and generally do not need to be called directly.
For the full documentation of all the Core API calls, please refer to the official documentation.
Note
Below you will find the documentation for both the synchronous and asynchronous versions of the Core API methods.
It should be made clear, that they do exactly the same IOTA related operations, accept the same arguments and return the same structures. Asynchronous API calls are non-blocking, so your application can do other stuff while waiting for the result from the network.
While synchronous API calls are regular Python methods, their respective
asynchronous versions are Python coroutines. You can await
their
results, schedule them for execution inside and event loop and much more.
PyOTA uses the built-in asyncio Python module for asynchronous operation.
For an overview of what you can do with it, head over to this article.
add_neighbors
¶
-
Iota.
add_neighbors
(uris: Iterable[str]) → dict¶ Add one or more neighbors to the node. Lasts until the node is restarted.
- Parameters
uris (Iterable[str]) –
Use format
<protocol>://<ip address>:<port>
. Example:add_neighbors(['udp://example.com:14265'])
Note
These URIs are for node-to-node communication (e.g., weird things will happen if you specify a node’s HTTP API URI here).
- Returns
dict
with the following structure:{ 'addedNeighbors': int, Total number of added neighbors. 'duration': int, Number of milliseconds it took to complete the request. }
References:
-
async
AsyncIota.
add_neighbors
(uris: Iterable[str]) → dict¶ Add one or more neighbors to the node. Lasts until the node is restarted.
- Parameters
uris (Iterable[str]) –
Use format
<protocol>://<ip address>:<port>
. Example:add_neighbors(['udp://example.com:14265'])
Note
These URIs are for node-to-node communication (e.g., weird things will happen if you specify a node’s HTTP API URI here).
- Returns
dict
with the following structure:{ 'addedNeighbors': int, Total number of added neighbors. 'duration': int, Number of milliseconds it took to complete the request. }
References:
attach_to_tangle
¶
-
Iota.
attach_to_tangle
(trunk_transaction: iota.transaction.types.TransactionHash, branch_transaction: iota.transaction.types.TransactionHash, trytes: Iterable[iota.types.TryteString], min_weight_magnitude: Optional[int] = None) → dict¶ Attaches the specified transactions (trytes) to the Tangle by doing Proof of Work. You need to supply branchTransaction as well as trunkTransaction (basically the tips which you’re going to validate and reference with this transaction) - both of which you’ll get through the
get_transactions_to_approve()
API call.The returned value is a different set of tryte values which you can input into
broadcast_transactions()
andstore_transactions()
.- Parameters
trunk_transaction (TransactionHash) – Trunk transaction hash.
branch_transaction (TransactionHash) – Branch transaction hash.
trytes (Iterable[TransactionTrytes]) – List of transaction trytes in the bundle to be attached.
min_weight_magnitude (Optional[int]) – Minimum weight magnitude to be used for attaching trytes. 14 by default on mainnet, 9 on devnet/devnet.
- Returns
dict
with the following structure:{ 'trytes': List[TransactionTrytes], Transaction trytes that include a valid nonce field. }
References:
-
async
AsyncIota.
attach_to_tangle
(trunk_transaction: iota.transaction.types.TransactionHash, branch_transaction: iota.transaction.types.TransactionHash, trytes: Iterable[iota.types.TryteString], min_weight_magnitude: Optional[int] = None) → dict¶ Attaches the specified transactions (trytes) to the Tangle by doing Proof of Work. You need to supply branchTransaction as well as trunkTransaction (basically the tips which you’re going to validate and reference with this transaction) - both of which you’ll get through the
get_transactions_to_approve()
API call.The returned value is a different set of tryte values which you can input into
broadcast_transactions()
andstore_transactions()
.- Parameters
trunk_transaction (TransactionHash) – Trunk transaction hash.
branch_transaction (TransactionHash) – Branch transaction hash.
trytes (Iterable[TransactionTrytes]) – List of transaction trytes in the bundle to be attached.
min_weight_magnitude (Optional[int]) – Minimum weight magnitude to be used for attaching trytes. 14 by default on mainnet, 9 on devnet/devnet.
- Returns
dict
with the following structure:{ 'trytes': List[TransactionTrytes], Transaction trytes that include a valid nonce field. }
References:
broadcast_transactions
¶
-
Iota.
broadcast_transactions
(trytes: Iterable[iota.types.TryteString]) → dict¶ Broadcast a list of transactions to all neighbors.
The input trytes for this call are provided by
attach_to_tangle()
.- Parameters
trytes (Iterable[TransactionTrytes]) – List of transaction trytes to be broadcast.
- Returns
dict
with the following structure:{ 'duration': int, Number of milliseconds it took to complete the request. }
References:
-
async
AsyncIota.
broadcast_transactions
(trytes: Iterable[iota.types.TryteString]) → dict¶ Broadcast a list of transactions to all neighbors.
The input trytes for this call are provided by
attach_to_tangle()
.- Parameters
trytes (Iterable[TransactionTrytes]) – List of transaction trytes to be broadcast.
- Returns
dict
with the following structure:{ 'duration': int, Number of milliseconds it took to complete the request. }
References:
check_consistency
¶
-
Iota.
check_consistency
(tails: Iterable[iota.transaction.types.TransactionHash]) → dict¶ Used to ensure tail resolves to a consistent ledger which is necessary to validate before attempting promotion. Checks transaction hashes for promotability.
This is called with a pending transaction (or more of them) and it will tell you if it is still possible for this transaction (or all the transactions simultaneously if you give more than one) to be confirmed, or not (because it conflicts with another already confirmed transaction).
- Parameters
tails (Iterable[TransactionHash]) – Transaction hashes. Must be tail transactions.
- Returns
dict
with the following structure:{ 'state': bool, Whether tails resolve to consistent ledger. 'info': str, This field will only exist if 'state' is ``False``. }
References:
-
async
AsyncIota.
check_consistency
(tails: Iterable[iota.transaction.types.TransactionHash]) → dict¶ Used to ensure tail resolves to a consistent ledger which is necessary to validate before attempting promotion. Checks transaction hashes for promotability.
This is called with a pending transaction (or more of them) and it will tell you if it is still possible for this transaction (or all the transactions simultaneously if you give more than one) to be confirmed, or not (because it conflicts with another already confirmed transaction).
- Parameters
tails (Iterable[TransactionHash]) – Transaction hashes. Must be tail transactions.
- Returns
dict
with the following structure:{ 'state': bool, Whether tails resolve to consistent ledger. 'info': str, This field will only exist if 'state' is ``False``. }
References:
find_transactions
¶
-
Iota.
find_transactions
(bundles: Optional[Iterable[iota.transaction.types.BundleHash]] = None, addresses: Optional[Iterable[iota.types.Address]] = None, tags: Optional[Iterable[iota.types.Tag]] = None, approvees: Optional[Iterable[iota.transaction.types.TransactionHash]] = None) → dict¶ Find the transactions which match the specified input and return.
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.
approvees (Optional[Iterable[TransactionHash]]) – List of approvee transaction IDs.
- Returns
dict
with the following structure:{ 'hashes': List[TransationHash], Found transactions. }
References:
-
async
AsyncIota.
find_transactions
(bundles: Optional[Iterable[iota.transaction.types.BundleHash]] = None, addresses: Optional[Iterable[iota.types.Address]] = None, tags: Optional[Iterable[iota.types.Tag]] = None, approvees: Optional[Iterable[iota.transaction.types.TransactionHash]] = None) → dict¶ Find the transactions which match the specified input and return.
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.
approvees (Optional[Iterable[TransactionHash]]) – List of approvee transaction IDs.
- Returns
dict
with the following structure:{ 'hashes': List[TransationHash], Found transactions. }
References:
get_balances
¶
-
Iota.
get_balances
(addresses: Iterable[iota.types.Address], tips: Optional[Iterable[iota.transaction.types.TransactionHash]] = None) → dict¶ Returns the confirmed balance which a list of addresses have at the latest confirmed milestone.
In addition to the balances, it also returns the milestone as well as the index with which the confirmed balance was determined. The balances are returned as a list in the same order as the addresses were provided as input.
- Parameters
addresses (Iterable[Address]) – List of addresses to get the confirmed balance for.
tips (Optional[Iterable[TransactionHash]]) – Tips whose history of transactions to traverse to find the balance.
- Returns
dict
with the following structure:{ 'balances': List[int], List of balances in the same order as the addresses parameters that were passed to the endpoint. 'references': List[TransactionHash], The referencing tips. If no tips parameter was passed to the endpoint, this field contains the hash of the latest milestone that confirmed the balance. 'milestoneIndex': int, The index of the milestone that confirmed the most recent balance. 'duration': int, Number of milliseconds it took to process the request. }
References:
-
async
AsyncIota.
get_balances
(addresses: Iterable[iota.types.Address], tips: Optional[Iterable[iota.transaction.types.TransactionHash]] = None) → dict¶ Returns the confirmed balance which a list of addresses have at the latest confirmed milestone.
In addition to the balances, it also returns the milestone as well as the index with which the confirmed balance was determined. The balances are returned as a list in the same order as the addresses were provided as input.
- Parameters
addresses (Iterable[Address]) – List of addresses to get the confirmed balance for.
tips (Optional[Iterable[TransactionHash]]) – Tips whose history of transactions to traverse to find the balance.
- Returns
dict
with the following structure:{ 'balances': List[int], List of balances in the same order as the addresses parameters that were passed to the endpoint. 'references': List[TransactionHash], The referencing tips. If no tips parameter was passed to the endpoint, this field contains the hash of the latest milestone that confirmed the balance. 'milestoneIndex': int, The index of the milestone that confirmed the most recent balance. 'duration': int, Number of milliseconds it took to process the request. }
References:
get_inclusion_states
¶
-
Iota.
get_inclusion_states
(transactions: Iterable[iota.transaction.types.TransactionHash]) → dict¶ Get the inclusion states of a set of transactions. This is for determining if a transaction was accepted and confirmed by the network or not.
- Parameters
transactions (Iterable[TransactionHash]) – List of transactions you want to get the inclusion state for.
- Returns
dict
with the following structure:{ 'states': List[bool], List of boolean values in the same order as the transactions parameters. A ``True`` value means the transaction was confirmed. 'duration': int, Number of milliseconds it took to process the request. }
References:
-
async
AsyncIota.
get_inclusion_states
(transactions: Iterable[iota.transaction.types.TransactionHash]) → dict¶ Get the inclusion states of a set of transactions. This is for determining if a transaction was accepted and confirmed by the network or not.
- Parameters
transactions (Iterable[TransactionHash]) – List of transactions you want to get the inclusion state for.
- Returns
dict
with the following structure:{ 'states': List[bool], List of boolean values in the same order as the transactions parameters. A ``True`` value means the transaction was confirmed. 'duration': int, Number of milliseconds it took to process the request. }
References:
get_missing_transactions
¶
-
Iota.
get_missing_transactions
() → dict¶ Returns all transaction hashes that a node is currently requesting from its neighbors.
- Returns
dict
with the following structure:{ 'hashes': List[TransactionHash], Array of missing transaction hashes. 'duration': int, Number of milliseconds it took to process the request. }
References:
-
async
AsyncIota.
get_missing_transactions
() → dict¶ Returns all transaction hashes that a node is currently requesting from its neighbors.
- Returns
dict
with the following structure:{ 'hashes': List[TransactionHash], Array of missing transaction hashes. 'duration': int, Number of milliseconds it took to process the request. }
References:
get_neighbors
¶
-
Iota.
get_neighbors
() → dict¶ Returns the set of neighbors the node is connected with, as well as their activity count.
The activity counter is reset after restarting IRI.
- Returns
dict
with the following structure:{ 'neighbors': List[dict], Array of objects, including the following fields with example values: "address": "/8.8.8.8:14265", "numberOfAllTransactions": 158, "numberOfRandomTransactionRequests": 271, "numberOfNewTransactions": 956, "numberOfInvalidTransactions": 539, "numberOfStaleTransactions": 663, "numberOfSentTransactions": 672, "connectiontype": "TCP" 'duration': int, Number of milliseconds it took to process the request. }
References:
-
async
AsyncIota.
get_neighbors
() → dict¶ Returns the set of neighbors the node is connected with, as well as their activity count.
The activity counter is reset after restarting IRI.
- Returns
dict
with the following structure:{ 'neighbors': List[dict], Array of objects, including the following fields with example values: "address": "/8.8.8.8:14265", "numberOfAllTransactions": 158, "numberOfRandomTransactionRequests": 271, "numberOfNewTransactions": 956, "numberOfInvalidTransactions": 539, "numberOfStaleTransactions": 663, "numberOfSentTransactions": 672, "connectiontype": "TCP" 'duration': int, Number of milliseconds it took to process the request. }
References:
get_node_api_configuration
¶
-
Iota.
get_node_api_configuration
() → dict¶ Returns a node’s API configuration settings.
- Returns
dict
with the following structure:{ '<API-config-settings>': type, Configuration parameters for a node. ... ... ... }
References:
-
async
AsyncIota.
get_node_api_configuration
() → dict¶ Returns a node’s API configuration settings.
- Returns
dict
with the following structure:{ '<API-config-settings>': type, Configuration parameters for a node. ... ... ... }
References:
get_node_info
¶
-
Iota.
get_node_info
() → dict¶ Returns information about the node.
- Returns
dict
with the following structure:{ 'appName': str, Name of the IRI network. 'appVersion': str, Version of the IRI. 'jreAvailableProcessors': int, Available CPU cores on the node. 'jreFreeMemory': int, Amount of free memory in the Java virtual machine. 'jreMaxMemory': int, Maximum amount of memory that the Java virtual machine can use, 'jreTotalMemory': int, Total amount of memory in the Java virtual machine. 'jreVersion': str, The version of the Java runtime environment. 'latestMilestone': TransactionHash Transaction hash of the latest milestone. 'latestMilestoneIndex': int, Index of the latest milestone. 'latestSolidSubtangleMilestone': TransactionHash, Transaction hash of the latest solid milestone. 'latestSolidSubtangleMilestoneIndex': int, Index of the latest solid milestone. 'milestoneStartIndex': int, Start milestone for the current version of the IRI. 'neighbors': int, Total number of connected neighbor nodes. 'packetsQueueSize': int, Size of the packet queue. 'time': int, Current UNIX timestamp. 'tips': int, Number of tips in the network. 'transactionsToRequest': int, Total number of transactions that the node is missing in its ledger. 'features': List[str], Enabled configuration options. 'coordinatorAddress': Address, Address (Merkle root) of the Coordinator. 'duration': int, Number of milliseconds it took to process the request. }
References:
-
async
AsyncIota.
get_node_info
() → dict¶ Returns information about the node.
- Returns
dict
with the following structure:{ 'appName': str, Name of the IRI network. 'appVersion': str, Version of the IRI. 'jreAvailableProcessors': int, Available CPU cores on the node. 'jreFreeMemory': int, Amount of free memory in the Java virtual machine. 'jreMaxMemory': int, Maximum amount of memory that the Java virtual machine can use, 'jreTotalMemory': int, Total amount of memory in the Java virtual machine. 'jreVersion': str, The version of the Java runtime environment. 'latestMilestone': TransactionHash Transaction hash of the latest milestone. 'latestMilestoneIndex': int, Index of the latest milestone. 'latestSolidSubtangleMilestone': TransactionHash, Transaction hash of the latest solid milestone. 'latestSolidSubtangleMilestoneIndex': int, Index of the latest solid milestone. 'milestoneStartIndex': int, Start milestone for the current version of the IRI. 'neighbors': int, Total number of connected neighbor nodes. 'packetsQueueSize': int, Size of the packet queue. 'time': int, Current UNIX timestamp. 'tips': int, Number of tips in the network. 'transactionsToRequest': int, Total number of transactions that the node is missing in its ledger. 'features': List[str], Enabled configuration options. 'coordinatorAddress': Address, Address (Merkle root) of the Coordinator. 'duration': int, Number of milliseconds it took to process the request. }
References:
get_transactions_to_approve
¶
-
Iota.
get_transactions_to_approve
(depth: int, reference: Optional[iota.transaction.types.TransactionHash] = None) → dict¶ Tip selection which returns
trunkTransaction
andbranchTransaction
.- Parameters
depth (int) –
Number of milestones to go back to start the tip selection algorithm.
The higher the depth value, the more “babysitting” the node will perform for the network (as it will confirm more transactions that way).
reference (TransactionHash) – Transaction hash from which to start the weighted random walk. Use this parameter to make sure the returned tip transaction hashes approve a given reference transaction.
- Returns
dict
with the following structure:{ 'trunkTransaction': TransactionHash, Valid trunk transaction hash. 'branchTransaction': TransactionHash, Valid branch transaction hash. 'duration': int, Number of milliseconds it took to complete the request. }
References:
-
async
AsyncIota.
get_transactions_to_approve
(depth: int, reference: Optional[iota.transaction.types.TransactionHash] = None) → dict¶ Tip selection which returns
trunkTransaction
andbranchTransaction
.- Parameters
depth (int) –
Number of milestones to go back to start the tip selection algorithm.
The higher the depth value, the more “babysitting” the node will perform for the network (as it will confirm more transactions that way).
reference (TransactionHash) – Transaction hash from which to start the weighted random walk. Use this parameter to make sure the returned tip transaction hashes approve a given reference transaction.
- Returns
dict
with the following structure:{ 'trunkTransaction': TransactionHash, Valid trunk transaction hash. 'branchTransaction': TransactionHash, Valid branch transaction hash. 'duration': int, Number of milliseconds it took to complete the request. }
References:
get_trytes
¶
-
Iota.
get_trytes
(hashes: Iterable[iota.transaction.types.TransactionHash]) → dict¶ Returns the raw transaction data (trytes) of one or more transactions.
- Returns
dict
with the following structure:{ 'trytes': List[TransactionTrytes], List of transaction trytes for the given transaction hashes (in the same order as the parameters). 'duration': int, Number of milliseconds it took to complete the request. }
Note
If a node doesn’t have the trytes for a given transaction hash in its ledger, the value at the index of that transaction hash is either
null
or a string of 9s.
References:
-
async
AsyncIota.
get_trytes
(hashes: Iterable[iota.transaction.types.TransactionHash]) → dict¶ Returns the raw transaction data (trytes) of one or more transactions.
- Returns
dict
with the following structure:{ 'trytes': List[TransactionTrytes], List of transaction trytes for the given transaction hashes (in the same order as the parameters). 'duration': int, Number of milliseconds it took to complete the request. }
Note
If a node doesn’t have the trytes for a given transaction hash in its ledger, the value at the index of that transaction hash is either
null
or a string of 9s.
References:
interrupt_attaching_to_tangle
¶
-
Iota.
interrupt_attaching_to_tangle
() → dict¶ Interrupts and completely aborts the
attach_to_tangle()
process.- Returns
dict
with the following structure:{ 'duration': int, Number of milliseconds it took to complete the request. }
References:
-
async
AsyncIota.
interrupt_attaching_to_tangle
() → dict¶ Interrupts and completely aborts the
attach_to_tangle()
process.- Returns
dict
with the following structure:{ 'duration': int, Number of milliseconds it took to complete the request. }
References:
remove_neighbors
¶
-
Iota.
remove_neighbors
(uris: Iterable[str]) → dict¶ Removes one or more neighbors from the node. Lasts until the node is restarted.
- Parameters
uris (str) – Use format
<protocol>://<ip address>:<port>
. Example: remove_neighbors([‘udp://example.com:14265’])- Returns
dict
with the following structure:{ 'removedNeighbors': int, Total number of removed neighbors. 'duration': int, Number of milliseconds it took to complete the request. }
References:
-
async
AsyncIota.
remove_neighbors
(uris: Iterable[str]) → dict¶ Removes one or more neighbors from the node. Lasts until the node is restarted.
- Parameters
uris (str) – Use format
<protocol>://<ip address>:<port>
. Example: remove_neighbors([‘udp://example.com:14265’])- Returns
dict
with the following structure:{ 'removedNeighbors': int, Total number of removed neighbors. 'duration': int, Number of milliseconds it took to complete the request. }
References:
store_transactions
¶
-
Iota.
store_transactions
(trytes: Iterable[iota.types.TryteString]) → dict¶ Store transactions into local storage of the node.
The input trytes for this call are provided by
attach_to_tangle()
.- Parameters
trytes (TransactionTrytes) – Valid transaction trytes returned by
attach_to_tangle()
.- Returns
dict
with the following structure:{ 'trytes': TransactionTrytes, Stored trytes. 'duration': int, Number of milliseconds it took to complete the request. }
References:
-
async
AsyncIota.
store_transactions
(trytes: Iterable[iota.types.TryteString]) → dict¶ Store transactions into local storage of the node.
The input trytes for this call are provided by
attach_to_tangle()
.- Parameters
trytes (TransactionTrytes) – Valid transaction trytes returned by
attach_to_tangle()
.- Returns
dict
with the following structure:{ 'trytes': TransactionTrytes, Stored trytes. 'duration': int, Number of milliseconds it took to complete the request. }
References:
were_addresses_spent_from
¶
-
Iota.
were_addresses_spent_from
(addresses: Iterable[iota.types.Address]) → dict¶ Check if a list of addresses was ever spent from, in the current epoch, or in previous epochs.
If an address has a pending transaction, it’s also considered ‘spent’.
- Parameters
addresses (Iterable[Address]) – List of addresses to check.
- Returns
dict
with the following structure:{ 'states': List[bool], States of the specified addresses in the same order as the values in the addresses parameter. A ``True`` value means that the address has been spent from. 'duration': int, Number of milliseconds it took to complete the request. }
References:
-
async
AsyncIota.
were_addresses_spent_from
(addresses: Iterable[iota.types.Address]) → dict¶ Check if a list of addresses was ever spent from, in the current epoch, or in previous epochs.
If an address has a pending transaction, it’s also considered ‘spent’.
- Parameters
addresses (Iterable[Address]) – List of addresses to check.
- Returns
dict
with the following structure:{ 'states': List[bool], States of the specified addresses in the same order as the values in the addresses parameter. A ``True`` value means that the address has been spent from. 'duration': int, Number of milliseconds it took to complete the request. }
References:
Extended API Methods¶
The Extended API includes a number of “high level” commands to perform tasks such as sending and receiving transfers.
Note
Below you will find the documentation for both the synchronous and asynchronous versions of the Extebded API methods.
It should be made clear, that they do exactly the same IOTA related operations, accept the same arguments and return the same structures. Asynchronous API calls are non-blocking, so your application can do other stuff while waiting for the result from the network.
While synchronous API calls are regular Python methods, their respective
asynchronous versions are Python coroutines. You can await
their
results, schedule them for execution inside and event loop and much more.
PyOTA uses the built-in asyncio Python module for asynchronous operation.
For an overview of what you can do with it, head over to this article.
broadcast_and_store
¶
-
Iota.
broadcast_and_store
(trytes: Iterable[iota.transaction.types.TransactionTrytes]) → dict¶ Broadcasts and stores a set of transaction trytes.
- Parameters
trytes (Iterable[TransactionTrytes]) – Transaction trytes to broadcast and store.
- Returns
dict
with the following structure:{ 'trytes': List[TransactionTrytes], List of TransactionTrytes that were broadcast. Same as the input ``trytes``. }
References:
-
async
AsyncIota.
broadcast_and_store
(trytes: Iterable[iota.transaction.types.TransactionTrytes]) → dict¶ Broadcasts and stores a set of transaction trytes.
- Parameters
trytes (Iterable[TransactionTrytes]) – Transaction trytes to broadcast and store.
- Returns
dict
with the following structure:{ 'trytes': List[TransactionTrytes], List of TransactionTrytes that were broadcast. Same as the input ``trytes``. }
References:
broadcast_bundle
¶
-
Iota.
broadcast_bundle
(tail_transaction_hash: iota.transaction.types.TransactionHash) → dict¶ Re-broadcasts all transactions in a bundle given the tail transaction hash. It might be useful when transactions did not properly propagate, particularly in the case of large bundles.
- Parameters
tail_transaction_hash (TransactionHash) – Tail transaction hash of the bundle.
- Returns
dict
with the following structure:{ 'trytes': List[TransactionTrytes], List of TransactionTrytes that were broadcast. }
References:
-
async
AsyncIota.
broadcast_bundle
(tail_transaction_hash: iota.transaction.types.TransactionHash) → dict¶ Re-broadcasts all transactions in a bundle given the tail transaction hash. It might be useful when transactions did not properly propagate, particularly in the case of large bundles.
- Parameters
tail_transaction_hash (TransactionHash) – Tail transaction hash of the bundle.
- Returns
dict
with the following structure:{ 'trytes': List[TransactionTrytes], List of TransactionTrytes that were broadcast. }
References:
find_transaction_objects
¶
-
Iota.
find_transaction_objects
(bundles: Optional[Iterable[iota.transaction.types.BundleHash]] = None, addresses: Optional[Iterable[iota.types.Address]] = None, tags: Optional[Iterable[iota.types.Tag]] = None, approvees: Optional[Iterable[iota.transaction.types.TransactionHash]] = None) → dict¶ A more extensive version of
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.
approvees (Optional[Iterable[TransactionHash]]) – List of approvee transaction IDs.
- Returns
dict
with the following structure:{ 'transactions': List[Transaction], List of Transaction objects that match the input. }
-
async
AsyncIota.
find_transaction_objects
(bundles: Optional[Iterable[iota.transaction.types.BundleHash]] = None, addresses: Optional[Iterable[iota.types.Address]] = None, tags: Optional[Iterable[iota.types.Tag]] = None, approvees: Optional[Iterable[iota.transaction.types.TransactionHash]] = None) → dict¶ A more extensive version of
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.
approvees (Optional[Iterable[TransactionHash]]) – List of approvee transaction IDs.
- Returns
dict
with the following structure:{ 'transactions': List[Transaction], List of Transaction objects that match the input. }
get_account_data
¶
-
Iota.
get_account_data
(start: int = 0, stop: Optional[int] = None, inclusion_states: bool = False, security_level: Optional[int] = None) → dict¶ More comprehensive version of
get_transfers()
that returns addresses and account balance in addition to bundles.This function is useful in getting all the relevant information of your account.
- Parameters
start (int) – Starting key index.
stop (Optional[int]) –
Stop before this index.
Note that this parameter behaves like the
stop
attribute in aslice
object; the stop index is not included in the result.If
None
(default), then this method will check every address until it finds one that is unused.Note
An unused address is an address that has not been spent from and has no transactions referencing it on the Tangle.
A snapshot removes transactions from the Tangle. As a consequence, after a snapshot, it may happen that this API does not return the correct account data with
stop
beingNone
.As a workaround, you can save your used addresses and their
key_index
attribute in a local database. Use thestart
andstop
parameters to tell the API from where to start checking and where to stop.inclusion_states (bool) –
Whether to also fetch the inclusion states of the transfers.
This requires an additional API call to the node, so it is disabled by default.
security_level (Optional[int]) –
Number of iterations to use when generating new addresses (see
get_new_addresses()
).This value must be between 1 and 3, inclusive.
If not set, defaults to
AddressGenerator.DEFAULT_SECURITY_LEVEL
.
- Returns
dict
with the following structure:{ 'addresses': List[Address], List of generated addresses. Note that this list may include unused addresses. 'balance': int, Total account balance. Might be 0. 'bundles': List[Bundle], List of bundles with transactions to/from this account. }
-
async
AsyncIota.
get_account_data
(start: int = 0, stop: Optional[int] = None, inclusion_states: bool = False, security_level: Optional[int] = None) → dict¶ More comprehensive version of
get_transfers()
that returns addresses and account balance in addition to bundles.This function is useful in getting all the relevant information of your account.
- Parameters
start (int) – Starting key index.
stop (Optional[int]) –
Stop before this index.
Note that this parameter behaves like the
stop
attribute in aslice
object; the stop index is not included in the result.If
None
(default), then this method will check every address until it finds one that is unused.Note
An unused address is an address that has not been spent from and has no transactions referencing it on the Tangle.
A snapshot removes transactions from the Tangle. As a consequence, after a snapshot, it may happen that this API does not return the correct account data with
stop
beingNone
.As a workaround, you can save your used addresses and their
key_index
attribute in a local database. Use thestart
andstop
parameters to tell the API from where to start checking and where to stop.inclusion_states (bool) –
Whether to also fetch the inclusion states of the transfers.
This requires an additional API call to the node, so it is disabled by default.
security_level (Optional[int]) –
Number of iterations to use when generating new addresses (see
get_new_addresses()
).This value must be between 1 and 3, inclusive.
If not set, defaults to
AddressGenerator.DEFAULT_SECURITY_LEVEL
.
- Returns
dict
with the following structure:{ 'addresses': List[Address], List of generated addresses. Note that this list may include unused addresses. 'balance': int, Total account balance. Might be 0. 'bundles': List[Bundle], List of bundles with transactions to/from this account. }
get_bundles
¶
-
Iota.
get_bundles
(transactions: Iterable[iota.transaction.types.TransactionHash]) → dict¶ Returns the bundle(s) associated with the specified transaction hashes.
- Parameters
transactions (Iterable[TransactionHash]) – Transaction hashes. Must be a tail transaction.
- Returns
dict
with the following structure:{ 'bundles': List[Bundle], List of matching bundles. Note that this value is always a list, even if only one bundle was found. }
- :raise
iota.adapter.BadApiResponse
: if any of the bundles fails validation.
if any of the bundles is not visible on the Tangle.
References:
-
async
AsyncIota.
get_bundles
(transactions: Iterable[iota.transaction.types.TransactionHash]) → dict¶ Returns the bundle(s) associated with the specified transaction hashes.
- Parameters
transactions (Iterable[TransactionHash]) – Transaction hashes. Must be a tail transaction.
- Returns
dict
with the following structure:{ 'bundles': List[Bundle], List of matching bundles. Note that this value is always a list, even if only one bundle was found. }
- :raise
iota.adapter.BadApiResponse
: if any of the bundles fails validation.
if any of the bundles is not visible on the Tangle.
References:
get_inputs
¶
-
Iota.
get_inputs
(start: int = 0, stop: Optional[int] = None, threshold: Optional[int] = None, security_level: Optional[int] = None) → dict¶ Gets all possible inputs of a seed and returns them, along with the total balance.
This is either done deterministically (by generating all addresses until
find_transactions()
returns an empty result), or by providing a key range to search.- Parameters
start (int) – Starting key index. Defaults to 0.
stop (Optional[int]) –
Stop before this index.
Note that this parameter behaves like the
stop
attribute in aslice
object; the stop index is not included in the result.If
None
(default), then this method will not stop until it finds an unused address.Note
An unused address is an address that has not been spent from and has no transactions referencing it on the Tangle.
A snapshot removes transactions from the Tangle. As a consequence, after a snapshot, it may happen that this API does not return the correct inputs with
stop
beingNone
.As a workaround, you can save your used addresses and their
key_index
attribute in a local database. Use thestart
andstop
parameters to tell the API from where to start checking for inputs and where to stop.threshold (Optional[int]) –
If set, determines the minimum threshold for a successful result:
As soon as this threshold is reached, iteration will stop.
If the command runs out of addresses before the threshold is reached, an exception is raised.
Note
This method does not attempt to “optimize” the result (e.g., smallest number of inputs, get as close to
threshold
as possible, etc.); it simply accumulates inputs in order until the threshold is met.If
threshold
is 0, the first address in the key range with a non-zero balance will be returned (if it exists).If
threshold
isNone
(default), this method will return all inputs in the specified key range.security_level (Optional[int]) –
Number of iterations to use when generating new addresses (see
get_new_addresses()
).This value must be between 1 and 3, inclusive.
If not set, defaults to
AddressGenerator.DEFAULT_SECURITY_LEVEL
.
- Returns
dict
with the following structure:{ 'inputs': List[Address], Addresses with nonzero balances that can be used as inputs. 'totalBalance': int, Aggregate balance from all matching addresses. }
Note that each
Address
in the result has itsAddress.balance
attribute set.Example:
response = iota.get_inputs(...) input0 = response['inputs'][0] # type: Address input0.balance # 42
- Raise
iota.adapter.BadApiResponse
ifthreshold
is not met. Not applicable ifthreshold
isNone
.
References:
-
async
AsyncIota.
get_inputs
(start: int = 0, stop: Optional[int] = None, threshold: Optional[int] = None, security_level: Optional[int] = None) → dict¶ Gets all possible inputs of a seed and returns them, along with the total balance.
This is either done deterministically (by generating all addresses until
find_transactions()
returns an empty result), or by providing a key range to search.- Parameters
start (int) – Starting key index. Defaults to 0.
stop (Optional[int]) –
Stop before this index.
Note that this parameter behaves like the
stop
attribute in aslice
object; the stop index is not included in the result.If
None
(default), then this method will not stop until it finds an unused address.Note
An unused address is an address that has not been spent from and has no transactions referencing it on the Tangle.
A snapshot removes transactions from the Tangle. As a consequence, after a snapshot, it may happen that this API does not return the correct inputs with
stop
beingNone
.As a workaround, you can save your used addresses and their
key_index
attribute in a local database. Use thestart
andstop
parameters to tell the API from where to start checking for inputs and where to stop.threshold (Optional[int]) –
If set, determines the minimum threshold for a successful result:
As soon as this threshold is reached, iteration will stop.
If the command runs out of addresses before the threshold is reached, an exception is raised.
Note
This method does not attempt to “optimize” the result (e.g., smallest number of inputs, get as close to
threshold
as possible, etc.); it simply accumulates inputs in order until the threshold is met.If
threshold
is 0, the first address in the key range with a non-zero balance will be returned (if it exists).If
threshold
isNone
(default), this method will return all inputs in the specified key range.security_level (Optional[int]) –
Number of iterations to use when generating new addresses (see
get_new_addresses()
).This value must be between 1 and 3, inclusive.
If not set, defaults to
AddressGenerator.DEFAULT_SECURITY_LEVEL
.
- Returns
dict
with the following structure:{ 'inputs': List[Address], Addresses with nonzero balances that can be used as inputs. 'totalBalance': int, Aggregate balance from all matching addresses. }
Note that each
Address
in the result has itsAddress.balance
attribute set.Example:
response = iota.get_inputs(...) input0 = response['inputs'][0] # type: Address input0.balance # 42
- Raise
iota.adapter.BadApiResponse
ifthreshold
is not met. Not applicable ifthreshold
isNone
.
References:
get_new_addresses
¶
-
Iota.
get_new_addresses
(index: int = 0, count: int = 1, security_level: int = 2, checksum: bool = False)¶ Generates one or more new addresses from the seed.
- Parameters
index (int) – The key index of the first new address to generate (must be >= 0).
count (int) –
Number of addresses to generate (must be >= 1).
Tip
This is more efficient than calling
get_new_addresses()
inside a loop.If
None
, this method will progressively generate addresses and scan the Tangle until it finds one that has no transactions referencing it and was never spent from.Note
A snapshot removes transactions from the Tangle. As a consequence, after a snapshot, it may happen that when
count
isNone
, this API call returns a “new” address that used to have transactions before the snapshot. As a workaround, you can save your used addresses and theirkey_index
attribute in a local database. Use theindex
parameter to tell the API from where to start generating and checking new addresses.security_level (int) –
Number of iterations to use when generating new addresses.
Larger values take longer, but the resulting signatures are more secure.
This value must be between 1 and 3, inclusive.
checksum (bool) – Specify whether to return the address with the checksum. Defaults to
False
.
- Returns
dict
with the following structure:{ 'addresses': List[Address], Always a list, even if only one address was generated. }
References:
-
async
AsyncIota.
get_new_addresses
(index: int = 0, count: int = 1, security_level: int = 2, checksum: bool = False)¶ Generates one or more new addresses from the seed.
- Parameters
index (int) – The key index of the first new address to generate (must be >= 0).
count (int) –
Number of addresses to generate (must be >= 1).
Tip
This is more efficient than calling
get_new_addresses()
inside a loop.If
None
, this method will progressively generate addresses and scan the Tangle until it finds one that has no transactions referencing it and was never spent from.Note
A snapshot removes transactions from the Tangle. As a consequence, after a snapshot, it may happen that when
count
isNone
, this API call returns a “new” address that used to have transactions before the snapshot. As a workaround, you can save your used addresses and theirkey_index
attribute in a local database. Use theindex
parameter to tell the API from where to start generating and checking new addresses.security_level (int) –
Number of iterations to use when generating new addresses.
Larger values take longer, but the resulting signatures are more secure.
This value must be between 1 and 3, inclusive.
checksum (bool) – Specify whether to return the address with the checksum. Defaults to
False
.
- Returns
dict
with the following structure:{ 'addresses': List[Address], Always a list, even if only one address was generated. }
References:
get_transaction_objects
¶
-
Iota.
get_transaction_objects
(hashes: [typing.Iterable[iota.transaction.types.TransactionHash]]) → dict¶ Fetches transaction objects from the Tangle given their transaction IDs (hashes).
Effectively, this is
get_trytes()
+ converting the trytes into transaction objects.Similar to
find_transaction_objects()
, but accepts list of transaction hashes as input.- Parameters
hashes (Iterable[TransactionHash]) – List of transaction IDs (transaction hashes).
- Returns
dict
with the following structure:{ 'transactions': List[Transaction], List of Transaction objects that match the input. }
-
async
AsyncIota.
get_transaction_objects
(hashes: [typing.Iterable[iota.transaction.types.TransactionHash]]) → dict¶ Fetches transaction objects from the Tangle given their transaction IDs (hashes).
Effectively, this is
get_trytes()
+ converting the trytes into transaction objects.Similar to
find_transaction_objects()
, but accepts list of transaction hashes as input.- Parameters
hashes (Iterable[TransactionHash]) – List of transaction IDs (transaction hashes).
- Returns
dict
with the following structure:{ 'transactions': List[Transaction], List of Transaction objects that match the input. }
get_transfers
¶
-
Iota.
get_transfers
(start: int = 0, stop: Optional[int] = None, inclusion_states: bool = False) → dict¶ Returns all transfers associated with the seed.
- Parameters
start (int) – Starting key index.
stop (Optional[int]) –
Stop before this index.
Note that this parameter behaves like the
stop
attribute in aslice
object; the stop index is not included in the result.If
None
(default), then this method will check every address until it finds one that is unused.Note
An unused address is an address that has not been spent from and has no transactions referencing it on the Tangle.
A snapshot removes transactions from the Tangle. As a consequence, after a snapshot, it may happen that this API does not return the expected transfers with
stop
beingNone
.As a workaround, you can save your used addresses and their
key_index
attribute in a local database. Use thestart
andstop
parameters to tell the API from where to start checking for transfers and where to stop.inclusion_states (bool) –
Whether to also fetch the inclusion states of the transfers.
This requires an additional API call to the node, so it is disabled by default.
- Returns
dict
with the following structure:{ 'bundles': List[Bundle], Matching bundles, sorted by tail transaction timestamp. This value is always a list, even if only one bundle was found. }
References:
-
async
AsyncIota.
get_transfers
(start: int = 0, stop: Optional[int] = None, inclusion_states: bool = False) → dict¶ Returns all transfers associated with the seed.
- Parameters
start (int) – Starting key index.
stop (Optional[int]) –
Stop before this index.
Note that this parameter behaves like the
stop
attribute in aslice
object; the stop index is not included in the result.If
None
(default), then this method will check every address until it finds one that is unused.Note
An unused address is an address that has not been spent from and has no transactions referencing it on the Tangle.
A snapshot removes transactions from the Tangle. As a consequence, after a snapshot, it may happen that this API does not return the expected transfers with
stop
beingNone
.As a workaround, you can save your used addresses and their
key_index
attribute in a local database. Use thestart
andstop
parameters to tell the API from where to start checking for transfers and where to stop.inclusion_states (bool) –
Whether to also fetch the inclusion states of the transfers.
This requires an additional API call to the node, so it is disabled by default.
- Returns
dict
with the following structure:{ 'bundles': List[Bundle], Matching bundles, sorted by tail transaction timestamp. This value is always a list, even if only one bundle was found. }
References:
is_confirmed
¶
-
Iota.
is_confirmed
(transactions: Iterable[iota.transaction.types.TransactionHash]) → dict¶ Get the inclusion states of a set of transactions. This is for determining if a transaction was accepted and confirmed by the network or not.
- Parameters
transactions (Iterable[TransactionHash]) – List of transactions you want to get the inclusion state for.
- Returns
dict
with the following structure:{ 'states': List[bool], List of boolean values in the same order as the transactions parameters. A ``True`` value means the transaction was confirmed. 'duration': int, Number of milliseconds it took to process the request. }
References:
-
async
AsyncIota.
is_confirmed
(transactions: Iterable[iota.transaction.types.TransactionHash]) → dict¶ Get the inclusion states of a set of transactions. This is for determining if a transaction was accepted and confirmed by the network or not.
- Parameters
transactions (Iterable[TransactionHash]) – List of transactions you want to get the inclusion state for.
- Returns
dict
with the following structure:{ 'states': List[bool], List of boolean values in the same order as the transactions parameters. A ``True`` value means the transaction was confirmed. 'duration': int, Number of milliseconds it took to process the request. }
References:
is_promotable
¶
-
Iota.
is_promotable
(tails: Iterable[iota.transaction.types.TransactionHash]) → dict¶ Checks if tail transaction(s) is promotable by calling
check_consistency()
and verifying thatattachmentTimestamp
is above a lower bound. Lower bound is calculated based on number of milestones issued since transaction attachment.- Parameters
tails (Iterable(TransactionHash)) – List of tail transaction hashes.
- Returns
The return type mimics that of
check_consistency()
.dict
with the following structure:{ 'promotable': bool, If ``True``, all tails are promotable. If ``False``, see `info` field. 'info': Optional(List[str]) If `promotable` is ``False``, this contains info about what went wrong. Note that when 'promotable' is ``True``, 'info' does not exist. }
References: - https://github.com/iotaledger/iota.js/blob/next/api_reference.md#module_core.isPromotable
-
async
AsyncIota.
is_promotable
(tails: Iterable[iota.transaction.types.TransactionHash]) → dict¶ Checks if tail transaction(s) is promotable by calling
check_consistency()
and verifying thatattachmentTimestamp
is above a lower bound. Lower bound is calculated based on number of milestones issued since transaction attachment.- Parameters
tails (Iterable(TransactionHash)) – List of tail transaction hashes.
- Returns
The return type mimics that of
check_consistency()
.dict
with the following structure:{ 'promotable': bool, If ``True``, all tails are promotable. If ``False``, see `info` field. 'info': Optional(List[str]) If `promotable` is ``False``, this contains info about what went wrong. Note that when 'promotable' is ``True``, 'info' does not exist. }
References: - https://github.com/iotaledger/iota.js/blob/next/api_reference.md#module_core.isPromotable
is_reattachable
¶
-
Iota.
is_reattachable
(addresses: Iterable[iota.types.Address]) → dict¶ This API function helps you to determine whether you should replay a transaction or make a new one (either with the same input, or a different one).
This method takes one or more input addresses (i.e. from spent transactions) as input and then checks whether any transactions with a value transferred are confirmed.
If yes, it means that this input address has already been successfully used in a different transaction, and as such you should no longer replay the transaction.
- Parameters
addresses (Iterable[Address]) – List of addresses.
- Returns
dict
with the following structure:{ 'reattachable': List[bool], Always a list, even if only one address was queried. }
-
async
AsyncIota.
is_reattachable
(addresses: Iterable[iota.types.Address]) → dict¶ This API function helps you to determine whether you should replay a transaction or make a new one (either with the same input, or a different one).
This method takes one or more input addresses (i.e. from spent transactions) as input and then checks whether any transactions with a value transferred are confirmed.
If yes, it means that this input address has already been successfully used in a different transaction, and as such you should no longer replay the transaction.
- Parameters
addresses (Iterable[Address]) – List of addresses.
- Returns
dict
with the following structure:{ 'reattachable': List[bool], Always a list, even if only one address was queried. }
prepare_transfer
¶
-
Iota.
prepare_transfer
(transfers: Iterable[iota.transaction.creation.ProposedTransaction], inputs: Optional[Iterable[iota.types.Address]] = None, change_address: Optional[iota.types.Address] = None, security_level: Optional[int] = None) → dict¶ Prepares transactions to be broadcast to the Tangle, by generating the correct bundle, as well as choosing and signing the inputs (for value transfers).
- Parameters
transfers (Iterable[ProposedTransaction]) – Transaction objects to prepare.
inputs (Optional[Iterable[Address]]) –
List of addresses used to fund the transfer. Ignored for zero-value transfers.
If not provided, addresses will be selected automatically by scanning the Tangle for unspent inputs. Depending on how many transfers you’ve already sent with your seed, this process could take awhile.
change_address (Optional[Address]) –
If inputs are provided, any unspent amount will be sent to this address.
If not specified, a change address will be generated automatically.
security_level (Optional[int]) –
Number of iterations to use when generating new addresses (see
get_new_addresses()
).This value must be between 1 and 3, inclusive.
If not set, defaults to
AddressGenerator.DEFAULT_SECURITY_LEVEL
.
- Returns
dict
with the following structure:{ 'trytes': List[TransactionTrytes], Raw trytes for the transactions in the bundle, ready to be provided to :py:meth:`send_trytes`. }
References:
-
async
AsyncIota.
prepare_transfer
(transfers: Iterable[iota.transaction.creation.ProposedTransaction], inputs: Optional[Iterable[iota.types.Address]] = None, change_address: Optional[iota.types.Address] = None, security_level: Optional[int] = None) → dict¶ Prepares transactions to be broadcast to the Tangle, by generating the correct bundle, as well as choosing and signing the inputs (for value transfers).
- Parameters
transfers (Iterable[ProposedTransaction]) – Transaction objects to prepare.
inputs (Optional[Iterable[Address]]) –
List of addresses used to fund the transfer. Ignored for zero-value transfers.
If not provided, addresses will be selected automatically by scanning the Tangle for unspent inputs. Depending on how many transfers you’ve already sent with your seed, this process could take awhile.
change_address (Optional[Address]) –
If inputs are provided, any unspent amount will be sent to this address.
If not specified, a change address will be generated automatically.
security_level (Optional[int]) –
Number of iterations to use when generating new addresses (see
get_new_addresses()
).This value must be between 1 and 3, inclusive.
If not set, defaults to
AddressGenerator.DEFAULT_SECURITY_LEVEL
.
- Returns
dict
with the following structure:{ 'trytes': List[TransactionTrytes], Raw trytes for the transactions in the bundle, ready to be provided to :py:meth:`send_trytes`. }
References:
promote_transaction
¶
-
Iota.
promote_transaction
(transaction: iota.transaction.types.TransactionHash, depth: int = 3, min_weight_magnitude: Optional[int] = None) → dict¶ Promotes a transaction by adding spam on top of it.
- Parameters
transaction (TransactionHash) – Transaction hash. Must be a tail transaction.
depth (int) – Depth at which to attach the bundle. Defaults to 3.
min_weight_magnitude (Optional[int]) –
Min weight magnitude, used by the node to calibrate Proof of Work.
If not provided, a default value will be used.
- Returns
dict
with the following structure:{ 'bundle': Bundle, The newly-published bundle. }
-
async
AsyncIota.
promote_transaction
(transaction: iota.transaction.types.TransactionHash, depth: int = 3, min_weight_magnitude: Optional[int] = None) → dict¶ Promotes a transaction by adding spam on top of it.
- Parameters
transaction (TransactionHash) – Transaction hash. Must be a tail transaction.
depth (int) – Depth at which to attach the bundle. Defaults to 3.
min_weight_magnitude (Optional[int]) –
Min weight magnitude, used by the node to calibrate Proof of Work.
If not provided, a default value will be used.
- Returns
dict
with the following structure:{ 'bundle': Bundle, The newly-published bundle. }
replay_bundle
¶
-
Iota.
replay_bundle
(transaction: iota.transaction.types.TransactionHash, depth: int = 3, min_weight_magnitude: Optional[int] = None) → dict¶ Takes a tail transaction hash as input, gets the bundle associated with the transaction and then replays the bundle by attaching it to the Tangle.
- Parameters
transaction (TransactionHash) – Transaction hash. Must be a tail.
depth (int) – Depth at which to attach the bundle. Defaults to 3.
min_weight_magnitude (Optional[int]) –
Min weight magnitude, used by the node to calibrate Proof of Work.
If not provided, a default value will be used.
- Returns
dict
with the following structure:{ 'trytes': List[TransactionTrytes], Raw trytes that were published to the Tangle. }
References:
-
async
AsyncIota.
replay_bundle
(transaction: iota.transaction.types.TransactionHash, depth: int = 3, min_weight_magnitude: Optional[int] = None) → dict¶ Takes a tail transaction hash as input, gets the bundle associated with the transaction and then replays the bundle by attaching it to the Tangle.
- Parameters
transaction (TransactionHash) – Transaction hash. Must be a tail.
depth (int) – Depth at which to attach the bundle. Defaults to 3.
min_weight_magnitude (Optional[int]) –
Min weight magnitude, used by the node to calibrate Proof of Work.
If not provided, a default value will be used.
- Returns
dict
with the following structure:{ 'trytes': List[TransactionTrytes], Raw trytes that were published to the Tangle. }
References:
send_transfer
¶
-
Iota.
send_transfer
(transfers: Iterable[iota.transaction.creation.ProposedTransaction], depth: int = 3, inputs: Optional[Iterable[iota.types.Address]] = None, change_address: Optional[iota.types.Address] = None, min_weight_magnitude: Optional[int] = None, security_level: Optional[int] = None) → dict¶ Prepares a set of transfers and creates the bundle, then attaches the bundle to the Tangle, and broadcasts and stores the transactions.
- Parameters
transfers (Iterable[ProposedTransaction]) – Transfers to include in the bundle.
depth (int) – Depth at which to attach the bundle. Defaults to 3.
inputs (Optional[Iterable[Address]]) – List of inputs used to fund the transfer. Not needed for zero-value transfers.
change_address (Optional[Address]) –
If inputs are provided, any unspent amount will be sent to this address.
If not specified, a change address will be generated automatically.
min_weight_magnitude (Optional[int]) –
Min weight magnitude, used by the node to calibrate Proof of Work.
If not provided, a default value will be used.
security_level (Optional[int]) –
Number of iterations to use when generating new addresses (see
get_new_addresses()
).This value must be between 1 and 3, inclusive.
If not set, defaults to
AddressGenerator.DEFAULT_SECURITY_LEVEL
.
- Returns
dict
with the following structure:{ 'bundle': Bundle, The newly-published bundle. }
References:
-
async
AsyncIota.
send_transfer
(transfers: Iterable[iota.transaction.creation.ProposedTransaction], depth: int = 3, inputs: Optional[Iterable[iota.types.Address]] = None, change_address: Optional[iota.types.Address] = None, min_weight_magnitude: Optional[int] = None, security_level: Optional[int] = None) → dict¶ Prepares a set of transfers and creates the bundle, then attaches the bundle to the Tangle, and broadcasts and stores the transactions.
- Parameters
transfers (Iterable[ProposedTransaction]) – Transfers to include in the bundle.
depth (int) – Depth at which to attach the bundle. Defaults to 3.
inputs (Optional[Iterable[Address]]) – List of inputs used to fund the transfer. Not needed for zero-value transfers.
change_address (Optional[Address]) –
If inputs are provided, any unspent amount will be sent to this address.
If not specified, a change address will be generated automatically.
min_weight_magnitude (Optional[int]) –
Min weight magnitude, used by the node to calibrate Proof of Work.
If not provided, a default value will be used.
security_level (Optional[int]) –
Number of iterations to use when generating new addresses (see
get_new_addresses()
).This value must be between 1 and 3, inclusive.
If not set, defaults to
AddressGenerator.DEFAULT_SECURITY_LEVEL
.
- Returns
dict
with the following structure:{ 'bundle': Bundle, The newly-published bundle. }
References:
send_trytes
¶
-
Iota.
send_trytes
(trytes: Iterable[iota.transaction.types.TransactionTrytes], depth: int = 3, min_weight_magnitude: Optional[int] = None) → dict¶ Attaches transaction trytes to the Tangle, then broadcasts and stores them.
- Parameters
trytes (Iterable[TransactionTrytes]) – Transaction encoded as a tryte sequence.
depth (int) – Depth at which to attach the bundle. Defaults to 3.
min_weight_magnitude (Optional[int]) –
Min weight magnitude, used by the node to calibrate Proof of Work.
If not provided, a default value will be used.
- Returns
dict
with the following structure:{ 'trytes': List[TransactionTrytes], Raw trytes that were published to the Tangle. }
References:
-
async
AsyncIota.
send_trytes
(trytes: Iterable[iota.transaction.types.TransactionTrytes], depth: int = 3, min_weight_magnitude: Optional[int] = None) → dict¶ Attaches transaction trytes to the Tangle, then broadcasts and stores them.
- Parameters
trytes (Iterable[TransactionTrytes]) – Transaction encoded as a tryte sequence.
depth (int) – Depth at which to attach the bundle. Defaults to 3.
min_weight_magnitude (Optional[int]) –
Min weight magnitude, used by the node to calibrate Proof of Work.
If not provided, a default value will be used.
- Returns
dict
with the following structure:{ 'trytes': List[TransactionTrytes], Raw trytes that were published to the Tangle. }
References:
traverse_bundle
¶
-
Iota.
traverse_bundle
(tail_hash: iota.transaction.types.TransactionHash) → dict¶ Fetches and traverses a bundle from the Tangle given a tail transaction hash. Recursively traverse the Tangle, collecting transactions until we hit a new bundle.
This method is (usually) faster than
find_transactions()
, and it ensures we don’t collect transactions from replayed bundles.- Parameters
tail_hash (TransactionHash) – Tail transaction hash of the bundle.
- Returns
dict
with the following structure:{ 'bundle': List[Bundle], List of matching bundles. Note that this value is always a list, even if only one bundle was found. }
-
async
AsyncIota.
traverse_bundle
(tail_hash: iota.transaction.types.TransactionHash) → dict¶ Fetches and traverses a bundle from the Tangle given a tail transaction hash. Recursively traverse the Tangle, collecting transactions until we hit a new bundle.
This method is (usually) faster than
find_transactions()
, and it ensures we don’t collect transactions from replayed bundles.- Parameters
tail_hash (TransactionHash) – Tail transaction hash of the bundle.
- Returns
dict
with the following structure:{ 'bundle': List[Bundle], List of matching bundles. Note that this value is always a list, even if only one bundle was found. }
Generating Addresses¶
In IOTA, addresses are generated deterministically from seeds. This ensures that your account can be accessed from any location, as long as you have the seed.
Warning
Note that this also means that anyone with access to your seed can spend your iotas! Treat your seed(s) the same as you would the password for any other financial service.
Note
PyOTA’s crytpo functionality is currently very slow; on average it takes 8-10 seconds to generate each address.
These performance issues will be fixed in a future version of the library; please bear with us!
In the meantime, you can install a C extension that boosts PyOTA’s performance significantly (speedups of 60x are common!).
To install the extension, run
pip install pyota[ccurl]
.
Algorithm¶
Deriving addresses from a seed.¶
The following process takes place when you generate addresses in IOTA:
First, a sub-seed is derived from your seed by adding
index
to it, and hashing it once with the Kerl hash function.Then the sub-seed is absorbed and squeezed in a sponge function 27 times for each security level. The result is a private key that varies in length depending on security level.
Note
A private key with
security_level = 1
consists of 2187 trytes, which is exactly 27 x 81 trytes. As the security level increases, so does the length of the private key: 2 x 2187 trytes forsecurity_level = 2
, and 3 x 2187 trytes forsecurity_level = 3
.A private key is split into 81-tryte segments, and these segments are hashed 26 times. A group of 27 hashed segments is called a key fragment. Observe, that a private key has one key fragment for each security level.
Each key fragment is hashed once more to generate key digests, that are combined and hashed once more to get the 81-tryte address.
Note
An address is the public key pair of the corresponding private key. When you spend iotas from an address, you need to sign the transaction with a key digest that was generated from the address’s corresponing private key. This way you prove that you own the funds on that address.
PyOTA provides two methods for generating addresses:
Using the API¶
from iota import Iota
api = Iota('http://localhost:14265', b'SEED9GOES9HERE')
# Generate 5 addresses, starting with index 0.
gna_result = api.get_new_addresses(count=5)
# Result is a dict that contains a list of addresses.
addresses = gna_result['addresses']
# Generate 1 address, starting with index 42:
gna_result = api.get_new_addresses(index=42)
addresses = gna_result['addresses']
# Find the first unused address, starting with index 86:
gna_result = api.get_new_addresses(index=86, count=None)
addresses = gna_result['addresses']
To generate addresses using the API, invoke its iota.Iota.get_new_addresses()
method, using the following parameters:
index: int
: The starting index (defaults to 0). This can be used to skip over addresses that have already been generated.count: Optional[int]
: The number of addresses to generate (defaults to 1).If
None
, the API will generate addresses until it finds one that has not been used (has no transactions associated with it on the Tangle and was never spent from). It will then return the unused address and discard the rest.
security_level: int
: Determines the security level of the generated addresses. See Security Levels below.
Depending on the count
parameter, Iota.get_new_addresses()
can be
operated in two modes.
Offline mode¶
When
count
is greater than 0, the API generatescount
number of addresses starting fromindex
. It does not check the Tangle if addresses were used or spent from before.
Online mode¶
When
count
isNone
, the API starts generating addresses starting fromindex
. Then, for each generated address, it checks the Tangle if the address has any transactions associated with it, or if the address was ever spent from. If both of the former checks return “no”, address generation stops and the address is returned (a new address is found).
Warning
Take care when using the online mode after a snapshot. Transactions referencing a generated address may have been pruned from a node’s ledger, therefore the API could return an already-used address as “new” (note: The snapshot has no effect on the “was ever spent from” check).
To make your application more robust to handle snapshots, it is recommended
that you keep a local database with at least the indices of your used addresses.
After a snapshot, you could specify index
parameter as the last
index in your local used addresses database, and keep on generating truly
new addresses.
PyOTA is planned to receive the account module in the future, that makes the library stateful and hence would solve the issue mentioned above.
Using AddressGenerator¶
from iota.crypto.addresses import AddressGenerator
generator = AddressGenerator(b'SEED9GOES9HERE')
# Generate a list of addresses:
addresses = generator.get_addresses(start=0, count=5)
# Generate a list of addresses in reverse order:
addresses = generator.get_addresses(start=42, count=10, step=-1)
# Create an iterator, advancing 5 indices each iteration.
iterator = generator.create_iterator(start=86, step=5)
for address in iterator:
...
If you want more control over how addresses are generated, you can use
iota.crypto.addresses.AddressGenerator
.
AddressGenerator
can create iterators, allowing your application to
generate addresses as needed, instead of having to generate lots of
addresses up front.
You can also specify an optional step
parameter, which allows you to
skip over multiple addresses between iterations… or even iterate over
addresses in reverse order!
AddressGenerator¶
-
class
iota.crypto.addresses.
AddressGenerator
(seed: Union[AnyStr, bytearray, TryteString], security_level: int = 2, checksum: bool = False)¶ Generates new addresses using a standard algorithm.
Note
This class does not check if addresses have already been used; if you want to exclude used addresses, invoke
iota.Iota.get_new_addresses()
instead.Note also that
iota.Iota.get_new_addresses()
usesAddressGenerator
internally, so you get the best of both worlds when you use the API (:- Parameters
seed (TrytesCompatible) – The seed to derive addresses from.
security_level (int) –
When generating a new address, you can specify a security level for it. The security level of an address affects how long the private key is, how secure a spent address is against brute-force attacks, and how many transactions are needed to contain the signature.
Could be either 1, 2 or 3.
Reference:
checksum (bool) – Whether to generate address with or without checksum.
- Returns
get_addresses¶
-
AddressGenerator.
get_addresses
(start: int, count: int = 1, step: int = 1) → List[iota.types.Address]¶ Generates and returns one or more addresses at the specified index(es).
This is a one-time operation; if you want to create lots of addresses across multiple contexts, consider invoking
create_iterator()
and sharing the resulting generator object instead.Warning
This method may take awhile to run if the starting index and/or the number of requested addresses is a large number!
- Parameters
start (int) – Starting index. Must be >= 0.
count (int) – Number of addresses to generate. Must be > 0.
step (int) – Number of indexes to advance after each address. This may be any non-zero (positive or negative) integer.
- Returns
List[Address]
Always returns a list, even if only one address is generated.
The returned list will contain
count
addresses, except whenstep * count < start
(only applies whenstep
is negative).- Raises
ValueError –
if
count
is lower than 1.if
step
is zero.
create_iterator¶
-
AddressGenerator.
create_iterator
(start: int = 0, step: int = 1) → Generator[iota.types.Address, None, None]¶ Creates an iterator that can be used to progressively generate new addresses.
Returns an iterator that will create addresses endlessly. Use this if you have a feature that needs to generate addresses “on demand”.
- Parameters
start (int) –
Starting index.
Warning
This method may take awhile to reset if
start
is a large number!step (int) –
Number of indexes to advance after each address.
Warning
The generator may take awhile to advance between iterations if
step
is a large number!
- Returns
Generator[Address, None, None]
object that you can iterate to generate addresses.
Security Levels¶
gna_result = api.get_new_addresses(security_level=3)
generator =\
AddressGenerator(
seed = b'SEED9GOES9HERE',
security_level = 3,
)
If desired, you may change the number of iterations that
iota.crypto.addresses.AddressGenerator
or
iota.Iota.get_new_addresses
uses internally when generating new
addresses, by specifying a different security_level
when creating a new
instance.
security_level
should be between 1 and 3, inclusive. Values outside
this range are not supported by the IOTA protocol.
Use the following guide when deciding which security level to use:
security_level=1
: Least secure, but generates addresses the fastest.security_level=2
: Default; good compromise between speed and security.security_level=3
: Most secure; results in longer signatures in transactions.
Creating transfers¶
IOTA is a permissionless DLT solution, therefore anyone can send transactions to the network and initiate transfers. The IOTA client libraries help you to abstract away low-level operations required to construct and send a transfer to the Tangle.
In this section, we will explore in depth how to create transactions and bundles with IOTA, furthermore what tools you can use in PyOTA to ease your development process.
Note
Before proceeding, make sure you read and understood the Basic Concepts and PyOTA Types sections!
Anatomy of a Transfer¶
We already know that the Tangle consists of transactions referencing each other, each of them two others to be more precise. Transactions can be grouped together in bundles. Zero-value bundles contain only zero value transactions, while transfer bundles may also contain input and output transactions.
But how to construct these bundles and send them to the network?
The process can be boiled down to 5 steps:
Create individual transaction(s).
Construct a bundle from the transaction(s).
Obtain references to two unconfirmed transactions (“tips”) from the Tangle.
Do proof-of-work for each transaction in the bundle.
Send the bundle to the network.
Process of creating and sending a transfer to the Tangle.¶
1. Create Transactions¶
The first step is to create the individual transaction objects. You have to
specify address
and value
for each transaction. Furthermore, you can
define a tag
, and for zero-value transactions, a message
. A
timestamp
is also required, though this value is usually auto-generated
by the IOTA libraries.
Note
Unlike on other decentralised ledgers, IOTA transactions can have positive
or negative value
amounts. In order to send iotas from one address to
another, at least two transactions are required:
one with positive
value
(to increment the balance of the receiver), andone with negative
value
(to decrement the balance of the sender).
In PyOTA, use ProposedTransaction
to declare transactions.
2. Create Bundle from Transactions¶
A bundle is a collection of transactions, treated as an atomic unit when sent to the network. A bundle makes a value (iota token) transfer possible by grouping together input and output transactions.
A bundle always has to be balanced: the sum of value
attributes of the
transactions in the bundle should always be zero. Transactions in the bundle
are also indexed individually and contain information on how many other
transactions there are in the bundle.
Once complete, a bundle has to be finalized to generate the bundle hash based on the bundle essence. The bundle hash is the unique identifier of the bundle.
After finalization, input transactions in the bundle need to be signed to prove ownership of iotas being transferred.
Tip
ProposedBundle
helps you in PyOTA to create bundles, add transactions,
finalize the bundle and sign the inputs. We’ll see how to use
ProposedBundle
in Use the Library below.
3. Select two tips¶
Tips are transactions that are yet to be confirmed by the network. We can
obtain two tips by requesting them from a node. In PyOTA, get_transactions_to_approve()
does the job: it returns a trunk
and a branch
TransactionHash
.
Because our bundle references these two transactions, it will validate them once it is added to the Tangle.
4. Do Proof-of-Work¶
The bundle has been finalized, inputs have been signed, and we have two tips; now it’s time to prepare the bundle to be attached to the Tangle. As noted in the previous section, every transaction references two other transactions in the Tangle; therefore we need to select these references for each transaction in our bundle.
We also know that transactions within the bundle are linked together through their trunk references. So how do we construct the correct bundle structure and also reference two tips from the network?

Structure of a bundle with four transactions. Numbers in brackets denote
(currentIndex
, lastIndex
) fields. Head of the bundle has index 3,
while tail has index 0.¶
For all non-head transactions in the bundle, the trunk reference is the next transaction in the bundle, while the branch reference is the trunk transaction hash, one of the tips.
The head transaction is different: the trunk reference is the trunk tip, while the branch reference is the branch tip.
The proof-of-work calculation has to be done for each transaction individually, therefore the more transactions you have in the bundle, the more time it will take. The difficulty of the calculation also depends on the minimum weight magnitude set by the network.
The output of the proof-of-work algorithm is a nonce
value that is appended
to the the transaction, resulting in the attached transaction trytes.
Nodes validate the proof-of-work of a transaction by calculating the transaction’s
hash from the attached transaction trytes. If the resulting hash has at least
minimum weight magnitude
number of trailing zero trits, the transaction is valid.
In PyOTA, use attach_to_tangle()
to carry out this step.
5. Broadcast and Store¶
The final step is to send the bundle to the network. Nodes will broadcast the transactions in the network, and store them in their local database.
In PyOTA, use broadcast_and_store()
to achieve this.
Observe the bird’s-eye view of the Tangle depicted at the last step of the process. Our transactions are part of the Tangle, referencing each other and the two tips. Newer transactions may reference our transactions as branch or trunk.
Note
As more transactions are added to the Tangle that reference our transactions – and then more are added that reference those transactions, and so on – this increases the cumulative weight of our transactions. The higher the cumulative weight of our transactions, the higher the chance for them to get confirmed.
Use the Library¶
The IOTA libraries help you to abstract away the low-level operations needed to create transfers. The figure below illustrates the different ways you can build and send a transfer.
API commands for sending transfers.¶
Let’s look at some code snippets on how to perform the above with an imaginary bundle that has 3 fictional transactions.
1. Level Padawan¶
The easiest and most convenient way is to use send_transfer()
extended API method. You still need to create the transactions yourself
with ProposedTransaction
.
from iota import Iota, ProposedTransaction, Address
api = Iota('https://nodes.devnet.iota.org:443')
fictional_transactions = [
ProposedTransaction(
address=Address(b'FIRSTRANDOMADDRESS'),
value=0,
# You could add a tag or message here too!
),
ProposedTransaction(
address=Address(b'SECONDRANDOMADDRESS'),
value=0,
),
ProposedTransaction(
address=Address(b'THIRDRANDOMADDRESS'),
value=0,
)
]
imaginary_bundle = api.send_transfer(
transfers=fictional_transactions
)['bundle']
As all API methods in PyOTA, send_transfer()
also returns
a dict
. The bundle
key holds the value of Bundle
.
It’s important to note, that for value transfers, you will need your seed as well.
send_transfer()
will look for input addresses
to fund outgoing
transactions in the bundle, and auto-generate an unused change address
if
there is a remainder amount of tokens. It will also take care of finalizing the
bundle and signing the necessary input transactions.
2. Level Obi-Wan¶
Instead of send_transfer()
, you can use the combination of
prepare_transfer()
and send_trytes()
to achieve
the same result.
Tip
This can be useful if you want to prepare the transactions (including signing inputs) on one device, but you want to then transfer the data to another device for transmission to the Tangle. For example, you might prepare_transfer()
on an air-gapped computer that has your seed stored on it, but then transfer the resulting trytes to a networked computer (that does not have your seed) to send_trytes()
.
from iota import Iota, ProposedTransaction, Address
api = Iota('https://nodes.devnet.iota.org:443')
transactions = [
ProposedTransaction(
address=Address(b'FIRSTRANDOMADDRESS'),
value=0,
),
ProposedTransaction(
address=Address(b'SECONDRANDOMADDRESS'),
value=0,
),
ProposedTransaction(
address=Address(b'THIRDRANDOMADDRESS'),
value=0,
)
]
prepared_trytes = api.prepare_transfer(
transfers=transactions
)['trytes']
imaginary_bundle_trytes = api.send_trytes(
trytes=prepared_trytes
)['trytes']
A difference here is that the end result, imaginary_bundle_trytes
is a list
of TransactionTrytes
, and not a Bundle
object.
3. Level Yoda¶
Being the master Jedi of the PyOTA universe means that you know the most about the force of low-level API methods. Use it wisely!
Tip
You generally won’t need to split out the process explicitly like this in your application code, but it is useful to understand what send_transfer()
does under-the-hood, so that you are better-equipped to troubleshoot any issues that may occur during the process.
from iota import Iota, ProposedTransaction, Address, ProposedBundle
api = Iota('https://nodes.devnet.iota.org:443')
transactions = [
ProposedTransaction(
address=Address(b'FIRSTRANDOMADDRESS'),
value=0,
),
ProposedTransaction(
address=Address(b'SECONDRANDOMADDRESS'),
value=0,
),
ProposedTransaction(
address=Address(b'THIRDRANDOMADDRESS'),
value=0,
)
]
bundle = ProposedBundle()
for tx in transactions:
bundle.add_transaction(tx)
# If it was a value transfer, we would also need to:
# bundle.add_inputs()
# bundle.send_unspent_inputs_to()
bundle.finalize()
# Again, for value transfers, we would need to:
# bundle.sign_inputs(KeyGenerator(b'SEEDGOESHERE'))
gtta_response = api.get_transactions_to_approve(depth=3)
trunk = gtta_response['trunkTransaction']
branch = gtta_response['branchTransaction']
attached_trytes = api.attach_to_tangle(
trunk_transaction=trunk,
branch_transaction=branch,
trytes=bundle.as_tryte_strings()
)['trytes']
api.broadcast_transactions(attached_trytes)
api.store_transactions(attached_trytes)
imaginary_bundle = Bundle.from_tryte_strings(attached_trytes)
Multisignature¶
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.
It is standard functionality in blockchain systems and it is also implemented in IOTA.
Note
You can read more about IOTA multisignature on the wiki.
First, we will take a look on what Multisig API (s) you can use, and what PyOTA Multisignature Types are there for you if the standard API is not enough for your application and you want to take more control.
Starting from Generating multisignature address, a tutorial follows to show you how to use the multisignature API to execute multisig transfers. The complete source code for the tutorial can be found here.
Multisig API¶
The multisignature API builds on top of the extended API to add multisignature features. Just like for the regular APIs, there is both a synchronous and an asynchronous version of the multisignature API, however, as there is no networking required during the multisignature API calls, the difference between them is only how you can call them.
Synchronous Multisignature API Class¶
-
class
iota.multisig.
MultisigIota
(adapter: Union[str, BaseAdapter], seed: Union[AnyStr, bytearray, TryteString, None] = None, devnet: bool = False, local_pow: bool = False)¶ Extends the IOTA API so that it can send multi-signature transactions. Synchronous API.
Caution
Make sure you understand how multisig works before attempting to use it. If you are not careful, you could easily compromise the security of your private keys, send IOTAs to unspendable addresses, etc.
Example Usage:
# Import API class from iota.multisig import MultisigIota # Declare a multisig API instance api = MultisigIota( adapter = 'http://localhost:14265', seed = Seed( b'TESTVALUE9DONTUSEINPRODUCTION99999JYFRTI' b'WMKVVBAIEIYZDWLUVOYTZBKPKLLUMPDF9PPFLO9KT', ), ) response = api.get_digests(...)
References:
Asynchronous Multisignature API Class¶
-
class
iota.multisig.
AsyncMultisigIota
(adapter: Union[str, BaseAdapter], seed: Union[AnyStr, bytearray, TryteString, None] = None, devnet: bool = False, local_pow: bool = False)¶ Extends the IOTA API so that it can send multi-signature transactions. Asynchronous API.
Caution
Make sure you understand how multisig works before attempting to use it. If you are not careful, you could easily compromise the security of your private keys, send IOTAs to unspendable addresses, etc.
Example Usage:
# Import API class from iota.multisig import AsyncMultisigIota # Declare a multisig API instance api = AsyncMultisigIota( adapter = 'http://localhost:14265', seed = Seed( b'TESTVALUE9DONTUSEINPRODUCTION99999JYFRTI' b'WMKVVBAIEIYZDWLUVOYTZBKPKLLUMPDF9PPFLO9KT', ), ) response = await api.get_digests(...)
References:
create_multisig_address
¶
-
MultisigIota.
create_multisig_address
(digests: Iterable[iota.crypto.types.Digest]) → dict¶ Generates a multisig address from a collection of digests.
- Parameters
digests (Iterable[Digest]) –
Digests to use to create the multisig address.
Important
In order to spend IOTAs from a multisig address, the signature must be generated from the corresponding private keys in the exact same order.
- Returns
dict
with the following items:{ 'address': MultisigAddress, The generated multisig address. }
-
async
AsyncMultisigIota.
create_multisig_address
(digests: Iterable[iota.crypto.types.Digest]) → dict¶ Generates a multisig address from a collection of digests.
- Parameters
digests (Iterable[Digest]) –
Digests to use to create the multisig address.
Important
In order to spend IOTAs from a multisig address, the signature must be generated from the corresponding private keys in the exact same order.
- Returns
dict
with the following items:{ 'address': MultisigAddress, The generated multisig address. }
get_digests
¶
-
MultisigIota.
get_digests
(index: int = 0, count: int = 1, security_level: int = 2) → dict¶ Generates one or more key digests from the seed.
Digests are safe to share; use them to generate multisig addresses.
- Parameters
index (int) – The starting key index.
count (int) – Number of digests to generate.
security_level (int) –
Number of iterations to use when generating new addresses.
Larger values take longer, but the resulting signatures are more secure.
This value must be between 1 and 3, inclusive.
- Returns
dict
with the following items:{ 'digests': List[Digest], Always contains a list, even if only one digest was generated. }
-
async
AsyncMultisigIota.
get_digests
(index: int = 0, count: int = 1, security_level: int = 2) → dict¶ Generates one or more key digests from the seed.
Digests are safe to share; use them to generate multisig addresses.
- Parameters
index (int) – The starting key index.
count (int) – Number of digests to generate.
security_level (int) –
Number of iterations to use when generating new addresses.
Larger values take longer, but the resulting signatures are more secure.
This value must be between 1 and 3, inclusive.
- Returns
dict
with the following items:{ 'digests': List[Digest], Always contains a list, even if only one digest was generated. }
get_private_keys
¶
-
MultisigIota.
get_private_keys
(index: int = 0, count: int = 1, security_level: int = 2) → dict¶ Generates one or more private keys from the seed.
As the name implies, private keys should not be shared. However, in a few cases it may be necessary (e.g., for M-of-N transactions).
- Parameters
index (int) – The starting key index.
count (int) – Number of keys to generate.
security_level (int) –
Number of iterations to use when generating new keys.
Larger values take longer, but the resulting signatures are more secure.
This value must be between 1 and 3, inclusive.
- Returns
dict
with the following items:{ 'keys': List[PrivateKey], Always contains a list, even if only one key was generated. }
References:
iota.crypto.signing.KeyGenerator
https://github.com/iotaledger/wiki/blob/master/multisigs.md#how-m-of-n-works
-
async
AsyncMultisigIota.
get_private_keys
(index: int = 0, count: int = 1, security_level: int = 2) → dict¶ Generates one or more private keys from the seed.
As the name implies, private keys should not be shared. However, in a few cases it may be necessary (e.g., for M-of-N transactions).
- Parameters
index (int) – The starting key index.
count (int) – Number of keys to generate.
security_level (int) –
Number of iterations to use when generating new keys.
Larger values take longer, but the resulting signatures are more secure.
This value must be between 1 and 3, inclusive.
- Returns
dict
with the following items:{ 'keys': List[PrivateKey], Always contains a list, even if only one key was generated. }
References:
iota.crypto.signing.KeyGenerator
https://github.com/iotaledger/wiki/blob/master/multisigs.md#how-m-of-n-works
prepare_multisig_transfer
¶
-
MultisigIota.
prepare_multisig_transfer
(transfers: Iterable[iota.transaction.creation.ProposedTransaction], multisig_input: iota.multisig.types.MultisigAddress, change_address: Optional[iota.types.Address] = None) → dict¶ Prepares a bundle that authorizes the spending of IOTAs from a multisig address.
Note
This method is used exclusively to spend IOTAs from a multisig address.
If you want to spend IOTAs from non-multisig addresses, or if you want to create 0-value transfers (i.e., that don’t require inputs), use
prepare_transfer()
instead.- Parameters
transfers (terable[ProposedTransaction]) –
Transaction objects to prepare.
Important
Must include at least one transaction that spends IOTAs (i.e., has a nonzero
value
). If you want to prepare a bundle that does not spend any IOTAs, useprepare_transfer()
instead.multisig_input (MultisigAddress) –
The multisig address to use as the input for the transfers.
Note
This method only supports creating a bundle with a single multisig input.
If you would like to spend from multiple multisig addresses in the same bundle, create the
ProposedMultisigBundle
object manually.change_address (Optional[Address]) –
If inputs are provided, any unspent amount will be sent to this address.
If the bundle has no unspent inputs, ``change_address` is ignored.
Important
Unlike
prepare_transfer()
, this method will NOT generate a change address automatically.If there are unspent inputs and
change_address
is empty, an exception will be raised.This is because multisig transactions typically involve multiple individuals, and it would be unfair to the participants if we generated a change address automatically using the seed of whoever happened to run the
prepare_multisig_transfer
method!Danger
Note that this protective measure is not a substitute for due diligence!
Always verify the details of every transaction in a bundle (including the change transaction) before signing the input(s)!
- Returns
dict
containing the following values:{ 'trytes': List[TransactionTrytes], Finalized bundle, as trytes. The input transactions are not signed. }
In order to authorize the spending of IOTAs from the multisig input, you must generate the correct private keys and invoke the
iota.crypto.types.PrivateKey.sign_input_at()
method for each key, in the correct order.Once the correct signatures are applied, you can then perform proof of work (
attachToTangle
) and broadcast the bundle usingsend_trytes()
.
-
async
AsyncMultisigIota.
prepare_multisig_transfer
(transfers: Iterable[iota.transaction.creation.ProposedTransaction], multisig_input: iota.multisig.types.MultisigAddress, change_address: Optional[iota.types.Address] = None) → dict¶ Prepares a bundle that authorizes the spending of IOTAs from a multisig address.
Note
This method is used exclusively to spend IOTAs from a multisig address.
If you want to spend IOTAs from non-multisig addresses, or if you want to create 0-value transfers (i.e., that don’t require inputs), use
prepare_transfer()
instead.- Parameters
transfers (terable[ProposedTransaction]) –
Transaction objects to prepare.
Important
Must include at least one transaction that spends IOTAs (i.e., has a nonzero
value
). If you want to prepare a bundle that does not spend any IOTAs, useprepare_transfer()
instead.multisig_input (MultisigAddress) –
The multisig address to use as the input for the transfers.
Note
This method only supports creating a bundle with a single multisig input.
If you would like to spend from multiple multisig addresses in the same bundle, create the
ProposedMultisigBundle
object manually.change_address (Optional[Address]) –
If inputs are provided, any unspent amount will be sent to this address.
If the bundle has no unspent inputs, ``change_address` is ignored.
Important
Unlike
prepare_transfer()
, this method will NOT generate a change address automatically.If there are unspent inputs and
change_address
is empty, an exception will be raised.This is because multisig transactions typically involve multiple individuals, and it would be unfair to the participants if we generated a change address automatically using the seed of whoever happened to run the
prepare_multisig_transfer
method!Danger
Note that this protective measure is not a substitute for due diligence!
Always verify the details of every transaction in a bundle (including the change transaction) before signing the input(s)!
- Returns
dict
wontaining the following values:{ 'trytes': List[TransactionTrytes], Finalized bundle, as trytes. The input transactions are not signed. }
In order to authorize the spending of IOTAs from the multisig input, you must generate the correct private keys and invoke the
iota.crypto.types.PrivateKey.sign_input_at()
method for each key, in the correct order.Once the correct signatures are applied, you can then perform proof of work (
attachToTangle
) and broadcast the bundle usingsend_trytes()
.
PyOTA Multisignature Types¶
There are some specific types defined in PyOTA to help you work with creating multisignature addresses and bundles.
Multisignature Address¶
-
class
iota.multisig.types.
MultisigAddress
(trytes: Union[AnyStr, bytearray, TryteString], digests: Iterable[iota.crypto.types.Digest], balance: Optional[int] = None)¶ An address that was generated using digests from multiple private keys.
In order to spend inputs from a multisig address, the same private keys must be used, in the same order that the corresponding digests were used to generate the address.
Note
Usually, you don’t have to create a MultisigAddress manually. Use
create_multisig_address()
to derive an address from a list of digests.MultisigAddress
is a subclass ofiota.Address
, so you can use all the regulariota.Address
methods on aMultisigAddress
object.- Parameters
trytes (TrytesCompatible) – Address trytes (81 trytes long).
digests (Iterable[Digest]) – List of digests that were used to create the address. Order is important!
balance (Optional[int]) – Available balance of the address.
- Returns
MultisigAddress
object.
-
as_json_compatible
() → dict¶ Get a JSON represenation of the
MultisigAddress
object.- Returns
dict
with the following structure:{ 'trytes': str, String representation of the address trytes. 'balance': int, Balance of the address. 'digests': Iterable[Digest] Digests that were used to create the address. }
Multisignature ProposedBundle¶
-
class
iota.multisig.transaction.
ProposedMultisigBundle
(transactions: Optional[Iterable[iota.transaction.creation.ProposedTransaction]] = None, inputs: Optional[Iterable[iota.types.Address]] = None, change_address: Optional[iota.types.Address] = None)¶ A collection of proposed transactions, with multisig inputs.
Note: at this time, only a single multisig input is supported per bundle.
Note
Usually you don’t have to construct
ProposedMultisigBundle
bundle manually,prepare_multisig_transfer()
does it for you.- Parameters
transactions (Optional[Iterable[ProposedTransaction]]) – Proposed transactions that should be put into the proposed bundle.
inputs (Optional[Iterable[Address]]) – Addresses that hold iotas to fund outgoing transactions in the bundle. Currently PyOTA supports only one mutlisig input address per bundle.
change_address (Optional[Address]) – Due to the signatures scheme of IOTA, you can only spend once from an address. Therefore the library will always deduct the full available amount from an input address. The unused tokens will be sent to
change_address
if provided.
- Returns
ProposedMultisigBundle
object.
-
add_inputs
(inputs: Iterable[iota.multisig.types.MultisigAddress]) → None¶ Adds inputs to spend in the bundle.
Note that each input may require multiple transactions, in order to hold the entire signature.
- Parameters
inputs (Iterable[MultisigAddress]) –
MultisigAddresses to use as the inputs for this bundle.
Note: at this time, only a single multisig input is supported.
Generating multisignature address¶
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 (Kerl state trits) into multisignature address.
Note
Each multisignature addresses participant has to create its own digest locally. Then, when it is created it can be safely shared with other participants, in order to build list of digests which then will be converted into multisignature address.
Created digests should be shared with each multisignature participant, so each one of them could regenerate address and ensure it is OK.
Here is the example where digest is created:
# Create digest 3 of 3.
api_3 =\
MultisigIota(
adapter = 'http://localhost:14265',
seed =
Seed(
b'TESTVALUE9DONTUSEINPRODUCTION99999JYFRTI'
b'WMKVVBAIEIYZDWLUVOYTZBKPKLLUMPDF9PPFLO9KT',
),
)
gd_result = api_3.get_digests(index=8, count=1, security_level=2)
digest_3 = gd_result['digests'][0] # type: Digest
And here is example where digests are converted into multisignature address:
cma_result =\
api_1.create_multisig_address(digests=[digest_1,
digest_2,
digest_3])
# For consistency, every API command returns a dict, even if it only
# has a single value.
multisig_address = cma_result['address'] # type: MultisigAddress
Note
As you can see in above example, multisignature addresses is created from list of digests, and in this case order is important. The same order need to be used in signing transfer.
Prepare transfer¶
Note
Since spending tokens from the same address more than once is insecure, remainder should be transferred to other address. So, this address should be created before as next to be used multisignature address.
First signer for multisignature wallet is defining address where tokens should be transferred and next wallet address for reminder:
pmt_result =\
api_1.prepare_multisig_transfer(
# These are the transactions that will spend the IOTAs.
# You can divide up the IOTAs to send to multiple addresses if you
# want, but to keep this example focused, we will only include a
# single spend transaction.
transfers = [
ProposedTransaction(
address =
Address(
b'TESTVALUE9DONTUSEINPRODUCTION99999NDGYBC'
b'QZJFGGWZ9GBQFKDOLWMVILARZRHJMSYFZETZTHTZR',
),
value = 42,
# If you'd like, you may include an optional tag and/or
# message.
tag = Tag(b'KITTEHS'),
message = TryteString.from_unicode('thanx fur cheezburgers'),
),
],
# Specify our multisig address as the input for the spend
# transaction(s).
# Note that PyOTA currently only allows one multisig input per
# bundle (although the protocol does not impose a limit).
multisig_input = multisig_address,
# If there will be change from this transaction, you MUST specify
# the change address! Unlike regular transfers, multisig transfers
# will NOT automatically generate a change address; that wouldn't
# be fair to the other participants!
change_address = None,
)
prepared_trytes = pmt_result['trytes'] # type: List[TransactionTrytes]
Sign the inputs¶
When trytes are prepared, round of signing must be performed. Order of signing must be the same as in generate multisignature addresses procedure (as described above).
Note
In example below, all signing is done on one local machine. In real case, each participant sign bundle locally and then passes it to next participant in previously defined order
index, count and security_lavel parameters for each private key should be the same as used in get_digests function in previous steps.
bundle = Bundle.from_tryte_strings(prepared_trytes)
gpk_result = api_1.get_private_keys(index=0, count=1, security_level=3)
private_key_1 = gpk_result['keys'][0] # type: PrivateKey
private_key_1.sign_input_transactions(bundle, 1)
gpk_result = api_2.get_private_keys(index=42, count=1, security_level=3)
private_key_2 = gpk_result['keys'][0] # type: PrivateKey
private_key_2.sign_input_transactions(bundle, 4)
gpk_result = api_3.get_private_keys(index=8, count=1, security_level=2)
private_key_3 = gpk_result['keys'][0] # type: PrivateKey
private_key_3.sign_input_transactions(bundle, 7)
signed_trytes = bundle.as_tryte_strings()
Note
After creation, bundle can be optionally validated:
validator = BundleValidator(bundle)
if not validator.is_valid():
raise ValueError(
'Bundle failed validation:\n{errors}'.format(
errors = '\n'.join((' - ' + e) for e in validator.errors),
),
)
Broadcast the bundle¶
When bundle is created it can be broadcasted in standard way:
api_1.send_trytes(trytes=signed_trytes, depth=3)
Remarks¶
Full code example.
Note
How M-of-N works
One of the key differences between IOTA multi-signatures is that M-of-N (e.g. 3 of 5) works differently. What this means is that in order to successfully spend inputs, all of the co-signers have to sign the transaction. As such, in order to enable M-of-N we have to make use of a simple trick: sharing of private keys.
This concept is best explained with a concrete example:
Lets say that we have a multi-signature between 3 parties: Alice, Bob and Carol. Each has their own private key, and they generated a new multi-signature address in the aforementioned order. Currently, this is a 3 of 3 multisig. This means that all 3 participants (Alice, Bob and Carol) need to sign the inputs with their private keys in order to successfully spend them.
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:
Alice -> Bob
Bob -> Carol
Carol -> Alice
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.
Important¶
There are some general rules (repeated once again for convenience) which should be followed while working with multisignature addresses (and in general with IOTA):
Signing order is important¶
When creating a multi-signature address and when signing a transaction for that address, it is important to follow the exact order that was used during the initial creation. If we have a multi-signature address that was signed in the following order: Alice -> Bob -> Carol. You will not be able to spend these inputs if you provide the signatures in a different order (e.g. Bob -> Alice -> Carol). As such, keep the signing order in mind.
Never re-use keys¶
Probably the most important rule to keep in mind: absolutely never re-use private keys. IOTA uses one-time Winternitz signatures, which means that if you re-use private keys you significantly decrease the security of your private keys, up to the point where signing of another transaction can be done on a conventional computer within few days. Therefore, when generating a new multi-signature with your co-signers, always increase the private key index counter and only use a single private key once. Don’t use it for any other multi-signatures and don’t use it for any personal transactions.
Advanced: PyOTA Commands¶
Note
This page contains information about how PyOTA works under the hood.
It is absolutely not necessary to be familiar with the content described below if you just want to use the library.
However, if you are a curious mind or happen to do development on the library, the following information might be useful.
PyOTA provides the API interface (Core API Methods and Extended API Methods) for users of the library. These handle constructing and sending HTTP requests to the specified node through adapters, furthermore creating, transforming and translating between PyOTA-specific types and (JSON-encoded) raw data. They also filter outgoing requests and incoming responses to ensure that only appropriate data is communicated with the node.
PyOTA implements the Command Design Pattern. High level API interface methods (Core API Methods and Extended API Methods) internally call PyOTA commands to get the job done.
Most PyOTA commands are sub-classed from FilterCommand
class, which
is in turn sub-classed from BaseCommand
class. The reason for the
2-level inheritance is simple: separating functionality. As the name implies,
FilterCommand
adds filtering capabilities to
BaseCommand
, that contains the logic of constructing the request
and using its adapter to send it and receive a response.
Command Flow¶
As mentioned earlier, API methods rely on PyOTA commands to carry out specific operations. It is important to understand what happens during command execution so you are able to implement new methods that extend the current capabilities of PyOTA.
Let’s investigate the process through an example of a core API method, for
instance find_transactions()
, that calls
FindTransactionCommand
PyOTA command internally.
Note
FindTransactionCommand
is sub-classed from FilterCommand
.
To illustrate what the happens inside the API method, take a look at the following figure
Inner workings of a PyOTA Command.¶
When you call
find_transactions()
core API method, it initializes aFindTransactionCommand
object with the adapter of the API instance it belongs to.Then calls this command with the keyword arguments it was provided with.
The command prepares the request by applying a
RequestFilter
on the payload. The command specificRequestFilter
validates that the payload has correct types, in some cases it is even able to convert the payload to the required type and format.Command execution injects the name of the API command (see IRI API Reference for command names) in the request and sends it to the adapter.
The adapter communicates with the node and returns its response.
The response is prepared by going through a command-specific
ResponseFilter
.The response is returned to the high level API method as a
dict
, ready to be returned to the main application.
Note
A command object can only be called once without resetting it. When you use the high level API methods, you don’t need to worry about resetting commands as each call to an API method will initialize a new command object.
Filters¶
If you take a look at the actual implementation of
FindTransactionsCommand
, you notice that you have to define your
own request and response filter classes.
Filters in PyOTA are based on the Filters library. Read more about how they work at the filters documentation site.
In short, you can create filter chains through which the filtered value passes,
and generates errors if something failed validation. Filter chains are specified
in the custom filter class’s __init__()
function. If you also want to
modify the filtered value before returning it, override the _apply()
method of its base class. Read more about how to create custom filters.
PyOTA offers you some custom filters for PyOTA-specific types:
Trytes¶
-
class
iota.filters.
Trytes
(result_type: type = <class 'iota.types.TryteString'>)¶ Validates a sequence as a sequence of trytes.
When a value doesn’t pass the filter, a
ValueError
is raised with lots of contextual info attached to it.- Parameters
result_type (TryteString) – Any subclass of
TryteString
that you want the filter to validate.- Raises
TypeError – if value is not of
result_type
.ValueError – if
result_type
is not ofTryteString
type.
- Returns
Trytes
object.
StringifiedTrytesArray¶
-
filters.
StringifiedTrytesArray
(**runtime_kwargs) → filters.base.FilterChain¶ Validates that the incoming value is an array containing tryte strings corresponding to the specified type (e.g.,
TransactionHash
).When a value doesn’t pass the filter, a
ValueError
is raised with lots of contextual info attached to it.- Parameters
trytes_type (TryteString) – Any subclass of
TryteString
that you want the filter to validate.- Returns
filters.FilterChain
object.
Important
This filter will return string values, suitable for inclusion in an API request. If you are expecting objects (e.g.,
Address
), then this is not the filter to use!Note
This filter will allow empty arrays and None. If this is not desirable, chain this filter with
f.NotEmpty
orf.Required
, respectively.
AddressNoChecksum¶
-
class
iota.filters.
AddressNoChecksum
¶ Validates a sequence as an
Address
, then chops off the checksum if present.When a value doesn’t pass the filter, a
ValueError
is raised with lots of contextual info attached to it.- Returns
AddressNoChecksum
object.
GeneratedAddress¶
-
class
iota.filters.
GeneratedAddress
¶ Validates an incoming value as a generated
Address
(must havekey_index
andsecurity_level
set).When a value doesn’t pass the filter, a
ValueError
is raised with lots of contextual info attached to it.- Returns
GeneratedAddress
object.
NodeUri¶
SecurityLevel¶
-
filters.
SecurityLevel
(**runtime_kwargs) → filters.base.FilterChain¶ Generates a filter chain for validating a security level.
- Returns
filters.FilterChain
object.
Important
The general rule in PyOTA is that all requests going to a node are validated, but only responses that contain transaction/bundle trytes or hashes are checked.
Also note, that for extended commands, ResponseFilter
is usually
implemented with just a “pass” statement. The reason being that these
commands do not directly receive their result a node, but rather from
core commands that do have their ResponseFilter
implemented.
More about this topic in the next section.
Extended Commands¶
Core commands, like find_transactions()
in the example above,
are for direct communication with the node for simple tasks such
as finding a transaction on the Tangle or getting info about the node.
Extended commands (that serve Extended API Methods) on the other hand
carry out more complex operations such as combining core commands, building
objects, etc…
As a consequence, extended commands override the default execution phase of their base class.
Observe for example FindTransactionObjectsCommand
extended command
that is called in find_transaction_objects()
extended API
method. It overrides the _execute()
method of its base class.
Let’s take a closer look at the implementation:
...
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,
}
...
Instead of sending the request to the adapter,
FindTransactionObjectsCommand._execute()
calls
FindTransactionsCommand
core command, gathers the transaction hashes
that it found, and collects the trytes of those transactions by calling
GetTrytesCommand
core command. Finally, using the obtained trytes,
it constructs a list of transaction objects that are returned to
find_transaction_objects()
.
Important
If you come up with a new functionality for the PyOTA API, please raise an issue in the PyOTA Bug Tracker to facilitate discussion.
Once the community agrees on your proposal, you may start implementing a new extended API method and the corresponding extended PyOTA command.
Contributions are always welcome! :)
Visit the Contributing to PyOTA page to find out how you can make a difference!
Tutorials¶
Are you new to IOTA in Python? Don’t worry, we got you covered! With the walkthrough examples of this section, you will be a master of PyOTA.
In each section below, a code snippet will be shown and discussed in detail to help you understand how to carry out specific tasks with PyOTA.
The example scripts displayed here can also be found under examples/tutorials/
directory in the repository. Run them in a Python environment that has PyOTA
installed. See Install PyOTA for more info.
If you feel that something is missing or not clear, please post your questions and suggestions in the PyOTA Bug Tracker.
Let’s get to it then!
1. Hello Node¶
In this example, you will learn how to:
Import the
iota
package into your application.Instantiate an API object for communication with the IOTA network.
Request information about the IOTA node you are connected to.
Code¶
1 2 3 4 5 6 7 8 9 10 11 12 | # Import neccessary modules
from iota import Iota
from pprint import pprint
# Declare an API object
api = Iota('https://nodes.devnet.iota.org:443')
# Request information about the node
response = api.get_node_info()
# Using pprint instead of print for a nicer looking result in the console
pprint(response)
|
Discussion¶
1 2 3 | # Import neccessary modules
from iota import Iota
from pprint import pprint
|
First things first, we need to import in our application the modules we intend
to use. PyOTA provide the iota
package, therefore, whenever you need
something from the library, you need to import it from there.
Notice, how we import the Iota
object, that defines a
so-called extended API object. We will use this to send and receive data from
the network. Read more about API objects at PyOTA API Classes.
We also import the pprint
method that prettifies the output before printing
it to the console.
5 6 | # Declare an API object
api = Iota('https://nodes.devnet.iota.org:443')
|
Next, we declare an API object. Since this object handles the communication, we need to specify an IOTA node to connect to in the form of an URI. Note, that the library will parse this string and will throw an exception if it is not a valid one.
8 9 | # Request information about the node
response = api.get_node_info()
|
Then we can call the Iota.get_node_info()
method of the API
object to get some basic info about the node.
11 12 | # Using pprint instead of print for a nicer looking result in the console
pprint(response)
|
Finally, we print out the response. It is important to note, that all API
methods return a python dictionary. Refer to the method’s documentation to
determine what exactly is in the response dict
. Here for example,
we could list the features
of the node:
pprint(response['features'])
2. Send Data¶
In this example, you will learn how to:
Encode data to be stored on the Tangle.
Generate a random IOTA address that doesn’t belong to anyone.
Create a zero-value transaction with custom payload.
Send a transaction to the network.
Code¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | from iota import Iota, TryteString, Address, Tag, ProposedTransaction
from pprint import pprint
# Declare an API object
api = Iota('https://nodes.devnet.iota.org:443', devnet=True)
# Prepare custom data
my_data = TryteString.from_unicode('Hello from the Tangle!')
# Generate a random address that doesn't have to belong to anyone
my_address = Address.random()
# Tag is optional here
my_tag = Tag(b'MY9FIRST9TAG')
# Prepare a transaction object
tx = ProposedTransaction(
address=my_address,
value=0,
tag=my_tag,
message=my_data
)
# Send the transaction to the network
response = api.send_transfer([tx])
pprint('Check your transaction on the Tangle!')
pprint('https://utils.iota.org/transaction/%s/devnet' % response['bundle'][0].hash)
|
Discussion¶
1 2 3 4 5 | from iota import Iota, TryteString, Address, Tag, ProposedTransaction
from pprint import pprint
# Declare an API object
api = Iota('https://nodes.devnet.iota.org:443', devnet=True)
|
We have seen this part before. Note, that now we import more objects which we will use to construct our transaction.
Notice devnet=True
in the argument list of the API instantiation. We
tell the API directly that we will use IOTA’s testnet, known as the devnet.
By default, the API is configured for the mainnet.
7 8 | # Prepare custom data
my_data = TryteString.from_unicode('Hello from the Tangle!')
|
If you read Basic Concepts and PyOTA Types, it shouldn’t be a
surprise to you that most things in IOTA are represented as trytes, that are
TryteString
in PyOTA.
Here, we encode our message with TryteString.from_unicode()
into
trytes.
10 11 | # Generate a random address that doesn't have to belong to anyone
my_address = Address.random()
|
To put anything (transactions) on the Tangle, it needs to be associated with
an address. Since we will be posting a zero-value transaction, nobody has to
own this address; therefore we can use the TryteString.random()
(an
Address
is just a TryteString
with some additional
attributes and fixed length) method to generate one.
13 14 | # Tag is optional here
my_tag = Tag(b'MY9FIRST9TAG')
|
To tag our transaction, we might define a custom Tag
object.
Notice, that the b
means we are passing a bytestring value instead of a
unicode string. This is so that PyOTA interprets our input as literal trytes,
rather than a unicode string that needs to be encoded into trytes.
When passing a bytestring to a PyOTA class, each byte is interpreted as a tryte; therefore we are restricted to the tryte alphabet.
16 17 18 19 20 21 22 | # Prepare a transaction object
tx = ProposedTransaction(
address=my_address,
value=0,
tag=my_tag,
message=my_data
)
|
It’s time to construct the transaction. According to Transaction Types,
PyOTA uses ProposedTransaction
to build transactions that are not
yet broadcast to the network. Oberve, that the value=0
means this is
a zero-value transaction.
24 25 | # Send the transaction to the network
response = api.send_transfer([tx])
|
Next, we send the transfer to the node for tip selection,
proof-of-work calculation, broadcasting and storing. The API takes care of
all these tasks, and returns the resulting Bundle
object.
Note
send_transfer()
takes a list of ProposedTransaction
objects as its transfers
argument. An IOTA transfer (bundle) usually
consists of multiple transactions linked together, however, in this simple
example, there is only one transaction in the bundle. Regardless, you need
to pass this sole transaction as a list of one transaction.
27 28 | pprint('Check your transaction on the Tangle!')
pprint('https://utils.iota.org/transaction/%s/devnet' % response['bundle'][0].hash)
|
Finally, we print out the transaction’s link on the Tangle Explorer.
Observe how we extract the transaction hash from the response dict
. We take
the first element of the bundle, as it is just a sequence of transactions, and
access its hash
attribute.
3. Fetch Data¶
In this example, you will learn how to:
Fetch transaction objects from the Tangle based on a criteria.
Decode messages from transactions.
Code¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | from iota import Iota, Address
from iota.codecs import TrytesDecodeError
# Declare an API object
api = Iota('https://nodes.devnet.iota.org:443', devnet=True)
# Address to fetch data from
# Replace with your random generated address from Tutorial 2. to fetch the data
# that you uploaded.
addy = Address(b'WWO9DRAUDDSDSTTUPKJRNPSYLWAVQBBXISLKLTNDPVKOPMUERDUELLUPHNT9L9YWBDKOLYVWRAFRKIBLP')
print('Fetching data from the Tangle...')
# Fetch the transaction objects of the address from the Tangle
response = api.find_transaction_objects(addresses=[addy])
if not response['transactions']:
print('Couldn\'t find data for the given address.')
else:
print('Found:')
# Iterate over the fetched transaction objects
for tx in response['transactions']:
# data is in the signature_message_fragment attribute as trytes, we need
# to decode it into a unicode string
data = tx.signature_message_fragment.decode(errors='ignore')
print(data)
|
Discussion¶
1 2 3 4 5 | from iota import Iota, Address
from iota.codecs import TrytesDecodeError
# Declare an API object
api = Iota('https://nodes.devnet.iota.org:443', devnet=True)
|
The usual part again, but we also import TrytesDecodeError
from
iota.codec
. We will use this to detect if the fetched trytes contain
encoded text.
7 8 9 10 | # Address to fetch data from
# Replace with your random generated address from Tutorial 2. to fetch the data
# that you uploaded.
addy = Address(b'WWO9DRAUDDSDSTTUPKJRNPSYLWAVQBBXISLKLTNDPVKOPMUERDUELLUPHNT9L9YWBDKOLYVWRAFRKIBLP')
|
We declare an IOTA address on the Tangle to fetch data from. You can replace this address with your own from the previous example 2. Send Data, or just run it as it is.
12 13 14 | print('Fetching data from the Tangle...')
# Fetch the transaction objects of the address from the Tangle
response = api.find_transaction_objects(addresses=[addy])
|
We use find_transaction_objects()
extended API method to gather
the transactions that belong to our address. This method is also capable of
returning Transaction
objects for bundle hashes, tags or approving
transactions. Note that you can supply multiple of these, the method always
returns a dict
with a list of transactions.
Note
Remember, that the parameters need to be supplied as lists, even if there is only one value.
16 17 18 19 20 21 22 23 24 25 | if not response['transactions']:
print('Couldn\'t find data for the given address.')
else:
print('Found:')
# Iterate over the fetched transaction objects
for tx in response['transactions']:
# data is in the signature_message_fragment attribute as trytes, we need
# to decode it into a unicode string
data = tx.signature_message_fragment.decode(errors='ignore')
print(data)
|
Finally, we extract the data we are looking for from the transaction objects.
A Transaction
has several attributes, one of which is the
signature_message_fragment
. This contains the payload message for zero-value
transactions, and the digital signature that authorizes spending for value
transactions.
Since we are interested in data now, we decode its content (raw trytes) into
text. Notice, that we pass the errors='ignore'
argument to the decode()
method to drop values we can’t decode using utf-8
, or if the raw trytes
can’t be decoded into legit bytes. A possible reason for the latter can be if
the attribute contains a signature rather than a message.
4.a Generate Address¶
In this example, you will learn how to:
Generate a random seed.
Generate an IOTA address that belongs to your seed.
Acquire free devnet IOTA tokens that you can use to play around with.
Code¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | from iota import Iota, Seed
# Generate a random seed, or use one you already have (for the devnet)
print('Generating a random seed...')
my_seed = Seed.random()
# my_seed = Seed(b'MYCUSTOMSEED')
print('Your seed is: ' + str(my_seed))
# Declare an API object
api = Iota(
adapter='https://nodes.devnet.iota.org:443',
seed=my_seed,
devnet=True,
)
print('Generating the first unused address...')
# Generate the first unused address from the seed
response = api.get_new_addresses()
addy = response['addresses'][0]
print('Your new address is: ' + str(addy))
print('Go to https://faucet.devnet.iota.org/ and enter you address to receive free devnet tokens.')
|
Discussion¶
1 2 3 4 5 6 7 | from iota import Iota, Seed
# Generate a random seed, or use one you already have (for the devnet)
print('Generating a random seed...')
my_seed = Seed.random()
# my_seed = Seed(b'MYCUSTOMSEED')
print('Your seed is: ' + str(my_seed))
|
We start off by generating a random seed with the help of the library. You are also free to use your own seed, just uncomment line 6 and put it there.
If you choose to generate one, your seed is written to the console so that you can save it for later. Be prepared to do so, because you will have to use it in the following tutorials.
9 10 11 12 13 14 | # Declare an API object
api = Iota(
adapter='https://nodes.devnet.iota.org:443',
seed=my_seed,
devnet=True,
)
|
Notice, how we pass the seed
argument to the API class’s init method.
Whenever the API needs to work with addresses or private keys, it will derive
them from this seed.
Important
Your seed never leaves the library and your computer. Treat your (mainnet) seed like any other password for a financial service: safe. If your seed is compromised, attackers can steal your funds.
16 17 18 19 20 | print('Generating the first unused address...')
# Generate the first unused address from the seed
response = api.get_new_addresses()
addy = response['addresses'][0]
|
To generate a new address, we call get_new_addresses()
extended API method. Without arguments, this will return a dict
with the
first unused address starting from index
0. An unused address is address
that has no transactions referencing it on the Tangle and was never spent from.
If we were to generate more addresses starting from a desired index,
we could specify the start
and count
parameters. Read more about how to
generate addresses in PyOTA at Generating Addresses.
On line 20 we access the first element of the list of addresses in the response dictionary.
22 23 | print('Your new address is: ' + str(addy))
print('Go to https://faucet.devnet.iota.org/ and enter you address to receive free devnet tokens.')
|
Lastly, the address is printed to the console, so that you can copy it. Visit https://faucet.devnet.iota.org/ and enter the address to receive free devnet tokens of 1000i.
You might need to wait 1-2 minutes until the sum arrives to you address. To check your balance, go to 4.b Check Balance or 4.c Get Account Data.
4.b Check Balance¶
In this example, you will learn how to:
Check the balance of a specific IOTA address.
Code¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | from iota import Iota, Address
import time
# Put your address from Tutorial 4.a here
my_address = Address(b'YOURADDRESSFROMTHEPREVIOUSTUTORIAL')
# Declare an API object
api = Iota(adapter='https://nodes.devnet.iota.org:443', devnet=True)
# Script actually runs until you load up your address
success = False
while not success:
print('Checking balance on the Tangle for a specific address...')
# API method to check balance
response = api.get_balances(addresses=[my_address])
# response['balances'] is a list!
if response['balances'][0]:
print('Found the following information for address ' + str(my_address) + ':')
print('Balance: ' + str(response['balances'][0]) + 'i')
success = True
else:
print('Zero balance found, retrying in 30 seconds...')
time.sleep(30)
|
Discussion¶
1 2 3 4 5 6 7 8 | from iota import Iota, Address
import time
# Put your address from Tutorial 4.a here
my_address = Address(b'YOURADDRESSFROMTHEPREVIOUSTUTORIAL')
# Declare an API object
api = Iota(adapter='https://nodes.devnet.iota.org:443', devnet=True)
|
The first step to check the balance of an address is to actually have an address. Exchange the sample address on line 5 with your generated address from 4.a Generate Address.
Since we don’t need to generate an address, there is no need for a seed to be
employed in the API object. Note the time
import, we need it for later.
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | # Script actually runs until you load up your address
success = False
while not success:
print('Checking balance on the Tangle for a specific address...')
# API method to check balance
response = api.get_balances(addresses=[my_address])
# response['balances'] is a list!
if response['balances'][0]:
print('Found the following information for address ' + str(my_address) + ':')
print('Balance: ' + str(response['balances'][0]) + 'i')
success = True
else:
print('Zero balance found, retrying in 30 seconds...')
time.sleep(30)
|
Our script will poll the network for the address balance as long as the returned
balance is zero. Therefore, the address you declared as my_address
should
have some balance. If you see the Zero balance found...
message a couple of
times, head over to https://faucet.devnet.iota.org/ and load up your address.
get_balances()
returns the confirmed balance of the address.
You could supply multiple addresses at the same time and get their respective
balances in a single call. Don’t forget, that the method returns a dict
.
More details about it can be found at get_balances()
.
4.c Get Account Data¶
In this example, you will learn how to:
Gather addresses, balance and bundles associated with your seed on the Tangle.
Warning
Account in the context of this example is not to be confused with the Account Module, that is a feature yet to be implemented in PyOTA.
Account here simply means the addresses and funds that belong to your seed.
Code¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | from iota import Iota, Seed
from pprint import pprint
import time
# Put your seed from Tutorial 4.a here
my_seed = Seed(b'YOURSEEDFROMTHEPREVIOUSTUTORIAL99999999999999999999999999999999999999999999999999')
# Declare an API object
api = Iota(
adapter='https://nodes.devnet.iota.org:443',
seed=my_seed,
devnet=True
)
# Script actually runs until it finds balance
success = False
while not success:
print('Checking account information on the Tangle...')
# Gather addresses, balance and bundles
response = api.get_account_data()
# response['balance'] is an integer!
if response['balance']:
print('Found the following information based on your seed:')
pprint(response)
success = True
else:
print('Zero balance found, retrying in 30 seconds...')
time.sleep(30)
|
Discussion¶
1 2 3 | from iota import Iota, Seed
from pprint import pprint
import time
|
We will need pprint
for a prettified output of the response dict
and
time
for polling until we find non-zero balance.
5 6 7 8 9 10 11 12 13 | # Put your seed from Tutorial 4.a here
my_seed = Seed(b'YOURSEEDFROMTHEPREVIOUSTUTORIAL99999999999999999999999999999999999999999999999999')
# Declare an API object
api = Iota(
adapter='https://nodes.devnet.iota.org:443',
seed=my_seed,
devnet=True
)
|
Copy your seed from 4.a Generate Address onto line 6. The API will use your seed to generate addresses and look for corresponding transactions on the Tangle.
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | # Script actually runs until it finds balance
success = False
while not success:
print('Checking account information on the Tangle...')
# Gather addresses, balance and bundles
response = api.get_account_data()
# response['balance'] is an integer!
if response['balance']:
print('Found the following information based on your seed:')
pprint(response)
success = True
else:
print('Zero balance found, retrying in 30 seconds...')
time.sleep(30)
|
Just like in the previous example, we will poll for information until we find
a non-zero balance. get_account_data()
without arguments
generates addresses from index
0 until it finds the first unused. Then, it
queries the node about bundles of those addresses and sums up their balance.
Note
If you read get_account_data()
documentation carefully, you
notice that you can gain control over which addresses are checked during
the call by specifying the start
and stop
index parameters.
This can be useful when your addresses with funds do not follow each other in the address namespace, or a snapshot removed transactions from the Tangle. It is recommended that you keep a local database of your already used address indices.
Once implemented in PyOTA, Account Module will address the aforementioned problems.
The response dict
contains the addresses, bundles and total balance of
your seed.
5. Send Tokens¶
In this example, you will learn how to:
Construct a value transfer with PyOTA.
Send a value transfer to an arbitrary IOTA address.
Analyze a bundle of transactions on the Tangle.
Note
As a prerequisite to this tutorial, you need to have completed 4.a Generate Address, and have a seed that owns devnet tokens.
Code¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | from iota import Iota, Seed, Address, TryteString, ProposedTransaction, Tag
# Put your seed here from Tutorial 4.a, or a seed that owns tokens (devnet)
my_seed = Seed(b'YOURSEEDFROMTHEPREVIOUSTUTORIAL')
# Declare an API object
api = Iota(
adapter='https://nodes.devnet.iota.org:443',
seed=my_seed,
devnet=True,
)
# Addres to receive 1i
# Feel free to replace it. For example, run the code from Tutorial 4.a
# and use that newly generated address with a 'fresh' seed.
receiver = Address(b'WWUTQBO99YDCBVBPAPVCANW9ATSNUPPLCPGDQXGQEVLUBSFHCEWOA9DIYYOXJONDIRHYPXQXOYXDPHREZ')
print('Constructing transfer of 1i...')
# Create the transfer object
tx = ProposedTransaction(
address=receiver,
value=1,
message=TryteString.from_unicode('I just sent you 1i, use it wisely!'),
tag=Tag('VALUETX'),
)
print('Preparing bundle and sending it to the network...')
# Prepare the transfer and send it to the network
response = api.send_transfer(transfers=[tx])
print('Check your transaction on the Tangle!')
print('https://utils.iota.org/bundle/%s/devnet' % response['bundle'].hash)
|
Discussion¶
1 2 3 4 5 6 7 8 9 10 11 | from iota import Iota, Seed, Address, TryteString, ProposedTransaction, Tag
# Put your seed here from Tutorial 4.a, or a seed that owns tokens (devnet)
my_seed = Seed(b'YOURSEEDFROMTHEPREVIOUSTUTORIAL')
# Declare an API object
api = Iota(
adapter='https://nodes.devnet.iota.org:443',
seed=my_seed,
devnet=True,
)
|
We are going to send a value transaction, that requires us to prove that we own the address containg the funds to spend. Therefore, we need our seed from which the address was generated.
Put your seed from 4.a Generate Address onto line 4. We pass this seed to the API object, that will utilize it for signing the transfer.
13 14 15 16 | # Addres to receive 1i
# Feel free to replace it. For example, run the code from Tutorial 4.a
# and use that newly generated address with a 'fresh' seed.
receiver = Address(b'WWUTQBO99YDCBVBPAPVCANW9ATSNUPPLCPGDQXGQEVLUBSFHCEWOA9DIYYOXJONDIRHYPXQXOYXDPHREZ')
|
In IOTA, funds move accross addresses, therefore we need to define a receiver address. For testing value transfers, you should send the funds only to addresses that you control; if you use a randomly-generated receiver address, you won’t be able to recover the funds afterward! Re-run 4.a Generate Address for a new seed and a new address, or just paste a valid IOTA address that you own onto line 16.
18 19 20 21 22 23 24 25 | print('Constructing transfer of 1i...')
# Create the transfer object
tx = ProposedTransaction(
address=receiver,
value=1,
message=TryteString.from_unicode('I just sent you 1i, use it wisely!'),
tag=Tag('VALUETX'),
)
|
We declare a ProposedTransaction
object like we did before, but
this time, with value=1
parameter. The smallest value you can send is 1
iota (“1i”), there is no way to break it into smaller chunks. It is a really small
value anyway. You can also attach a message to the transaction, for example a
little note to the beneficiary of the payment.
27 28 29 | print('Preparing bundle and sending it to the network...')
# Prepare the transfer and send it to the network
response = api.send_transfer(transfers=[tx])
|
To actually send the transfer, all you need to do is call
send_transfer()
extended API method. This method will take care
of:
Gathering
inputs
(addresses you own and have funds) to fund the 1i transfer.Generating a new
change_address
, and automatically sending the remaining funds (balance of chosen inputs
- 1i) frominputs
tochange_address
.Warning
This step is extremely important, as it prevents you from spending twice from the same address.
When an address is used as an input, all tokens will be withdrawn. Part of the tokens will be used to fund your transaction, the rest will be transferred to
change_address
.Constructing the transfer bundle with necessary input and output transactions.
Finalizing the bundle and signing the spending transactions.
Doing proof-of-work for each transaction in the bundle and sending it to the network.
31 32 | print('Check your transaction on the Tangle!')
print('https://utils.iota.org/bundle/%s/devnet' % response['bundle'].hash)
|
Open the link and observe the bundle you have just sent to the Tangle. Probably it will take a couple of seconds for the network to confirm it.
What you see is a bundle with 4 transactions in total, 1 input and 3 outputs. But why are there so many transactions?
There is one transaction that withdraws iotas, this has negative value. To authorize this spending, a valid signature is included in the transaction’s
signature_message_fragment
field. The signature however is too long to fit into one transaction, therefore the library appends a new, zero-value transaction to the bundle that holds the second part of the signature. This you see on the output side of the bundle.A 1i transaction to the receiver address spends part of the withdrawn amount.
The rest is transfered to
change_address
in a new output transaction.
Once the bundle is confirmed, try rerunning the script from
4.c Get Account Data with the same seed as in this tutorial. Your balance
should be decremented by 1i, and you should see a new address, which was
actually the change_address
.
6. Store Encrypted Data¶
In this example, you will learn how to:
Convert Python data structures to JSON format.
Encrypt data and include it in a zero-value transaction.
Store the zero-value transaction with encrypted data on the Tangle.
Warning
We will use the simple-crypt
external library for encryption/decryption.
Before proceeding to the tutorial, make sure you install it by running:
pip install simple-crypt
Code¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | """
Encrypt data and store it on the Tangle.
simplecrypt library is needed for this example (`pip install simple-crypt`)!
"""
from iota import Iota, TryteString, Tag, ProposedTransaction
from simplecrypt import encrypt
from base64 import b64encode
from getpass import getpass
import json
# Declare an API object
api = Iota(
adapter='https://nodes.devnet.iota.org:443',
seed=b'YOURSEEDFROMTHEPREVIOUSTUTORIAL',
devnet=True,
)
# Some confidential information
data = {
'name' : 'James Bond',
'age' : '32',
'job' : 'agent',
'address' : 'London',
}
# Convert to JSON format
json_data = json.dumps(data)
# Ask user for a password to use for encryption
password = getpass('Please supply a password for encryption:')
print('Encrypting data...')
# Encrypt data
# Note, that in Python 3, encrypt returns 'bytes'
cipher = encrypt(password, json_data)
# Encode to base64, output contains only ASCII chars
b64_cipher = b64encode(cipher)
print('Constructing transaction locally...')
# Convert to trytes
trytes_encrypted_data = TryteString.from_bytes(b64_cipher)
# Generate an address from your seed to post the transfer to
my_address = api.get_new_addresses(index=42)['addresses'][0]
# Tag is optional here
my_tag = Tag(b'CONFIDENTIALINFORMATION')
# Prepare a transaction object
tx = ProposedTransaction(
address=my_address,
value=0,
tag=my_tag,
message=trytes_encrypted_data,
)
print('Sending transfer...')
# Send the transaction to the network
response = api.send_transfer([tx])
print('Check your transaction on the Tangle!')
print('https://utils.iota.org/transaction/%s/devnet' % response['bundle'][0].hash)
print('Tail transaction hash of the bundle is: %s' % response['bundle'].tail_transaction.hash)
|
Discussion¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | """
Encrypt data and store it on the Tangle.
simplecrypt library is needed for this example (`pip install simple-crypt`)!
"""
from iota import Iota, TryteString, Tag, ProposedTransaction
from simplecrypt import encrypt
from base64 import b64encode
from getpass import getpass
import json
# Declare an API object
api = Iota(
adapter='https://nodes.devnet.iota.org:443',
seed=b'YOURSEEDFROMTHEPREVIOUSTUTORIAL',
devnet=True,
)
|
We will use the encrypt
method to encipher the data, and b64encode
for
representing it as ASCII characters. getpass
will prompt the user for a
password, and the json
library is used for JSON formatting.
We will need an address to upload the data, therefore we need to supply the
seed to the Iota
API instance. The address will be generated from this
seed.
20 21 22 23 24 25 26 | # Some confidential information
data = {
'name' : 'James Bond',
'age' : '32',
'job' : 'agent',
'address' : 'London',
}
|
The data to be stored is considered confidential information, therefore we can’t just put it on the Tangle as plaintext so everyone can read it. Think of what would happen if the world’s most famous secret agent’s identity was leaked on the Tangle…
28 29 | # Convert to JSON format
json_data = json.dumps(data)
|
Notice, that data
is a Python dict
object. As a common way of exchanging
data on the web, we would like to convert it to JSON format. The json.dumps()
method does exactly that, and the result is a JSON formatted plaintext.
31 32 33 34 35 36 37 38 39 40 | # Ask user for a password to use for encryption
password = getpass('Please supply a password for encryption:')
print('Encrypting data...')
# Encrypt data
# Note, that in Python 3, encrypt returns 'bytes'
cipher = encrypt(password, json_data)
# Encode to base64, output contains only ASCII chars
b64_cipher = b64encode(cipher)
|
Next, we will encrypt this data with a secret password we obtain from the user.
Note
When you run this example, please remember the password at least until the next tutorial!
The output of the encrypt
method is a bytes
object in Python3 and
contains many special characters. This is a problem, since we can only convert
ASCII characters from bytes
directly into TryteString
.
Therefore, we first encode our binary data into ASCII characters with Base64 encoding.
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | print('Constructing transaction locally...')
# Convert to trytes
trytes_encrypted_data = TryteString.from_bytes(b64_cipher)
# Generate an address from your seed to post the transfer to
my_address = api.get_new_addresses(index=42)['addresses'][0]
# Tag is optional here
my_tag = Tag(b'CONFIDENTIALINFORMATION')
# Prepare a transaction object
tx = ProposedTransaction(
address=my_address,
value=0,
tag=my_tag,
message=trytes_encrypted_data,
)
|
Now, we are ready to construct the transfer. We convert the encrypted Base64
encoded data to trytes and assign it to the ProposedTransaction
object’s message
argument.
An address is also needed, so we generate one with the help of
get_new_addresses()
extended API method. Feel free to choose the
index of the generated address, and don’t forget, that the method returns a
dict
with a list of addresses, even if it contains only one.
For more detailed explanation on how addresses are generated in PyOTA,
refer to the adresses:Generating Addresses page.
We also attach a custom Tag
to our ProposedTransaction
.
Note, that if our trytes_encrypted_data
was longer than the maximum payload
of a transaction, the library would split it accross more transactions that
together form the transfer bundle.
60 61 62 63 64 65 66 | print('Sending transfer...')
# Send the transaction to the network
response = api.send_transfer([tx])
print('Check your transaction on the Tangle!')
print('https://utils.iota.org/transaction/%s/devnet' % response['bundle'][0].hash)
print('Tail transaction hash of the bundle is: %s' % response['bundle'].tail_transaction.hash)
|
Finally, we use Iota.send_transfer()
to prepare the transfer and
send it to the network.
Click on the link to check your transaction on the Tangle Explorer.
The tail transaction (a tail transaction is the one with index 0 in the bundle) hash is printed on the console, because you will need it in the next tutorial, and anyway, it is a good practice to keep a reference to your transfers.
In the next example, we will try to decode the confidential information from the Tangle.
7. Fetch Encrypted Data¶
In this example, you will learn how to:
Fetch bundles from the Tangle based on their tail transaction hashes.
Extract messages from a bundle.
Decrypt encrypted messages from a bundle.
Warning
We will use the simple-crypt
external library for encryption/decryption.
Before proceeding to the tutorial, make sure you install it by running:
pip install simple-crypt
Code¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | """
Decrypt data fetched from the Tangle.
simplecrypt library is needed for this example (`pip install simple-crypt`)!
"""
from iota import Iota
from simplecrypt import decrypt
from base64 import b64decode
from getpass import getpass
import json
# Declare an API object
api = Iota('https://nodes.devnet.iota.org:443', devnet=True)
# Prompt user for tail tx hash of the bundle
tail_hash = input('Tail transaction hash of the bundle: ')
print('Looking for bundle on the Tangle...')
# Fetch bundle
bundle = api.get_bundles([tail_hash])['bundles'][0]
print('Extracting data from bundle...')
# Get all messages from the bundle and concatenate them
b64_encrypted_data = "".join(bundle.get_messages())
# Decode from base64
encrypted_data = b64decode(b64_encrypted_data)
# Prompt for passwword
password = getpass('Password to be used for decryption:')
print('Decrypting data...')
# Decrypt data
# decrypt returns 'bytes' in Python 3, decode it into string
json_data = decrypt(password, encrypted_data).decode('utf-8')
# Convert JSON string to python dict object
data = json.loads(json_data)
print('Succesfully decrypted the following data:')
print(data)
|
Discussion¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | """
Decrypt data fetched from the Tangle.
simplecrypt library is needed for this example (`pip install simple-crypt`)!
"""
from iota import Iota
from simplecrypt import decrypt
from base64 import b64decode
from getpass import getpass
import json
# Declare an API object
api = Iota('https://nodes.devnet.iota.org:443', devnet=True)
|
In contrast to 6. Store Encrypted Data where we intended to encrypt data, in
this tutorial we will do the reverse, and decrypt data from the Tangle.
Therefore, we need the decrypt
method from simplecrypt
library and the
b64decode
method from base64
library.
Furthermore, getpass
is needed to prompt the user for a decryption
password, and json
for deserializing JSON formatted string into Python
object.
16 17 | # Prompt user for tail tx hash of the bundle
tail_hash = input('Tail transaction hash of the bundle: ')
|
To fetch transactions or bundles from the Tangle, a reference is required to retreive them from the network. Transactions are identified by their transaction hash, while a group of transaction (a bundle) by bundle hash. Hashes ensure the integrity of the Tangle, since they contain verifiable information about the content of the transfer objects.
input()
asks the user to give the tail transaction hash of the bundle
that holds the encrypted messages. The tail transaction is the first in the
bundle with index 0. Copy and paste the tail transaction hash from the console
output of 6. Store Encrypted Data when prompted.
19 20 21 | print('Looking for bundle on the Tangle...')
# Fetch bundle
bundle = api.get_bundles([tail_hash])['bundles'][0]
|
Next, we fetch the bundle from the Tangle with the help of the
get_bundles()
extended API command. It takes a list of tail
transaction hashes and returns the bundles for each of them. The response
dict
contains a bundles
key with the value being a list of bundles
in the same order as the input argument hashes. Also note, that the bundles
in the response are actual PyOTA Bundle
objects.
To simplify the code, several operations are happening on line 21:
Calling
get_bundles()
that returns adict
,accessing the
'bundles'
key in thedict
,and taking the first element of the the list of bundles in the value associated with the key.
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | print('Extracting data from bundle...')
# Get all messages from the bundle and concatenate them
b64_encrypted_data = "".join(bundle.get_messages())
# Decode from base64
encrypted_data = b64decode(b64_encrypted_data)
# Prompt for passwword
password = getpass('Password to be used for decryption:')
print('Decrypting data...')
# Decrypt data
# decrypt returns 'bytes' in Python 3, decode it into string
json_data = decrypt(password, encrypted_data).decode('utf-8')
# Convert JSON string to python dict object
data = json.loads(json_data)
|
The next step is to extract the content of the message fields of the
transactions in the bundle. We call Bundle.get_messages()
to carry
out this operation. The method returns a list of unicode strings, essentially
the signature_message_fragment
fields of the transactions, decoded from
trytes into unicode characters.
We then combine these message chunks into one stream of characters by using
string.join()
.
We know that at this stage that we can’t make sense of our message, because it is encrypted and encoded into Base64. Let’s peel that onion layer by layer:
On line 28, we decode the message into bytes with
b64decode
.On line 31, we ask the user for thr decryption password (from the previous tutorial).
On line 36, we decrypt the bytes cipher with the password and decode the result into a unicode string.
Since we used JSON formatting in the previous tutorial, there is one additional step to arrive at our original data. On line 39, we deserialize the JSON string into a Python object, namely a
dict
.
41 42 | print('Succesfully decrypted the following data:')
print(data)
|
If everything went according to plan and the user supplied the right password, we should see our original data printed out to the console.
Now you know how to use the Tangle for data storage while keeping privacy. When you need more granular access control on how and when one could read data from the Tangle, consider using Masked Authenticated Messaging (MAM).
8. Send and Monitor Concurrently¶
In this example, you will learn how to:
Use the asynchronous PyOTA API.
Send transactions concurrently.
Monitor confirmation of transactions concurrently.
Execute arbitrary code concurrently while doing the former two.
Warning
If you are new to coroutines and asynchronous programming in Python, it is strongly recommended that you check out this article and the official asyncio documentation before proceeding.
Code¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | from iota import AsyncIota, ProposedTransaction, Address, TryteString
from typing import List
import asyncio
# Asynchronous API instance.
api = AsyncIota(
adapter='https://nodes.devnet.iota.org:443',
devnet=True,
)
# An arbitrary address to send zero-value transactions to.
addy = Address('PZITJTHCIIANKQWEBWXUPHWPWVNBKW9GMNALMGGSIAUOYCKNWDLUUIGAVMJYCHZXHUBRIVPLFZHUVDLME')
# Timeout after which confirmation monitoring stops (seconds).
timeout = 120
# How often should we poll for confirmation? (seconds)
polling_interval = 5
async def send_and_monitor(
transactions: List[ProposedTransaction]
) -> bool:
"""
Send a list of transactions as a bundle and monitor their confirmation
by the network.
"""
print('Sending bundle...')
st_response = await api.send_transfer(transactions)
sent_tx_hashes = [tx.hash for tx in st_response['bundle']]
print('Sent bundle with transactions: ')
print(*sent_tx_hashes, sep='\n')
# Measure elapsed time
elapsed = 0
print('Checking confirmation...')
while len(sent_tx_hashes) > 0:
# Determine if transactions are confirmed
gis_response = await api.get_inclusion_states(sent_tx_hashes, None)
for i, (tx, is_confirmed) in enumerate(zip(sent_tx_hashes, gis_response['states'])):
if is_confirmed:
print('Transaction %s is confirmed.' % tx)
# No need to check for this any more
del sent_tx_hashes[i]
del gis_response['states'][i]
if len(sent_tx_hashes) > 0:
if timeout <= elapsed:
# timeout reached, terminate checking
return False
# Show some progress on the screen
print('.')
# Put on hold for polling_interval. Non-blocking, so you can
# do other stuff in the meantime.
await asyncio.sleep(polling_interval)
elapsed = elapsed + polling_interval
# All transactions in the bundle are confirmed
return True
async def do_something() -> None:
"""
While waiting for confirmation, you can execute arbitrary code here.
"""
for _ in range(5):
print('Doing something in the meantime...')
await asyncio.sleep(2)
async def main() -> None:
"""
A simple application that sends zero-value transactions to the Tangle and
monitors the confirmation by the network. While waiting for the
confirmation, we schedule a task (`do_something()`) to be executed concurrently.
"""
# Transactions to be sent.
transactions = [
ProposedTransaction(
address=addy,
value=0,
message=TryteString.from_unicode('First'),
),
ProposedTransaction(
address=addy,
value=0,
message=TryteString.from_unicode('Second'),
),
ProposedTransaction(
address=addy,
value=0,
message=TryteString.from_unicode('Third'),
),
]
# Schedule coroutines as tasks, wait for them to finish and gather their
# results.
result = await asyncio.gather(
send_and_monitor(transactions),
# Send the same content. Bundle will be different!
send_and_monitor(transactions),
do_something(),
)
if not (result[0] and result[1]):
print('Transactions did not confirm after %s seconds!' % timeout)
else:
print('All transactions are confirmed!')
if __name__ == '__main__':
# Execute main() inside an event loop if the file is ran
asyncio.run(main())
|
Discussion¶
This example is divided into 4 logical parts:
Let’s start with the most simple one: Imports and Constants.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | from iota import AsyncIota, ProposedTransaction, Address, TryteString
from typing import List
import asyncio
# Asynchronous API instance.
api = AsyncIota(
adapter='https://nodes.devnet.iota.org:443',
devnet=True,
)
# An arbitrary address to send zero-value transactions to.
addy = Address('PZITJTHCIIANKQWEBWXUPHWPWVNBKW9GMNALMGGSIAUOYCKNWDLUUIGAVMJYCHZXHUBRIVPLFZHUVDLME')
# Timeout after which confirmation monitoring stops (seconds).
timeout = 120
# How often should we poll for confirmation? (seconds)
polling_interval = 5
|
Notice, that we import the AsyncIota
api class, because we
would like to use the asynchronous and concurrent features of PyOTA.
List
from the typing
library is needed for correct
type annotations, and we also import the asyncio library. This will come
in handy when we want to schedule and run the coroutines.
On line 6, we instantiate an asynchronous IOTA api. Functionally, it does the
same operations as Iota
, but the api calls are defined as
coroutines. For this tutorial, we connect to a devnet node, and explicitly tell
this as well to the api on line 8.
On line 12, we declare an IOTA address. We will send our zero value transactions to this address. Feel free to change it to your own address.
Once we have sent the transactions, we start monitoring their confirmation by the
network. Confirmation time depends on current network activity, the referenced
tips, etc., therefore we set a timeout
of 120 seconds on line 15. You might
have to modify this value later to see the confirmation of your transactions.
You can also fine-tune the example code by tinkering with polling_interval
.
This is the interval between two subsequent confirmation checks.
Let’s move on to the next block, namely the send and monitor coroutine.
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | async def send_and_monitor(
transactions: List[ProposedTransaction]
) -> bool:
"""
Send a list of transactions as a bundle and monitor their confirmation
by the network.
"""
print('Sending bundle...')
st_response = await api.send_transfer(transactions)
sent_tx_hashes = [tx.hash for tx in st_response['bundle']]
print('Sent bundle with transactions: ')
print(*sent_tx_hashes, sep='\n')
# Measure elapsed time
elapsed = 0
print('Checking confirmation...')
while len(sent_tx_hashes) > 0:
# Determine if transactions are confirmed
gis_response = await api.get_inclusion_states(sent_tx_hashes, None)
for i, (tx, is_confirmed) in enumerate(zip(sent_tx_hashes, gis_response['states'])):
if is_confirmed:
print('Transaction %s is confirmed.' % tx)
# No need to check for this any more
del sent_tx_hashes[i]
del gis_response['states'][i]
if len(sent_tx_hashes) > 0:
if timeout <= elapsed:
# timeout reached, terminate checking
return False
# Show some progress on the screen
print('.')
# Put on hold for polling_interval. Non-blocking, so you can
# do other stuff in the meantime.
await asyncio.sleep(polling_interval)
elapsed = elapsed + polling_interval
# All transactions in the bundle are confirmed
return True
|
Notice, that coroutines are defined in python by the async def
keywords.
This makes them awaitable.
From the type annotations, we see that send_and_monitor()
accepts a
list of ProposedTransaction
objects and return a bool
.
On line 28, we send the transfers with the help of
AsyncIota.send_transfer()
. Since this is not a regular method, but a
coroutine, we have to await
its result. AsyncIota.send_transfer()
takes care of building the bundle, doing proof-of-work and sending the
transactions within the bundle to the network.
Once we sent the transfer, we collect individual transaction hashes from the bundle, which we will use for confirmation checking.
On line 39, the so-called confirmation checking starts. With the help of
AsyncIota.get_inclusion_states()
, we determine if our transactions
have been confirmed by the network.
Note
You might wonder how your transactions get accepted by the network, that is, how they become confirmed.
Pre-Coordicide (current state), transactions are confirmed by directly or indirectly being referenced by a milestone. A milestone is a special transaction issued by the Coordinator.
Post-Coordicide , confirmation is the result of nodes reaching consensus by a voting mechanism.
The None
value for the tips
parameter in the argument list basically means that we check against the latest
milestone.
On line 43, we iterate over our original sent_tx_hashes
list of sent
transaction hashes and gis_response['states']
, which is a list of bool
values, at the same time using the built-in zip method. We also employ
enumerate, because we need the index of the elements in each iteration.
If a transaction is confirmed, we delete the corresponding elements from the
lists. When all transactions are confirmed, sent_tx_hashes
becomes empty,
and the loop condition becomes False
.
If however, not all transactions have been confirmed, we should continue
checking for confirmation. Observe line 58, where we suspend the coroutine
with asyncio.sleep()
for polling_interval
seconds. Awaiting the
result of asyncio.sleep()
will cause our coroutine to continue
execution in polling_interval
time. While our coroutine is sleeping,
other coroutines can run concurrently, hence it is a non-blocking call.
To do something in the meantime, we can execute another coroutine concurrently:
65 66 67 68 69 70 71 | async def do_something() -> None:
"""
While waiting for confirmation, you can execute arbitrary code here.
"""
for _ in range(5):
print('Doing something in the meantime...')
await asyncio.sleep(2)
|
This is really just a dummy coroutine that prints something to the terminal and then goes to sleep periodically, but in a real application, you could do meaningful tasks here.
Now let’s look at how to schedule the execution of our application with the main coroutine:
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | async def main() -> None:
"""
A simple application that sends zero-value transactions to the Tangle and
monitors the confirmation by the network. While waiting for the
confirmation, we schedule a task (`do_something()`) to be executed concurrently.
"""
# Transactions to be sent.
transactions = [
ProposedTransaction(
address=addy,
value=0,
message=TryteString.from_unicode('First'),
),
ProposedTransaction(
address=addy,
value=0,
message=TryteString.from_unicode('Second'),
),
ProposedTransaction(
address=addy,
value=0,
message=TryteString.from_unicode('Third'),
),
]
# Schedule coroutines as tasks, wait for them to finish and gather their
# results.
result = await asyncio.gather(
send_and_monitor(transactions),
# Send the same content. Bundle will be different!
send_and_monitor(transactions),
do_something(),
)
if not (result[0] and result[1]):
print('Transactions did not confirm after %s seconds!' % timeout)
else:
print('All transactions are confirmed!')
if __name__ == '__main__':
# Execute main() inside an event loop if the file is ran
asyncio.run(main())
|
First, we declare a list of ProposedTransaction()
objects, that will
be the input for our send_and_monitor()
coroutine.
The important stuff begins on line 101. We use asyncio.gather()
to
submit our coroutines for execution, wait for their results and then return
them in a list. gather takes our coroutines, transforms them into runnable
tasks, and runs them concurrently.
Notice, that we listed send_and_monitor()
twice in
asyncio.gather()
with the same list of ProposedTransaction()
objects. This is to showcase how you can send and monitor multiple transfers
concurrently. In this example, two different bundles will be created from the
same ProposedTransaction()
objects. The two bundles post zero value
transactions to the same address, contain the same messages respectively,
but are not dependent on each other in any way. That is why we can send them
concurrently.
As discussed previously, result
will be a list of results of the coroutines
submitted to asyncio.gather()
, preserving their order.
result[0]
is the result from the first send_and_monitor()
, and
result[1]
is the result from the second send_and_monitor()
from the
argument list. If any of these are False
, confirmation did not happen
before timeout
.
When you see the message from line 109 in your terminal, try increasing
timeout
, or check the status of the network, maybe there is a temporary
downtime on the devnet due to maintenance.
Lastly, observe lines 113-115. If the current file (python module) is run
from the terminal, we use ayncio.run()
to execute the main coroutine
inside an event loop.
To run this example, navigate to examples/tutorial
inside the cloned
PyOTA repository, or download the source file of Tutorial 8 from GitHub
and run the following in a terminal:
$ python 08_async_send_monitor.py
PyOTA¶
This is the official Python library for the IOTA Core.
It implements both the official API, as well as newly-proposed functionality (such as signing, bundles, utilities and conversion).
Join the Discussion¶
If you want to get involved in the community, need help with getting setup, have any issues related with the library or just want to discuss Blockchain, Distributed Ledgers and IoT with other people, feel free to join our Discord.
If you encounter any issues while using PyOTA, please report them using the PyOTA Bug Tracker.
Dependencies¶
PyOTA is compatible with Python 3.7 and 3.6.
Install PyOTA¶
To install the latest version:
pip install pyota
Optional C Extension¶
PyOTA has an optional C extension that improves the performance of its cryptography features significantly (speedups of 60x are common!).
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¶
Create virtualenv (recommended, but not required).
git clone https://github.com/iotaledger/iota.py.git
pip install -e .
Running Unit Tests¶
To run unit tests after installing from source:
python setup.py test
PyOTA is also compatible with tox, which will run the unit tests in different virtual environments (one for each supported version of Python).
To run the unit tests, it is recommended that you use the -p
argument.
This speeds up the tests by running them in parallel.
Install PyOTA with the test-runner
extra to set up the necessary
dependencies, and then you can run the tests with the tox
command:
pip install -e .[test-runner]
tox -v -p all
Documentation¶
PyOTA’s documentation is available on ReadTheDocs.
If you are installing from source (see above), you can also build the documentation locally:
Install extra dependencies (you only have to do this once):
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]
Switch to the
docs
directory:cd docs
Build the documentation:
make html