-
Notifications
You must be signed in to change notification settings - Fork 3.3k
/
safety_checkers.go
189 lines (163 loc) · 5.96 KB
/
safety_checkers.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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
package db
import (
"errors"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/db/entrydb"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/db/heads"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/db/logs"
backendTypes "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/types"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types"
)
const (
Unsafe = "unsafe"
Safe = "safe"
Finalized = "finalized"
)
// SafetyChecker is an interface for checking the safety of a log entry
// and updating the local head for a chain.
type SafetyChecker interface {
LocalHeadForChain(chainID types.ChainID) entrydb.EntryIdx
CrossHeadForChain(chainID types.ChainID) entrydb.EntryIdx
Check(chain types.ChainID, blockNum uint64, logIdx uint32, logHash backendTypes.TruncatedHash) bool
Update(chain types.ChainID, index entrydb.EntryIdx) heads.OperationFn
Name() string
SafetyLevel() types.SafetyLevel
}
// unsafeChecker is a SafetyChecker that uses the unsafe head as the view into the database
type unsafeChecker struct {
chainsDB *ChainsDB
}
// safeChecker is a SafetyChecker that uses the safe head as the view into the database
type safeChecker struct {
chainsDB *ChainsDB
}
// finalizedChecker is a SafetyChecker that uses the finalized head as the view into the database
type finalizedChecker struct {
chainsDB *ChainsDB
}
// NewSafetyChecker creates a new SafetyChecker of the given type
func NewSafetyChecker(t types.SafetyLevel, chainsDB *ChainsDB) SafetyChecker {
switch t {
case Unsafe:
return &unsafeChecker{
chainsDB: chainsDB,
}
case Safe:
return &safeChecker{
chainsDB: chainsDB,
}
case Finalized:
return &finalizedChecker{
chainsDB: chainsDB,
}
default:
panic("unknown safety checker type")
}
}
// Name returns the safety checker type, using the same strings as the constants used in construction
func (c *unsafeChecker) Name() string {
return Unsafe
}
func (c *safeChecker) Name() string {
return Safe
}
func (c *finalizedChecker) Name() string {
return Finalized
}
// LocalHeadForChain returns the local head for the given chain
// based on the type of SafetyChecker
func (c *unsafeChecker) LocalHeadForChain(chainID types.ChainID) entrydb.EntryIdx {
heads := c.chainsDB.heads.Current().Get(chainID)
return heads.Unsafe
}
func (c *safeChecker) LocalHeadForChain(chainID types.ChainID) entrydb.EntryIdx {
heads := c.chainsDB.heads.Current().Get(chainID)
return heads.LocalSafe
}
func (c *finalizedChecker) LocalHeadForChain(chainID types.ChainID) entrydb.EntryIdx {
heads := c.chainsDB.heads.Current().Get(chainID)
return heads.LocalFinalized
}
// CrossHeadForChain returns the x-head for the given chain
// based on the type of SafetyChecker
func (c *unsafeChecker) CrossHeadForChain(chainID types.ChainID) entrydb.EntryIdx {
heads := c.chainsDB.heads.Current().Get(chainID)
return heads.CrossUnsafe
}
func (c *safeChecker) CrossHeadForChain(chainID types.ChainID) entrydb.EntryIdx {
heads := c.chainsDB.heads.Current().Get(chainID)
return heads.CrossSafe
}
func (c *finalizedChecker) CrossHeadForChain(chainID types.ChainID) entrydb.EntryIdx {
heads := c.chainsDB.heads.Current().Get(chainID)
return heads.CrossFinalized
}
func (c *unsafeChecker) SafetyLevel() types.SafetyLevel {
return types.CrossUnsafe
}
func (c *safeChecker) SafetyLevel() types.SafetyLevel {
return types.CrossSafe
}
func (c *finalizedChecker) SafetyLevel() types.SafetyLevel {
return types.CrossFinalized
}
// check checks if the log entry is safe, provided a local head for the chain
// it is used by the individual SafetyCheckers to determine if a log entry is safe
func check(
chainsDB *ChainsDB,
localHead entrydb.EntryIdx,
chain types.ChainID,
blockNum uint64,
logIdx uint32,
logHash backendTypes.TruncatedHash) bool {
// for the Check to be valid, the log must:
// exist at the blockNum and logIdx
// have a hash that matches the provided hash (implicit in the Contains call), and
// be less than or equal to the local head for the chain
index, err := chainsDB.logDBs[chain].Contains(blockNum, logIdx, logHash)
if err != nil {
if errors.Is(err, logs.ErrFuture) {
return false // TODO(#12031)
}
if errors.Is(err, logs.ErrConflict) {
return false // TODO(#12031)
}
return false
}
return index <= localHead
}
// Check checks if the log entry is safe, provided a local head for the chain
// it passes on the local head this checker is concerned with, along with its view of the database
func (c *unsafeChecker) Check(chain types.ChainID, blockNum uint64, logIdx uint32, logHash backendTypes.TruncatedHash) bool {
return check(c.chainsDB, c.LocalHeadForChain(chain), chain, blockNum, logIdx, logHash)
}
func (c *safeChecker) Check(chain types.ChainID, blockNum uint64, logIdx uint32, logHash backendTypes.TruncatedHash) bool {
return check(c.chainsDB, c.LocalHeadForChain(chain), chain, blockNum, logIdx, logHash)
}
func (c *finalizedChecker) Check(chain types.ChainID, blockNum uint64, logIdx uint32, logHash backendTypes.TruncatedHash) bool {
return check(c.chainsDB, c.LocalHeadForChain(chain), chain, blockNum, logIdx, logHash)
}
// Update creates an Operation that updates the x-head for the chain, given an index to set it to
func (c *unsafeChecker) Update(chain types.ChainID, index entrydb.EntryIdx) heads.OperationFn {
return func(heads *heads.Heads) error {
chainHeads := heads.Get(chain)
chainHeads.CrossUnsafe = index
heads.Put(chain, chainHeads)
return nil
}
}
func (c *safeChecker) Update(chain types.ChainID, index entrydb.EntryIdx) heads.OperationFn {
return func(heads *heads.Heads) error {
chainHeads := heads.Get(chain)
chainHeads.CrossSafe = index
heads.Put(chain, chainHeads)
return nil
}
}
func (c *finalizedChecker) Update(chain types.ChainID, index entrydb.EntryIdx) heads.OperationFn {
return func(heads *heads.Heads) error {
chainHeads := heads.Get(chain)
chainHeads.CrossFinalized = index
heads.Put(chain, chainHeads)
return nil
}
}