Try using Hyperledger Iroha's Java SDK

Article content

I tried running the Java SDK sample code in the Hyperledger Iroha documentation. It took a long time to move it, so I will leave what I did.

environment

Environment

I referred to this document for the procedure. https://iroha.readthedocs.io/ja/latest/getting_started/index.html

Implementation

I created a maven project in eclipse and ran the sample code. pom.xml It was a pretty stumbling block before the sample code, but I had a lot of trouble getting it through the compilation. As a result of various trials, it became the following pom.xml.

pom.xml


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <name>iroha</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <java.version>1.8</java.version>
    <maven.compiler.target>${java.version}</maven.compiler.target>
    <maven.compiler.source>${java.version}</maven.compiler.source>
  </properties>
  <repositories>
    <repository>
        <id>jitpack.io</id>
        <url>https://jitpack.io</url>
    </repository>
    <repository>
      <id>mvn</id>
      <url>https://mvnrepository.com</url>
    </repository>
  </repositories>
  <dependencies>
    <!-- https://mvnrepository.com/artifact/io.grpc/protoc-gen-grpc-java -->
    <dependency>
     <groupId>io.grpc</groupId>
     <artifactId>grpc-protobuf</artifactId>
     <version>1.22.1</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/io.grpc/grpc-netty -->
    <dependency>
      <groupId>io.grpc</groupId>
      <artifactId>grpc-netty</artifactId>
      <version>1.22.1</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/io.grpc/grpc-stub -->
    <dependency>
      <groupId>io.grpc</groupId>
      <artifactId>grpc-stub</artifactId>
      <version>1.22.1</version>
    </dependency>
    <dependency>
      <groupId>io.grpc</groupId>
      <artifactId>protoc-gen-grpc-java</artifactId>
      <version>1.12.0</version>
    </dependency>
    <dependency>
      <groupId>com.github.hyperledger</groupId>
      <artifactId>iroha-java</artifactId>
      <version>6.1.0</version>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
    <!-- https://mvnrepository.com/artifact/jp.co.soramitsu/iroha -->
    <dependency>
      <groupId>jp.co.soramitsu</groupId>
      <artifactId>iroha</artifactId>
      <version>0.0.8</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.10</version>
      <scope>provided</scope>
    </dependency>
    <!-- https://mvnrepository.com/artifact/com.google.protobuf/protobuf-java-util -->
    <dependency>
      <groupId>com.google.protobuf</groupId>
      <artifactId>protobuf-java-util</artifactId>
      <version>3.10.0</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/com.google.protobuf/protobuf-java -->
    <dependency>
      <groupId>com.google.protobuf</groupId>
      <artifactId>protobuf-java</artifactId>
      <version>3.10.0</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.testinfected.hamcrest-matchers/core-matchers -->
    <dependency>
      <groupId>org.testinfected.hamcrest-matchers</groupId>
      <artifactId>core-matchers</artifactId>
      <version>1.5</version>
    </dependency>

  </dependencies>
  <build>
  <plugins>
  <plugin>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.1</version>
    <configuration>
      <source>1.8</source>
      <target>1.8</target>
      <encoding>UTF-8</encoding>
      <compilerArgument>-Werror</compilerArgument>
   </configuration>
  </plugin>
  </plugins>
  </build>
</project>

There are two points that stumbled.

The first point seems to have some dependency on the jar file of "protoc-gen-grpc-java", and an error occurred if there was no jar file. I added a dependency, but the jar file did not exist in the "protoc-gen-grpc-java" maven repository. After trying various things, I solved it by renaming an appropriate jar file and placing it locally. Apparently, it has a dependency but has not been called.

The second point seems to refer to junit's "TestRule" class in iroha's jar. This class seems to have been added since version 4.12 of junit and had to be version 4.12 or higher.

Sample code

This is the sample code in the documentation.

Example1.java


import iroha.protocol.BlockOuterClass;
import iroha.protocol.Primitive.RolePermission;
import java.math.BigDecimal;
import java.security.KeyPair;
import java.util.Arrays;
import jp.co.soramitsu.crypto.ed25519.Ed25519Sha3;
import jp.co.soramitsu.iroha.testcontainers.IrohaContainer;
import jp.co.soramitsu.iroha.testcontainers.PeerConfig;
import jp.co.soramitsu.iroha.testcontainers.detail.GenesisBlockBuilder;
import lombok.val;

public class Example1 {

  private static final String bankDomain = "bank";
  private static final String userRole = "user";
  private static final String usdName = "usd";

