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

docs: Add Creating Transfers section #316

Merged
merged 5 commits into from
Mar 6, 2020
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions docs/commands.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ Advanced: PyOTA Commands
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 (:ref:`Core API Methods` and
:ref:`Extended API Methods`) for users of the library. These handle
PyOTA provides the API interface (:ref:`core_api:Core API Methods` and
:ref:`extended_api: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 (:ref:`Core API Methods` and :ref:`Extended API Methods`)
methods (:ref:`core_api:Core API Methods` and :ref:`extended_api:Extended API Methods`)
internally call PyOTA commands to get the job done.

Most PyOTA commands are sub-classed from :py:class:`FilterCommand` class, which
Expand Down Expand Up @@ -142,7 +142,7 @@ Extended Commands
Core commands, like :py:meth:`~Iota.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 :ref:`Extended API Methods`) on the other hand
Extended commands (that serve :ref:`extended_api:Extended API Methods`) on the other hand
carry out more complex operations such as combining core commands, building
objects, etc...

Expand Down
3 changes: 3 additions & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@
'sphinx.ext.autosectionlabel',
]

# Add a document prefix to the created section lables
autosectionlabel_prefix_document = True

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

Expand Down
3 changes: 3 additions & 0 deletions docs/images/create_transfer.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions docs/images/transfer_api.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
core_api
extended_api
addresses
transfers
multisig
commands
tutorials
Expand Down
296 changes: 296 additions & 0 deletions docs/transfers.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,296 @@
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
lzpap marked this conversation as resolved.
Show resolved Hide resolved
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
:ref:`basic_concepts:Basic Concepts` and :ref:`types:PyOTA Types` sections!

Anatomy of a Transfer
---------------------

We already now that the Tangle consists of :ref:`transactions <basic_concepts:Transaction>`
lzpap marked this conversation as resolved.
Show resolved Hide resolved
referencing each other, each of them two others to be more precise.
Transactions can be grouped together in :ref:`bundles <basic_concepts:Bundle>`.
`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:

1. Create individual transaction(s).
2. Construct a bundle from the transaction(s).
3. Obtain references to two transactions waiting to be confirmed (tips) from the Tangle.
lzpap marked this conversation as resolved.
Show resolved Hide resolved
4. Do proof-of-work for each transaction in the bundle.
5. Send the bundle to the network.


.. figure:: images/create_transfer.svg
:scale: 100 %
:alt: Process of sending a transfer in IOTA.

Process of creating and sending a transfer to the Tangle.

.. py:currentmodule:: iota

1. Create Transactions
~~~~~~~~~~~~~~~~~~~~~~
The first step is to create the individual transaction objects. You have to
specify ``address`` and ``value`` for each transaction. A negative ``value``
means spending from ``address``. Furthermore, you can define a ``tag``, and for
zero-value transactions, a ``message``. ``timestamp`` is usually auto-generated
by the IOTA libraries.
lzpap marked this conversation as resolved.
Show resolved Hide resolved

In PyOTA, use :py:class:`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 (token) transfer possible by
lzpap marked this conversation as resolved.
Show resolved Hide resolved
grouping together input an output transactions.
lzpap marked this conversation as resolved.
Show resolved Hide resolved

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 indexed individually, and also contain information on how many other
lzpap marked this conversation as resolved.
Show resolved Hide resolved
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 tokens being transferred.
lzpap marked this conversation as resolved.
Show resolved Hide resolved

:py:class:`ProposedBundle` helps you in PyOTA to create bundles, add transactions,
finalize the bundle and sign the inputs.
lzpap marked this conversation as resolved.
Show resolved Hide resolved

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, :py:meth:`~Iota.get_transactions_to_approve`
does the job: it returns a ``trunk`` and a ``branch`` :py:class:`TransactionHash`.

Our bundle will validate these two transactions once in the Tangle.
lzpap marked this conversation as resolved.
Show resolved Hide resolved

4. Do Proof-of-Work
~~~~~~~~~~~~~~~~~~~

The bundle has been finalized, inputs have been signed, we have two tips,
lzpap marked this conversation as resolved.
Show resolved Hide resolved
now it's time to prepare the bundle to be attached to the Tangle. All
transactions reference two other transactions in the Tangle, therefore we need
to select these references for each transaction in our bundle.
lzpap marked this conversation as resolved.
Show resolved Hide resolved

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?

.. figure:: images/bundle-structure.png
:scale: 100 %
:alt: Bundle structure with four transactions.

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.
Comment on lines +119 to +120
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
transaction in the bundle, while the branch reference is the trunk transaction
hash, one of the tips.
transaction in the bundle, while the branch reference is one of the tips.

