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 ```