  private static final Ed25519Sha3 crypto = new Ed25519Sha3();

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

  private static final KeyPair useraKeypair = crypto.generateKeypair();
  private static final KeyPair userbKeypair = crypto.generateKeypair();

  private static String user(String name) {
    return String.format("%s@%s", name, bankDomain);
  }

  private static final String usd = String.format("%s#%s", usdName, bankDomain);

  /**
   * <pre>
   * Our initial state cosists of:
   * - domain "bank", with default role "user" - can transfer assets and can query their amount
   * - asset usd#bank with precision 2
   * - user_a@bank, which has 100 usd
   * - user_b@bank, which has 0 usd
   * </pre>
   */
  private static BlockOuterClass.Block getGenesisBlock() {
    return new GenesisBlockBuilder()
        // first transaction
        .addTransaction(
            // transactions in genesis block can have no creator
            Transaction.builder(null)
                // by default peer is listening on port 10001
                .addPeer("0.0.0.0:10001", peerKeypair.getPublic())
                // create default "user" role
                .createRole(userRole,
                    Arrays.asList(
                        RolePermission.can_transfer,
                        RolePermission.can_get_my_acc_ast,
                        RolePermission.can_get_my_txs,
                        RolePermission.can_receive
                    )
                )
                .createDomain(bankDomain, userRole)
                // create user A
                .createAccount("user_a", bankDomain, useraKeypair.getPublic())
                // create user B
                .createAccount("user_b", bankDomain, userbKeypair.getPublic())
                // create usd#bank with precision 2
                .createAsset(usdName, bankDomain, 2)
                // transactions in genesis block can be unsigned
                .build() // returns ipj model Transaction
                .build() // returns unsigned protobuf Transaction
        )
        // we want to increase user_a balance by 100 usd
        .addTransaction(
            Transaction.builder(user("user_a"))
                .addAssetQuantity(usd, new BigDecimal("100"))
                .build()
                .build()
        )
        .build();
  }

  public static PeerConfig getPeerConfig() {
    PeerConfig config = PeerConfig.builder()
        .genesisBlock(getGenesisBlock())
        .build();

    // don't forget to add peer keypair to config
    config.withPeerKeyPair(peerKeypair);

    return config;
  }

  /**
   * Custom facade over GRPC Query
   */
  public static int getBalance(IrohaAPI api, String userId, KeyPair keyPair) {
    // build protobuf query, sign it
    val q = Query.builder(userId, 1)
        .getAccountAssets(userId)
        .buildSigned(keyPair);

    // execute query, get response
    val res = api.query(q);

    // get list of assets from our response
    val assets = res.getAccountAssetsResponse().getAccountAssetsList();

    // find usd asset
    val assetUsdOptional = assets
        .stream()
        .filter(a -> a.getAssetId().equals(usd))
        .findFirst();

    // numbers are small, so we use int here for simplicity
    return assetUsdOptional
        .map(a -> Integer.parseInt(a.getBalance()))
        .orElse(0);
  }

  public static void main(String[] args) {
    // for simplicity, we will create Iroha peer in place
    IrohaContainer iroha = new IrohaContainer()
        .withPeerConfig(getPeerConfig());

    // start the peer. blocking call
    iroha.start();

    // create API wrapper
    IrohaAPI api = new IrohaAPI(iroha.getToriiAddress());

    // transfer 100 usd from user_a to user_b
    val tx = Transaction.builder("user_a@bank")
        .transferAsset("user_a@bank", "user_b@bank", usd, "For pizza", "10")
        .sign(useraKeypair)
        .build();

    // create transaction observer
    // here you can specify any kind of handlers on transaction statuses
    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();

    // blocking send.
    // use .subscribe() for async sending
    api.transaction(tx)
        .blockingSubscribe(observer);

    /// now lets query balances
    val balanceUserA = getBalance(api, user("user_a"), useraKeypair);
    val balanceUserB = getBalance(api, user("user_b"), userbKeypair);

    // ensure we got correct balances
    assert balanceUserA == 90;
    assert balanceUserB == 10;
  }
}

For the time being, I will move it.

