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/addresses.rst b/docs/addresses.rst new file mode 100644 index 00000000..6571e7f2 --- /dev/null +++ b/docs/addresses.rst @@ -0,0 +1,140 @@ +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. + +.. 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, if you are using Python 3, 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]``. + + **Important:** The extension is not yet compatible with Python 2. + + 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: + +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. 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. 🚧** 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.