[JAVA] How to add Hyperledger Iroha Peer

Article content

This article describes how to add Peer with Hyperledger Iroha. There is also a way to add it with "iroha-cli", but this time it is added using the Java library. If you want to add it with "iroha-cli", you can get the public key and private key with the code in the article, so I think that you can easily do it by using the key information.

I'll write the conclusion first, but this procedure fails to add Peer. The Peer additional program ends normally and is also included in the block, but the communication between Peers does not work.

I'm not sure if this is an environmental issue or an Iroha issue, so I'll leave a note as a way that didn't work.

environment

In the work of this article, four consoles are started and the work is done. Write carefully so that the console in the article is not confusing.

  1. iroha Unit 1 iroha daemon log confirmation console
  2. iroha Unit 1 iroha container operation console
  3. iroha Unit 1 some-postgres container operation console
  4. iroha Unit 2 iroha container operation console

Advance preparation

We will build the environment for iroha for two cars. Remittance blocks etc. should be loaded in advance on Unit 1. Unit 2 will be left with only the Genesis block.

Prepare a key to use when adding Peer

Originally? I wonder if I will prepare a public key using OpenSSL etc. You can easily create a key by using the sample code written in the Iroha document, so this time we will create the key information in Java.

Additional processing of Peer

Peer additional program

Code that only adds Peer.

import java.net.URI;
import java.security.KeyPair;

import org.testcontainers.shaded.org.apache.commons.codec.binary.Hex;

import jp.co.soramitsu.crypto.ed25519.Ed25519Sha3;
import jp.co.soramitsu.iroha.java.IrohaAPI;
import jp.co.soramitsu.iroha.java.Transaction;
import jp.co.soramitsu.iroha.java.TransactionStatusObserver;
import lombok.val;

public class AddPeer {

    private static final Ed25519Sha3 crypto = new Ed25519Sha3();

    private static final KeyPair peerKeypair = crypto.generateKeypair();

    public static void main(String[] args) throws Exception{

 // This IP is the address of iroha Unit 1
        URI uri = new URI(null,null, "192.168.33.20",50051,null,null,null);

        IrohaAPI api = new IrohaAPI(uri);

        byte[] pubByte = Hex.decodeHex("313a07e6384776ed95447710d15e59148473ccfc052a681317a72a69f2a49910");
        byte[] privByte = Hex.decodeHex("f101537e319568c765b2cc89698325604991dca57b9716b58016b253506cab70");

        KeyPair adminKeyPair = Ed25519Sha3.keyPairFromBytes(privByte, pubByte);

 // This IP is the IP address of iroha Unit 2
        val addPeerTx = Transaction.builder("admin@test")
                          .addPeer("192.168.33.21:10001", peerKeypair.getPublic())
                          .sign(adminKeyPair)
                          .build();
                          
        val observer = TransactionStatusObserver.builder()
            // executed when stateless or stateful validation is failed
            .onTransactionFailed(t -> System.out.println(String.format(
                "transaction %s failed with msg: %s",
                t.getTxHash(),
                t.getErrOrCmdName()
            )))
            // executed when got any exception in handlers or grpc
            .onError(e -> System.out.println("Failed with exception: " + e))
            // executed when we receive "committed" status
            .onTransactionCommitted((t) -> System.out.println("Committed :)"))
            // executed when transfer is complete (failed or succeed) and observable is closed
            .onComplete(() -> System.out.println("Complete"))
            .build();

 // Execute Transaction
        api.transaction(addPeerTx).blockingSubscribe(observer);

        api.close();
        System.out.println("PubKey :" + Hex.encodeHexString(peerKeypair.getPublic().getEncoded()));
        System.out.println("PrivKey:" + Hex.encodeHexString(peerKeypair.getPrivate().getEncoded()));       
    }
}

Confirmation of execution result (block)

A block with AddPeer is created under "/ tmp / block_store" of the iroha container.

(Iroha Unit 1 iroha container operation console)

{
  "blockV1": {
    "payload": {
      "transactions": [
        {
          "payload": {
            "reducedPayload": {
              "commands": [
                {
                  "addPeer": {
                    "peer": {
                      "address": "192.168.33.21:10001",
                      "peerKey": "28CF79DEF117456ABC5413DC15F7B99B192C6D5A162ABDEF39F0BAF6C7CE0B48"
                    }
                  }
                }
              ],
              "creatorAccountId": "admin@test",
              "createdTime": "1571213725207",
              "quorum": 1
            }
          },
          "signatures": [
            {
              "publicKey": "313A07E6384776ED95447710D15E59148473CCFC052A681317A72A69F2A49910",
              "signature": "A018F697EB5F6797BD478F8CF04759F8D0ACDA48D4E1FE8B8BBE5D538E6D47A423896E689AA780DD260763ED1B97B12F9AF1EA41D7D391FB2F9C49EDB429DC0C"
            }
          ]
        }
      ],
      "height": "16",
      "prevBlockHash": "e602693381ff5ac383a2d62503d94ede87951ffdedefe9b4628a188036fd6c89",
      "createdTime": "1571213727036"
    },
    "signatures": [
      {
        "publicKey": "bddd58404d1315e0eb27902c5d7c8eb0602c16238f005773df406bc191308929",
        "signature": "863b38942a290640e05bbb2339ab63d94a8ab6ca3bfba1abaf673fe490f7eb46236a72cdb54d95a3c11254c440ebe3ef1110466a989e0dc4221952749b87ec0b"
      }
    ]
  }
}