[main] INFO org.testcontainers.dockerclient.DockerClientProviderStrategy - Will use 'okhttp' transport
[main] INFO org.testcontainers.dockerclient.DockerClientProviderStrategy - Will use 'okhttp' transport
[main] INFO org.testcontainers.dockerclient.DockerMachineClientProviderStrategy - Found docker-machine, and will use machine named default
[main] INFO org.testcontainers.dockerclient.DockerMachineClientProviderStrategy - Docker daemon IP address for docker machine default is 192.168.99.100
[main] INFO org.testcontainers.dockerclient.DockerClientProviderStrategy - Will use 'okhttp' transport
[main] ERROR org.testcontainers.dockerclient.DockerClientProviderStrategy - Could not find a valid Docker environment. Please check configuration. Attempted configurations were:
[main] ERROR org.testcontainers.dockerclient.DockerClientProviderStrategy -     NpipeSocketClientProviderStrategy: failed with exception InvalidConfigurationException (ping failed). Root cause TimeoutException (null)
[main] ERROR org.testcontainers.dockerclient.DockerClientProviderStrategy -     WindowsClientProviderStrategy: failed with exception TimeoutException (Timeout waiting for result with exception). Root cause ConnectException (Connection refused: connect)
[main] ERROR org.testcontainers.dockerclient.DockerClientProviderStrategy -     DockerMachineClientProviderStrategy: failed with exception TimeoutException (Timeout waiting for result with exception). Root cause ConnectException (Connection refused: connect)
[main] ERROR org.testcontainers.dockerclient.DockerClientProviderStrategy - As no valid configuration was found, execution cannot continue
Exception in thread "main" java.lang.IllegalStateException: Could not find a valid Docker environment. Please see logs and check configuration

Yes. I got an error.

「Could not find a valid Docker environment.」 This error seems to occur when I try to operate Docker from Java and the "Dockerfile" does not exist.

I'm using VirtualBox instead of Docker, so it didn't work.

If you read the program properly here, Peer is added, an account is created in the added Peer, and Asset (token) is sent.

Delete the part that operates Docker from the processing of the main statement, change the user to "admin @ test" and "test @ test" that are created by default, and try to move it.

Example1.java


  public static void main(String[] args) throws Exception{
    URI uri = new URI(null,null, "IP address",50051,null,null,null);

    // create API wrapper
    IrohaAPI api = new IrohaAPI(uri);

    // transfer 100 usd from user_a to user_b
    val tx = Transaction.builder("admin@test")
        .transferAsset("admin@test", "test@test", usd, "For pizza", "10")
        .sign(useraKeypair)
        .build();

    // create transaction observer
    // here you can specify any kind of handlers on transaction statuses
    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();

    // blocking send.
    // use .subscribe() for async sending
    api.transaction(tx)
        .blockingSubscribe(observer);

    /// now lets query balances
    val balanceUserA = getBalance(api, user("user_a"), useraKeypair);
    val balanceUserB = getBalance(api, user("user_b"), userbKeypair);

    // ensure we got correct balances
    assert balanceUserA == 90;
    assert balanceUserB == 10;
  }

The point I modified is the constructor of the "IrohaApi" class. This constructor takes a URI class, but it just gets the domain and port from the URI class it receives in the constructor. So, create a URI class that sets only the domain and port, and set it as an argument. If you can do it so far, execute it.

transaction c3d966d1cbc521b99e1cb60cbe444f129947bdfbe348abe7b7262dd81079c505 failed with msg: signatures validation
Complete

I got an error, but it worked for the time being. The cause of this error is that the KeyPair used to sign the transaction is not from admin @ test.

I know the cause, but I'm still investigating how to deal with it, so I'll update it as soon as I find out.

2019/10/09 postscript

Now that I know how to send a Transaction, I will add it.

Example1.java


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

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

    // create API wrapper
    IrohaAPI api = new IrohaAPI(uri);

    byte[] pubByte = Hex.decodeHex("[email protected] of pub (public key)");
    byte[] privByte = Hex.decodeHex("[email protected] of priv (private key)");

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

    // transfer 100 usd from user_a to user_b
    val tx = Transaction.builder("admin@test")
        .transferAsset("admin@test", "test@test", usd, "For pizza", "10")
        .sign(adminKeyPair)
        .build();

    // create transaction observer
    // here you can specify any kind of handlers on transaction statuses
    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();

    // blocking send.
    // use .subscribe() for async sending
    api.transaction(tx)
        .blockingSubscribe(observer);

    /// now lets query balances
    val balanceUserA = getBalance(api, user("user_a"), useraKeypair);
    val balanceUserB = getBalance(api, user("user_b"), userbKeypair);

    // ensure we got correct balances
    assert balanceUserA == 90;
    assert balanceUserB == 10;
  }

As a point, create a KeyPair object from the public key and private key.

    byte[] pubByte = Hex.decodeHex("[email protected] of pub (public key)");
    byte[] privByte = Hex.decodeHex("[email protected] of priv (private key)");

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

