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
190
191
192
193
194
195
196
197
198
199
200
201
package l2
import (
"fmt"
"math/big"
"github.com/ethereum-optimism/optimism/op-program/client/l2/engineapi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/beacon"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
)
type OracleBackedL2Chain struct {
log log.Logger
oracle Oracle
chainCfg *params.ChainConfig
engine consensus.Engine
head *types.Header
safe *types.Header
finalized *types.Header
vmCfg vm.Config
// Inserted blocks
blocks map[common.Hash]*types.Block
db ethdb.KeyValueStore
}
var _ engineapi.EngineBackend = (*OracleBackedL2Chain)(nil)
func NewOracleBackedL2Chain(logger log.Logger, oracle Oracle, chainCfg *params.ChainConfig, l2Head common.Hash) (*OracleBackedL2Chain, error) {
head, err := oracle.BlockByHash(l2Head)
if err != nil {
return nil, fmt.Errorf("loading l2 head: %w", err)
}
logger.Info("Loaded L2 head", "hash", head.Hash(), "number", head.Number())
return &OracleBackedL2Chain{
log: logger,
oracle: oracle,
chainCfg: chainCfg,
engine: beacon.New(nil),
// Treat the agreed starting head as finalized - nothing before it can be disputed
head: head.Header(),
safe: head.Header(),
finalized: head.Header(),
blocks: make(map[common.Hash]*types.Block),
db: NewOracleBackedDB(oracle),
}, nil
}
func (o *OracleBackedL2Chain) CurrentHeader() *types.Header {
return o.head
}
func (o *OracleBackedL2Chain) GetHeaderByNumber(n uint64) *types.Header {
// Walk back from current head to the requested block number
h := o.head
if h.Number.Uint64() < n {
return nil
}
for h.Number.Uint64() > n {
h = o.GetHeaderByHash(h.ParentHash)
}
return h
}
func (o *OracleBackedL2Chain) GetTd(hash common.Hash, number uint64) *big.Int {
// Difficulty is always 0 post-merge and bedrock starts post-merge so total difficulty also always 0
return common.Big0
}
func (o *OracleBackedL2Chain) CurrentSafeBlock() *types.Header {
return o.safe
}
func (o *OracleBackedL2Chain) CurrentFinalBlock() *types.Header {
return o.finalized
}
func (o *OracleBackedL2Chain) GetHeaderByHash(hash common.Hash) *types.Header {
block := o.GetBlockByHash(hash)
if block == nil {
return nil
}
return block.Header()
}
func (o *OracleBackedL2Chain) GetBlockByHash(hash common.Hash) *types.Block {
// Check inserted blocks
block, ok := o.blocks[hash]
if ok {
return block
}
// Retrieve from the oracle
block, err := o.oracle.BlockByHash(hash)
if err != nil {
handleError(err)
}
if block == nil {
return nil
}
return block
}
func (o *OracleBackedL2Chain) GetBlock(hash common.Hash, number uint64) *types.Block {
block := o.GetBlockByHash(hash)
if block == nil {
return nil
}
if block.NumberU64() != number {
return nil
}
return block
}
func (o *OracleBackedL2Chain) GetHeader(hash common.Hash, u uint64) *types.Header {
block := o.GetBlock(hash, u)
if block == nil {
return nil
}
return block.Header()
}
func (o *OracleBackedL2Chain) HasBlockAndState(hash common.Hash, number uint64) bool {
block := o.GetBlock(hash, number)
return block != nil
}
func (o *OracleBackedL2Chain) GetCanonicalHash(n uint64) common.Hash {
header := o.GetHeaderByNumber(n)
if header == nil {
return common.Hash{}
}
return header.Hash()
}
func (o *OracleBackedL2Chain) GetVMConfig() *vm.Config {
return &o.vmCfg
}
func (o *OracleBackedL2Chain) Config() *params.ChainConfig {
return o.chainCfg
}
func (o *OracleBackedL2Chain) Engine() consensus.Engine {
return o.engine
}
func (o *OracleBackedL2Chain) StateAt(root common.Hash) (*state.StateDB, error) {
return state.New(root, state.NewDatabase(rawdb.NewDatabase(o.db)), nil)
}
func (o *OracleBackedL2Chain) InsertBlockWithoutSetHead(block *types.Block) error {
processor, err := engineapi.NewBlockProcessorFromHeader(o, block.Header())
if err != nil {
return err
}
for i, tx := range block.Transactions() {
err = processor.AddTx(tx)
if err != nil {
return fmt.Errorf("invalid transaction (%d): %w", i, err)
}
}
expected, err := processor.Assemble()
if err != nil {
return fmt.Errorf("invalid block: %w", err)
}
if expected.Hash() != block.Hash() {
return fmt.Errorf("block root mismatch, expected: %v, actual: %v", expected.Hash(), block.Hash())
}
err = processor.Commit()
if err != nil {
return fmt.Errorf("commit block: %w", err)
}
o.blocks[block.Hash()] = block
return nil
}
func (o *OracleBackedL2Chain) SetCanonical(head *types.Block) (common.Hash, error) {
o.head = head.Header()
return head.Hash(), nil
}
func (o *OracleBackedL2Chain) SetFinalized(header *types.Header) {
o.finalized = header
}
func (o *OracleBackedL2Chain) SetSafe(header *types.Header) {
o.safe = header
}
func handleError(err error) {
panic(err)
}