[JAVA] I tried using Scalar DL with Docker

The term name of UDF has been changed to Function, so the text has been revised. (2020/1/10)

What is Scalar DL?

Blockchain-inspired distributed ledger platform software developed by Scalar.

(reference) Scalar DLT | Scalar, Inc. Scalar DL Docs

Similar to blockchain in that it is tamper resistant and decentralized, Scalar DL has the following additional features:

At the same time, some restrictions are set to prevent unintended contract behavior by designers and developers.

The execution result of the smart contract executed by Scalar DL is managed by Scalar DB, which is the distributed database management software developed by the company.

Try running Scalar DB on WSL Ubuntu (Environment construction)

This time, this Scalar DL was updated, and it was said that a unique mechanism called Function (described later) that other blockchains and similar services do not have was added, so I actually tried running it with docker.

Caution

** As of December 2019, Scalar DL is provided only with a commercial license, so you need to contact Scalar separately for actual use. ** **

Contact Form

Development environment

Ubuntu16.04 LTS (built on Windows 10 with Hyper-V) Oracle Java 8

Advance preparation

A docker container is provided by Scalar as an execution environment for Scalar DL. https://scalardl.readthedocs.io/en/latest/installation-with-docker/

Docker Engine and Docker Compose are required to start the docker container, so install it referring to the following site. https://docs.docker.com/install/ https://docs.docker.com/compose/install/

You also need a java execution environment.

$ apt update
$ sudo apt install openjdk-8-jdk

Preparing to run Scalar DL

clone of scalar-samples repository

$ git clone https://github.com/scalar-labs/scalar-samples.git
$ cd scalar-samples

Login

$ docker login

Service build

$ sudo docker-compose build

Start container

$ docker-compose up

If you add the -d option at the end, you can start it in the background, but since you can not perform further operations until Cassandra's start is completed, it is better to display the log and check the start. Is done.

If an error such as "There is no executable file" appears during execution, check the execution permission of the file and grant the execution permission as appropriate. $ chmod +x /path/to/file

Loading the initial schema

You need to load the Cassandra server with the initial schema needed to use Scalar DL. This command only needs to be executed once at the first startup. $ docker-compose exec cassandra cqlsh -f /create_schema.cql

Stop container

Ctrl + C or $ docker-compose down

Edit property file

Edit scalar-samples / conf / client.properties and configure the properties file. The following are the minimum settings.

client.properties


# A host name of Scalar DL network server.
scalar.ledger.client.server_host=localhost

# An ID of a certificate holder. It must be configured for each private key and unique in the system.
scalar.ledger.client.cert_holder_id=foo

# A certificate file path to use.
scalar.ledger.client.cert_path=/path/to/foo.pem

# A private key file path to use. 
scalar.ledger.client.private_key_path=/path/to/foo-key.pem

Edit the following items according to your environment

Creating a contract

The Scalar DL contract is a Java class that extends the Contract class and overrides the invoke method. This time, as a sample, when asset_id and state are input, a sample contract that registers state as a value in the asset specified by asset_id is created.

$ vi src/main/java/com/org1/contract/StateUpdater.java

StateUpdater.java


package com.org1.contract;

import com.scalar.ledger.asset.Asset;
import com.scalar.ledger.contract.Contract;
import com.scalar.ledger.exception.ContractContextException;
import com.scalar.ledger.ledger.Ledger;
import java.util.Optional;
import javax.json.Json;
import javax.json.JsonObject;

public class StateUpdater extends Contract {
  @Override
  public JsonObject invoke(Ledger ledger, JsonObject argument, Optional<JsonObject> properties) {

    if (!argument.containsKey("asset_id") || !argument.containsKey("state")) {
      // ContractContextException is the only throwable exception in a contract and
      // it should be thrown when a contract faces some non-recoverable error
      throw new ContractContextException("please set asset_id and state in the argument");
    }

    String assetId = argument.getString("asset_id");
    int state = argument.getInt("state");

    Optional<Asset> asset = ledger.get(assetId);

    if (!asset.isPresent() || asset.get().data().getInt("state") != state) {
      ledger.put(assetId, Json.createObjectBuilder().add("state", state).build());
    }
    return null;
  }
}

The procedure for compiling and executing will be described later.

Creating Functions and Schemas for Use with Functions

What is Function

It is a program written in Java language. Get, Put, and Delete can be executed for Scalar DB within the same transaction as contract execution.

