github.com/onflow/flow-go@v0.33.17/engine/execution/checker/core.go (about) 1 package checker 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 8 "github.com/rs/zerolog" 9 10 "github.com/onflow/flow-go/engine/execution/state" 11 "github.com/onflow/flow-go/model/flow" 12 "github.com/onflow/flow-go/state/protocol" 13 "github.com/onflow/flow-go/storage" 14 ) 15 16 // Core is the core logic of the checker engine that checks if the execution result matches the sealed result. 17 type Core struct { 18 log zerolog.Logger 19 state protocol.State 20 execState state.ExecutionState 21 } 22 23 func NewCore( 24 logger zerolog.Logger, 25 state protocol.State, 26 execState state.ExecutionState, 27 ) *Core { 28 e := &Core{ 29 log: logger.With().Str("engine", "checker").Logger(), 30 state: state, 31 execState: execState, 32 } 33 34 return e 35 } 36 37 // checkMyCommitWithSealedCommit is the main check of the checker engine 38 func checkMyCommitWithSealedCommit( 39 executedBlock *flow.Header, 40 myCommit flow.StateCommitment, 41 sealedCommit flow.StateCommitment, 42 ) error { 43 if myCommit != sealedCommit { 44 // mismatch 45 return fmt.Errorf("execution result is different from the sealed result, height: %v, block_id: %v, sealed_commit: %v, my_commit: %v", 46 executedBlock.Height, 47 executedBlock.ID(), 48 sealedCommit, 49 myCommit, 50 ) 51 } 52 53 // match 54 return nil 55 } 56 57 // RunCheck skips when the last sealed has not been executed, and last executed has not been finalized. 58 func (c *Core) RunCheck() error { 59 // find last sealed block 60 lastSealedBlock, lastFinal, seal, err := c.findLastSealedBlock() 61 if err != nil { 62 return err 63 } 64 65 mycommitAtLastSealed, err := c.execState.StateCommitmentByBlockID(lastSealedBlock.ID()) 66 if err == nil { 67 // if last sealed block has been executed, then check if they match 68 return checkMyCommitWithSealedCommit(lastSealedBlock, mycommitAtLastSealed, seal.FinalState) 69 } 70 71 // if last sealed block has not been executed, then check if recent executed block has 72 // been sealed already, if yes, check if they match. 73 lastExecutedHeight, err := c.findLastExecutedBlockHeight() 74 if err != nil { 75 return err 76 } 77 78 if lastExecutedHeight > lastFinal.Height { 79 // last executed block has not been finalized yet, 80 // can't check since unfinalized block is also unsealed, skip 81 return nil 82 } 83 84 // TODO: better to query seals from protocol state, 85 // switch to state.Final().LastSealed() when available 86 sealedExecuted, seal, err := c.findLatestSealedAtHeight(lastExecutedHeight) 87 if err != nil { 88 return fmt.Errorf("could not get the last sealed block at height: %v, err: %w", lastExecutedHeight, err) 89 } 90 91 sealedCommit := seal.FinalState 92 93 mycommit, err := c.execState.StateCommitmentByBlockID(seal.BlockID) 94 if errors.Is(err, storage.ErrNotFound) { 95 // have not executed the sealed block yet 96 // in other words, this can't detect execution fork, if the execution is behind 97 // the sealing 98 return nil 99 } 100 101 if err != nil { 102 return fmt.Errorf("could not get my state commitment OnFinalizedBlock, blockID: %v", seal.BlockID) 103 } 104 105 return checkMyCommitWithSealedCommit(sealedExecuted, mycommit, sealedCommit) 106 } 107 108 // findLastSealedBlock finds the last sealed block 109 func (c *Core) findLastSealedBlock() (*flow.Header, *flow.Header, *flow.Seal, error) { 110 finalized := c.state.Final() 111 lastFinal, err := finalized.Head() 112 if err != nil { 113 return nil, nil, nil, err 114 } 115 116 _, lastSeal, err := finalized.SealedResult() 117 if err != nil { 118 return nil, nil, nil, fmt.Errorf("could not get the last sealed for the finalized block: %w", err) 119 } 120 121 lastSealed, err := c.state.AtBlockID(lastSeal.BlockID).Head() 122 if err != nil { 123 return nil, nil, nil, fmt.Errorf("could not get the last sealed block: %w", err) 124 } 125 126 return lastSealed, lastFinal, lastSeal, nil 127 } 128 129 // findLastExecutedBlockHeight finds the last executed block height 130 func (c *Core) findLastExecutedBlockHeight() (uint64, error) { 131 height, _, err := c.execState.GetHighestExecutedBlockID(context.Background()) 132 if err != nil { 133 return 0, fmt.Errorf("could not get the last executed block: %w", err) 134 } 135 return height, nil 136 } 137 138 // findLatestSealedAtHeight finds the latest sealed block at the given height 139 func (c *Core) findLatestSealedAtHeight(finalizedHeight uint64) (*flow.Header, *flow.Seal, error) { 140 _, seal, err := c.state.AtHeight(finalizedHeight).SealedResult() 141 if err != nil { 142 return nil, nil, fmt.Errorf("could not get the last sealed for the finalized block: %w", err) 143 } 144 145 sealed, err := c.state.AtBlockID(seal.BlockID).Head() 146 if err != nil { 147 return nil, nil, fmt.Errorf("could not get the last sealed block: %w", err) 148 } 149 return sealed, seal, nil 150 }