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

Commit 6072a83

Browse files
authored
Merge pull request #290 from lzpap/tutorial_4
docs: Add new tutorials 4a, 4b, 4c and 5
2 parents 5d9a950 + 603815c commit 6072a83

File tree

5 files changed

+390
-1
lines changed

5 files changed

+390
-1
lines changed

docs/tutorials.rst

Lines changed: 280 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ walkthrough examples of this section, you will be a master of PyOTA.
66
In each section below, a code snippet will be shown and discussed in detail
77
to help you understand how to carry out specific tasks with PyOTA.
88

9+
The example scripts displayed here can also be found under ``examples/tutorials/``
10+
directory in the repository. Run them in a Python environment that has PyOTA
11+
installed. See :ref:`Install PyOTA` for more info.
12+
913
If you feel that something is missing or not clear, please post your questions
1014
and suggestions in the `PyOTA Bug Tracker`_.
1115

@@ -228,7 +232,282 @@ method to drop values we can't decode using ``utf-8``, or if the raw trytes
228232
can't be decoded into legit bytes. A possible reason for the latter can be if
229233
the attribute contains a signature rather than a message.
230234

235+
4.a Generate Address
236+
--------------------
237+
238+
In this example, you will learn how to:
239+
240+
- **Generate a random seed.**
241+
- **Generate an IOTA address that belongs to your seed.**
242+
- **Acquire free devnet IOTA tokens that you can use to play around with.**
243+
244+
Code
245+
~~~~
246+
.. literalinclude:: ../examples/tutorials/04a_gen_address.py
247+
:linenos:
248+
249+
Discussion
250+
~~~~~~~~~~
251+
.. literalinclude:: ../examples/tutorials/04a_gen_address.py
252+
:lines: 1-7
253+
:lineno-start: 1
254+
255+
We start off by generating a random seed with the help of the library. You are
256+
also free to use your own seed, just uncomment line 6 and put it there.
257+
258+
If you choose to generate one, your seed is written to the console so that you
259+
can save it for later. Be prepared to do so, because you will have to use it
260+
in the following tutorials.
261+
262+
.. literalinclude:: ../examples/tutorials/04a_gen_address.py
263+
:lines: 9-14
264+
:lineno-start: 9
265+
266+
Notice, how we pass the ``seed`` argument to the API class's init method.
267+
Whenever the API needs to work with addresses or private keys, it will derive
268+
them from this seed.
269+
270+
.. important::
271+
272+
Your seed never leaves the library and your computer. Treat your (mainnet)
273+
seed like any other password for a financial service: safe. If your seed is
274+
compromised, attackers can steal your funds.
275+
276+
.. literalinclude:: ../examples/tutorials/04a_gen_address.py
277+
:lines: 16-20
278+
:lineno-start: 16
279+
280+
To generate a new address, we call :py:meth:`~Iota.get_new_addresses`
281+
extended API method. Without arguments, this will return a ``dict`` with the
282+
first unused address starting from ``index`` 0. An unused address is address
283+
that has no transactions referencing it on the Tangle and was never spent from.
284+
285+
If we were to generate more addresses starting from a desired index,
286+
we could specify the ``start`` and ``count`` parameters. Read more about how to
287+
generate addresses in PyOTA at :ref:`Generating Addresses`.
288+
289+
On line 20 we access the first element of the list of addresses in the response
290+
dictionary.
291+
292+
.. literalinclude:: ../examples/tutorials/04a_gen_address.py
293+
:lines: 22-23
294+
:lineno-start: 22
295+
296+
Lastly, the address is printed to the console, so that you can copy it.
297+
Visit https://faucet.devnet.iota.org/ and enter the address to receive free
298+
devnet tokens of 1000i.
299+
300+
You might need to wait 1-2 minutes until the sum arrives to you address. To
301+
check your balance, go to `4.b Check Balance`_ or `4.c Get Account Data`_.
302+
303+
4.b Check Balance
304+
-----------------
305+
306+
In this example, you will learn how to:
307+
308+
- **Check the balance of a specific IOTA address.**
309+
310+
Code
311+
~~~~
312+
.. literalinclude:: ../examples/tutorials/04b_check_balance.py
313+
:linenos:
314+
315+
Discussion
316+
~~~~~~~~~~
317+
.. literalinclude:: ../examples/tutorials/04b_check_balance.py
318+
:lines: 1-8
319+
:lineno-start: 1
320+
321+
The first step to check the balance of an address is to actually have an
322+
address. Exchange the sample address on line 5 with your generated address from
323+
`4.a Generate Address`_.
324+
325+
Since we don't need to generate an address, there is no need for a seed to be
326+
employed in the API object. Note the ``time`` import, we need it for later.
327+
328+
.. literalinclude:: ../examples/tutorials/04b_check_balance.py
329+
:lines: 10-25
330+
:lineno-start: 10
331+
332+
Our script will poll the network for the address balance as long as the returned
333+
balance is zero. Therefore, the address you declared as ``my_address`` should
334+
have some balance. If you see the ``Zero balance found...`` message a couple of
335+
times, head over to https://faucet.devnet.iota.org/ and load up your address.
336+
337+
:py:meth:`~Iota.get_balances` returns the confirmed balance of the address.
338+
You could supply multiple addresses at the same time and get their respective
339+
balances in a single call. Don't forget, that the method returns a ``dict``.
340+
More details about it can be found at :py:meth:`~Iota.get_balances`.
341+
342+
4.c Get Account Data
343+
--------------------
344+
345+
In this example, you will learn how to:
346+
347+
- **Gather addresses, balance and bundles associated with your seed on the Tangle.**
348+
349+
.. warning::
350+
351+
**Account** in the context of this example is not to be confused with the
352+
`Account Module`_, that is a feature yet to be implemented in PyOTA.
353+
354+
**Account** here simply means the addresses and funds that belong to your
355+
seed.
356+
357+
Code
358+
~~~~
359+
.. literalinclude:: ../examples/tutorials/04c_get_acc_data.py
360+
:linenos:
361+
362+
Discussion
363+
~~~~~~~~~~
364+
.. literalinclude:: ../examples/tutorials/04c_get_acc_data.py
365+
:lines: 1-3
366+
:lineno-start: 1
367+
368+
We will need ``pprint`` for a prettified output of the response ``dict`` and
369+
``time`` for polling until we find non-zero balance.
370+
371+
.. literalinclude:: ../examples/tutorials/04c_get_acc_data.py
372+
:lines: 5-13
373+
:lineno-start: 5
374+
375+
Copy your seed from `4.a Generate Address`_ onto line 6. The API will use your
376+
seed to generate addresses and look for corresponding transactions on the
377+
Tangle.
378+
379+
.. literalinclude:: ../examples/tutorials/04c_get_acc_data.py
380+
:lines: 15-30
381+
:lineno-start: 15
382+
383+
Just like in the prevoius example, we will poll for information until we find
384+
a non-zero balance. :py:meth:`~Iota.get_account_data` without arguments
385+
generates addresses from ``index`` 0 until it finds the first unused. Then, it
386+
queries the node about bundles of those addresses and sums up their balance.
387+
388+
.. note::
389+
390+
If you read :py:meth:`~Iota.get_account_data` documentation carefully, you
391+
notice that you can gain control over which addresses are checked during
392+
the call by specifying the ``start`` and ``stop`` index parameters.
393+
394+
This can be useful when your addresses with funds do not follow each other
395+
in the address namespace, or a snapshot removed transactions from the
396+
Tangle. It is recommended that you keep a local database of your already
397+
used address indices.
398+
399+
Once implemented in PyOTA, `Account Module`_ will address the aforementioned
400+
problems.
401+
402+
The response ``dict`` contains the addresses, bundles and total balance of
403+
your seed.
404+
405+
5. Send Tokens
406+
--------------
407+
408+
In this example, you will learn how to:
409+
410+
- **Construct a value transfer with PyOTA.**
411+
- **Send a value transfer to an arbitrary IOTA address.**
412+
- **Analyze a bundle of transactions on the Tangle.**
413+
414+
.. note::
415+
416+
As a prerequisite to this tutorial, you need to have completed
417+
`4.a Generate Address`_, and have a seed that owns devnet tokens.
418+
419+
Code
420+
~~~~
421+
.. literalinclude:: ../examples/tutorials/05_send_tokens.py
422+
:linenos:
423+
424+
Discussion
425+
~~~~~~~~~~
426+
.. literalinclude:: ../examples/tutorials/05_send_tokens.py
427+
:lines: 1-11
428+
:lineno-start: 1
429+
430+
We are going to send a value transaction, that requires us to prove that we
431+
own the address containg the funds to spend. Therefore, we need our seed from
432+
which the address was generated.
433+
434+
Put your seed from `4.a Generate Address`_ onto line 4. We pass this seed to
435+
the API object, that will utilize it for signing the transfer.
436+
437+
.. literalinclude:: ../examples/tutorials/05_send_tokens.py
438+
:lines: 13-16
439+
:lineno-start: 13
440+
441+
In IOTA, funds move accross addresses, therefore we need to define a **receiver
442+
address**. For testing value transfers, you should send the funds only to
443+
addresses that you control; if you use a randomly-generated receiver address,
444+
you won't be able to recover the funds afterward!
445+
Re-run `4.a Generate Address`_ for a new seed and a new address, or just paste
446+
a valid IOTA address that you own onto line 16.
447+
448+
.. literalinclude:: ../examples/tutorials/05_send_tokens.py
449+
:lines: 18-25
450+
:lineno-start: 18
451+
452+
We declare a :py:class:`ProposedTransaction` object like we did before, but
453+
this time, with ``value=1`` parameter. The smallest value you can send is 1
454+
iota ("1i"), there is no way to break it into smaller chunks. It is a really small
455+
value anyway. You can also attach a message to the transaction, for example a
456+
little note to the beneficiary of the payment.
457+
458+
.. literalinclude:: ../examples/tutorials/05_send_tokens.py
459+
:lines: 27-29
460+
:lineno-start: 27
461+
462+
To actually send the transfer, all you need to do is call
463+
:py:meth:`~Iota.send_transfer` extended API method. This method will take care
464+
of:
465+
466+
- Gathering ``inputs`` (addresses you own and have funds) to fund the 1i transfer.
467+
- Generating a new ``change_address``, and automatically sending the remaining
468+
funds (``balance of chosen inputs`` - 1i) from ``inputs`` to ``change_address``.
469+
470+
.. warning::
471+
472+
This step is extremely important, as it prevents you from `spending twice
473+
from the same address`_.
474+
475+
When an address is used as an input, all tokens will be withdrawn. Part
476+
of the tokens will be used to fund your transaction, the rest will be
477+
transferred to ``change_address``.
478+
479+
- Constructing the transfer bundle with necessary input and output transactions.
480+
- Finalizing the bundle and signing the spending transactions.
481+
- Doing proof-of-work for each transaction in the bundle and sending it to the
482+
network.
483+
484+
.. literalinclude:: ../examples/tutorials/05_send_tokens.py
485+
:lines: 31-32
486+
:lineno-start: 31
487+
488+
Open the link and observe the bundle you have just sent to the Tangle. Probably
489+
it will take a couple of seconds for the network to confirm it.
490+
491+
What you see is a bundle with 4 transactions in total, 1 input and 3 outputs.
492+
But why are there so many transactions?
493+
494+
- There is one transaction that withdraws iotas, this has negative value.
495+
To authorize this spending, a valid signature is included in the transaction's
496+
``signature_message_fragment`` field. The signature however is too long to
497+
fit into one transaction, therefore the library appends a new, zero-value
498+
transaction to the bundle that holds the second part of the signature. This
499+
you see on the output side of the bundle.
500+
- A 1i transaction to the receiver address spends part of the withdrawn amount.
501+
- The rest is transfered to ``change_address`` in a new output transaction.
502+
503+
Once the bundle is confirmed, try rerunning the script from
504+
`4.c Get Account Data`_ with the same seed as in this tutorial. Your balance
505+
should be decremented by 1i, and you should see a new address, which was
506+
actually the ``change_address``.
507+
231508
.. _PyOTA Bug Tracker: https://github.com/iotaledger/iota.py/issues
232509
.. _bytestring: https://docs.python.org/3/library/stdtypes.html#bytes
233510
.. _tryte alphabet: https://docs.iota.org/docs/getting-started/0.1/introduction/ternary#tryte-encoding
234-
.. _Tangle Explorer: https://utils.iota.org
511+
.. _Tangle Explorer: https://utils.iota.org
512+
.. _Account Module: https://docs.iota.org/docs/client-libraries/0.1/account-module/introduction/overview
513+
.. _spending twice from the same address: https://docs.iota.org/docs/getting-started/0.1/clients/addresses#spent-addresses