The IP address embedded in the program and the created pubKey are registered as Peer information in the addPeer command.

Confirmation of execution result (postgres)

Connect to "some-postgres" in the Docker container and take a look at the contents of the table that holds Peer information. (Iroha Unit 1 some-postgres container operation console)

root@f7d412e03bfb:/# psql -U postgres
psql (9.5.19)
Type "help" for help.

postgres=# \l
                                 List of databases
    Name    |  Owner   | Encoding |  Collate   |   Ctype    |   Access privileges
-----------+----------+----------+------------+------------+-----------------------
  postgres  | postgres | UTF8     | en_US.utf8 | en_US.utf8 |
  template0 | postgres | UTF8     | en_US.utf8 | en_US.utf8 | =c/postgres          +
            |          |          |            |            | postgres=CTc/postgres
  template1 | postgres | UTF8     | en_US.utf8 | en_US.utf8 | =c/postgres          +
            |          |          |            |            | postgres=CTc/postgres
(3 rows)

postgres=# \d
                        List of relations
  Schema |               Name                |   Type   |  Owner
--------+-----------------------------------+----------+----------
  public | account                           | table    | postgres
  public | account_has_asset                 | table    | postgres
  public | account_has_grantable_permissions | table    | postgres
  public | account_has_roles                 | table    | postgres
  public | account_has_signatory             | table    | postgres
  public | asset                             | table    | postgres
  public | domain                            | table    | postgres
  public | height_by_account_set             | table    | postgres
  public | index_by_creator_height           | table    | postgres
  public | index_by_creator_height_id_seq    | sequence | postgres
  public | peer                              | table    | postgres
  public | position_by_account_asset         | table    | postgres
  public | position_by_hash                  | table    | postgres
  public | role                              | table    | postgres
  public | role_has_permissions              | table    | postgres
  public | signatory                         | table    | postgres
  public | tx_status_by_hash                 | table    | postgres
(17 rows)

postgres=# select * from peer;
                            public_key                            |       address
------------------------------------------------------------------+---------------------
 bddd58404d1315e0eb27902c5d7c8eb0602c16238f005773df406bc191308929 | 127.0.0.1:10001
 28cf79def117456abc5413dc15f7b99b192c6d5a162abdef39f0baf6c7ce0b48 | 192.168.33.21:10001
(2 rows)

The public Key and IP address set by addPeer are registered in the Peer table.

Confirmation of execution result (iroha Unit 2)

Next, check if the block information has been synchronized with Unit 2.

(Iroha Unit 2 iroha container operation console)

root@eaa7050903db:/opt/iroha_data# ls /tmp/block_store/
0000000000000001

Not synchronized. .. Let's check the log of irohad.

Confirmation of execution result (iroha daemon)

(Iroha Unit 1 iroha daemon log confirmation console)

root@c8d233a3c261:/opt/iroha_data# [2019-10-17 00:12:09.459123458][I][Irohad/CommandService/Processor]: handle batch
[2019-10-17 00:12:09.459140102][I][Irohad/CommandService/Processor]: propagating batch to PCS
[2019-10-17 00:12:09.459158309][I][Irohad/PeerCommunicationService]: propagate batch
[2019-10-17 00:12:09.460952243][I][Irohad/Ordering/Service]: onBatches => collection size = 1
[2019-10-17 00:12:10.400058025][W][Irohad/AsyncNetworkClient]: RPC failed: Connect Failed
[2019-10-17 00:12:10.400080412][W][Irohad/AsyncNetworkClient]: RPC failed: Connect Failed
[2019-10-17 00:12:10.400088212][W][Irohad/AsyncNetworkClient]: RPC failed: Connect Failed
[2019-10-17 00:12:10.400095314][W][Irohad/AsyncNetworkClient]: RPC failed: Connect Failed
[2019-10-17 00:12:10.400102280][W][Irohad/AsyncNetworkClient]: RPC failed: Connect Failed

It seems that the connection with Unit 2 using gRPC has failed.