I thought that branch is the same for all transactions in a bundle (including the head tx)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Take a look at https://github.com/iotaledger/iri/blob/e1776fbad5d90df86a26402f9025e4b0b2ef7d3e/src/main/java/com/iota/iri/service/API.java#L1256-L1278
The result of this algo is the figure of the bundle structure on the docs site. And btw the documentation on top of the function in IRI is broken I think 😸

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, that's interesting. I didn't realise they were flipped 😬


The head transaction is different: the trunk reference is the trunk tip, while
the branch reference is the branch tip.
todofixthis marked this conversation as resolved.
Show resolved Hide resolved

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, it is correct.
lzpap marked this conversation as resolved.
Show resolved Hide resolved

In PyOTA, use :py:meth:`~Iota.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 :py:meth:`~Iota.broadcast_and_store` to achieve this.

Observe the bird-eye view of the Tangle depicted at the last step of the
lzpap marked this conversation as resolved.
Show resolved Hide resolved
process. Our transactions are part of the Tangle, reference each other and
lzpap marked this conversation as resolved.
Show resolved Hide resolved
the two tips. Newer transactions may reference our transactions as branch or
trunk.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
trunk.
trunk. 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 `depth`_ of our transactions.

Maybe add a link and/or explanation of depth and how that is used?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for the suggestion! I will come up with something.


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.

.. figure:: images/transfer_api.svg
:scale: 100 %
:alt: Different ways of sending a transfer in IOTA.

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 :py:meth:`~Iota.send_transfer`
extended API method. You still need to create the transactions yourself
with :py:class:`ProposedTransaction`.

.. code-block::

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=transactions
)['bundle']

As all API methods in PyOTA, :py:meth:`~Iota.send_transfer` also returns
a ``dict``. The ``bundle`` key holds the value of :py:class:`Bundle`.

It's important to note, that for value transfers, you will need your seed as well.
:py:meth:`~Iota.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 :py:meth:`~Iota.send_transfer`, you can use the combination of
:py:meth:`~Iota.prepare_transfer` and :py:meth:`~Iota.send_trytes` to achieve
the same result.
lzpap marked this conversation as resolved.
Show resolved Hide resolved

.. code-block::

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 :py:class:`TransactionTrytes`, and not a :py:class:`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!
lzpap marked this conversation as resolved.
Show resolved Hide resolved

.. code-block::

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 could
lzpap marked this conversation as resolved.
Show resolved Hide resolved
# bundle.add_inputs()
# bundle.send_unspent_inputs_to()

bundle.finalize()

# Again, for value transfers, we could:
lzpap marked this conversation as resolved.
Show resolved Hide resolved
# 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)


.. _transfer bundles: https://docs.iota.org/docs/getting-started/0.1/transactions/bundles#transfer-bundles
.. _zero-value bundles: https://docs.iota.org/docs/getting-started/0.1/transactions/bundles#zero-value-bundle
.. _bundle essence: https://docs.iota.org/docs/getting-started/0.1/transactions/bundles#bundle-essence
.. _within the bundle are linked together: https://docs.iota.org/docs/getting-started/0.1/transactions/bundles
.. _minimum weight magnitude: https://docs.iota.org/docs/getting-started/0.1/network/minimum-weight-magnitude
10 changes: 5 additions & 5 deletions docs/tutorials.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ 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 :ref:`Install PyOTA` for more info.
installed. See :ref:`README: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`_.
Expand Down Expand Up @@ -43,7 +43,7 @@ something from the library, you need to import it from there.

Notice, how we import the :py:class:`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 :ref:`PyOTA API Classes`.
the network. Read more about API objects at :ref:`api:PyOTA API Classes`.

We also import the ``pprint`` method that prettifies the output before printing
it to the console.
Expand Down Expand Up @@ -141,7 +141,7 @@ therefore we are restricted to the `tryte alphabet`_.
:lines: 16-22
:lineno-start: 16

It's time to construct the transaction. According to :ref:`Transaction Types`,
It's time to construct the transaction. According to :ref:`types:Transaction Types`,
PyOTA uses :py:class:`ProposedTransaction` to build transactions that are not
yet broadcast to the network. Oberve, that the ``value=0`` means this is
a zero-value transaction.
Expand Down Expand Up @@ -284,7 +284,7 @@ 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 :ref:`Generating Addresses`.
generate addresses in PyOTA at :ref:`addresses:Generating Addresses`.

On line 20 we access the first element of the list of addresses in the response
dictionary.
Expand Down Expand Up @@ -588,7 +588,7 @@ An address is also needed, so we generate one with the help of
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 :ref:`Generating Addresses` page.
refer to the :ref:`adresses:Generating Addresses` page.

We also attach a custom :py:class:`Tag` to our :py:class:`ProposedTransaction`.
Note, that if our ``trytes_encrypted_data`` was longer than the maximum payload
Expand Down
Loading