In other words, what can be done is that Function can "realize both Immutable Data processing that requires tamper resistance and Mutable Data processing that requires change in the same transaction." become.

At the time of design / implementation, it should be noted that the argument of the contract can be referred to in the Function and used for processing, but the argument of the Function cannot be referred to on the contract side.

Function sample

Get ʻasset_id and state from the argument of the contract, get ʻuser_id from the argument of Function, and create a Function that registers ʻasset_id with ʻuser_id and state as keys.

$ vi src/main/java/com/scalar/ist/function/SchemaUpdater.java

SchemaUpdater.java


package com.scalar.ist.function;

import com.scalar.database.api.Get;
import com.scalar.database.api.Put;
import com.scalar.database.api.Result;
import com.scalar.database.io.IntValue;
import com.scalar.database.io.Key;
import com.scalar.database.io.TextValue;
import com.scalar.ledger.database.MutableDatabase;
import com.scalar.ledger.udf.Function;
import java.util.Optional;
import javax.json.JsonObject;

public class SchemaUpdater extends Function {
  @Override
  public void invoke(
    MutableDatabase database,
    JsonObject contractArgument,
    Optional<JsonObject> functionArgument) {

    String userId = functionArgument.get().getString("user_id");
    int state = contractArgument.getInt("state");
    String assetId = contractArgument.getString("asset_id");

    Get get =
      new Get(
        new Key(new TextValue("user_id", userId)),
        new Key(new IntValue("state", state)))
        .forNamespace("test")
        .forTable("test_schema");
    database.get(get);

    Put put =
      new Put(
        new Key(new TextValue("user_id", userId)),
        new Key(new IntValue("state", state)))
        .withValue(new TextValue("value",assetId))
        .forNamespace("test")
        .forTable("test_schema");
    database.put(put);
  }
}

Creating a schema for use with a Function

Function creates a schema for registering values in the Scalar DB. However, the schema created here must be a schema that corresponds to the transaction. Reference: Scalar DB Docs --Internal metadata in Scalar DB

Schema creation is done by entering commands from the Cassandra shell

$ docker-compose exec cassandra cqlsh
cqlsh> create table test.test_schema	
 (
  user_id text,
  state int,
  value text,
  before_value text,

  before_tx_committed_at bigint,
  before_tx_id text,
  before_tx_prepared_at bigint,
  before_tx_state int,
  before_tx_version int,
  tx_committed_at bigint,
  tx_id text,
  tx_prepared_at bigint,
  tx_state int,
  tx_version int,
  primary key (user_id, state)
 );

Check the created table

cqlsh> use test;
cqlsh:test> describe tables;

test_schema

Registration / execution of contracts and functions

In order to execute a contract or function, it is necessary to specify a unique ID for each contract or function, sign it with a private key, and register it in Scalar DL. With this mechanism, it becomes clear who executed it, and it is possible to prevent the execution of unauthorized users.

compile

$ ./gradlew assemble

The contract class file is created in build / classes / java / main / com / org1 / contract / StateUpdater.class.

Contract registration

A simple tool for registration is available, so use it. When registering, you will need the path of the property file, the ID of the globally unique contract, the binary name of the contract, and the path of the class file.

$ client/bin/register-contract -properties conf/client.properties -contract-id StateUpdater -contract-binary-name com.org1.contract.StateUpdater -contract-class-file build/classes/java/main/com/org1/contract/StateUpdater.class

If successful, status: 200 will be displayed.

Function registration

Register using the same tool as the contract. The same goes for the property file path, globally unique ID, binary name, and class file path.

client/bin/register-function -properties conf/client.properties -function-id SchemaUpdater -function-binary-name com.scalar.ist.function.SchemaUpdater -function-class-file build/classes/java/main/com/scalar/ist/function/SchemaUpdater.class

If successful, status: 200 will be displayed.

Run

Execute the contract and Function registered in the above procedure. It is also executed by the tool, and the argument of the contract is specified by the -contract-argument option, and it is in the format of"_functions_": ["Function ID1", "Function ID2", ...](array). Specify the Function to be executed in the same transaction. Arguments used in Function are specified by the -function-argument option.

client/bin/execute-contract -properties conf/client.properties -contract-id StateUpdater -contract-argument '{"asset_id": "my_asset", "state": 1, "_functions_": ["SchemaUpdater"]}' -function-argument '{"user_id": "john"}'

