github.com/pokt-network/tendermint@v0.32.11-0.20230426215212-59310158d3e9/state/execution.go (about) 1 package state 2 3 import ( 4 "fmt" 5 "github.com/tendermint/tendermint/state/txindex" 6 "time" 7 8 dbm "github.com/tendermint/tm-db" 9 10 abci "github.com/tendermint/tendermint/abci/types" 11 "github.com/tendermint/tendermint/libs/fail" 12 "github.com/tendermint/tendermint/libs/log" 13 mempl "github.com/tendermint/tendermint/mempool" 14 "github.com/tendermint/tendermint/proxy" 15 "github.com/tendermint/tendermint/types" 16 ) 17 18 //----------------------------------------------------------------------------- 19 // BlockExecutor handles block execution and state updates. 20 // It exposes ApplyBlock(), which validates & executes the block, updates state w/ ABCI responses, 21 // then commits and updates the mempool atomically, then saves state. 22 23 // BlockExecutor provides the context and accessories for properly executing a block. 24 type BlockExecutor struct { 25 // save state, validators, consensus params, abci responses here 26 db dbm.DB 27 28 // execute the app against this 29 proxyApp proxy.AppConnConsensus 30 31 // events 32 eventBus types.BlockEventPublisher 33 34 // manage the mempool lock during commit 35 // and update both with block results after commit. 36 mempool mempl.Mempool 37 evpool EvidencePool 38 39 logger log.Logger 40 41 metrics *Metrics 42 43 indexer txindex.TxIndexer 44 } 45 46 type BlockExecutorOption func(executor *BlockExecutor) 47 48 func BlockExecutorWithMetrics(metrics *Metrics) BlockExecutorOption { 49 return func(blockExec *BlockExecutor) { 50 blockExec.metrics = metrics 51 } 52 } 53 54 // NewBlockExecutor returns a new BlockExecutor with a NopEventBus. 55 // Call SetEventBus to provide one. 56 func NewBlockExecutor( 57 db dbm.DB, 58 logger log.Logger, 59 proxyApp proxy.AppConnConsensus, 60 mempool mempl.Mempool, 61 evpool EvidencePool, 62 indexer txindex.TxIndexer, 63 options ...BlockExecutorOption, 64 ) *BlockExecutor { 65 res := &BlockExecutor{ 66 db: db, 67 proxyApp: proxyApp, 68 eventBus: types.NopEventBus{}, 69 mempool: mempool, 70 evpool: evpool, 71 logger: logger, 72 metrics: NopMetrics(), 73 indexer: indexer, 74 } 75 76 for _, option := range options { 77 option(res) 78 } 79 80 return res 81 } 82 83 func (blockExec *BlockExecutor) DB() dbm.DB { 84 return blockExec.db 85 } 86 87 // SetEventBus - sets the event bus for publishing block related events. 88 // If not called, it defaults to types.NopEventBus. 89 func (blockExec *BlockExecutor) SetEventBus(eventBus types.BlockEventPublisher) { 90 blockExec.eventBus = eventBus 91 } 92 93 // CreateProposalBlock calls state.MakeBlock with evidence from the evpool 94 // and txs from the mempool. The max bytes must be big enough to fit the commit. 95 // Up to 1/10th of the block space is allcoated for maximum sized evidence. 96 // The rest is given to txs, up to the max gas. 97 func (blockExec *BlockExecutor) CreateProposalBlock( 98 height int64, 99 state State, commit *types.Commit, 100 proposerAddr []byte, 101 ) (*types.Block, *types.PartSet) { 102 103 maxBytes := state.ConsensusParams.Block.MaxBytes 104 maxGas := state.ConsensusParams.Block.MaxGas 105 106 // Fetch a limited amount of valid evidence 107 maxNumEvidence, _ := types.MaxEvidencePerBlock(maxBytes) 108 evidence := blockExec.evpool.PendingEvidence(maxNumEvidence) 109 110 // Fetch a limited amount of valid txs 111 maxDataBytes := types.MaxDataBytes(maxBytes, state.Validators.Size(), len(evidence)) 112 txs := blockExec.mempool.ReapMaxBytesMaxGas(maxDataBytes, maxGas) 113 114 return state.MakeBlock(height, txs, commit, evidence, proposerAddr) 115 } 116 117 // ValidateBlock validates the given block against the given state. 118 // If the block is invalid, it returns an error. 119 // Validation does not mutate state, but does require historical information from the stateDB, 120 // ie. to verify evidence from a validator at an old height. 121 func (blockExec *BlockExecutor) ValidateBlock(state State, block *types.Block) error { 122 defer types.TimeTrack(time.Now(), blockExec.logger) 123 return validateBlock(blockExec.evpool, blockExec.db, state, block) 124 } 125 126 // ApplyBlock validates the block against the state, executes it against the app, 127 // fires the relevant events, commits the app, and saves the new state and responses. 128 // It returns the new state and the block height to retain (pruning older blocks). 129 // It's the only function that needs to be called 130 // from outside this package to process and commit an entire block. 131 // It takes a blockID to avoid recomputing the parts hash. 132 func (blockExec *BlockExecutor) ApplyBlock( 133 state State, blockID types.BlockID, block *types.Block, 134 ) (State, int64, error) { 135 defer types.TimeTrack(time.Now(), blockExec.logger) 136 137 if err := blockExec.ValidateBlock(state, block); err != nil { 138 return state, 0, ErrInvalidBlock(err) 139 } 140 141 startTime := time.Now().UnixNano() 142 abciResponses, err := execBlockOnProxyApp(blockExec.logger, blockExec.proxyApp, block, blockExec.db) 143 endTime := time.Now().UnixNano() 144 blockExec.metrics.BlockProcessingTime.Observe(float64(endTime-startTime) / 1000000) 145 if err != nil { 146 return state, 0, ErrProxyAppConn(err) 147 } 148 149 fail.Fail() // XXX 150 151 // Save the results before we commit. 152 SaveABCIResponses(blockExec.db, block.Height, abciResponses) 153 154 fail.Fail() // XXX 155 156 // validate the validator updates and convert to tendermint types 157 abciValUpdates := abciResponses.EndBlock.ValidatorUpdates 158 err = validateValidatorUpdates(abciValUpdates, state.ConsensusParams.Validator) 159 if err != nil { 160 return state, 0, fmt.Errorf("error in validator updates: %v", err) 161 } 162 validatorUpdates, err := types.PB2TM.ValidatorUpdates(abciValUpdates) 163 if err != nil { 164 return state, 0, err 165 } 166 if len(validatorUpdates) > 0 { 167 blockExec.logger.Info("Updates to validators", "updates", types.ValidatorListString(validatorUpdates)) 168 } 169 170 // Update the state with the block and responses. 171 state, err = updateState(state, blockID, &block.Header, abciResponses, validatorUpdates) 172 if err != nil { 173 return state, 0, fmt.Errorf("commit failed for application: %v", err) 174 } 175 176 // Lock mempool, commit app state, update mempoool. 177 appHash, retainHeight, err := blockExec.Commit(state, block, abciResponses.DeliverTx) 178 if err != nil { 179 return state, 0, fmt.Errorf("commit failed for application: %v", err) 180 } 181 182 // Update evpool with the block and state. 183 blockExec.evpool.Update(block, state) 184 185 fail.Fail() // XXX 186 187 // Update the app hash and save the state. 188 state.AppHash = appHash 189 SaveState(blockExec.db, state) 190 191 fail.Fail() // XXX 192 193 // Events are fired after everything else. 194 // NOTE: if we crash between Commit and Save, events wont be fired during replay 195 fireEvents(blockExec.logger, blockExec.eventBus, block, abciResponses, validatorUpdates) 196 197 return state, retainHeight, nil 198 } 199 200 // Commit locks the mempool, runs the ABCI Commit message, and updates the 201 // mempool. 202 // It returns the result of calling abci.Commit (the AppHash) and the height to retain (if any). 203 // The Mempool must be locked during commit and update because state is 204 // typically reset on Commit and old txs must be replayed against committed 205 // state before new txs are run in the mempool, lest they be invalid. 206 func (blockExec *BlockExecutor) Commit( 207 state State, 208 block *types.Block, 209 deliverTxResponses []*abci.ResponseDeliverTx, 210 ) ([]byte, int64, error) { 211 defer types.TimeTrack(time.Now(), blockExec.logger) 212 213 blockExec.mempool.Lock() 214 defer blockExec.mempool.Unlock() 215 216 // while mempool is Locked, flush to ensure all async requests have completed 217 // in the ABCI app before Commit. 218 err := blockExec.mempool.FlushAppConn() 219 if err != nil { 220 blockExec.logger.Error("Client error during mempool.FlushAppConn", "err", err) 221 return nil, 0, err 222 } 223 224 // Commit block, get hash back 225 res, err := blockExec.proxyApp.CommitSync() 226 if err != nil { 227 blockExec.logger.Error( 228 "Client error during proxyAppConn.CommitSync", 229 "err", err, 230 ) 231 return nil, 0, err 232 } 233 // ResponseCommit has no error code - just data 234 235 blockExec.logger.Info( 236 "Committed state", 237 "height", block.Height, 238 "txs", block.NumTxs, 239 "appHash", fmt.Sprintf("%X", res.Data), 240 ) 241 242 // Update mempool. 243 err = blockExec.mempool.Update( 244 block.Height, 245 block.Txs, 246 deliverTxResponses, 247 TxPreCheck(state), 248 TxPostCheck(state), 249 ) 250 // create a new batch 251 b := txindex.NewBatch(block.NumTxs) 252 for i, tx := range block.Txs { 253 err := b.Add(&types.TxResult{ 254 Height: block.Height, 255 Index: uint32(i), 256 Tx: tx, 257 Result: *(deliverTxResponses[i]), 258 }) 259 if err != nil { 260 return nil, 0, err 261 } 262 } 263 // update txIndexer 264 txindex.SyncTxIndexer(blockExec.logger, blockExec.indexer, b, block.Height) 265 return res.Data, res.RetainHeight, err 266 } 267 268 //--------------------------------------------------------- 269 // Helper functions for executing blocks and updating state 270 271 // Executes block's transactions on proxyAppConn. 272 // Returns a list of transaction results and updates to the validator set 273 func execBlockOnProxyApp( 274 logger log.Logger, 275 proxyAppConn proxy.AppConnConsensus, 276 block *types.Block, 277 stateDB dbm.DB, 278 ) (*ABCIResponses, error) { 279 defer types.TimeTrack(time.Now(), logger) 280 281 var validTxs, invalidTxs = 0, 0 282 283 txIndex := 0 284 abciResponses := NewABCIResponses(block) 285 286 // Execute transactions and get hash. 287 proxyCb := func(req *abci.Request, res *abci.Response) { 288 if r, ok := res.Value.(*abci.Response_DeliverTx); ok { 289 // TODO: make use of res.Log 290 // TODO: make use of this info 291 // Blocks may include invalid txs. 292 txRes := r.DeliverTx 293 if txRes.Code == abci.CodeTypeOK { 294 validTxs++ 295 } else { 296 logger.Debug("Invalid tx", "code", txRes.Code, "log", txRes.Log) 297 invalidTxs++ 298 } 299 abciResponses.DeliverTx[txIndex] = txRes 300 txIndex++ 301 } 302 } 303 proxyAppConn.SetResponseCallback(proxyCb) 304 305 commitInfo, byzVals := getBeginBlockValidatorInfo(block, stateDB) 306 307 // Begin block 308 var err error 309 abciResponses.BeginBlock, err = proxyAppConn.BeginBlockSync(abci.RequestBeginBlock{ 310 Hash: block.Hash(), 311 Header: types.TM2PB.Header(&block.Header), 312 LastCommitInfo: commitInfo, 313 ByzantineValidators: byzVals, 314 }) 315 if err != nil { 316 logger.Error("Error in proxyAppConn.BeginBlock", "err", err) 317 return nil, err 318 } 319 320 // Run txs of block. 321 for _, tx := range block.Txs { 322 proxyAppConn.DeliverTxAsync(abci.RequestDeliverTx{Tx: tx}) 323 if err := proxyAppConn.Error(); err != nil { 324 return nil, err 325 } 326 } 327 328 // End block. 329 abciResponses.EndBlock, err = proxyAppConn.EndBlockSync(abci.RequestEndBlock{Height: block.Height}) 330 if err != nil { 331 logger.Error("Error in proxyAppConn.EndBlock", "err", err) 332 return nil, err 333 } 334 335 logger.Info("Executed block", "height", block.Height, "validTxs", validTxs, "invalidTxs", invalidTxs) 336 337 return abciResponses, nil 338 } 339 340 func getBeginBlockValidatorInfo(block *types.Block, stateDB dbm.DB) (abci.LastCommitInfo, []abci.Evidence) { 341 defer types.TimeTrack(time.Now(), nil) 342 343 voteInfos := make([]abci.VoteInfo, block.LastCommit.Size()) 344 // block.Height=1 -> LastCommitInfo.Votes are empty. 345 // Remember that the first LastCommit is intentionally empty, so it makes 346 // sense for LastCommitInfo.Votes to also be empty. 347 if block.Height > 1 { 348 lastValSet, err := LoadValidators(stateDB, block.Height-1) 349 if err != nil { 350 panic(err) 351 } 352 353 // Sanity check that commit size matches validator set size - only applies 354 // after first block. 355 var ( 356 commitSize = block.LastCommit.Size() 357 valSetLen = len(lastValSet.Validators) 358 ) 359 if commitSize != valSetLen { 360 panic(fmt.Sprintf("commit size (%d) doesn't match valset length (%d) at height %d\n\n%v\n\n%v", 361 commitSize, valSetLen, block.Height, block.LastCommit.Precommits, lastValSet.Validators)) 362 } 363 364 for i, val := range lastValSet.Validators { 365 commitSig := block.LastCommit.Precommits[i] 366 voteInfos[i] = abci.VoteInfo{ 367 Validator: types.TM2PB.Validator(val), 368 SignedLastBlock: commitSig != nil, 369 } 370 } 371 } 372 373 byzVals := make([]abci.Evidence, len(block.Evidence.Evidence)) 374 for i, ev := range block.Evidence.Evidence { 375 // We need the validator set. We already did this in validateBlock. 376 // TODO: Should we instead cache the valset in the evidence itself and add 377 // `SetValidatorSet()` and `ToABCI` methods ? 378 valset, err := LoadValidators(stateDB, ev.Height()) 379 if err != nil { 380 panic(err) 381 } 382 byzVals[i] = types.TM2PB.Evidence(ev, valset, block.Time) 383 } 384 385 return abci.LastCommitInfo{ 386 Round: int32(block.LastCommit.Round()), 387 Votes: voteInfos, 388 }, byzVals 389 } 390 391 func validateValidatorUpdates(abciUpdates []abci.ValidatorUpdate, 392 params types.ValidatorParams) error { 393 defer types.TimeTrack(time.Now(), nil) 394 395 for _, valUpdate := range abciUpdates { 396 if valUpdate.GetPower() < 0 { 397 return fmt.Errorf("voting power can't be negative %v", valUpdate) 398 } else if valUpdate.GetPower() == 0 { 399 // continue, since this is deleting the validator, and thus there is no 400 // pubkey to check 401 continue 402 } 403 404 // Check if validator's pubkey matches an ABCI type in the consensus params 405 thisKeyType := valUpdate.PubKey.Type 406 if !params.IsValidPubkeyType(thisKeyType) { 407 return fmt.Errorf("validator %v is using pubkey %s, which is unsupported for consensus", 408 valUpdate, thisKeyType) 409 } 410 } 411 return nil 412 } 413 414 // updateState returns a new State updated according to the header and responses. 415 func updateState( 416 state State, 417 blockID types.BlockID, 418 header *types.Header, 419 abciResponses *ABCIResponses, 420 validatorUpdates []*types.Validator, 421 ) (State, error) { 422 423 defer types.TimeTrack(time.Now(), nil) 424 425 // Copy the valset so we can apply changes from EndBlock 426 // and update s.LastValidators and s.Validators. 427 nValSet := state.NextValidators.Copy() 428 429 // Update the validator set with the latest abciResponses. 430 lastHeightValsChanged := state.LastHeightValidatorsChanged 431 if len(validatorUpdates) > 0 { 432 err := nValSet.UpdateWithChangeSet(validatorUpdates) 433 if err != nil { 434 return state, fmt.Errorf("error changing validator set: %v", err) 435 } 436 // Change results from this height but only applies to the next next height. 437 lastHeightValsChanged = header.Height + 1 + 1 438 } 439 440 // Update validator proposer priority and set state variables. 441 nValSet.IncrementProposerPriority(1) 442 443 // Update the params with the latest abciResponses. 444 nextParams := state.ConsensusParams 445 lastHeightParamsChanged := state.LastHeightConsensusParamsChanged 446 if abciResponses.EndBlock.ConsensusParamUpdates != nil { 447 // NOTE: must not mutate s.ConsensusParams 448 nextParams = state.ConsensusParams.Update(abciResponses.EndBlock.ConsensusParamUpdates) 449 err := nextParams.Validate() 450 if err != nil { 451 return state, fmt.Errorf("error updating consensus params: %v", err) 452 } 453 // Change results from this height but only applies to the next height. 454 lastHeightParamsChanged = header.Height + 1 455 } 456 457 // TODO: allow app to upgrade version 458 nextVersion := state.Version 459 460 // NOTE: the AppHash has not been populated. 461 // It will be filled on state.Save. 462 return State{ 463 Version: nextVersion, 464 ChainID: state.ChainID, 465 LastBlockHeight: header.Height, 466 LastBlockTotalTx: state.LastBlockTotalTx + header.NumTxs, 467 LastBlockID: blockID, 468 LastBlockTime: header.Time, 469 NextValidators: nValSet, 470 Validators: state.NextValidators.Copy(), 471 LastValidators: state.Validators.Copy(), 472 LastHeightValidatorsChanged: lastHeightValsChanged, 473 ConsensusParams: nextParams, 474 LastHeightConsensusParamsChanged: lastHeightParamsChanged, 475 LastResultsHash: abciResponses.ResultsHash(), 476 AppHash: nil, 477 }, nil 478 } 479 480 // Fire NewBlock, NewBlockHeader. 481 // Fire TxEvent for every tx. 482 // NOTE: if Tendermint crashes before commit, some or all of these events may be published again. 483 func fireEvents( 484 logger log.Logger, 485 eventBus types.BlockEventPublisher, 486 block *types.Block, 487 abciResponses *ABCIResponses, 488 validatorUpdates []*types.Validator, 489 ) { 490 eventBus.PublishEventNewBlock(types.EventDataNewBlock{ 491 Block: block, 492 ResultBeginBlock: *abciResponses.BeginBlock, 493 ResultEndBlock: *abciResponses.EndBlock, 494 }) 495 eventBus.PublishEventNewBlockHeader(types.EventDataNewBlockHeader{ 496 Header: block.Header, 497 NumTxs: int64(len(block.Txs)), 498 ResultBeginBlock: *abciResponses.BeginBlock, 499 ResultEndBlock: *abciResponses.EndBlock, 500 }) 501 502 for i, tx := range block.Data.Txs { 503 eventBus.PublishEventTx(types.EventDataTx{TxResult: types.TxResult{ 504 Height: block.Height, 505 Index: uint32(i), 506 Tx: tx, 507 Result: *(abciResponses.DeliverTx[i]), 508 }}) 509 } 510 511 if len(validatorUpdates) > 0 { 512 eventBus.PublishEventValidatorSetUpdates( 513 types.EventDataValidatorSetUpdates{ValidatorUpdates: validatorUpdates}) 514 } 515 } 516 517 //---------------------------------------------------------------------------------------------------- 518 // Execute block without state. TODO: eliminate 519 520 // ExecCommitBlock executes and commits a block on the proxyApp without validating or mutating the state. 521 // It returns the application root hash (result of abci.Commit). 522 func ExecCommitBlock( 523 appConnConsensus proxy.AppConnConsensus, 524 block *types.Block, 525 logger log.Logger, 526 stateDB dbm.DB, 527 indexer txindex.TxIndexer, 528 ) ([]byte, error) { 529 defer types.TimeTrack(time.Now(), logger) 530 531 resp, err := execBlockOnProxyApp(logger, appConnConsensus, block, stateDB) 532 if err != nil { 533 logger.Error("Error executing block on proxy app", "height", block.Height, "err", err) 534 return nil, err 535 } 536 // Commit block, get hash back 537 res, err := appConnConsensus.CommitSync() 538 if err != nil { 539 logger.Error("Client error during proxyAppConn.CommitSync", "err", res) 540 return nil, err 541 } 542 // create a new batch 543 b := txindex.NewBatch(block.NumTxs) 544 for i, tx := range block.Txs { 545 err := b.Add(&types.TxResult{ 546 Height: block.Height, 547 Index: uint32(i), 548 Tx: tx, 549 Result: *(resp.DeliverTx[i]), 550 }) 551 if err != nil { 552 return nil, err 553 } 554 } 555 // update txIndexer 556 txindex.SyncTxIndexer(logger, indexer, b, block.Height) 557 // ResponseCommit has no error or log, just data 558 return res.Data, nil 559 }