From 184708f392011c3d277b6d3ccaddbe7ecbd3d727 Mon Sep 17 00:00:00 2001 From: Damian Melniczuk Date: Wed, 29 Nov 2017 13:14:09 +0100 Subject: [PATCH 1/4] Converted and fixed **adapters** documentation --- docs/adapters.rst | 221 ++++++++++++++++++++++++++++++++++++++++++++++ docs/index.rst | 3 + 2 files changed, 224 insertions(+) create mode 100644 docs/adapters.rst diff --git a/docs/adapters.rst b/docs/adapters.rst new file mode 100644 index 00000000..7551d729 --- /dev/null +++ b/docs/adapters.rst @@ -0,0 +1,221 @@ +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. + +AdapterSpec +----------- + +In a few places in the PyOTA codebase, you may see references to a +meta-type called ``AdapterSpec``. + +``AdapterSpec`` is a placeholder that means "URI or adapter instance". + +For example, 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: ``Iota('http://localhost:14265')`` +- Adapter instance: ``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 +~~~~~~~~~~~ + +.. code:: python + + from iota import Iota + from iota.adapter import HttpAdapter + + # Use HTTP: + api = Iota('http://localhost:14265') + api = Iota(HttpAdapter('http://localhost:14265')) + + # Use HTTPS: + api = Iota('https://service.iotasupport.com:14265') + api = Iota(HttpAdapter('https://service.iotasupport.com:14265')) + +``HttpAdapter`` uses the HTTP protocol to send requests to the node. + +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 +^^^^^^^^^^^^^^^^^^^^^^^ + +.. code:: python + + from logging import getLogger + + from iota import Iota + + api = Iota('http://localhost:14265') + api.adapter.set_logger(getLogger(__name__)) + +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! + +SandboxAdapter +~~~~~~~~~~~~~~ + +.. code:: python + + from iota import Iota + from iota.adapter.sandbox import SandboxAdapter + + api =\ + Iota( + SandboxAdapter( + uri = 'https://sandbox.iotatoken.com/api/v1/', + auth_token = 'demo7982-be4a-4afa-830e-7859929d892c', + ), + ) + +The ``SandboxAdapter`` is a specialized ``HttpAdapter`` that sends +authenticated requests to sandbox nodes. + +.. note:: + + See `Sandbox `_ Documentation for more information about sandbox nodes. + +Sandbox nodes process certain commands asynchronously. When +``SandboxAdapter`` determines that a request is processed +asynchronously, it will block, then poll the node periodically until it +receives a response. + +The result is that ``SandboxAdapter`` abstracts away the sandbox node's +asynchronous functionality so that your API client behaves exactly the +same as if it were connecting to a non-sandbox node. + +To create a ``SandboxAdapter``, you must provide the URI of the sandbox +node and the auth token that you received from the node maintainer. Note +that ``SandboxAdapter`` only works with ``http://`` and ``https://`` +URIs. + +You may also specify the polling interval (defaults to 15 seconds) and +the number of polls before giving up on an asynchronous job (defaults to +8 times). + +.. note:: + + For parity with the other adapters, ``SandboxAdapter`` blocks until it receives a response from the node. + + If you do not want ``SandboxAdapter`` to block the main thread, it is recommended that you execute it in a separate thread or process. + + +MockAdapter +~~~~~~~~~~~ + +.. code:: python + + from iota import Iota + from iota.adapter import 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 + +``MockAdapter`` is used to simulate the behavior of an adapter 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 configure an ``Iota`` instance to use ``MockAdapter``, specify +``mock://`` as the node URI, or provide a ``MockAdapter`` instance. + +To use ``MockAdapter``, you must first seed the responses that you want +it to return by calling its ``seed_response`` method. + +``seed_response`` takes two parameters: + +- ``command: Text``: The name of the command. Note that this is the + camelCase version of the command name (e.g., ``getNodeInfo``, not + ``get_node_info``). +- ``response: dict``: The response that the adapter will return. + +You can seed multiple responses for the same command; the +``MockAdapter`` maintains a queue for each command internally, and it +will pop a response off of the corresponding queue each time it +processes a request. + +Note that you have to call ``seed_response`` once for each request you +expect it to process. If ``MockAdapter`` does not have a seeded response +for a particular command, it will raise a ``BadApiResponse`` exception +(simulates a 404 response). + +Wrappers +-------- + +Wrappers act like decorators for adapters; they are used to enhance or +otherwise modify the behavior of adapters. + +RoutingWrapper +~~~~~~~~~~~~~~ + +.. code:: python + + from iota import Iota + from iota.adapter.wrappers import RoutingWrapper + + api =\ + Iota( + # Send PoW requests to local node. + # All other requests go to light wallet node. + RoutingWrapper('https://service.iotasupport.com:14265') + .add_route('attachToTangle', 'http://localhost:14265') + .add_route('interruptAttachingToTangle', 'http://localhost:14265') + ) + +``RoutingWrapper`` allows you to route API requests to different nodes +depending on the command name. + +For example, you could use this wrapper to direct all PoW requests to a +local node, while sending the other requests to a light wallet node. + +``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. + +Once you've initialized the ``RoutingWrapper``, invoke its ``add_route`` +method to specify a different adapter to use for a particular command. + +``add_route`` requires two arguments: + +- ``command: Text``: The name of the command. Note that this is the + camelCase version of the command name (e.g., ``getNodeInfo``, not + ``get_node_info``). +- ``adapter: AdapterSpec``: The adapter or URI to send this request to. diff --git a/docs/index.rst b/docs/index.rst index 625e1baa..8eb81e68 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -4,6 +4,9 @@ getting_started types + adapters + addresses + api .. note:: **🚧 PyOTA documentation is still under construction. 🚧** From 0663a42875e8c130fe8fced14a36cb8630ca2b40 Mon Sep 17 00:00:00 2001 From: Damian Melniczuk Date: Wed, 29 Nov 2017 13:20:57 +0100 Subject: [PATCH 2/4] added raw **addresses** and **api** rst files --- docs/addresses.rst | 198 +++++++++++++++++++++++++++++ docs/api.rst | 311 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 509 insertions(+) create mode 100644 docs/addresses.rst create mode 100644 docs/api.rst diff --git a/docs/addresses.rst b/docs/addresses.rst new file mode 100644 index 00000000..e61958f5 --- /dev/null +++ b/docs/addresses.rst @@ -0,0 +1,198 @@ +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. + +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. + +.. raw:: html + + + +PyOTA provides two methods for generating addresses: + +Using the API +------------- + +.. code:: python + + 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) + addresses = gna_result['addresses'] + + # Generate 1 address, starting with index 42: + gna_result = api.get_new_addresses(start=42) + addresses = gna_result['addresses'] + + # Find the first unused address, starting with index 86: + gna_result = api.get_new_addresses(start=86, count=None) + addresses = gna_result['addresses'] + +To generate addresses using the API, invoke its ``get_new_addresses`` +method, using the following parameters: + +- ``start: 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). 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 <#security-levels>`__ + below. + +``get_new_addresses`` returns a dict with the following items: + +- ``addresses: List[Address]``: The generated address(es). Note that + this value is always a list, even if only one address was generated. + +Using AddressGenerator +---------------------- + +.. code:: python + + 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 +the ``AddressGenerator`` class. + +``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`` provides two methods: + +- ``get_addresses: (int, int, int) -> List[Address]``: Returns a list + of addresses. This is the same method that the ``get_new_addresses`` + API command uses internally. +- ``create_iterator: (int, int) -> Generator[Address]``: Returns an + iterator that will create addresses endlessly. Use this if you have a + feature that needs to generate addresses "on demand". + +Security Levels +=============== + +.. code:: python + + 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 +``AddressGenerator`` 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. diff --git a/docs/api.rst b/docs/api.rst new file mode 100644 index 00000000..9073b166 --- /dev/null +++ b/docs/api.rst @@ -0,0 +1,311 @@ +Standard API +============ + +The Standard 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 Standard API calls, please refer +to the `official documentation `__. + +Extended API +============ + +The Extended API includes a number of "high level" commands to perform +tasks such as sending and receiving transfers. + +``broadcast_and_store`` +----------------------- + +Broadcasts and stores a set of transaction trytes. + +Parameters +~~~~~~~~~~ + +- ``trytes: Iterable[TransactionTrytes]``: Transaction trytes. + +Return +~~~~~~ + +This method returns a ``dict`` with the following items: + +- ``trytes: List[TransactionTrytes]``: Transaction trytes that were + broadcast/stored. Should be the same as the value of the ``trytes`` + parameter. + +``get_account_data`` +-------------------- + +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 a ``slice`` object; + the stop index is *not* included in the result. + +- If ``None`` (default), then this method will check every address + until it finds one without any transfers. + +- ``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. + +Return +~~~~~~ + +This method returns a dict with the following items: + +- ``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[Bundles]``: List of bundles with transactions to/from + this account. + +``get_bundles`` +--------------- + +Given a ``TransactionHash``, returns the bundle(s) associated with it. + +Parameters +~~~~~~~~~~ + +- ``transaction: TransactionHash``: Hash of a tail transaction. + +Return +~~~~~~ + +This method returns a ``dict`` with the following items: + +- ``bundles: List[Bundle]``: List of matching bundles. Note that this + value is always a list, even if only one bundle was found. + +``get_inputs`` +-------------- + +Gets all possible inputs of a seed and returns them 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 a + ``slice`` 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. +- ``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. +- If ``threshold`` is 0, the first address in the key range with a + non-zero balance will be returned (if it exists). +- If ``threshold`` is ``None`` (default), this method will return + **all** inputs in the specified key range. + +Note that 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. + +Return +~~~~~~ + +This method returns a ``dict`` with the following items: + +- ``inputs: List[Address]``: Addresses with nonzero balances that can + be used as inputs. +- ``totalBalance: int``: Aggregate balance of all inputs found. + +``get_latest_inclusion`` +------------------------ + +Fetches the inclusion state for the specified transaction hashes, as of +the latest milestone that the node has processed. + +Parameters +~~~~~~~~~~ + +- ``hashes: Iterable[TransactionHash]``: Iterable of transaction + hashes. + +Return +~~~~~~ + +This method returns a ``dict`` with the following items: + +- ``: bool``: Inclusion state for a single + transaction. + +There will be one item per transaction hash in the ``hashes`` parameter. + +``get_new_addresses`` +--------------------- + +Generates one or more new addresses from the seed. + +Parameters +~~~~~~~~~~ + +- ``index: int``: Specify the index of the new address (must be >= 1). +- ``count: Optional[int]``: Number of addresses to generate (must be >= + 1). +- If ``None``, this method will scan the Tangle to find the next + available unused address and return that. +- ``security_level: int``: Number of iterations to use when generating + new addresses. Lower values generate addresses faster, higher values + result in more secure signatures in transactions. + +Return +~~~~~~ + +This method returns a ``dict`` with the following items: + +- ``addresses: List[Address]``: The generated address(es). Note that + this value is always a list, even if only one address was generated. + +``get_transfers`` +----------------- + +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 a + ``slice`` object; the stop index is *not* included in the result. +- If ``None`` (default), then this method will check every address + until it finds one without any transfers. + +Return +~~~~~~ + +This method returns a ``dict`` with the following items: + +- ``bundles: List[Bundle]``: Matching bundles, sorted by tail + transaction timestamp. + +``prepare_transfer`` +-------------------- + +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. +- ``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. + +Return +~~~~~~ + +This method returns a ``dict`` with the following items: + +- ``trytes: List[TransactionTrytes]``: Raw trytes for the transactions + in the bundle, ready to be provided to ``send_trytes``. + +``replay_bundle`` +----------------- + +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. +- ``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. + +Return +~~~~~~ + +This method returns a ``dict`` with the following items: + +- ``trytes: List[TransactionTrytes]``: Raw trytes that were published + to the Tangle. + +``send_transfer`` +----------------- + +Prepares a set of transfers and creates the bundle, then attaches the +bundle to the Tangle, and broadcasts and stores the transactions. + +Parameters +~~~~~~~~~~ + +- ``depth: int``: Depth at which to attach the bundle. +- ``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. +- ``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. + +Return +~~~~~~ + +This method returns a ``dict`` with the following items: + +- ``bundle: Bundle``: The newly-published bundle. + +``send_trytes`` +--------------- + +Attaches transaction trytes to the Tangle, then broadcasts and stores +them. + +Parameters +~~~~~~~~~~ + +- ``trytes: Iterable[TransactionTrytes]``: Transaction trytes to + publish. +- ``depth: int``: Depth at which to attach the bundle. +- ``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. + +Return +~~~~~~ + +This method returns a ``dict`` with the following items: + +- ``trytes: List[TransactionTrytes]``: Raw trytes that were published + to the Tangle. From f94788f2832531e8b47c1e6fd6d5d852e557f64a Mon Sep 17 00:00:00 2001 From: Damian Melniczuk Date: Wed, 29 Nov 2017 13:43:48 +0100 Subject: [PATCH 3/4] Converted and fixed **addresses** documentation --- docs/addresses.rst | 82 +++++++--------------------------------------- 1 file changed, 12 insertions(+), 70 deletions(-) diff --git a/docs/addresses.rst b/docs/addresses.rst index e61958f5..6571e7f2 100644 --- a/docs/addresses.rst +++ b/docs/addresses.rst @@ -9,82 +9,24 @@ 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. -.. raw:: html +.. note:: - + If you are familiar with Python 2's C API, we'd love to hear from you! + Check the `GitHub issue `_ + for more information. PyOTA provides two methods for generating addresses: From b46b4417f438a448af91096fa4161491fb1743eb Mon Sep 17 00:00:00 2001 From: Damian Melniczuk Date: Fri, 1 Dec 2017 10:21:21 +0100 Subject: [PATCH 4/4] converted and adjusted **types.rst** --- docs/types.rst | 541 +++++++++++++++++++++++++++++-------------------- 1 file changed, 316 insertions(+), 225 deletions(-) diff --git a/docs/types.rst b/docs/types.rst index aa9b54e8..f8f6cbdd 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -1,264 +1,355 @@ -================ -PyOTA Data Types -================ -.. important:: +Basic Concepts +============== - Before diving into the API, it's important to understand the fundamental data - types of IOTA. +Before diving into the API, it's important to understand the fundamental +data types of IOTA. - For an introduction to the IOTA protocol and the Tangle, give the - `protocol documentation`_ a once-over. +:todo: Link to IOTA docs -PyOTA defines a few types that will make it easy for you to model objects like -Transactions and Bundles in your own code. +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. TryteString ----------- -A :py:class:`TryteString` is an ASCII representation of a sequence of trytes. -In many respects, it is similar to a Python ``bytes`` 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``, a :py:class:`TryteString` can only contain uppercase -letters and the number 9 (as a regular expression: ``^[A-Z9]*$``). - -.. admonition:: Why only these characters? - - You can find the answer on the - `IOTA Forum `__. - -As you go through the API documentation, you will see many references to -:py:class:`TryteString` and its subclasses: - -- :py:class:`Fragment` - - A signature or message fragment inside a transaction. - Fragments are always 2187 trytes long. -- :py:class:`Hash` +.. code:: python - An object identifier. Hashes are always 81 trytes long. + from iota import TryteString - There are many different types of hashes: + trytes_1 = TryteString(b'RBTC9D9DCDQAEASBYBCCKBFA') + trytes_2 = TryteString(b'LH9GYEMHCF9GWHZFEELHVFOEOHNEEEWHZFUD') - :py:class:`Address` - Identifies an address on the Tangle. - :py:class:`BundleHash` - Identifies a bundle on the Tangle. - :py:class:`TransactionHash` - Identifies a transaction on the Tangle. + if trytes_1 != trytes_2: + trytes_combined = trytes_1 + trytes_2 -- :py:class:`Seed` + index = { + trytes_1: 42, + trytes_2: 86, + } - A TryteString that is used for crypto functions such as generating addresses, - signing inputs, etc. +A ``TryteString`` is an ASCII representation of a sequence of trytes. In +many respects, it is similar to a Python ``bytes`` object (which is an +ASCII representation of a sequence of bytes). - .. important:: +In fact, the two objects behave very similarly; they support +concatenation, comparison, can be used as dict keys, etc. - Seeds can be any length, but 81 trytes offers the best security. - More information is available on the - `IOTA Forum `__. - -- :py:class:`Tag` - - A tag used to classify a transaction. Tags are always 27 trytes long. - -- :py:class:`TransactionTrytes` - - A TryteString representation of a transaction on the Tangle. - :py:class:`TransactionTrytes` are always 2673 trytes long. - -Creating TryteStrings -~~~~~~~~~~~~~~~~~~~~~ -To create a new :py:class:`TryteString` from a sequence of trytes, simply -wrap the trytes inside the :py:class:`TryteString` initializer: - -.. code-block:: python - - from iota import TryteString - - trytes = TryteString('RBTC9D9DCDQAEASBYBCCKBFA') +However, unlike ``bytes``, a ``TryteString`` can only contain uppercase +letters and the number 9 (as a regular expression: ``^[A-Z9]*$``). -To encode ASCII text into trytes, use the :py:meth:`TryteString.from_string` -method: +As you go through the API documentation, you will see many references to +``TryteString`` and its subclasses: -.. code-block:: python +- ``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. - from iota import TryteString +Encoding +~~~~~~~~ - message_trytes = TryteString.from_string('Hello, IOTA!') +.. code:: python - print(message_trytes) # RBTC9D9DCDQAEASBYBCCKBFA + from iota import TryteString -To decode a sequence of trytes back into ASCII text, use -:py:meth:`TryteString.as_string`: + message_trytes = TryteString.from_string('Hello, IOTA!') -.. code-block:: python +To encode character data into trytes, use the +``TryteString.from_string`` method. - from iota import TryteString +You can also convert a tryte sequence into characters using +``TryteString.as_string``. Note that not every tryte sequence can be +converted; garbage in, garbage out! - message_trytes = TryteString('RBTC9D9DCDQAEASBYBCCKBFA') +.. code:: python - message_str = message_trytes.as_string() + from iota import TryteString - print(message_str) # Hello, IOTA! + trytes = TryteString(b'RBTC9D9DCDQAEASBYBCCKBFA') + message = trytes.as_string() .. note:: - PyOTA also supports encoding non-ASCII characters, but this functionality is - **experimental** and has not yet been standardized. + PyOTA also supports encoding non-ASCII characters, but this functionality is + **experimental** and has not yet been evaluated by the IOTA + community! - If you encode non-ASCII characters, be aware that other IOTA libraries - (possibly including future versions of PyOTA!) might not be able to decode - them! + Until this feature has been standardized, it is recommended that you only + use ASCII characters when generating ``TryteString`` objects from + character strings. Transaction Types ----------------- -PyOTA defines two different types used to represent transactions: - -:py:class:`Transaction` - A transaction that has been loaded from the Tangle. -:py:class:`ProposedTransaction` - A transaction that was created locally and hasn't been broadcast to the - Tangle yet. +PyOTA defines two different types used to represent transactions: Transaction ~~~~~~~~~~~ -Generally, you will never need to create `Transaction` objects; the API will -build them for you, as the result of various API methods. - -.. tip:: - - If you have a TryteString representation of a transaction, and you'd like to - convert it into a :py:class:`Transaction` object, use the - :py:meth:`Transaction.from_tryte_string` method: - - .. code-block:: python - - from iota import Transaction - - txn_1 =\ - Transaction.from_tryte_string( - 'GYPRVHBEZOOFXSHQBLCYW9ICTCISLHDBNMMVYD9JJHQMPQCTIQ...', - ) - - This is equivalent to the `Paste Trytes`_ feature from the IOTA Wallet. - -Each :py:class:`Transaction` object has the following attributes: - -- ``address`` (:py:class:`Address`) - - The address associated with this transaction. Depending on the transaction's - ``value``, this address may be a sender or a recipient. - -- ``attachment_timestamp`` (:py:class:`int`) - Timestamp after completing the Proof of Work process. - - See the `timestamps white paper`_ for more information. - -- ``attachment_timestamp_lower_bound`` (:py:class:`int`) - Lower bound of the timestamp. - - See the `timestamps white paper`_ for more information. - -- ``attachment_timestamp_upper_bound`` (:py:class:`int`) - Upper bound of the timestamp. - See the `timestamps white paper`_ for more information. - -- ``branch_transaction_hash`` (:py:class:`TransactionHash`) - - An unrelated transaction that this transaction "approves". - Refer to the `protocol documentation`_ for more information. - -- ``bundle_hash`` (:py:class:`BundleHash`) - - 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. - -- ``current_index`` (:py:class:`int`) - - The transaction's position in the bundle. - - - If the ``current_index`` value is 0, then this is the "tail transaction". - - If it is equal to ``last_index``, then this is the "head transaction". - -- ``hash`` (:py:class:`TransactionHash`) - - The transaction hash, used to uniquely identify the transaction on the - Tangle. This value is generated by taking a hash of the raw transaction - trytes. - -- ``last_index`` (:py:class:`int`) - - 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. - -- ``nonce`` (:py:class:`Nonce`) - - This is the product of the PoW process. - - Refer to the `protocol documentation`_ for more information. - -- ``signature_message_fragment`` (:py:class:`Fragment`) - - Additional data attached to the transaction: - - - If ``value < 0``, this value contains a fragment of the cryptographic - signature authorizing the spending of the IOTAs. - - If ``value > 0``, this value is an (optional) string message attached to - the transaction. - - If ``value = 0``, this value could be either a signature or message - fragment, depending on the previous transaction. - - .. tip:: - - Read this as "Signature/Message Fragment". That is, it could be a - fragment of a signature **or** a message, depending on the transaction. - -- ``tag`` (:py:class:`Tag`) - - Used to classify the transaction. - - Every transaction has a tag, but many transactions have empty tags. - -- ``timestamp`` (:py:class:`int`) - - Unix timestamp when the transaction was created. - - Note that devices can specify any timestamp when creating transactions, so - this value is not safe to use by itself for security measures (such as - resolving double-spends). - - .. note:: - - The IOTA protocol does support verifiable timestamps. Refer to the - `timestamps white paper`_ for more information. - -- ``trunk_transaction_hash`` (:py:class:`TransactionHash`) - - The transaction hash of the next transaction in the bundle. - - If this transaction is the head transaction, its ``trunk_transaction_hash`` - will be pseudo-randomly selected, similarly to ``branch_transaction_hash``. - -- ``value`` (:py:class:`int`) - - 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. - - -:todo: ProposedTransaction +.. code:: python + + from iota import Address, ProposedTransaction, Tag, Transaction + + txn_1 =\ + 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' + ) + +``Transaction`` is a transaction that has been loaded from the Tangle. + +Generally, you will never need to create ``Transaction`` objects; the +API will build them for you, as the result of various API methods. + +Each ``Transaction`` has the following attributes: + +- ``address: Address``: The address associated with this transaction. + Depending on the transaction's ``value``, this address may be a + sender or a recipient. +- ``branch_transaction_hash: TransactionHash``: An unrelated + transaction that this transaction "approves". Refer to the Basic + Concepts section for more information. +- ``bundle_hash: BundleHash``: 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. +- ``current_index: int``: The transaction's position in the bundle. +- If the ``current_index`` value is 0, then this is the "tail + transaction". +- If it is equal to ``last_index``, then this is the "head + transaction". +- ``hash: TransactionHash``: 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. +- ``is_confirmed: Optional[bool]``: Whether this transaction has been + "confirmed". Refer to the Basic Concepts section for more + information. +- ``last_index: int``: 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. +- ``nonce: Hash``: This is the product of the PoW process. +- ``signature_message_fragment: Fragment``: Additional data attached to + the transaction: +- If ``value < 0``, this value contains a fragment of the cryptographic + signature authorizing the spending of the IOTAs. +- If ``value > 0``, this value is an (optional) string message attached + to the transaction. +- If ``value = 0``, this value could be either a signature or message + fragment, depending on the previous transaction. +- ``tag: Tag``: Used to classify the transaction. Many transactions + have empty tags (``Tag(b'999999999999999999999999999')``). +- ``timestamp: int``: Unix timestamp when the transaction was created. + Note that devices can specify any timestamp when creating + transactions, so this value is not safe to use for security measures + (such as resolving double-spends). +- ``trunk_transaction_hash: TransactionHash``: The transaction hash of + the next transaction in the bundle. If this transaction is the head + transaction, its ``trunk_transaction_hash`` will be pseudo-randomly + selected, similarly to ``branch_transaction_hash``. +- ``value: int``: 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. + +ProposedTransaction +~~~~~~~~~~~~~~~~~~~ + +``ProposedTransaction`` is a transaction that was created locally and +hasn't been broadcast yet. + +.. code:: python + + txn_2 =\ + ProposedTransaction( + address = + Address( + b'TESTVALUE9DONTUSEINPRODUCTION99999XE9IVG' + b'EFNDOCQCMERGUATCIEGGOHPHGFIAQEZGNHQ9W99CH' + ), + + message = TryteString.from_string('thx fur cheezburgers'), + tag = Tag(b'KITTEHS'), + value = 42, + ) + +This type is useful when creating new transactions to broadcast to the +Tangle. Note that creating a ``ProposedTransaction`` requires only a +small subset of the attributes needed to create a ``Transaction`` +object. + +To create a ``ProposedTransaction``, specify the following values: + +- ``address: Address``: The address associated with the transaction. + Note that each transaction references exactly one address; in order + to transfer IOTAs from one address to another, you must create at + least two transactions: One to deduct the IOTAs from the sender's + balance, and one to add the IOTAs to the recipient's balance. +- ``message: Optional[TryteString]``: Optional trytes to attach to the + transaction. This could be any value (character strings, binary data, + or raw trytes), as long as it's converted to a ``TryteString`` first. +- ``tag: Optional[Tag]``: Optional tag to classify this transaction. + Each transaction may have exactly one tag, and the tag is limited to + 27 trytes. +- ``value: int``: The number of IOTAs being transferred in this + transaction. This value can be 0; for example, to send a message + without spending any IOTAs. + +Bundle Types +------------ + +As with transactions, PyOTA defines two bundle types. + +Bundle +~~~~~~ + +.. code:: python + + from iota import Bundle + + bundle = Bundle.from_tryte_strings([ + b'GYPRVHBEZOOFXSHQBLCYW9ICTCISLHDBNMMVYD9JJHQMPQCTIQAQTJNNNJ9IDXLRCC...', + b'OYOXYPCLR9PBEY9ORZIEPPDNTI9CQWYZUOTAVBXPSBOFEQAPFLWXSWUIUSJMSJIIIZ...', + # etc. + ]) + +``Bundle`` represents a bundle of transactions published on the Tangle. +It is intended to be a read-only object, allowing you to inspect the +transactions and bundle metadata. + +Each bundle has the following attributes: + +- ``hash: BundleHash``: The hash of this bundle. This value is + generated by taking a hash of the metadata from all transactions in + the bundle. +- ``is_confirmed: Optional[bool]``: Whether the transactions in this + bundle have been confirmed. Refer to the Basic Concepts section for + more information. +- ``tail_transaction: Optional[Transaction]``: The bundle's tail + transaction. +- ``transactions: List[Transaction]``: The transactions associated with + this bundle. + +ProposedBundle +~~~~~~~~~~~~~~ + +.. code:: python + + 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')) +.. note:: -.. _protocol documentation: https://iota.readme.io/docs/ -.. _paste trytes: https://forum.iota.org/t/3457/3 -.. _timestamps white paper: https://iota.org/timestamps.pdf + This section contains information about how PyOTA works "under the + hood". + + The ``prepare_transfer`` API method encapsulates this functionality + for you; it is not necessary to understand how ``ProposedBundle`` + works in order to use PyOTA. + + +``ProposedBundle`` provides a convenient interface for creating new +bundles, listed in the order that they should be invoked: + +- ``add_transaction: (ProposedTransaction) -> None``: Adds a + transaction to the bundle. If necessary, it may split the transaction + into multiple (for example, if the transaction's message is too long + to fit into 2187 trytes). +- ``add_inputs: (List[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. +- You can use the ``get_inputs`` API command to find suitable inputs. +- ``send_unspent_inputs_to: (Address) -> None``: Specifies the address + that will receive unspent IOTAs. The ``ProposedBundle`` will use this + to create the necessary change transaction, if necessary. +- ``finalize: () -> None``: Prepares the bundle for PoW. Once this + method is invoked, no new transactions may be added to the bundle. +- ``sign_inputs: (KeyGenerator) -> None``: Generates the necessary + cryptographic signatures to authorize spending the inputs. You do not + need to invoke this method if the bundle does not contain any + transactions that spend IOTAs. + +Once the ``ProposedBundle`` has been finalized (and inputs signed, if +necessary), invoke its ``as_tryte_strings`` method to generate the raw +trytes that should be included in an ``attach_to_tangle`` API request.