5.4.1. Java Library

Client library of Iroha written completely in Java 8, which includes:

  • SDK to work with Iroha API
  • async wrapper over Iroha API
  • testcontainers wrapper for convenient integration testing with Iroha
  • examples in Java and Groovy

Both options are described in the following sections. Please check readme file in project’s repo.

5.4.1.1. How to use

5.4.1.2. Example code

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;
  }
}