github.com/fiagdao/tendermint@v0.32.11-0.20220824195748-2087fcc480c1/docs/guides/java.md (about)

     1  <!---
     2  order: 3
     3  --->
     4  
     5  # Creating an application in Java
     6  
     7  ## Guide Assumptions
     8  
     9  This guide is designed for beginners who want to get started with a Tendermint
    10  Core application from scratch. It does not assume that you have any prior
    11  experience with Tendermint Core.
    12  
    13  Tendermint Core is Byzantine Fault Tolerant (BFT) middleware that takes a state
    14  transition machine (your application) - written in any programming language - and securely
    15  replicates it on many machines.
    16  
    17  By following along with this guide, you'll create a Tendermint Core project
    18  called kvstore, a (very) simple distributed BFT key-value store. The application (which should
    19  implementing the blockchain interface (ABCI)) will be written in Java.
    20  
    21  This guide assumes that you are not new to JVM world. If you are new please see [JVM Minimal Survival Guide](https://hadihariri.com/2013/12/29/jvm-minimal-survival-guide-for-the-dotnet-developer/#java-the-language-java-the-ecosystem-java-the-jvm) and [Gradle Docs](https://docs.gradle.org/current/userguide/userguide.html).
    22  
    23  ## Built-in app vs external app
    24  
    25  If you use Golang, you can run your app and Tendermint Core in the same process to get maximum performance.
    26  [Cosmos SDK](https://github.com/cosmos/cosmos-sdk) is written this way.
    27  Please refer to [Writing a built-in Tendermint Core application in Go](./go-built-in.md) guide for details.
    28  
    29  If you choose another language, like we did in this guide, you have to write a separate app,
    30  which will communicate with Tendermint Core via a socket (UNIX or TCP) or gRPC.
    31  This guide will show you how to build external application using RPC server.
    32  
    33  Having a separate application might give you better security guarantees as two
    34  processes would be communicating via established binary protocol. Tendermint
    35  Core will not have access to application's state.
    36  
    37  ## 1.1 Installing Java and Gradle
    38  
    39  Please refer to [the Oracle's guide for installing JDK](https://www.oracle.com/technetwork/java/javase/downloads/index.html).
    40  
    41  Verify that you have installed Java successfully:
    42  
    43  ```sh
    44  $ java -version
    45  java version "12.0.2" 2019-07-16
    46  Java(TM) SE Runtime Environment (build 12.0.2+10)
    47  Java HotSpot(TM) 64-Bit Server VM (build 12.0.2+10, mixed mode, sharing)
    48  ```
    49  
    50  You can choose any version of Java higher or equal to 8.
    51  This guide is written using Java SE Development Kit 12.
    52  
    53  Make sure you have `$JAVA_HOME` environment variable set:
    54  
    55  ```sh
    56  $ echo $JAVA_HOME
    57  /Library/Java/JavaVirtualMachines/jdk-12.0.2.jdk/Contents/Home
    58  ```
    59  
    60  For Gradle installation, please refer to [their official guide](https://gradle.org/install/).
    61  
    62  ## 1.2 Creating a new Java project
    63  
    64  We'll start by creating a new Gradle project.
    65  
    66  ```sh
    67  $ export KVSTORE_HOME=~/kvstore
    68  $ mkdir $KVSTORE_HOME
    69  $ cd $KVSTORE_HOME
    70  ```
    71  
    72  Inside the example directory run:
    73  
    74  ```sh
    75  gradle init --dsl groovy --package io.example --project-name example --type java-application --test-framework junit
    76  ```
    77  
    78  This will create a new project for you. The tree of files should look like:
    79  
    80  ```sh
    81  $ tree
    82  .
    83  |-- build.gradle
    84  |-- gradle
    85  |   `-- wrapper
    86  |       |-- gradle-wrapper.jar
    87  |       `-- gradle-wrapper.properties
    88  |-- gradlew
    89  |-- gradlew.bat
    90  |-- settings.gradle
    91  `-- src
    92      |-- main
    93      |   |-- java
    94      |   |   `-- io
    95      |   |       `-- example
    96      |   |           `-- App.java
    97      |   `-- resources
    98      `-- test
    99          |-- java
   100          |   `-- io
   101          |       `-- example
   102          |           `-- AppTest.java
   103          `-- resources
   104  ```
   105  
   106  When run, this should print "Hello world." to the standard output.
   107  
   108  ```sh
   109  $ ./gradlew run
   110  > Task :run
   111  Hello world.
   112  ```
   113  
   114  ## 1.3 Writing a Tendermint Core application
   115  
   116  Tendermint Core communicates with the application through the Application
   117  BlockChain Interface (ABCI). All message types are defined in the [protobuf
   118  file](https://github.com/tendermint/tendermint/blob/master/abci/types/types.proto).
   119  This allows Tendermint Core to run applications written in any programming
   120  language.
   121  
   122  ### 1.3.1 Compile .proto files
   123  
   124  Add the following piece to the top of the `build.gradle`:
   125  
   126  ```groovy
   127  buildscript {
   128      repositories {
   129          mavenCentral()
   130      }
   131      dependencies {
   132          classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.8'
   133      }
   134  }
   135  ```
   136  
   137  Enable the protobuf plugin in the `plugins` section of the `build.gradle`:
   138  
   139  ```groovy
   140  plugins {
   141      id 'com.google.protobuf' version '0.8.8'
   142  }
   143  ```
   144  
   145  Add the following code to `build.gradle`:
   146  
   147  ```groovy
   148  protobuf {
   149      protoc {
   150          artifact = "com.google.protobuf:protoc:3.7.1"
   151      }
   152      plugins {
   153          grpc {
   154              artifact = 'io.grpc:protoc-gen-grpc-java:1.22.1'
   155          }
   156      }
   157      generateProtoTasks {
   158          all()*.plugins {
   159              grpc {}
   160          }
   161      }
   162  }
   163  ```
   164  
   165  Now we should be ready to compile the `*.proto` files.
   166  
   167  Copy the necessary `.proto` files to your project:
   168  
   169  ```sh
   170  mkdir -p \
   171    $KVSTORE_HOME/src/main/proto/github.com/tendermint/tendermint/abci/types \
   172    $KVSTORE_HOME/src/main/proto/github.com/tendermint/tendermint/crypto/merkle \
   173    $KVSTORE_HOME/src/main/proto/github.com/tendermint/tendermint/libs/kv \
   174    $KVSTORE_HOME/src/main/proto/github.com/gogo/protobuf/gogoproto
   175  
   176  cp $GOPATH/src/github.com/tendermint/tendermint/abci/types/types.proto \
   177     $KVSTORE_HOME/src/main/proto/github.com/tendermint/tendermint/abci/types/types.proto
   178  cp $GOPATH/src/github.com/tendermint/tendermint/crypto/merkle/merkle.proto \
   179     $KVSTORE_HOME/src/main/proto/github.com/tendermint/tendermint/crypto/merkle/merkle.proto
   180  cp $GOPATH/src/github.com/tendermint/tendermint/libs/kv/types.proto \
   181     $KVSTORE_HOME/src/main/proto/github.com/tendermint/tendermint/libs/kv/types.proto
   182  cp $GOPATH/src/github.com/gogo/protobuf/gogoproto/gogo.proto \
   183     $KVSTORE_HOME/src/main/proto/github.com/gogo/protobuf/gogoproto/gogo.proto
   184  ```
   185  
   186  Add these dependencies to `build.gradle`:
   187  
   188  ```groovy
   189  dependencies {
   190      implementation 'io.grpc:grpc-protobuf:1.22.1'
   191      implementation 'io.grpc:grpc-netty-shaded:1.22.1'
   192      implementation 'io.grpc:grpc-stub:1.22.1'
   193  }
   194  ```
   195  
   196  To generate all protobuf-type classes run:
   197  
   198  ```sh
   199  ./gradlew generateProto
   200  ```
   201  
   202  To verify that everything went smoothly, you can inspect the `build/generated/` directory:
   203  
   204  ```sh
   205  $ tree build/generated/
   206  build/generated/
   207  |-- source
   208  |   `-- proto
   209  |       `-- main
   210  |           |-- grpc
   211  |           |   `-- types
   212  |           |       `-- ABCIApplicationGrpc.java
   213  |           `-- java
   214  |               |-- com
   215  |               |   `-- google
   216  |               |       `-- protobuf
   217  |               |           `-- GoGoProtos.java
   218  |               |-- common
   219  |               |   `-- Types.java
   220  |               |-- merkle
   221  |               |   `-- Merkle.java
   222  |               `-- types
   223  |                   `-- Types.java
   224  ```
   225  
   226  ### 1.3.2 Implementing ABCI
   227  
   228  The resulting `$KVSTORE_HOME/build/generated/source/proto/main/grpc/types/ABCIApplicationGrpc.java` file
   229  contains the abstract class `ABCIApplicationImplBase`, which is an interface we'll need to implement.
   230  
   231  Create `$KVSTORE_HOME/src/main/java/io/example/KVStoreApp.java` file with the following content:
   232  
   233  ```java
   234  package io.example;
   235  
   236  import io.grpc.stub.StreamObserver;
   237  import types.ABCIApplicationGrpc;
   238  import types.Types.*;
   239  
   240  class KVStoreApp extends ABCIApplicationGrpc.ABCIApplicationImplBase {
   241  
   242      // methods implementation
   243  
   244  }
   245  ```
   246  
   247  Now I will go through each method of `ABCIApplicationImplBase` explaining when it's called and adding
   248  required business logic.
   249  
   250  ### 1.3.3 CheckTx
   251  
   252  When a new transaction is added to the Tendermint Core, it will ask the
   253  application to check it (validate the format, signatures, etc.).
   254  
   255  ```java
   256  @Override
   257  public void checkTx(RequestCheckTx req, StreamObserver<ResponseCheckTx> responseObserver) {
   258      var tx = req.getTx();
   259      int code = validate(tx);
   260      var resp = ResponseCheckTx.newBuilder()
   261              .setCode(code)
   262              .setGasWanted(1)
   263              .build();
   264      responseObserver.onNext(resp);
   265      responseObserver.onCompleted();
   266  }
   267  
   268  private int validate(ByteString tx) {
   269      List<byte[]> parts = split(tx, '=');
   270      if (parts.size() != 2) {
   271          return 1;
   272      }
   273      byte[] key = parts.get(0);
   274      byte[] value = parts.get(1);
   275  
   276      // check if the same key=value already exists
   277      var stored = getPersistedValue(key);
   278      if (stored != null && Arrays.equals(stored, value)) {
   279          return 2;
   280      }
   281  
   282      return 0;
   283  }
   284  
   285  private List<byte[]> split(ByteString tx, char separator) {
   286      var arr = tx.toByteArray();
   287      int i;
   288      for (i = 0; i < tx.size(); i++) {
   289          if (arr[i] == (byte)separator) {
   290              break;
   291          }
   292      }
   293      if (i == tx.size()) {
   294          return Collections.emptyList();
   295      }
   296      return List.of(
   297              tx.substring(0, i).toByteArray(),
   298              tx.substring(i + 1).toByteArray()
   299      );
   300  }
   301  ```
   302  
   303  Don't worry if this does not compile yet.
   304  
   305  If the transaction does not have a form of `{bytes}={bytes}`, we return `1`
   306  code. When the same key=value already exist (same key and value), we return `2`
   307  code. For others, we return a zero code indicating that they are valid.
   308  
   309  Note that anything with non-zero code will be considered invalid (`-1`, `100`,
   310  etc.) by Tendermint Core.
   311  
   312  Valid transactions will eventually be committed given they are not too big and
   313  have enough gas. To learn more about gas, check out ["the
   314  specification"](https://docs.tendermint.com/master/spec/abci/apps.html#gas).
   315  
   316  For the underlying key-value store we'll use
   317  [JetBrains Xodus](https://github.com/JetBrains/xodus), which is a transactional schema-less embedded high-performance database written in Java.
   318  
   319  `build.gradle`:
   320  
   321  ```groovy
   322  dependencies {
   323      implementation 'org.jetbrains.xodus:xodus-environment:1.3.91'
   324  }
   325  ```
   326  
   327  ```java
   328  ...
   329  import jetbrains.exodus.ArrayByteIterable;
   330  import jetbrains.exodus.ByteIterable;
   331  import jetbrains.exodus.env.Environment;
   332  import jetbrains.exodus.env.Store;
   333  import jetbrains.exodus.env.StoreConfig;
   334  import jetbrains.exodus.env.Transaction;
   335  
   336  class KVStoreApp extends ABCIApplicationGrpc.ABCIApplicationImplBase {
   337      private Environment env;
   338      private Transaction txn = null;
   339      private Store store = null;
   340  
   341      KVStoreApp(Environment env) {
   342          this.env = env;
   343      }
   344  
   345      ...
   346  
   347      private byte[] getPersistedValue(byte[] k) {
   348          return env.computeInReadonlyTransaction(txn -> {
   349              var store = env.openStore("store", StoreConfig.WITHOUT_DUPLICATES, txn);
   350              ByteIterable byteIterable = store.get(txn, new ArrayByteIterable(k));
   351              if (byteIterable == null) {
   352                  return null;
   353              }
   354              return byteIterable.getBytesUnsafe();
   355          });
   356      }
   357  }
   358  ```
   359  
   360  ### 1.3.4 BeginBlock -> DeliverTx -> EndBlock -> Commit
   361  
   362  When Tendermint Core has decided on the block, it's transferred to the
   363  application in 3 parts: `BeginBlock`, one `DeliverTx` per transaction and
   364  `EndBlock` in the end. `DeliverTx` are being transferred asynchronously, but the
   365  responses are expected to come in order.
   366  
   367  ```java
   368  @Override
   369  public void beginBlock(RequestBeginBlock req, StreamObserver<ResponseBeginBlock> responseObserver) {
   370      txn = env.beginTransaction();
   371      store = env.openStore("store", StoreConfig.WITHOUT_DUPLICATES, txn);
   372      var resp = ResponseBeginBlock.newBuilder().build();
   373      responseObserver.onNext(resp);
   374      responseObserver.onCompleted();
   375  }
   376  ```
   377  
   378  Here we begin a new transaction, which will accumulate the block's transactions and open the corresponding store.
   379  
   380  ```java
   381  @Override
   382  public void deliverTx(RequestDeliverTx req, StreamObserver<ResponseDeliverTx> responseObserver) {
   383      var tx = req.getTx();
   384      int code = validate(tx);
   385      if (code == 0) {
   386          List<byte[]> parts = split(tx, '=');
   387          var key = new ArrayByteIterable(parts.get(0));
   388          var value = new ArrayByteIterable(parts.get(1));
   389          store.put(txn, key, value);
   390      }
   391      var resp = ResponseDeliverTx.newBuilder()
   392              .setCode(code)
   393              .build();
   394      responseObserver.onNext(resp);
   395      responseObserver.onCompleted();
   396  }
   397  ```
   398  
   399  If the transaction is badly formatted or the same key=value already exist, we
   400  again return the non-zero code. Otherwise, we add it to the store.
   401  
   402  In the current design, a block can include incorrect transactions (those who
   403  passed `CheckTx`, but failed `DeliverTx` or transactions included by the proposer
   404  directly). This is done for performance reasons.
   405  
   406  Note we can't commit transactions inside the `DeliverTx` because in such case
   407  `Query`, which may be called in parallel, will return inconsistent data (i.e.
   408  it will report that some value already exist even when the actual block was not
   409  yet committed).
   410  
   411  `Commit` instructs the application to persist the new state.
   412  
   413  ```java
   414  @Override
   415  public void commit(RequestCommit req, StreamObserver<ResponseCommit> responseObserver) {
   416      txn.commit();
   417      var resp = ResponseCommit.newBuilder()
   418              .setData(ByteString.copyFrom(new byte[8]))
   419              .build();
   420      responseObserver.onNext(resp);
   421      responseObserver.onCompleted();
   422  }
   423  ```
   424  
   425  ### 1.3.5 Query
   426  
   427  Now, when the client wants to know whenever a particular key/value exist, it
   428  will call Tendermint Core RPC `/abci_query` endpoint, which in turn will call
   429  the application's `Query` method.
   430  
   431  Applications are free to provide their own APIs. But by using Tendermint Core
   432  as a proxy, clients (including [light client
   433  package](https://godoc.org/github.com/tendermint/tendermint/lite)) can leverage
   434  the unified API across different applications. Plus they won't have to call the
   435  otherwise separate Tendermint Core API for additional proofs.
   436  
   437  Note we don't include a proof here.
   438  
   439  ```java
   440  @Override
   441  public void query(RequestQuery req, StreamObserver<ResponseQuery> responseObserver) {
   442      var k = req.getData().toByteArray();
   443      var v = getPersistedValue(k);
   444      var builder = ResponseQuery.newBuilder();
   445      if (v == null) {
   446          builder.setLog("does not exist");
   447      } else {
   448          builder.setLog("exists");
   449          builder.setKey(ByteString.copyFrom(k));
   450          builder.setValue(ByteString.copyFrom(v));
   451      }
   452      responseObserver.onNext(builder.build());
   453      responseObserver.onCompleted();
   454  }
   455  ```
   456  
   457  The complete specification can be found
   458  [here](https://docs.tendermint.com/master/spec/abci/).
   459  
   460  ## 1.4 Starting an application and a Tendermint Core instances
   461  
   462  Put the following code into the `$KVSTORE_HOME/src/main/java/io/example/App.java` file:
   463  
   464  ```java
   465  package io.example;
   466  
   467  import jetbrains.exodus.env.Environment;
   468  import jetbrains.exodus.env.Environments;
   469  
   470  import java.io.IOException;
   471  
   472  public class App {
   473      public static void main(String[] args) throws IOException, InterruptedException {
   474          try (Environment env = Environments.newInstance("tmp/storage")) {
   475              var app = new KVStoreApp(env);
   476              var server = new GrpcServer(app, 26658);
   477              server.start();
   478              server.blockUntilShutdown();
   479          }
   480      }
   481  }
   482  ```
   483  
   484  It is the entry point of the application.
   485  Here we create a special object `Environment`, which knows where to store the application state.
   486  Then we create and start the gRPC server to handle Tendermint Core requests.
   487  
   488  Create the `$KVSTORE_HOME/src/main/java/io/example/GrpcServer.java` file with the following content:
   489  
   490  ```java
   491  package io.example;
   492  
   493  import io.grpc.BindableService;
   494  import io.grpc.Server;
   495  import io.grpc.ServerBuilder;
   496  
   497  import java.io.IOException;
   498  
   499  class GrpcServer {
   500      private Server server;
   501  
   502      GrpcServer(BindableService service, int port) {
   503          this.server = ServerBuilder.forPort(port)
   504                  .addService(service)
   505                  .build();
   506      }
   507  
   508      void start() throws IOException {
   509          server.start();
   510          System.out.println("gRPC server started, listening on $port");
   511          Runtime.getRuntime().addShutdownHook(new Thread(() -> {
   512              System.out.println("shutting down gRPC server since JVM is shutting down");
   513              GrpcServer.this.stop();
   514              System.out.println("server shut down");
   515          }));
   516      }
   517  
   518      private void stop() {
   519          server.shutdown();
   520      }
   521  
   522      /**
   523       * Await termination on the main thread since the grpc library uses daemon threads.
   524       */
   525      void blockUntilShutdown() throws InterruptedException {
   526          server.awaitTermination();
   527      }
   528  }
   529  ```
   530  
   531  ## 1.5 Getting Up and Running
   532  
   533  To create a default configuration, nodeKey and private validator files, let's
   534  execute `tendermint init`. But before we do that, we will need to install
   535  Tendermint Core.
   536  
   537  ```sh
   538  $ rm -rf /tmp/example
   539  $ cd $GOPATH/src/github.com/tendermint/tendermint
   540  $ make install
   541  $ TMHOME="/tmp/example" tendermint init
   542  
   543  I[2019-07-16|18:20:36.480] Generated private validator                  module=main keyFile=/tmp/example/config/priv_validator_key.json stateFile=/tmp/example2/data/priv_validator_state.json
   544  I[2019-07-16|18:20:36.481] Generated node key                           module=main path=/tmp/example/config/node_key.json
   545  I[2019-07-16|18:20:36.482] Generated genesis file                       module=main path=/tmp/example/config/genesis.json
   546  ```
   547  
   548  Feel free to explore the generated files, which can be found at
   549  `/tmp/example/config` directory. Documentation on the config can be found
   550  [here](https://docs.tendermint.com/master/tendermint-core/configuration.html).
   551  
   552  We are ready to start our application:
   553  
   554  ```sh
   555  ./gradlew run
   556  
   557  gRPC server started, listening on 26658
   558  ```
   559  
   560  Then we need to start Tendermint Core and point it to our application. Staying
   561  within the application directory execute:
   562  
   563  ```sh
   564  $ TMHOME="/tmp/example" tendermint node --abci grpc --proxy_app tcp://127.0.0.1:26658
   565  
   566  I[2019-07-28|15:44:53.632] Version info                                 module=main software=0.32.1 block=10 p2p=7
   567  I[2019-07-28|15:44:53.677] Starting Node                                module=main impl=Node
   568  I[2019-07-28|15:44:53.681] Started node                                 module=main nodeInfo="{ProtocolVersion:{P2P:7 Block:10 App:0} ID_:7639e2841ccd47d5ae0f5aad3011b14049d3f452 ListenAddr:tcp://0.0.0.0:26656 Network:test-chain-Nhl3zk Version:0.32.1 Channels:4020212223303800 Moniker:Ivans-MacBook-Pro.local Other:{TxIndex:on RPCAddress:tcp://127.0.0.1:26657}}"
   569  I[2019-07-28|15:44:54.801] Executed block                               module=state height=8 validTxs=0 invalidTxs=0
   570  I[2019-07-28|15:44:54.814] Committed state                              module=state height=8 txs=0 appHash=0000000000000000
   571  ```
   572  
   573  Now open another tab in your terminal and try sending a transaction:
   574  
   575  ```sh
   576  $ curl -s 'localhost:26657/broadcast_tx_commit?tx="tendermint=rocks"'
   577  {
   578    "jsonrpc": "2.0",
   579    "id": "",
   580    "result": {
   581      "check_tx": {
   582        "gasWanted": "1"
   583      },
   584      "deliver_tx": {},
   585      "hash": "CDD3C6DFA0A08CAEDF546F9938A2EEC232209C24AA0E4201194E0AFB78A2C2BB",
   586      "height": "33"
   587  }
   588  ```
   589  
   590  Response should contain the height where this transaction was committed.
   591  
   592  Now let's check if the given key now exists and its value:
   593  
   594  ```sh
   595  $ curl -s 'localhost:26657/abci_query?data="tendermint"'
   596  {
   597    "jsonrpc": "2.0",
   598    "id": "",
   599    "result": {
   600      "response": {
   601        "log": "exists",
   602        "key": "dGVuZGVybWludA==",
   603        "value": "cm9ja3My"
   604      }
   605    }
   606  }
   607  ```
   608  
   609  `dGVuZGVybWludA==` and `cm9ja3M=` are the base64-encoding of the ASCII of `tendermint` and `rocks` accordingly.
   610  
   611  ## Outro
   612  
   613  I hope everything went smoothly and your first, but hopefully not the last,
   614  Tendermint Core application is up and running. If not, please [open an issue on
   615  Github](https://github.com/tendermint/tendermint/issues/new/choose). To dig
   616  deeper, read [the docs](https://docs.tendermint.com/master/).
   617  
   618  The full source code of this example project can be found [here](https://github.com/climber73/tendermint-abci-grpc-java).