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

Commit 25d3914

Browse files
authored
Merge pull request #316 from lzpap/docs_transfers
docs: Add `Creating Transfers` section
2 parents 202c811 + 06d5a94 commit 25d3914

12 files changed

+366
-22
lines changed

docs/commands.rst

+4-4
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@ Advanced: PyOTA Commands
1010
However, if you are a curious mind or happen to do development on the
1111
library, the following information might be useful.
1212

13-
PyOTA provides the API interface (:ref:`Core API Methods` and
14-
:ref:`Extended API Methods`) for users of the library. These handle
13+
PyOTA provides the API interface (:ref:`core_api:Core API Methods` and
14+
:ref:`extended_api:Extended API Methods`) for users of the library. These handle
1515
constructing and sending HTTP requests to the specified node through adapters,
1616
furthermore creating, transforming and translating between PyOTA-specific types
1717
and (JSON-encoded) raw data. They also filter outgoing requests and incoming
1818
responses to ensure that only appropriate data is communicated with the node.
1919

2020
PyOTA implements the `Command Design Pattern`_. High level API interface
21-
methods (:ref:`Core API Methods` and :ref:`Extended API Methods`)
21+
methods (:ref:`core_api:Core API Methods` and :ref:`extended_api:Extended API Methods`)
2222
internally call PyOTA commands to get the job done.
2323

2424
Most PyOTA commands are sub-classed from :py:class:`FilterCommand` class, which
@@ -142,7 +142,7 @@ Extended Commands
142142
Core commands, like :py:meth:`~Iota.find_transactions` in the example above,
143143
are for direct communication with the node for simple tasks such
144144
as finding a transaction on the Tangle or getting info about the node.
145-
Extended commands (that serve :ref:`Extended API Methods`) on the other hand
145+
Extended commands (that serve :ref:`extended_api:Extended API Methods`) on the other hand
146146
carry out more complex operations such as combining core commands, building
147147
objects, etc...
148148

docs/conf.py

+3
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@
3636
'sphinx.ext.autosectionlabel',
3737
]
3838

39+
# Add a document prefix to the created section lables
40+
autosectionlabel_prefix_document = True
41+
3942
# Add any paths that contain templates here, relative to this directory.
4043
templates_path = ['_templates']
4144

docs/images/create_transfer.svg

+3
Loading

docs/images/transfer_api.svg

+3
Loading

docs/index.rst

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
core_api
1111
extended_api
1212
addresses
13+
transfers
1314
multisig
1415
commands
1516
tutorials

docs/transfers.rst