examples/tutorials/04a_gen_address.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
from iota import Iota, Seed
2+
3+
# Generate a random seed, or use one you already have (for the devnet)
4+
print('Generating a random seed...')
5+
my_seed = Seed.random()
6+
# my_seed = Seed(b'MYCUSTOMSEED')
7+
print('Your seed is: ' + str(my_seed))
8+
9+
# Declare an API object
10+
api = Iota(
11+
adapter='https://nodes.devnet.iota.org:443',
12+
seed=my_seed,
13+
testnet=True,
14+
)
15+
16+
print('Generating the first unused address...')
17+
# Generate the first unused address from the seed
18+
response = api.get_new_addresses()
19+
20+
addy = response['addresses'][0]
21+
22+
print('Your new address is: ' + str(addy))
23+
print('Go to https://faucet.devnet.iota.org/ and enter you address to receive free devnet tokens.')
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
from iota import Iota, Address
2+
import time
3+
4+
# Put your address from Tutorial 4.a here
5+
my_address = Address(b'YOURADDRESSFROMTHEPREVIOUSTUTORIAL')
6+
7+
# Declare an API object
8+
api = Iota(adapter='https://nodes.devnet.iota.org:443', testnet=True)
9+
10+
# Script actually runs until you load up your address
11+
success = False
12+
13+
while not success:
14+
print('Checking balance on the Tangle for a specific address...')
15+
# API method to check balance
16+
response = api.get_balances(addresses=[my_address])
17+
18+
# response['balances'] is a list!
19+
if response['balances'][0]:
20+
print('Found the following information for address ' + str(my_address) + ':')
21+
print('Balance: ' + str(response['balances'][0]) + 'i')
22+
success = True
23+
else:
24+
print('Zero balance found, retrying in 30 seconds...')
25+
time.sleep(30)
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
from iota import Iota, Seed
2+
from pprint import pprint
3+
import time
4+
5+
# Put your seed from Tutorial 4.a here
6+
my_seed = Seed(b'YOURSEEDFROMTHEPREVIOUSTUTORIAL99999999999999999999999999999999999999999999999999')
7+
8+
# Declare an API object
9+
api = Iota(
10+
adapter='https://nodes.devnet.iota.org:443',
11+
seed=my_seed,
12+
testnet=True
13+
)
14+
15+
# Script actually runs until it finds balance
16+
success = False
17+
18+
while not success:
19+
print('Checking account information on the Tangle...')
20+
# Gather addresses, balance and bundles
21+
response = api.get_account_data()
22+
23+
# response['balance'] is an integer!
24+
if response['balance']:
25+
print('Found the following information based on your seed:')
26+
pprint(response)
27+
success = True
28+
else:
29+
print('Zero balance found, retrying in 30 seconds...')
30+
time.sleep(30)

0 commit comments

Comments
 (0)