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