@@ -380,7 +380,7 @@ Tangle.
380
380
:lines: 15-30
381
381
:lineno-start: 15
382
382
383
- Just like in the prevoius example, we will poll for information until we find
383
+ Just like in the previous example, we will poll for information until we find
384
384
a non-zero balance. :py:meth: `~Iota.get_account_data ` without arguments
385
385
generates addresses from ``index `` 0 until it finds the first unused. Then, it
386
386
queries the node about bundles of those addresses and sums up their balance.
@@ -505,9 +505,222 @@ Once the bundle is confirmed, try rerunning the script from
505
505
should be decremented by 1i, and you should see a new address, which was
506
506
actually the ``change_address ``.
507
507
508
+ 6. Store Encrypted Data
509
+ -----------------------
510
+
511
+ In this example, you will learn how to:
512
+
513
+ - **Convert Python data structures to JSON format. **
514
+ - **Encrypt data and include it in a zero-value transaction. **
515
+ - **Store the zero-value transaction with encrypted data on the Tangle. **
516
+
517
+ .. warning ::
518
+
519
+ We will use the ``simple-crypt `` external library for encryption/decryption.
520
+ Before proceeding to the tutorial, make sure you install it by running::
521
+
522
+ pip install simple-crypt
523
+
524
+ Code
525
+ ~~~~
526
+ .. literalinclude :: ../examples/tutorials/06_store_encrypted.py
527
+ :linenos:
528
+
529
+ Discussion
530
+ ~~~~~~~~~~
531
+ .. literalinclude :: ../examples/tutorials/06_store_encrypted.py
532
+ :lines: 1-18
533
+ :lineno-start: 1
534
+
535
+ We will use the ``encrypt `` method to encipher the data, and ``b64encode `` for
536
+ representing it as ASCII characters. ``getpass `` will prompt the user for a
537
+ password, and the ``json `` library is used for JSON formatting.
538
+
539
+ We will need an address to upload the data, therefore we need to supply the
540
+ seed to the ``Iota `` API instance. The address will be generated from this
541
+ seed.
542
+
543
+ .. literalinclude :: ../examples/tutorials/06_store_encrypted.py
544
+ :lines: 20-26
545
+ :lineno-start: 20
546
+
547
+ The data to be stored is considered confidential information, therefore we
548
+ can't just put it on the Tangle as plaintext so everyone can read it. Think of
549
+ what would happen if the world's most famous secret agent's identity was leaked
550
+ on the Tangle...
551
+
552
+ .. literalinclude :: ../examples/tutorials/06_store_encrypted.py
553
+ :lines: 28-29
554
+ :lineno-start: 28
555
+
556
+ Notice, that ``data `` is a Python ``dict `` object. As a common way of exchanging
557
+ data on the web, we would like to convert it to JSON format. The ``json.dumps() ``
558
+ method does exactly that, and the result is a JSON formatted plaintext.
559
+
560
+ .. literalinclude :: ../examples/tutorials/06_store_encrypted.py
561
+ :lines: 31-40
562
+ :lineno-start: 31
563
+
564
+ Next, we will encrypt this data with a secret password we obtain from the user.
565
+
566
+ .. note ::
567
+
568
+ When you run this example, please remember the password at least until the
569
+ next tutorial!
570
+
571
+ The output of the ``encrypt `` method is a ``bytes `` object in Python3 and
572
+ contains many special characters. This is a problem, since we can only convert
573
+ ASCII characters from ``bytes `` directly into :py:class: `TryteString `.
574
+
575
+ Therefore, we first encode our binary data into ASCII characters with `Base64 `_
576
+ encoding.
577
+
578
+ .. literalinclude :: ../examples/tutorials/06_store_encrypted.py
579
+ :lines: 42-58
580
+ :lineno-start: 42
581
+
582
+ Now, we are ready to construct the transfer. We convert the encrypted `Base64 `_
583
+ encoded data to trytes and assign it to the :py:class: `ProposedTransaction `
584
+ object's ``message `` argument.
585
+
586
+ An address is also needed, so we generate one with the help of
587
+ :py:meth: `~Iota.get_new_addresses ` extended API method. Feel free to choose the
588
+ index of the generated address, and don't forget, that the method returns a
589
+ ``dict `` with a list of addresses, even if it contains only one.
590
+ For more detailed explanation on how addresses are generated in PyOTA,
591
+ refer to the :ref: `Generating Addresses ` page.
592
+
593
+ We also attach a custom :py:class: `Tag ` to our :py:class: `ProposedTransaction `.
594
+ Note, that if our ``trytes_encrypted_data `` was longer than the maximum payload
595
+ of a transaction, the library would split it accross more transactions that
596
+ together form the transfer bundle.
597
+
598
+ .. literalinclude :: ../examples/tutorials/06_store_encrypted.py
599
+ :lines: 60-66
600
+ :lineno-start: 60
601
+
602
+ Finally, we use :py:meth: `Iota.send_transfer ` to prepare the transfer and
603
+ send it to the network.
604
+
605
+ Click on the link to check your transaction on the Tangle Explorer.
606
+
607
+ The tail transaction (a tail transaction is the one with index 0 in the bundle)
608
+ hash is printed on the console, because you will need it in the next tutorial,
609
+ and anyway, it is a good practice to keep a reference to your transfers.
610
+
611
+ In the next example, we will try to decode the confidential information from
612
+ the Tangle.
613
+
614
+ 7. Fetch Encrypted Data
615
+ -----------------------
616
+
617
+ In this example, you will learn how to:
618
+
619
+ - **Fetch bundles from the Tangle based on their tail transaction hashes. **
620
+ - **Extract messages from a bundle. **
621
+ - **Decrypt encrypted messages from a bundle. **
622
+
623
+ .. warning ::
624
+
625
+ We will use the ``simple-crypt `` external library for encryption/decryption.
626
+ Before proceeding to the tutorial, make sure you install it by running::
627
+
628
+ pip install simple-crypt
629
+
630
+ Code
631
+ ~~~~
632
+ .. literalinclude :: ../examples/tutorials/07_fetch_encrypted.py
633
+ :linenos:
634
+
635
+ Discussion
636
+ ~~~~~~~~~~
637
+ .. literalinclude :: ../examples/tutorials/07_fetch_encrypted.py
638
+ :lines: 1-14
639
+ :lineno-start: 1
640
+
641
+ In contrast to `6. Store Encrypted Data `_ where we intended to encrypt data, in
642
+ this tutorial we will do the reverse, and decrypt data from the Tangle.
643
+ Therefore, we need the ``decrypt `` method from ``simplecrypt `` library and the
644
+ ``b64decode `` method from ``base64 `` library.
645
+
646
+ Furthermore, ``getpass `` is needed to prompt the user for a decryption
647
+ password, and ``json `` for deserializing JSON formatted string into Python
648
+ object.
649
+
650
+ .. literalinclude :: ../examples/tutorials/07_fetch_encrypted.py
651
+ :lines: 16-17
652
+ :lineno-start: 16
653
+
654
+ To fetch transactions or bundles from the Tangle, a reference is required to
655
+ retreive them from the network. Transactions are identified by their
656
+ transaction hash, while a group of transaction (a bundle) by bundle hash.
657
+ Hashes ensure the integrity of the Tangle, since they contain verifiable
658
+ information about the content of the transfer objects.
659
+
660
+ ``input() `` asks the user to give the tail transaction hash of the bundle
661
+ that holds the encrypted messages. The tail transaction is the first in the
662
+ bundle with index 0. Copy and paste the tail transaction hash from the console
663
+ output of `6. Store Encrypted Data `_ when prompted.
664
+
665
+ .. literalinclude :: ../examples/tutorials/07_fetch_encrypted.py
666
+ :lines: 19-21
667
+ :lineno-start: 19
668
+
669
+ Next, we fetch the bundle from the Tangle with the help of the
670
+ :py:meth: `~Iota.get_bundles ` extended API command. It takes a list of tail
671
+ transaction hashes and returns the bundles for each of them. The response
672
+ ``dict `` contains a ``bundles `` key with the value being a list of bundles
673
+ in the same order as the input argument hashes. Also note, that the bundles
674
+ in the response are actual PyOTA :py:class: `Bundle ` objects.
675
+
676
+ To simplify the code, several operations are happening on line 21:
677
+
678
+ - Calling :py:meth: `~Iota.get_bundles ` that returns a ``dict ``,
679
+ - accessing the ``'bundles' `` key in the ``dict ``,
680
+ - and taking the first element of the the list of bundles in the value
681
+ associated with the key.
682
+
683
+ .. literalinclude :: ../examples/tutorials/07_fetch_encrypted.py
684
+ :lines: 23-39
685
+ :lineno-start: 23
686
+
687
+ The next step is to extract the content of the message fields of the
688
+ transactions in the bundle. We call :py:meth: `Bundle.get_messages ` to carry
689
+ out this operation. The method returns a list of unicode strings, essentially
690
+ the ``signature_message_fragment `` fields of the transactions, decoded from
691
+ trytes into unicode characters.
692
+
693
+ We then combine these message chunks into one stream of characters by using
694
+ ``string.join() ``.
695
+
696
+ We know that at this stage that we can't make sense of our message, because it
697
+ is encrypted and encoded into `Base64 `_. Let's peel that onion layer by layer:
698
+
699
+ - On line 28, we decode the message into bytes with ``b64decode ``.
700
+ - On line 31, we ask the user for thr decryption password (from the previous
701
+ tutorial).
702
+ - On line 36, we decrypt the bytes cipher with the password and decode the
703
+ result into a unicode string.
704
+ - Since we used JSON formatting in the previous tutorial, there is one
705
+ additional step to arrive at our original data. On line 39, we deserialize
706
+ the JSON string into a Python object, namely a ``dict ``.
707
+
708
+ .. literalinclude :: ../examples/tutorials/07_fetch_encrypted.py
709
+ :lines: 41-42
710
+ :lineno-start: 41
711
+
712
+ If everything went according to plan and the user supplied the right password,
713
+ we should see our original data printed out to the console.
714
+
715
+ Now you know how to use the Tangle for data storage while keeping privacy.
716
+ When you need more granular access control on how and when one could read
717
+ data from the Tangle, consider using `Masked Authenticated Messaging `_ (MAM).
718
+
508
719
.. _PyOTA Bug Tracker : https://github.com/iotaledger/iota.py/issues
509
720
.. _bytestring : https://docs.python.org/3/library/stdtypes.html#bytes
510
721
.. _tryte alphabet : https://docs.iota.org/docs/getting-started/0.1/introduction/ternary#tryte-encoding
511
722
.. _Tangle Explorer : https://utils.iota.org
512
723
.. _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
724
+ .. _spending twice from the same address : https://docs.iota.org/docs/getting-started/0.1/clients/addresses#spent-addresses
725
+ .. _Base64 : https://en.wikipedia.org/wiki/Base64
726
+ .. _Masked Authenticated Messaging : https://docs.iota.org/docs/client-libraries/0.1/mam/introduction/overview?q=masked%20auth&highlights=author;authent
0 commit comments