If the execution is successful, status: 200 is displayed.

Note that if the contract does not update the asset, the Function will not update the schema either.

Verification

After executing the contract, check if it was executed without any problem. For the execution result of the contract, implement the contract that acquires the latest value of the asset, register and execute it, and check it.

StateReader.java


package com.org1.contract;

import com.scalar.ledger.asset.Asset;
import com.scalar.ledger.asset.InternalAsset;
import com.scalar.ledger.contract.Contract;
import com.scalar.ledger.ledger.Ledger;

import javax.json.Json;
import javax.json.JsonObject;
import javax.json.JsonObjectBuilder;
import java.util.Optional;

public class StateReader extends Contract {

  @Override
  public JsonObject invoke(Ledger ledger, JsonObject argument, Optional<JsonObject> properties) {
    String assetId = argument.getString("asset_id");

    Optional<Asset> asset = ledger.get(assetId);
    InternalAsset internal = (InternalAsset) asset.get();

    JsonObjectBuilder builder = Json.createObjectBuilder()
        .add("state", internal.data());

    return builder.build();
  }
}

Contract registration

$ client/bin/register-contract -properties conf/client.properties -contract-id StateReader -contract-binary-name com.org1.contract.StateReader -contract-class-file build/classes/java/main/com/org1/contract/StateReader.class

If successful, status: 200 will be displayed.

Run

client/bin/execute-contract -properties conf/client.properties -contract-id StateReader -contract-argument '{"asset_id": "my_asset"}'

If done correctly, you should see " state ": {" state ": 1}.

To check the execution of Function, check Cassandra's schema directly.

$ docker-compose exec cassandra cqlsh
cqlsh> select * from test.test_schema;

If executed correctly, you should get the following result (values such as tx_id are probably different) image.png

Recommended Posts

I tried using Scalar DL with Docker
I tried BIND with Docker
I tried using JOOQ with Gradle
I tried using Realm with Swift UI
I tried using OnlineConverter with SpringBoot + JODConverter
I tried using OpenCV with Java + Tomcat
I tried using Gson
I tried using TestNG
I tried using Galasa
I tried using Docker for the first time
I tried to verify AdoptOpenJDK 11 (11.0.2) with Docker image
I made blackjack with Ruby (I tried using minitest)
I tried DI with Ruby
I tried using azure cloud-init
I tried the Docker tutorial!
I tried using Docker Desktop for Windows on Windows 10 Home
I tried using Apache Wicket
I tried using Java REPL
I tried UPSERT with PostgreSQL.
Using PlantUml with Honkit [Docker]
I tried to build an environment using Docker (beginner)
I tried to make an introduction to PHP + MySQL with Docker
I tried to build the environment little by little using docker
I tried running WordPress with docker preview on M1 Mac.
I tried to create a padrino development environment with Docker
I tried to get started with Swagger using Spring Boot
I tried using the CameraX library with Android Java Fragment
I tried using anakia + Jing now
I tried using Spring + Mybatis + DbUnit
I tried morphological analysis with MeCab
I tried to interact with Java
I tried UDP communication with Java
I tried using Java8 Stream API
I tried using JWT in Java
I tried GraphQL with Spring Boot
[Android] I tried using Coordinator Layout.
I tried Flyway with Spring Boot
I tried using Pari gp container
I tried using WebAssembly Stadio (2018/4/17 version)
Use cuda11.0 with pytorch using Docker
I tried customizing slim with Scaffold
I tried using Java memo LocalDate
I tried using GoogleHttpClient of Java
I tried to integrate Docker and Maven / Netbean nicely using Jib
I tried to build the environment of PlantUML Server with Docker
I tried connecting to MySQL using JDBC Template with Spring MVC
I tried deploying a Docker container on Lambda with Serverless Framework
I tried using Elasticsearch API in Java
I tried running Docker on Windows Server 2019
I tried using UICollectionViewListCell added from Xcode12.
I tried to get started with WebAssembly
Proxy server with squid using docker image
I tried time-saving management learning with Studyplus.
It's new, but I tried using Groonga
I tried playing with BottomNavigationView a little ①
I tried Lazy Initialization with Spring Boot 2.2.0
GPU recognition with docker using WSL2 (August 2020)
Microservices 101-I tried putting Docker on Ubuntu-
What is Docker? I tried to summarize
Try using Kong + Konga with Docker Compose.
I tried to implement ModanShogi with Kinx