+322
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,322 @@
1+
Creating transfers
2+
==================
3+
4+
IOTA is a permissionless DLT solution, therefore anyone can send transactions
5+
to the network and initiate transfers. The IOTA client libraries help you to
6+
abstract away low-level operations required to construct and send a transfer
7+
to the Tangle.
8+
9+
In this section, we will explore in depth how to create transactions and
10+
bundles with IOTA, furthermore what tools you can use in PyOTA to ease your
11+
development process.
12+
13+
.. note::
14+
15+
Before proceeding, make sure you read and understood the
16+
:ref:`basic_concepts:Basic Concepts` and :ref:`types:PyOTA Types` sections!
17+
18+
Anatomy of a Transfer
19+
---------------------
20+
21+
We already know that the Tangle consists of :ref:`transactions <basic_concepts:Transaction>`
22+
referencing each other, each of them two others to be more precise.
23+
Transactions can be grouped together in :ref:`bundles <basic_concepts:Bundle>`.
24+
`Zero-value bundles`_ contain only zero value transactions, while
25+
`transfer bundles`_ may also contain input and output transactions.
26+
27+
But how to construct these bundles and send them to the network?
28+
29+
The process can be boiled down to 5 steps:
30+
31+
1. Create individual transaction(s).
32+
2. Construct a bundle from the transaction(s).
33+
3. Obtain references to two unconfirmed transactions ("tips") from the Tangle.
34+
4. Do proof-of-work for each transaction in the bundle.
35+
5. Send the bundle to the network.
36+
37+
38+
.. figure:: images/create_transfer.svg
39+
:scale: 100 %
40+
:alt: Process of sending a transfer in IOTA.
41+
42+
Process of creating and sending a transfer to the Tangle.
43+
44+
.. py:currentmodule:: iota
45+
46+
1. Create Transactions
47+
~~~~~~~~~~~~~~~~~~~~~~
48+
The first step is to create the individual transaction objects. You have to
49+
specify ``address`` and ``value`` for each transaction. Furthermore, you can
50+
define a ``tag``, and for zero-value transactions, a ``message``. A
51+
``timestamp`` is also required, though this value is usually auto-generated
52+
by the IOTA libraries.
53+
54+
.. note::
55+
Unlike on other decentralised ledgers, IOTA transactions can have positive
56+
*or* negative ``value`` amounts. In order to send iotas from one address to
57+
another, at least two transactions are required:
58+
59+
* one with *positive* ``value`` (to increment the balance of the receiver), and
60+
* one with *negative* ``value`` (to decrement the balance of the sender).
61+
62+
In PyOTA, use :py:class:`ProposedTransaction` to declare transactions.
63+
64+
2. Create Bundle from Transactions
65+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
66+
A bundle is a collection of transactions, treated as an atomic unit
67+
when sent to the network. A bundle makes a value (iota token) transfer possible by
68+
grouping together input and output transactions.
69+
70+
A bundle always has to be balanced: the sum of ``value`` attributes of the
71+
transactions in the bundle should always be zero. Transactions in the bundle
72+
are also indexed individually and contain information on how many other
73+
transactions there are in the bundle.
74+
75+
Once complete, a bundle has to be finalized to generate the bundle hash based
76+
on the `bundle essence`_. The bundle hash is the unique identifier of the
77+
bundle.
78+
79+
After finalization, input transactions in the bundle need to be signed to prove
80+
ownership of iotas being transferred.
81+
82+
.. tip::
83+
:py:class:`ProposedBundle` helps you in PyOTA to create bundles, add transactions,
84+
finalize the bundle and sign the inputs. We'll see how to use
85+
:py:class:`ProposedBundle` in :ref:`transfers:Use the Library` below.
86+
87+
3. Select two tips
88+
~~~~~~~~~~~~~~~~~~
89+
90+
Tips are transactions that are yet to be confirmed by the network. We can
91+
obtain two tips by requesting them from a node. In PyOTA, :py:meth:`~Iota.get_transactions_to_approve`
92+
does the job: it returns a ``trunk`` and a ``branch`` :py:class:`TransactionHash`.
93+
94+
Because our bundle references these two transactions, it will validate them once
95+
it is added to the Tangle.
96+
97+
4. Do Proof-of-Work
98+
~~~~~~~~~~~~~~~~~~~
99+
100+
The bundle has been finalized, inputs have been signed, and we have two tips;
101+
now it's time to prepare the bundle to be attached to the Tangle. As noted in
102+
the previous section, every transaction references two other transactions in
103+
the Tangle; therefore we need to select these references for each transaction
104+
in our bundle.
105+
106+
We also know that transactions `within the bundle are linked together`_ through
107+
their trunk references. So how do we construct the correct bundle structure
108+
and also reference two tips from the network?
109+
110+
.. figure:: images/bundle-structure.png
111+
:scale: 100 %
112+
:alt: Bundle structure with four transactions.
113+
114+
Structure of a bundle with four transactions. Numbers in brackets denote
115+
(``currentIndex``, ``lastIndex``) fields. Head of the bundle has index 3,
116+
while tail has index 0.
117+
118+
For all non-head transactions in the bundle, the trunk reference is the next
119+
transaction in the bundle, while the branch reference is the trunk transaction
120+
hash, one of the tips.
121+
122+
The head transaction is different: the trunk reference is the trunk tip, while
123+
the branch reference is the branch tip.
124+
125+
The proof-of-work calculation has to be done for each transaction individually,
126+
therefore the more transactions you have in the bundle, the more time it will
127+
take. The difficulty of the calculation also depends on the `minimum weight magnitude`_
128+
set by the network.
129+
130+
The output of the proof-of-work algorithm is a ``nonce`` value that is appended
131+
to the the transaction, resulting in the attached transaction trytes.
132+
Nodes validate the proof-of-work of a transaction by calculating the transaction's
133+
hash from the attached transaction trytes. If the resulting hash has at least
134+
``minimum weight magnitude`` number of trailing zero trits, the transaction is valid.
135+
136+
In PyOTA, use :py:meth:`~Iota.attach_to_tangle` to carry out this step.
137+
138+
5. Broadcast and Store
139+
~~~~~~~~~~~~~~~~~~~~~~
140+
141+
The final step is to send the bundle to the network. Nodes will broadcast
142+
the transactions in the network, and store them in their local database.
143+
144+
In PyOTA, use :py:meth:`~Iota.broadcast_and_store` to achieve this.
145+
146+
Observe the bird's-eye view of the Tangle depicted at the last step of the
147+
process. Our transactions are part of the Tangle, referencing each other and
148+
the two tips. Newer transactions may reference our transactions as branch or
149+
trunk.
150+
151+
.. note::
152+
As more transactions are added to the Tangle that reference our transactions
153+
– and then more are added that reference those transactions, and so on – this
154+
increases the `cumulative weight`_ of our transactions. The higher the
155+
cumulative weight of our transactions, the higher the chance for them to
156+
get confirmed.
157+
158+
Use the Library
159+
---------------
160+
161+
The IOTA libraries help you to abstract away the low-level operations needed
162+
to create transfers. The figure below illustrates the different ways you can
163+
build and send a transfer.
164+
165+
.. figure:: images/transfer_api.svg
166+
:scale: 100 %
167+
:alt: Different ways of sending a transfer in IOTA.
168+
169+
API commands for sending transfers.
170+
171+
Let's look at some code snippets on how to perform the above with an imaginary
172+
bundle that has 3 fictional transactions.
173+
174+
1. Level Padawan
175+
~~~~~~~~~~~~~~~~
176+
The easiest and most convenient way is to use :py:meth:`~Iota.send_transfer`
177+
extended API method. You still need to create the transactions yourself
178+
with :py:class:`ProposedTransaction`.
179+
180+
.. code-block::
181+
182+
from iota import Iota, ProposedTransaction, Address
183+
184+
api = Iota('https://nodes.devnet.iota.org:443')
185+
186+
fictional_transactions = [
187+
ProposedTransaction(
188+
address=Address(b'FIRSTRANDOMADDRESS'),
189+
value=0,
190+
# You could add a tag or message here too!
191+
),
192+
ProposedTransaction(
193+
address=Address(b'SECONDRANDOMADDRESS'),
194+
value=0,
195+
),
196+
ProposedTransaction(
197+
address=Address(b'THIRDRANDOMADDRESS'),
198+
value=0,
199+
)
200+
]
201+
202+
imaginary_bundle = api.send_transfer(
203+
transfers=transactions
204+
)['bundle']
205+
206+
As all API methods in PyOTA, :py:meth:`~Iota.send_transfer` also returns
207+
a ``dict``. The ``bundle`` key holds the value of :py:class:`Bundle`.
208+
209+
It's important to note, that for value transfers, you will need your seed as well.
210+
:py:meth:`~Iota.send_transfer` will look for ``input addresses`` to fund outgoing
211+
transactions in the bundle, and auto-generate an unused ``change address`` if
212+
there is a remainder amount of tokens. It will also take care of finalizing the
213+
bundle and signing the necessary input transactions.
214+
215+
2. Level Obi-Wan
216+
~~~~~~~~~~~~~~~~
217+
Instead of :py:meth:`~Iota.send_transfer`, you can use the combination of
218+
:py:meth:`~Iota.prepare_transfer` and :py:meth:`~Iota.send_trytes` to achieve
219+
the same result.
220+
221+
.. tip::
222+
This can be useful if you want to prepare the transactions (including signing inputs) on one device, but you want to then transfer the data to another device for transmission to the Tangle. For example, you might :py:meth:`~Iota.prepare_transfer` on an air-gapped computer that has your seed stored on it, but then transfer the resulting trytes to a networked computer (that does not have your seed) to :py:meth:`~Iota.send_trytes`.
223+
224+
.. code-block::
225+
226+
from iota import Iota, ProposedTransaction, Address
227+
228+
api = Iota('https://nodes.devnet.iota.org:443')
229+
230+
transactions = [
231+
ProposedTransaction(
232+
address=Address(b'FIRSTRANDOMADDRESS'),
233+
value=0,
234+
),
235+
ProposedTransaction(
236+
address=Address(b'SECONDRANDOMADDRESS'),
237+
value=0,
238+
),
239+
ProposedTransaction(
240+
address=Address(b'THIRDRANDOMADDRESS'),
241+
value=0,
242+
)
243+
]
244+
245+
prepared_trytes = api.prepare_transfer(
246+
transfers=transactions
247+
)['trytes']
248+
249+
imaginary_bundle_trytes = api.send_trytes(
250+
trytes=prepared_trytes
251+
)['trytes']
252+
253+
A difference here is that the end result, ``imaginary_bundle_trytes`` is a list
254+
of :py:class:`TransactionTrytes`, and not a :py:class:`Bundle` object.
255+
256+
3. Level Yoda
257+
~~~~~~~~~~~~~
258+
Being the master Jedi of the PyOTA universe means that you know the most about
259+
the force of low-level API methods. Use it wisely!
260+
261+
.. tip::
262+
You generally won't need to split out the process explicitly like this in your application code, but it is useful to understand what :py:meth:`~Iota.send_transfer` does under-the-hood, so that you are better-equipped to troubleshoot any issues that may occur during the process.
263+
264+
.. code-block::
265+
266+
from iota import Iota, ProposedTransaction, Address, ProposedBundle
267+
268+
api = Iota('https://nodes.devnet.iota.org:443')
269+
270+
transactions = [
271+
ProposedTransaction(
272+
address=Address(b'FIRSTRANDOMADDRESS'),
273+
value=0,
274+
),
275+
ProposedTransaction(
276+
address=Address(b'SECONDRANDOMADDRESS'),
277+
value=0,
278+
),
279+
ProposedTransaction(
280+
address=Address(b'THIRDRANDOMADDRESS'),
281+
value=0,
282+
)
283+
]
284+
285+
bundle = ProposedBundle()
286+
287+
for tx in transactions:
288+
bundle.add_transaction(tx)
289+
290+
# If it was a value transfer, we would also need to:
291+
# bundle.add_inputs()
292+
# bundle.send_unspent_inputs_to()
293+
294+
bundle.finalize()
295+
296+
# Again, for value transfers, we would need to:
297+
# bundle.sign_inputs(KeyGenerator(b'SEEDGOESHERE'))
298+
299+
gtta_response = api.get_transactions_to_approve(depth=3)
300+
301+
trunk = gtta_response['trunkTransaction']
302+
branch = gtta_response['branchTransaction']
303+
304+
attached_trytes = api.attach_to_tangle(
305+
trunk_transaction=trunk,
306+
branch_transaction=branch,
307+
trytes=bundle.as_tryte_strings()
308+
)['trytes']
309+
310+
api.broadcast_transactions(attached_trytes)
311+
312+
api.store_transactions(attached_trytes)
313+
314+
imaginary_bundle = Bundle.from_tryte_strings(attached_trytes)
315+
316+
317+
.. _transfer bundles: https://docs.iota.org/docs/getting-started/0.1/transactions/bundles#transfer-bundles
318+
.. _zero-value bundles: https://docs.iota.org/docs/getting-started/0.1/transactions/bundles#zero-value-bundle
319+
.. _bundle essence: https://docs.iota.org/docs/getting-started/0.1/transactions/bundles#bundle-essence
320+
.. _within the bundle are linked together: https://docs.iota.org/docs/getting-started/0.1/transactions/bundles
321+
.. _minimum weight magnitude: https://docs.iota.org/docs/getting-started/0.1/network/minimum-weight-magnitude
322+
.. _cumulative weight: https://blog.iota.org/the-tangle-an-illustrated-introduction-f359b8b2ec80

0 commit comments

Comments
 (0)