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