From d2901799d55fef0402fb29878b03be86425bf4b2 Mon Sep 17 00:00:00 2001 From: Levente Pap Date: Tue, 14 Jan 2020 15:12:10 +0100 Subject: [PATCH 1/4] docs: Add 'Hello World' tutorial --- docs/index.rst | 1 + docs/tutorials.rst | 93 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 docs/tutorials.rst diff --git a/docs/index.rst b/docs/index.rst index 3ecb0cf..2a6a0da 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -12,5 +12,6 @@ addresses multisig commands + tutorials .. include:: README.rst diff --git a/docs/tutorials.rst b/docs/tutorials.rst new file mode 100644 index 0000000..04943e0 --- /dev/null +++ b/docs/tutorials.rst @@ -0,0 +1,93 @@ +Tutorials +========= +Are you new to IOTA in Python? Don't worry, we got you covered! With the +walkthrough examples of this section, you will be a master of PyOTA. + +In each section below, a code snippet will be shown and discussed in detail +to help you understand how to carry out specific tasks with PyOTA. + +If you feel that something is missing or not clear, please post your questions +and suggestions in the `PyOTA Bug Tracker`_. + +Let's get to it then! + +Hello World +----------- +In this example, you will learn how to: + +- Import the ``iota`` package into your application. +- Instantiate an API object for communication with the IOTA network. +- Request information about the IOTA node you are connected to. + +.. py:currentmodule:: iota + +Code +~~~~ +.. highlight:: python + :linenothreshold: 5 + +.. code-block:: python + + # Import neccessary modules + from iota import Iota + from pprint import pprint + + # Declare an API object + api = Iota('https://nodes.devnet.iota.org:443') + + # Request information about the node + response = api.get_node_info() + + # Using pprint instead of print for a nicer looking result in the console + pprint(response) + +Discussion +~~~~~~~~~~ +:: + + # Import neccessary modules + from iota import Iota + from pprint import pprint + +First things first, we need to import in our application the modules we intend +to use. PyOTA provide the ``iota`` package, therefore, whenever you need +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`. + +We also import the ``pprint`` method that prettifies the output before printing +it to the output. + +:: + + # Declare an API object + api = Iota('https://nodes.devnet.iota.org:443') + +Next, we declare an API object. Since this object handles the communication, +we need to specify an IOTA node to connect to in the form of an URI. Note, that +the library will parse this string and will throw an exception if it is not +a valid one. + +:: + + # Request information about the node + response = api.get_node_info() + +Then we can call the :py:meth:`Iota.get_node_info` method of the API +object to get some basic info about the node. + +:: + + # Using pprint instead of print for a nicer looking result in the console + pprint(response) + +Finally, we print out the response. It is important to note, that all API +methods return a python dictionary. Refer to the method's documentation to +determine what exactly is there in the response ``dict``. Here for example, +we could list the ``features`` of the node:: + + pprint(response['features']) + +.. _PyOTA Bug Tracker: https://github.com/iotaledger/iota.py/issues \ No newline at end of file From b22e168011e34d07dbfc1237ee805d93dfd4e499 Mon Sep 17 00:00:00 2001 From: Levente Pap Date: Tue, 14 Jan 2020 17:34:08 +0100 Subject: [PATCH 2/4] docs: Add TryteString.random() to documentation --- docs/types.rst | 7 +++++++ iota/types.py | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/docs/types.rst b/docs/types.rst index b1780b1..0b24971 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -129,6 +129,13 @@ converted; garbage in, garbage out! ^^^^^^^^^^^^ .. automethod:: TryteString.as_trits +Generation +~~~~~~~~~~ + +**random** +^^^^^^^^^^ +.. automethod:: TryteString.random + Seed ---- .. autoclass:: Seed diff --git a/iota/types.py b/iota/types.py index 9ce1d19..abc7089 100644 --- a/iota/types.py +++ b/iota/types.py @@ -78,6 +78,10 @@ def random(cls, length=None): :return: :py:class:`TryteString` object. + + :raises TypeError: + - if ``length`` is negative, + - if ``length`` is not defined, and the class doesn't have ``LEN`` attribute. """ alphabet = list(itervalues(AsciiTrytesCodec.alphabet)) generator = SystemRandom() From fe5409ac9247cba82b5d5180eb3be1c6da809f1c Mon Sep 17 00:00:00 2001 From: Levente Pap Date: Tue, 14 Jan 2020 17:37:39 +0100 Subject: [PATCH 3/4] docs: Add 'Send Data' tutorial - store tutorial source code in examples/tutoials/ --- docs/tutorials.rst | 140 +++++++++++++++++++++++++--- examples/tutorials/01_hello_node.py | 12 +++ examples/tutorials/02_send_data.py | 28 ++++++ 3 files changed, 169 insertions(+), 11 deletions(-) create mode 100644 examples/tutorials/01_hello_node.py create mode 100644 examples/tutorials/02_send_data.py diff --git a/docs/tutorials.rst b/docs/tutorials.rst index 04943e0..fc24e27 100644 --- a/docs/tutorials.rst +++ b/docs/tutorials.rst @@ -11,15 +11,16 @@ and suggestions in the `PyOTA Bug Tracker`_. Let's get to it then! -Hello World ------------ +.. py:currentmodule:: iota + +Hello Node +---------- In this example, you will learn how to: -- Import the ``iota`` package into your application. -- Instantiate an API object for communication with the IOTA network. -- Request information about the IOTA node you are connected to. +- **Import the** ``iota`` **package into your application.** +- **Instantiate an API object for communication with the IOTA network.** +- **Request information about the IOTA node you are connected to.** -.. py:currentmodule:: iota Code ~~~~ @@ -43,7 +44,8 @@ Code Discussion ~~~~~~~~~~ -:: +.. code-block:: + :lineno-start: 1 # Import neccessary modules from iota import Iota @@ -60,7 +62,8 @@ the network. Read more about API objects at :ref:`PyOTA API Classes`. We also import the ``pprint`` method that prettifies the output before printing it to the output. -:: +.. code-block:: + :lineno-start: 5 # Declare an API object api = Iota('https://nodes.devnet.iota.org:443') @@ -70,7 +73,8 @@ we need to specify an IOTA node to connect to in the form of an URI. Note, that the library will parse this string and will throw an exception if it is not a valid one. -:: +.. code-block:: + :lineno-start: 8 # Request information about the node response = api.get_node_info() @@ -78,7 +82,8 @@ a valid one. Then we can call the :py:meth:`Iota.get_node_info` method of the API object to get some basic info about the node. -:: +.. code-block:: + :lineno-start: 11 # Using pprint instead of print for a nicer looking result in the console pprint(response) @@ -90,4 +95,117 @@ we could list the ``features`` of the node:: pprint(response['features']) -.. _PyOTA Bug Tracker: https://github.com/iotaledger/iota.py/issues \ No newline at end of file +Send Data +--------- +In this example, you will learn how to: + +- **Encode data to be stored on the Tangle.** +- **Generate a random IOTA address that doesn't belong to anyone.** +- **Create a zero-value transaction with custom payload.** +- **Send a transaction to the network.** + +Code +~~~~ +.. literalinclude:: ../examples/tutorials/02_send_data.py + :linenos: + +Discussion +~~~~~~~~~~ +.. code-block:: + :lineno-start: 1 + + from iota import Iota, TryteString, Address, Tag, ProposedTransaction + from pprint import pprint + + # Declare an API object + api = Iota('https://nodes.devnet.iota.org:443', testnet=True) + +We have seen this part before. Note, that now we import more objects which we +will use to construct our transaction. + +Notice ``testnet=True`` in the argument list of the API instantiation. We +tell the API directly that we will use the devnet/testnet. By default, the API +is configured for the mainnet. + +.. code-block:: + :lineno-start: 7 + + # Prepare custom data + my_data = TryteString.from_unicode('Hello from the Tangle!') + +If you read :ref:`Basic Concepts` and :ref:`PyOTA Types`, it shouldn't be a +surprise to you that most things in IOTA are represented as trytes, that are +:py:class:`TryteString` in PyOTA. + +Here, we encode our message with :py:meth:`TryteString.from_unicode` into +trytes. + +.. code-block:: + :lineno-start: 10 + + # Generate a random address that doesn't have to belong to anyone + my_address = Address.random() + +To put anything (transactions) on the Tangle, it needs to be associated with +an address. **Since we will be posting a zero-value transaction, nobody has to +own this address**, therefore we can use the :py:meth:`TryteString.random` (an +:py:class:`Address` is just a :py:class:`TryteString` with some additional +attributes and fixed length) method to generate one. + +.. code-block:: + :lineno-start: 13 + + # Tag is optional here + my_tag = Tag(b'MY9FIRST9TAG') + +To tag our transaction, we might define a custom :py:class:`Tag` object. Notice, +that the ``b`` means we are creating a bytestring now. Each byte in it is +interpreted as a tryte, therefore we are restricted to the tryte aphabet. + +.. code-block:: + :lineno-start: 16 + + # Prepare a transaction object + tx = ProposedTransaction( + address=my_address, + value=0, + tag=my_tag, + message=my_data + ) + +It's time to construct the transaction. According to :ref:`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. + +.. code-block:: + :lineno-start: 24 + + # Send the transaction to the network + response = api.send_transfer([tx]) + +Next, we send the transfer to the node for tip selection, +proof-of-work calculation, broadcasting and storing. The API takes care of +all these tasks, and returns the resulting ``Bundle`` object. + +.. note:: + + :py:meth:`~Iota.send_transfer` takes a list of :py:class:`ProposedTransaction` + objects as its ``transfers`` argument. An IOTA transfer (bundle) usually + consists of multiple transactions linked together, however, in this simple + example, there is only one transaction in the bundle. Regardless, you need + to pass this sole transaction as a list of one transaction. + +.. code-block:: + :lineno-start: 27 + + pprint('Check your transaction on the Tangle!') + pprint('https://utils.iota.org/transaction/%s/devnet' % response['bundle'][0].hash) + +Finally, we print out the transaction's link on the Tangle Explorer. +Observe how we extract the transaction hash from the response ``dict``. We take +the first element of the bundle, as it is just a sequence of transactions, and +access its ``hash`` attribute. + +.. _PyOTA Bug Tracker: https://github.com/iotaledger/iota.py/issues +.. _Tangle Explorer: https://utils.iota.org \ No newline at end of file diff --git a/examples/tutorials/01_hello_node.py b/examples/tutorials/01_hello_node.py new file mode 100644 index 0000000..456b088 --- /dev/null +++ b/examples/tutorials/01_hello_node.py @@ -0,0 +1,12 @@ +# Import neccessary modules +from iota import Iota +from pprint import pprint + +# Declare an API object +api = Iota('https://nodes.devnet.iota.org:443') + +# Request information about the node +response = api.get_node_info() + +# Using pprint instead of print for a nicer looking result in the console +pprint(response) \ No newline at end of file diff --git a/examples/tutorials/02_send_data.py b/examples/tutorials/02_send_data.py new file mode 100644 index 0000000..5732a5a --- /dev/null +++ b/examples/tutorials/02_send_data.py @@ -0,0 +1,28 @@ +from iota import Iota, TryteString, Address, Tag, ProposedTransaction +from pprint import pprint + +# Declare an API object +api = Iota('https://nodes.devnet.iota.org:443', testnet=True) + +# Prepare custom data +my_data = TryteString.from_unicode('Hello from the Tangle!') + +# Generate a random address that doesn't have to belong to anyone +my_address = Address.random() + +# Tag is optional here +my_tag = Tag(b'MY9FIRST9TAG') + +# Prepare a transaction object +tx = ProposedTransaction( + address=my_address, + value=0, + tag=my_tag, + message=my_data +) + +# Send the transaction to the network +response = api.send_transfer([tx]) + +pprint('Check your transaction on the Tangle!') +pprint('https://utils.iota.org/transaction/%s/devnet' % response['bundle'][0].hash) \ No newline at end of file From b3e3172f87e34a40794244a69b9830d4e52d2e4a Mon Sep 17 00:00:00 2001 From: Levente Pap Date: Wed, 15 Jan 2020 11:30:12 +0100 Subject: [PATCH 4/4] docs: Improve tutorial 1 and 2 --- docs/tutorials.rst | 124 ++++++++++++++++----------------------------- 1 file changed, 43 insertions(+), 81 deletions(-) diff --git a/docs/tutorials.rst b/docs/tutorials.rst index fc24e27..6be15eb 100644 --- a/docs/tutorials.rst +++ b/docs/tutorials.rst @@ -24,32 +24,14 @@ In this example, you will learn how to: Code ~~~~ -.. highlight:: python - :linenothreshold: 5 - -.. code-block:: python - - # Import neccessary modules - from iota import Iota - from pprint import pprint - - # Declare an API object - api = Iota('https://nodes.devnet.iota.org:443') - - # Request information about the node - response = api.get_node_info() - - # Using pprint instead of print for a nicer looking result in the console - pprint(response) +.. literalinclude:: ../examples/tutorials/01_hello_node.py + :linenos: Discussion ~~~~~~~~~~ -.. code-block:: - :lineno-start: 1 - - # Import neccessary modules - from iota import Iota - from pprint import pprint +.. literalinclude:: ../examples/tutorials/01_hello_node.py + :lines: 1-3 + :lineno-start: 1 First things first, we need to import in our application the modules we intend to use. PyOTA provide the ``iota`` package, therefore, whenever you need @@ -60,24 +42,20 @@ 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`. We also import the ``pprint`` method that prettifies the output before printing -it to the output. - -.. code-block:: - :lineno-start: 5 +it to the console. - # Declare an API object - api = Iota('https://nodes.devnet.iota.org:443') +.. literalinclude:: ../examples/tutorials/01_hello_node.py + :lines: 5-6 + :lineno-start: 5 Next, we declare an API object. Since this object handles the communication, we need to specify an IOTA node to connect to in the form of an URI. Note, that the library will parse this string and will throw an exception if it is not a valid one. -.. code-block:: - :lineno-start: 8 - - # Request information about the node - response = api.get_node_info() +.. literalinclude:: ../examples/tutorials/01_hello_node.py + :lines: 8-9 + :lineno-start: 8 Then we can call the :py:meth:`Iota.get_node_info` method of the API object to get some basic info about the node. @@ -90,7 +68,7 @@ object to get some basic info about the node. Finally, we print out the response. It is important to note, that all API methods return a python dictionary. Refer to the method's documentation to -determine what exactly is there in the response ``dict``. Here for example, +determine what exactly is in the response ``dict``. Here for example, we could list the ``features`` of the node:: pprint(response['features']) @@ -111,14 +89,9 @@ Code Discussion ~~~~~~~~~~ -.. code-block:: - :lineno-start: 1 - - from iota import Iota, TryteString, Address, Tag, ProposedTransaction - from pprint import pprint - - # Declare an API object - api = Iota('https://nodes.devnet.iota.org:443', testnet=True) +.. literalinclude:: ../examples/tutorials/02_send_data.py + :lines: 1-5 + :lineno-start: 1 We have seen this part before. Note, that now we import more objects which we will use to construct our transaction. @@ -127,11 +100,9 @@ Notice ``testnet=True`` in the argument list of the API instantiation. We tell the API directly that we will use the devnet/testnet. By default, the API is configured for the mainnet. -.. code-block:: - :lineno-start: 7 - - # Prepare custom data - my_data = TryteString.from_unicode('Hello from the Tangle!') +.. literalinclude:: ../examples/tutorials/02_send_data.py + :lines: 7-8 + :lineno-start: 7 If you read :ref:`Basic Concepts` and :ref:`PyOTA Types`, it shouldn't be a surprise to you that most things in IOTA are represented as trytes, that are @@ -140,49 +111,40 @@ surprise to you that most things in IOTA are represented as trytes, that are Here, we encode our message with :py:meth:`TryteString.from_unicode` into trytes. -.. code-block:: - :lineno-start: 10 - - # Generate a random address that doesn't have to belong to anyone - my_address = Address.random() +.. literalinclude:: ../examples/tutorials/02_send_data.py + :lines: 10-11 + :lineno-start: 10 To put anything (transactions) on the Tangle, it needs to be associated with an address. **Since we will be posting a zero-value transaction, nobody has to -own this address**, therefore we can use the :py:meth:`TryteString.random` (an +own this address**; therefore we can use the :py:meth:`TryteString.random` (an :py:class:`Address` is just a :py:class:`TryteString` with some additional attributes and fixed length) method to generate one. -.. code-block:: - :lineno-start: 13 +.. literalinclude:: ../examples/tutorials/02_send_data.py + :lines: 13-14 + :lineno-start: 13 - # Tag is optional here - my_tag = Tag(b'MY9FIRST9TAG') +To tag our transaction, we might define a custom :py:class:`Tag` object. +Notice, that the ``b`` means we are passing a `bytestring`_ value instead of a +unicode string. This is so that PyOTA interprets our input as literal trytes, +rather than a unicode string that needs to be encoded into trytes. -To tag our transaction, we might define a custom :py:class:`Tag` object. Notice, -that the ``b`` means we are creating a bytestring now. Each byte in it is -interpreted as a tryte, therefore we are restricted to the tryte aphabet. +When passing a bytestring to a PyOTA class, each byte is interpreted as a tryte; +therefore we are restricted to the `tryte alphabet`_. -.. code-block:: - :lineno-start: 16 - - # Prepare a transaction object - tx = ProposedTransaction( - address=my_address, - value=0, - tag=my_tag, - message=my_data - ) +.. literalinclude:: ../examples/tutorials/02_send_data.py + :lines: 16-22 + :lineno-start: 16 It's time to construct the transaction. According to :ref:`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. -.. code-block:: - :lineno-start: 24 - - # Send the transaction to the network - response = api.send_transfer([tx]) +.. literalinclude:: ../examples/tutorials/02_send_data.py + :lines: 24-25 + :lineno-start: 24 Next, we send the transfer to the node for tip selection, proof-of-work calculation, broadcasting and storing. The API takes care of @@ -196,11 +158,9 @@ all these tasks, and returns the resulting ``Bundle`` object. example, there is only one transaction in the bundle. Regardless, you need to pass this sole transaction as a list of one transaction. -.. code-block:: - :lineno-start: 27 - - pprint('Check your transaction on the Tangle!') - pprint('https://utils.iota.org/transaction/%s/devnet' % response['bundle'][0].hash) +.. literalinclude:: ../examples/tutorials/02_send_data.py + :lines: 27-28 + :lineno-start: 27 Finally, we print out the transaction's link on the Tangle Explorer. Observe how we extract the transaction hash from the response ``dict``. We take @@ -208,4 +168,6 @@ the first element of the bundle, as it is just a sequence of transactions, and access its ``hash`` attribute. .. _PyOTA Bug Tracker: https://github.com/iotaledger/iota.py/issues +.. _bytestring: https://docs.python.org/3/library/stdtypes.html#bytes +.. _tryte alphabet: https://docs.iota.org/docs/getting-started/0.1/introduction/ternary#tryte-encoding .. _Tangle Explorer: https://utils.iota.org \ No newline at end of file