Probably, but I think it is the same event. https://github.com/hyperledger/iroha/issues/191

It is managed here https://jira.hyperledger.org/browse/IR-588

Return (Delete Peer)

Since it cannot be used as it is, I also prepared a program for deleting Peer.

import java.net.URI;
import java.security.KeyPair;

import org.testcontainers.shaded.org.apache.commons.codec.binary.Hex;

import jp.co.soramitsu.crypto.ed25519.Ed25519Sha3;
import jp.co.soramitsu.iroha.java.IrohaAPI;
import jp.co.soramitsu.iroha.java.Transaction;
import jp.co.soramitsu.iroha.java.TransactionStatusObserver;
import lombok.val;

public class RemovePeer {

    public static void main(String[] args) throws Exception{

        URI uri = new URI(null,null, "192.168.33.20",50051,null,null,null);

        IrohaAPI api = new IrohaAPI(uri);

        byte[] pubByte = Hex.decodeHex("313a07e6384776ed95447710d15e59148473ccfc052a681317a72a69f2a49910");
        byte[] privByte = Hex.decodeHex("f101537e319568c765b2cc89698325604991dca57b9716b58016b253506cab70");
        
 // ↓ Please set the public key of Peer captured in the block ↓
        byte[] peerPubKey = Hex.decodeHex("28CF79DEF117456ABC5413DC15F7B99B192C6D5A162ABDEF39F0BAF6C7CE0B48");

        KeyPair adminKeyPair = Ed25519Sha3.keyPairFromBytes(privByte, pubByte);


        val addPeerTx = Transaction.builder("admin@test")
 .removePeer (peerPubKey) // ← This just changed from addPeer to removePeer
                            .sign(adminKeyPair)
                            .build();

        val observer = TransactionStatusObserver.builder()
            // executed when stateless or stateful validation is failed
            .onTransactionFailed(t -> System.out.println(String.format(
                "transaction %s failed with msg: %s",
                t.getTxHash(),
                t.getErrOrCmdName()
            )))
            // executed when got any exception in handlers or grpc
            .onError(e -> System.out.println("Failed with exception: " + e))
            // executed when we receive "committed" status
            .onTransactionCommitted((t) -> System.out.println("Committed :)"))
            // executed when transfer is complete (failed or succeed) and observable is closed
            .onComplete(() -> System.out.println("Complete"))
            .build();

 // Execute Transaction
        api.transaction(addPeerTx).blockingSubscribe(observer);

        api.close();
    }
}

I will do it.

transaction 6069fe1e6c2f6b7437ce145e0db552355f3d8631d16b7bc18b53894fcafec2bf failed with msg: Protobuf Transaction: [[Undefined command is found ]]

Complete

What! Undefined command is found! It seems that removePeer has not been implemented yet. When I checked the contents of Transaction by debugging, it looked like this.

reduced_payload {
  commands {
    remove_peer {
      public_key: "28CF79DEF117456ABC5413DC15F7B99B192C6D5A162ABDEF39F0BAF6C7CE0B48"
    }
  }
  creator_account_id: "admin@test"
  created_time: 1571276110853
  quorum: 1
}

I will continue to look for other ways and trace the information on the above sites. That's all for this time.

Points of concern

There are two points that I was interested in touching various things this time.

The first point is that Transaction is not captured in the block if the communication between Peers is not successful. Well, I think it's natural, but in actual operation, if there are two Peers, Transaction will not be imported at all if one of them goes down. In the future, I will try to increase the number of units to 3 and 4, but in order not to create a single point of failure, it seems that at least 3 units will need Peer.

The second point may not be a big problem, but it seems that all the existing block information is checked when the iroha daemon is started. (Iroha Unit 1 iroha daemon log confirmation console)

