github.com/koko1123/flow-go-1@v0.29.6/engine/execution/checker/engine.go (about) 1 package checker 2 3 import ( 4 "errors" 5 "fmt" 6 7 "github.com/rs/zerolog" 8 9 "github.com/koko1123/flow-go-1/consensus/hotstuff/model" 10 "github.com/koko1123/flow-go-1/consensus/hotstuff/notifications" 11 "github.com/koko1123/flow-go-1/engine" 12 "github.com/koko1123/flow-go-1/engine/execution/state" 13 "github.com/koko1123/flow-go-1/model/flow" 14 "github.com/koko1123/flow-go-1/state/protocol" 15 "github.com/koko1123/flow-go-1/storage" 16 ) 17 18 type Engine struct { 19 notifications.NoopConsumer // satisfy the FinalizationConsumer interface 20 21 unit *engine.Unit 22 log zerolog.Logger 23 state protocol.State 24 execState state.ExecutionState 25 sealsDB storage.Seals 26 } 27 28 func New( 29 logger zerolog.Logger, 30 state protocol.State, 31 execState state.ExecutionState, 32 sealsDB storage.Seals, 33 ) *Engine { 34 return &Engine{ 35 unit: engine.NewUnit(), 36 log: logger.With().Str("engine", "checker").Logger(), 37 state: state, 38 execState: execState, 39 sealsDB: sealsDB, 40 } 41 } 42 43 func (e *Engine) Ready() <-chan struct{} { 44 // make sure we will run into a crashloop if result gets inconsistent 45 // with sealed result. 46 47 finalized, err := e.state.Final().Head() 48 49 if err != nil { 50 e.log.Fatal().Err(err).Msg("could not get finalized block on startup") 51 } 52 53 err = e.checkLastSealed(finalized.ID()) 54 if err != nil { 55 e.log.Fatal().Err(err).Msg("execution consistency check failed on startup") 56 } 57 return e.unit.Ready() 58 } 59 60 func (e *Engine) Done() <-chan struct{} { 61 return e.unit.Done() 62 } 63 64 // when a block is finalized check if the last sealed has been executed, 65 // if it has been executed, check whether if the sealed result is consistent 66 // with the executed result 67 func (e *Engine) OnFinalizedBlock(block *model.Block) { 68 err := e.checkLastSealed(block.BlockID) 69 if err != nil { 70 e.log.Fatal().Err(err).Msg("execution consistency check failed") 71 } 72 } 73 74 func (e *Engine) checkLastSealed(finalizedID flow.Identifier) error { 75 // TODO: better to query seals from protocol state, 76 // switch to state.Final().LastSealed() when available 77 seal, err := e.sealsDB.HighestInFork(finalizedID) 78 if err != nil { 79 return fmt.Errorf("could not get the last sealed for the finalized block: %w", err) 80 } 81 82 blockID := seal.BlockID 83 sealedCommit := seal.FinalState 84 85 mycommit, err := e.execState.StateCommitmentByBlockID(e.unit.Ctx(), blockID) 86 if errors.Is(err, storage.ErrNotFound) { 87 // have not executed the sealed block yet 88 // in other words, this can't detect execution fork, if the execution is behind 89 // the sealing 90 return nil 91 } 92 93 if err != nil { 94 return fmt.Errorf("could not get my state commitment OnFinalizedBlock, blockID: %v", blockID) 95 } 96 97 if mycommit != sealedCommit { 98 sealed, err := e.state.AtBlockID(blockID).Head() 99 if err != nil { 100 return fmt.Errorf("could not get sealed block when checkLastSealed: %v, err: %w", blockID, err) 101 } 102 103 return fmt.Errorf("execution result is different from the sealed result, height: %v, block_id: %v, sealed_commit: %x, my_commit: %x", 104 sealed.Height, blockID, sealedCommit, mycommit) 105 } 106 107 return nil 108 }