github.com/okex/exchain@v1.8.0/libs/tendermint/docs/app-dev/app-development.md (about)

     1  ---
     2  order: 4
     3  ---
     4  
     5  # Application Development Guide
     6  
     7  ## XXX
     8  
     9  This page is undergoing deprecation. All content is being moved to the new [home
    10  of the ABCI specification](https://github.com/tendermint/spec/tree/master/spec/abci).
    11  
    12  ## ABCI Design
    13  
    14  The purpose of ABCI is to provide a clean interface between state
    15  transition machines on one computer and the mechanics of their
    16  replication across multiple computers. The former we call 'application
    17  logic' and the latter the 'consensus engine'. Application logic
    18  validates transactions and optionally executes transactions against some
    19  persistent state. A consensus engine ensures all transactions are
    20  replicated in the same order on every machine. We call each machine in a
    21  consensus engine a 'validator', and each validator runs the same
    22  transactions through the same application logic. In particular, we are
    23  interested in blockchain-style consensus engines, where transactions are
    24  committed in hash-linked blocks.
    25  
    26  The ABCI design has a few distinct components:
    27  
    28  - message protocol
    29    - pairs of request and response messages
    30    - consensus makes requests, application responds
    31    - defined using protobuf
    32  - server/client
    33    - consensus engine runs the client
    34    - application runs the server
    35    - two implementations:
    36      - async raw bytes
    37      - grpc
    38  - blockchain protocol
    39    - abci is connection oriented
    40    - Tendermint Core maintains three connections:
    41      - [mempool connection](#mempool-connection): for checking if
    42        transactions should be relayed before they are committed;
    43        only uses `CheckTx`
    44      - [consensus connection](#consensus-connection): for executing
    45        transactions that have been committed. Message sequence is
    46        -for every block -`BeginBlock, [DeliverTx, ...], EndBlock, Commit`
    47      - [query connection](#query-connection): for querying the
    48        application state; only uses Query and Info
    49  
    50  The mempool and consensus logic act as clients, and each maintains an
    51  open ABCI connection with the application, which hosts an ABCI server.
    52  Shown are the request and response types sent on each connection.
    53  
    54  Most of the examples below are from [kvstore
    55  application](https://github.com/tendermint/tendermint/blob/master/abci/example/kvstore/kvstore.go),
    56  which is a part of the abci repo. [persistent_kvstore
    57  application](https://github.com/tendermint/tendermint/blob/master/abci/example/kvstore/persistent_kvstore.go)
    58  is used to show `BeginBlock`, `EndBlock` and `InitChain` example
    59  implementations.
    60  
    61  ## Blockchain Protocol
    62  
    63  In ABCI, a transaction is simply an arbitrary length byte-array. It is
    64  the application's responsibility to define the transaction codec as they
    65  please, and to use it for both CheckTx and DeliverTx.
    66  
    67  Note that there are two distinct means for running transactions,
    68  corresponding to stages of 'awareness' of the transaction in the
    69  network. The first stage is when a transaction is received by a
    70  validator from a client into the so-called mempool or transaction pool
    71  -this is where we use CheckTx. The second is when the transaction is
    72  successfully committed on more than 2/3 of validators - where we use
    73  DeliverTx. In the former case, it may not be necessary to run all the
    74  state transitions associated with the transaction, as the transaction
    75  may not ultimately be committed until some much later time, when the
    76  result of its execution will be different. For instance, an Ethereum
    77  ABCI app would check signatures and amounts in CheckTx, but would not
    78  actually execute any contract code until the DeliverTx, so as to avoid
    79  executing state transitions that have not been finalized.
    80  
    81  To formalize the distinction further, two explicit ABCI connections are
    82  made between Tendermint Core and the application: the mempool connection
    83  and the consensus connection. We also make a third connection, the query
    84  connection, to query the local state of the app.
    85  
    86  ### Mempool Connection
    87  
    88  The mempool connection is used _only_ for CheckTx requests. Transactions
    89  are run using CheckTx in the same order they were received by the
    90  validator. If the CheckTx returns `OK`, the transaction is kept in
    91  memory and relayed to other peers in the same order it was received.
    92  Otherwise, it is discarded.
    93  
    94  CheckTx requests run concurrently with block processing; so they should
    95  run against a copy of the main application state which is reset after
    96  every block. This copy is necessary to track transitions made by a
    97  sequence of CheckTx requests before they are included in a block. When a
    98  block is committed, the application must ensure to reset the mempool
    99  state to the latest committed state. Tendermint Core will then filter
   100  through all transactions in the mempool, removing any that were included
   101  in the block, and re-run the rest using CheckTx against the post-Commit
   102  mempool state (this behaviour can be turned off with
   103  `[mempool] recheck = false`).
   104  
   105  In go:
   106  
   107  ```
   108  func (app *KVStoreApplication) CheckTx(req types.RequestCheckTx) types.ResponseCheckTx {
   109  	return types.ResponseCheckTx{Code: code.CodeTypeOK, GasWanted: 1}
   110  }
   111  ```
   112  
   113  In Java:
   114  
   115  ```
   116  ResponseCheckTx requestCheckTx(RequestCheckTx req) {
   117      byte[] transaction = req.getTx().toByteArray();
   118  
   119      // validate transaction
   120  
   121      if (notValid) {
   122          return ResponseCheckTx.newBuilder().setCode(CodeType.BadNonce).setLog("invalid tx").build();
   123      } else {
   124          return ResponseCheckTx.newBuilder().setCode(CodeType.OK).build();
   125      }
   126  }
   127  ```
   128  
   129  ### Replay Protection
   130  
   131  To prevent old transactions from being replayed, CheckTx must implement
   132  replay protection.
   133  
   134  Tendermint provides the first defence layer by keeping a lightweight
   135  in-memory cache of 100k (`[mempool] cache_size`) last transactions in
   136  the mempool. If Tendermint is just started or the clients sent more than
   137  100k transactions, old transactions may be sent to the application. So
   138  it is important CheckTx implements some logic to handle them.
   139  
   140  If there are cases in your application where a transaction may become invalid in some
   141  future state, you probably want to disable Tendermint's
   142  cache. You can do that by setting `[mempool] cache_size = 0` in the
   143  config.
   144  
   145  ### Consensus Connection
   146  
   147  The consensus connection is used only when a new block is committed, and
   148  communicates all information from the block in a series of requests:
   149  `BeginBlock, [DeliverTx, ...], EndBlock, Commit`. That is, when a block
   150  is committed in the consensus, we send a list of DeliverTx requests (one
   151  for each transaction) sandwiched by BeginBlock and EndBlock requests,
   152  and followed by a Commit.
   153  
   154  ### DeliverTx
   155  
   156  DeliverTx is the workhorse of the blockchain. Tendermint sends the
   157  DeliverTx requests asynchronously but in order, and relies on the
   158  underlying socket protocol (ie. TCP) to ensure they are received by the
   159  app in order. They have already been ordered in the global consensus by
   160  the Tendermint protocol.
   161  
   162  DeliverTx returns a abci.Result, which includes a Code, Data, and Log.
   163  The code may be non-zero (non-OK), meaning the corresponding transaction
   164  should have been rejected by the mempool, but may have been included in
   165  a block by a Byzantine proposer.
   166  
   167  The block header will be updated (TODO) to include some commitment to
   168  the results of DeliverTx, be it a bitarray of non-OK transactions, or a
   169  merkle root of the data returned by the DeliverTx requests, or both.
   170  
   171  In go:
   172  
   173  ```
   174  // tx is either "key=value" or just arbitrary bytes
   175  func (app *KVStoreApplication) DeliverTx(req types.RequestDeliverTx) types.ResponseDeliverTx {
   176  	var key, value []byte
   177  	parts := bytes.Split(req.Tx, []byte("="))
   178  	if len(parts) == 2 {
   179  		key, value = parts[0], parts[1]
   180  	} else {
   181  		key, value = req.Tx, req.Tx
   182  	}
   183  
   184  	app.state.db.Set(prefixKey(key), value)
   185  	app.state.Size += 1
   186  
   187  	events := []types.Event{
   188  		{
   189  			Type: "app",
   190  			Attributes: []kv.Pair{
   191  				{Key: []byte("creator"), Value: []byte("Cosmoshi Netowoko")},
   192  				{Key: []byte("key"), Value: key},
   193  			},
   194  		},
   195  	}
   196  
   197  	return types.ResponseDeliverTx{Code: code.CodeTypeOK, Events: events}
   198  }
   199  ```
   200  
   201  In Java:
   202  
   203  ```
   204  /**
   205   * Using Protobuf types from the protoc compiler, we always start with a byte[]
   206   */
   207  ResponseDeliverTx deliverTx(RequestDeliverTx request) {
   208      byte[] transaction  = request.getTx().toByteArray();
   209  
   210      // validate your transaction
   211  
   212      if (notValid) {
   213          return ResponseDeliverTx.newBuilder().setCode(CodeType.BadNonce).setLog("transaction was invalid").build();
   214      } else {
   215          ResponseDeliverTx.newBuilder().setCode(CodeType.OK).build();
   216      }
   217  
   218  }
   219  ```
   220  
   221  ### Commit
   222  
   223  Once all processing of the block is complete, Tendermint sends the
   224  Commit request and blocks waiting for a response. While the mempool may
   225  run concurrently with block processing (the BeginBlock, DeliverTxs, and
   226  EndBlock), it is locked for the Commit request so that its state can be
   227  safely updated during Commit. This means the app _MUST NOT_ do any
   228  blocking communication with the mempool (ie. broadcast_tx) during
   229  Commit, or there will be deadlock. Note also that all remaining
   230  transactions in the mempool are replayed on the mempool connection
   231  (CheckTx) following a commit.
   232  
   233  The app should respond to the Commit request with a byte array, which is
   234  the deterministic state root of the application. It is included in the
   235  header of the next block. It can be used to provide easily verified
   236  Merkle-proofs of the state of the application.
   237  
   238  It is expected that the app will persist state to disk on Commit. The
   239  option to have all transactions replayed from some previous block is the
   240  job of the [Handshake](#handshake).
   241  
   242  In go:
   243  
   244  ```
   245  func (app *KVStoreApplication) Commit() types.ResponseCommit {
   246  	// Using a memdb - just return the big endian size of the db
   247  	appHash := make([]byte, 8)
   248  	binary.PutVarint(appHash, app.state.Size)
   249  	app.state.AppHash = appHash
   250  	app.state.Height += 1
   251  	saveState(app.state)
   252  	return types.ResponseCommit{Data: appHash}
   253  }
   254  ```
   255  
   256  In Java:
   257  
   258  ```
   259  ResponseCommit requestCommit(RequestCommit requestCommit) {
   260  
   261      // update the internal app-state
   262      byte[] newAppState = calculateAppState();
   263  
   264      // and return it to the node
   265      return ResponseCommit.newBuilder().setCode(CodeType.OK).setData(ByteString.copyFrom(newAppState)).build();
   266  }
   267  ```
   268  
   269  ### BeginBlock
   270  
   271  The BeginBlock request can be used to run some code at the beginning of
   272  every block. It also allows Tendermint to send the current block hash
   273  and header to the application, before it sends any of the transactions.
   274  
   275  The app should remember the latest height and header (ie. from which it
   276  has run a successful Commit) so that it can tell Tendermint where to
   277  pick up from when it restarts. See information on the Handshake, below.
   278  
   279  In go:
   280  
   281  ```
   282  // Track the block hash and header information
   283  func (app *PersistentKVStoreApplication) BeginBlock(req types.RequestBeginBlock) types.ResponseBeginBlock {
   284  	// reset valset changes
   285  	app.ValUpdates = make([]types.ValidatorUpdate, 0)
   286  	return types.ResponseBeginBlock{}
   287  }
   288  ```
   289  
   290  In Java:
   291  
   292  ```
   293  /*
   294   * all types come from protobuf definition
   295   */
   296  ResponseBeginBlock requestBeginBlock(RequestBeginBlock req) {
   297  
   298      Header header = req.getHeader();
   299      byte[] prevAppHash = header.getAppHash().toByteArray();
   300      long prevHeight = header.getHeight();
   301  
   302      // run your pre-block logic. Maybe prepare a state snapshot, message components, etc
   303  
   304      return ResponseBeginBlock.newBuilder().build();
   305  }
   306  ```
   307  
   308  ### EndBlock
   309  
   310  The EndBlock request can be used to run some code at the end of every block.
   311  Additionally, the response may contain a list of validators, which can be used
   312  to update the validator set. To add a new validator or update an existing one,
   313  simply include them in the list returned in the EndBlock response. To remove
   314  one, include it in the list with a `power` equal to `0`. Validator's `address`
   315  field can be left empty. Tendermint core will take care of updating the
   316  validator set. Note the change in voting power must be strictly less than 1/3
   317  per block if you want a light client to be able to prove the transition
   318  externally. See the [light client
   319  docs](https://godoc.org/github.com/tendermint/tendermint/lite#hdr-How_We_Track_Validators)
   320  for details on how it tracks validators.
   321  
   322  In go:
   323  
   324  ```
   325  // Update the validator set
   326  func (app *PersistentKVStoreApplication) EndBlock(req types.RequestEndBlock) types.ResponseEndBlock {
   327  	return types.ResponseEndBlock{ValidatorUpdates: app.ValUpdates}
   328  }
   329  ```
   330  
   331  In Java:
   332  
   333  ```
   334  /*
   335   * Assume that one validator changes. The new validator has a power of 10
   336   */
   337  ResponseEndBlock requestEndBlock(RequestEndBlock req) {
   338      final long currentHeight = req.getHeight();
   339      final byte[] validatorPubKey = getValPubKey();
   340  
   341      ResponseEndBlock.Builder builder = ResponseEndBlock.newBuilder();
   342      builder.addDiffs(1, Types.Validator.newBuilder().setPower(10L).setPubKey(ByteString.copyFrom(validatorPubKey)).build());
   343  
   344      return builder.build();
   345  }
   346  ```
   347  
   348  ### Query Connection
   349  
   350  This connection is used to query the application without engaging
   351  consensus. It's exposed over the tendermint core rpc, so clients can
   352  query the app without exposing a server on the app itself, but they must
   353  serialize each query as a single byte array. Additionally, certain
   354  "standardized" queries may be used to inform local decisions, for
   355  instance about which peers to connect to.
   356  
   357  Tendermint Core currently uses the Query connection to filter peers upon
   358  connecting, according to IP address or node ID. For instance,
   359  returning non-OK ABCI response to either of the following queries will
   360  cause Tendermint to not connect to the corresponding peer:
   361  
   362  - `p2p/filter/addr/<ip addr>`, where `<ip addr>` is an IP address.
   363  - `p2p/filter/id/<id>`, where `<is>` is the hex-encoded node ID (the hash of
   364    the node's p2p pubkey).
   365  
   366  Note: these query formats are subject to change!
   367  
   368  In go:
   369  
   370  ```
   371  func (app *KVStoreApplication) Query(reqQuery types.RequestQuery) (resQuery types.ResponseQuery) {
   372  	if reqQuery.Prove {
   373  		value := app.state.db.Get(prefixKey(reqQuery.Data))
   374  		resQuery.Index = -1 // TODO make Proof return index
   375  		resQuery.Key = reqQuery.Data
   376  		resQuery.Value = value
   377  		if value != nil {
   378  			resQuery.Log = "exists"
   379  		} else {
   380  			resQuery.Log = "does not exist"
   381  		}
   382  		return
   383  	} else {
   384  		resQuery.Key = reqQuery.Data
   385  		value := app.state.db.Get(prefixKey(reqQuery.Data))
   386  		resQuery.Value = value
   387  		if value != nil {
   388  			resQuery.Log = "exists"
   389  		} else {
   390  			resQuery.Log = "does not exist"
   391  		}
   392  		return
   393  	}
   394  }
   395  ```
   396  
   397  In Java:
   398  
   399  ```
   400      ResponseQuery requestQuery(RequestQuery req) {
   401          final boolean isProveQuery = req.getProve();
   402          final ResponseQuery.Builder responseBuilder = ResponseQuery.newBuilder();
   403  		byte[] queryData = req.getData().toByteArray();
   404  
   405          if (isProveQuery) {
   406              com.app.example.QueryResultWithProof result = generateQueryResultWithProof(queryData);
   407              responseBuilder.setIndex(result.getLeftIndex());
   408              responseBuilder.setKey(req.getData());
   409              responseBuilder.setValue(result.getValueOrNull(0));
   410              responseBuilder.setHeight(result.getHeight());
   411              responseBuilder.setProof(result.getProof());
   412              responseBuilder.setLog(result.getLogValue());
   413          } else {
   414              com.app.example.QueryResult result = generateQueryResult(queryData);
   415              responseBuilder.setIndex(result.getIndex());
   416              responseBuilder.setValue(result.getValue());
   417              responseBuilder.setLog(result.getLogValue());
   418          }
   419  
   420          responseBuilder.setIndex(result.getIndex());
   421          responseBuilder.setValue(ByteString.copyFrom(result.getValue()));
   422          responseBuilder.setLog(result.getLogValue());
   423      }
   424  
   425      return responseBuilder.build();
   426  }
   427  ```
   428  
   429  ### Handshake
   430  
   431  When the app or tendermint restarts, they need to sync to a common
   432  height. When an ABCI connection is first established, Tendermint will
   433  call `Info` on the Query connection. The response should contain the
   434  LastBlockHeight and LastBlockAppHash - the former is the last block for
   435  which the app ran Commit successfully, the latter is the response from
   436  that Commit.
   437  
   438  Using this information, Tendermint will determine what needs to be
   439  replayed, if anything, against the app, to ensure both Tendermint and
   440  the app are synced to the latest block height.
   441  
   442  If the app returns a LastBlockHeight of 0, Tendermint will just replay
   443  all blocks.
   444  
   445  In go:
   446  
   447  ```
   448  func (app *KVStoreApplication) Info(req types.RequestInfo) (resInfo types.ResponseInfo) {
   449  	return types.ResponseInfo{
   450  		Data:       fmt.Sprintf("{\"size\":%v}", app.state.Size),
   451  		Version:    version.ABCIVersion,
   452  		AppVersion: ProtocolVersion.Uint64(),
   453  	}
   454  }
   455  ```
   456  
   457  In Java:
   458  
   459  ```
   460  ResponseInfo requestInfo(RequestInfo req) {
   461      final byte[] lastAppHash = getLastAppHash();
   462      final long lastHeight = getLastHeight();
   463      return ResponseInfo.newBuilder().setLastBlockAppHash(ByteString.copyFrom(lastAppHash)).setLastBlockHeight(lastHeight).build();
   464  }
   465  ```
   466  
   467  ### Genesis
   468  
   469  `InitChain` will be called once upon the genesis. `params` includes the
   470  initial validator set. Later on, it may be extended to take parts of the
   471  consensus params.
   472  
   473  In go:
   474  
   475  ```
   476  // Save the validators in the merkle tree
   477  func (app *PersistentKVStoreApplication) InitChain(req types.RequestInitChain) types.ResponseInitChain {
   478  	for _, v := range req.Validators {
   479  		r := app.updateValidator(v)
   480  		if r.IsErr() {
   481  			app.logger.Error("Error updating validators", "r", r)
   482  		}
   483  	}
   484  	return types.ResponseInitChain{}
   485  }
   486  ```
   487  
   488  In Java:
   489  
   490  ```
   491  /*
   492   * all types come from protobuf definition
   493   */
   494  ResponseInitChain requestInitChain(RequestInitChain req) {
   495      final int validatorsCount = req.getValidatorsCount();
   496      final List<Types.Validator> validatorsList = req.getValidatorsList();
   497  
   498      validatorsList.forEach((validator) -> {
   499          long power = validator.getPower();
   500          byte[] validatorPubKey = validator.getPubKey().toByteArray();
   501  
   502          // do somehing for validator setup in app
   503      });
   504  
   505      return ResponseInitChain.newBuilder().build();
   506  }
   507  ```