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