[2019-10-16 23:41:19.917112613][I][Irohad/Storage/MutableStorageImpl]: Applying block: height 1, hash 9debdb1a70db2cede2222427b849f6bf7ab20845da7c3db1837c0df25ec1c61a
[2019-10-16 23:41:19.930740040][I][Irohad/Storage/MutableStorageImpl]: Applying block: height 2, hash d2d9906eb82dd799e6cb68161208738e8383cae31bdf499b2b5f7f14d55b2ba0
[2019-10-16 23:41:19.950225041][I][Irohad/Storage/MutableStorageImpl]: Applying block: height 3, hash 686b05c2553b70cd26fdb6e9108066a33e2b09653ccb33b3b3c9f6e4f12e72d2
[2019-10-16 23:41:19.952860890][I][Irohad/Storage/MutableStorageImpl]: Applying block: height 4, hash fd8541954c9ab55229e8f6e095a7484ece7d64066e78c775359e0d8736a4ea4b
[2019-10-16 23:41:19.960015934][I][Irohad/Storage/MutableStorageImpl]: Applying block: height 5, hash cc5a4fe828811e217a5de828047b9c0d5939628d809b2265b66db1b73f9d0f98
[2019-10-16 23:41:19.972008320][I][Irohad/Storage/MutableStorageImpl]: Applying block: height 6, hash 4fa8f208d9c85b08edc3504aed35f257de943942f250639111c334724cfe6d26
[2019-10-16 23:41:19.983822479][I][Irohad/Storage/MutableStorageImpl]: Applying block: height 7, hash 471894ae7fa36f09d971be563686b2aa06ff28a9bd9a49d6e802451cb018ac1b
[2019-10-16 23:41:19.985555973][I][Irohad/Storage/MutableStorageImpl]: Applying block: height 8, hash 68ef83c09eb4bc8152dd7feaeb0e10a1fb0c71b63e6c6e66e829fd730a765f18
[2019-10-16 23:41:19.987011731][I][Irohad/Storage/MutableStorageImpl]: Applying block: height 9, hash e5dcd0505aa726d25d318af78ea41126f02c44f3647011582017140e1c7e55e5
[2019-10-16 23:41:19.988539990][I][Irohad/Storage/MutableStorageImpl]: Applying block: height 10, hash c6fb5fff4bf3c440b6846761d50d248beb75199342b8b9431e19c1e2b5e7e303
[2019-10-16 23:41:19.989874002][I][Irohad/Storage/MutableStorageImpl]: Applying block: height 11, hash 278c9c5e5a72b5513d55290f2687c623595b7825b747c525ee00135cccfb1149
[2019-10-16 23:41:19.991317395][I][Irohad/Storage/MutableStorageImpl]: Applying block: height 12, hash d33d2adabf75be7380cc6b35b84bcb4b2055f47a927cf1090c1d49853fee154b
[2019-10-16 23:41:19.992694976][I][Irohad/Storage/MutableStorageImpl]: Applying block: height 13, hash b0d19d57da0f0d0677052409e625adfea8dedbb6297edd49968d5f102de73c27
[2019-10-16 23:41:20.008512960][I][Irohad/Storage/MutableStorageImpl]: Applying block: height 14, hash 337830921603cc703cd8f95a6624b3d2d0fce090d3ddca53c69d9989620f2c44
[2019-10-16 23:41:20.022142829][I][Irohad/Storage/MutableStorageImpl]: Applying block: height 15, hash e602693381ff5ac383a2d62503d94ede87951ffdedefe9b4628a188036fd6c89
[2019-10-16 23:41:20.024153577][I][Irohad/Storage/MutableStorageImpl]: Applying block: height 16, hash d3e5c825cb9b1ca298ccf6b7b7f8a139803e6e1c9850fed65a0241f378f5125e

It seems that it takes about 0.01 seconds for a normally captured block and about 0.002 seconds for a rejected block. Of course, the processing speed will change depending on the performance of the server, but if you have to restart the daemon in actual operation, this processing may take longer than expected.

Will addPeer and removePeer work with the python library? Next, I would like to investigate this area.

Recommended Posts

How to add Hyperledger Iroha Peer
How to add ActionText function
How to add HDD to Ubuntu
How to add columns to a table
[Rails] How to add new pages
How to add the delete function
[Xcode] How to add a README.md file
How to add local jar to maven pom.xml
[Java] How to add data to List (add, addAll)
How to add a new hash / array
How to add jar file in ScalaIDE
How to add application version information to Sentry information
How to deploy
How to add / remove Ruby on Rails columns
How to add sound in the app (swift)
How to add a classpath in Spring Boot
How to conditionally add html.erb class in Rails
Add files to jar files
How to develop OpenSPIFe
How to call AmazonSQSAsync
How to use Map
How to write Rails
How to use rbenv
How to use letter_opener_web
How to use with_option
How to use fields_for
How to use java.util.logging
How to use map
How to add characters to display when using link_to method
How to use collection_select
How to adapt Bootstrap
How to use Twitter4J
How to use active_hash! !!
How to install Docker
How to use MapStruct
How to use hidden_field_tag
How to use TreeSet
How to write dockerfile
How to uninstall Rails
How to install docker-machine
[How to use label]
How to make shaded-jar
How to write docker-compose
How to use identity
How to use hashes
How to write Mockito
How to create docker-compose
How to use JUnit 5
How to install MySQL
How to write migrationfile
How to build android-midi-lib
How to use Dozer.mapper
How to use Gradle
How to use org.immutables
How to use java.util.stream.Collector
How to use VisualVM
How to use Map
How to install ngrok
How to type backslash \
How to concatenate strings
How to add elements without specifying the length of the array