Set this in the argument of the sign method and you're done. I will try it.

Committed :)
Complete

It ended normally.

Let's take a look at the contents of the block. If you build the environment according to the procedure in the official document, it should be output to the "/ tmp / block_store" directory. The contents are formatted JSON.

{
  "blockV1": {
    "payload": {
      "transactions": [
        {
          "payload": {
            "reducedPayload": {
              "commands": [
                {
                  "transferAsset": {
                    "srcAccountId": "admin@test",
                    "destAccountId": "test@test",
                    "assetId": "usd#bank",
                    "description": "For pizza",
                    "amount": "10"
                  }
                }
              ],
              "creatorAccountId": "admin@test",
              "createdTime": "1570624170543",
              "quorum": 1
            }
          },
          "signatures": [
            {
              "publicKey": "313A07E6384776ED95447710D15E59148473CCFC052A681317A72A69F2A49910",
              "signature": "4B2B45A4F2FDB9A7DE6F30E110D8DEA5E5AAB30C40F5685CFA71FDC38E72BF3839954DDA13FE027FEA18DA9F97332E5E265822922204D38F1667D60E5F8E9601"
            }
          ]
        }
      ],
      "height": "17",
      "prevBlockHash": "ff33c8483725758e09583e7f670b9c37a091357bc027602a72452d44907861f1",
      "createdTime": "1570624184188"
    },
    "signatures": [
      {
        "publicKey": "bddd58404d1315e0eb27902c5d7c8eb0602c16238f005773df406bc191308929",
        "signature": "680ee97a530314e877ee7518b22975607e328a232a60f963029cb07b18b9101d0de6bf174ecd07ab29bac8123e8c47f12761835a121b301f192c0de9a908670e"
      }
    ]
  }
}

I was able to confirm that it was safely imported into the block.

Impressions

I've been studying ethereum lately, so it's possible that it's taken into blocks without mining, yeah? But when you think about it, it's natural.

Even if I run this sample program, I got the impression that there is very little information in Japanese. I'm going to create a simple wallet application and deploy a chain code, so I think I should actively leave information.

Recommended Posts

Try using Hyperledger Iroha's Java SDK
Get block information with Hyperledger Iroha's Java SDK
Try similar search of Image Search using Java SDK [Search]
Try using RocksDB in Java
Try scraping using java [Notes]
Try Image Search's similar search using Java SDK [Registration]
Try using Redis with Java (jar)
[Java] Try to implement using generics
Try using IBM Java method tracing
Try Spark Submit to EMR using AWS SDK for Java
[Java] Where did you try using java?
Try using Java framework Nablarch [Web application]
Try using the Stream API in Java
Try using JSON format API in Java
Try using JobScheduler's REST-API --Java RestClient implementation--
Try using the Wii remote with Java
Try using libGDX
Try using Maven
Try using powermock-mockito2-2.0.2
Try using GraalVM
Try Java 8 Stream
Try using jmockit 1.48
Try using sql-migrate
Try using SwiftLint
Try using Log4j 2.0
Roughly try Java 9
Try using Firebase Cloud Functions on Android (Java)
Try using JobScheduler's REST-API --Java RestClient Test class-
Try using GCP's Cloud Vision API in Java
Try using Sourcetrail (macOS version) in Java code
Try accessing the dataset from Java using JZOS
Try communication using gRPC on Android + Java server
Sorting using java comparator
Try using JobScheduler's REST-API
Try using java.lang.Math methods
Try using PowerMock's WhiteBox
Try implementing the Eratosthenes sieve using the Java standard library
I tried to operate SQS using AWS Java SDK
Try using Talend Part 2
Scraping practice using Java ②
Try calling IBM Watson Assistant 2018-07-10 from the Java SDK.
Scraping practice using Java ①
Try image classification using TensorFlow Lite on Android (JAVA)
Try global hooking in Java using the JNativeHook library
Try using Talend Part 1
Try using F # list
Try to build a Java development environment using Docker
Try using each_with_index method
Try Java return value
Try using Spring JDBC
Implement Thread in Java and try using anonymous class, lambda
[Java] Try editing the elements of the Json string using the library
Try using GloVe with Deeplearning4j
Try using view_component with rails
Try DB connection with Java
Try calling JavaScript in Java
Try using Cocoa from Ruby
Try developing Spresense in Java (1)
Try functional type in Java! ①
Try using letter_opener_web for inquiries
Try gRPC with Java, Maven