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