-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Expand file tree
/
Copy pathdeployer.go
More file actions
500 lines (431 loc) · 17 KB
/
deployer.go
File metadata and controls
500 lines (431 loc) · 17 KB
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
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
package sysgo
import (
"math/big"
"path/filepath"
"slices"
"time"
"github.com/ethereum-optimism/optimism/op-chain-ops/devkeys"
opforks "github.com/ethereum-optimism/optimism/op-core/forks"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/artifacts"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/inspect"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state"
"github.com/ethereum-optimism/optimism/op-devstack/devtest"
"github.com/ethereum-optimism/optimism/op-devstack/stack"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/intentbuilder"
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-service/testreq"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/params/forks"
"github.com/holiman/uint256"
)
// funderMnemonicIndex the funding account is not one of the 30 standard account, but still derived from a user-key.
const funderMnemonicIndex = 10_000
const devFeatureBitmapKey = "devFeatureBitmap"
type DeployerOption func(p devtest.T, keys devkeys.Keys, builder intentbuilder.Builder)
func WithForkAtL1Genesis(fork forks.Fork) DeployerOption {
return func(_ devtest.T, _ devkeys.Keys, builder intentbuilder.Builder) {
builder.L1().WithL1ForkAtGenesis(fork)
}
}
func WithForkAtL1Offset(fork forks.Fork, offset uint64) DeployerOption {
return func(_ devtest.T, _ devkeys.Keys, builder intentbuilder.Builder) {
builder.L1().WithL1ForkAtOffset(fork, &offset)
}
}
func WithDefaultBPOBlobSchedule(_ devtest.T, _ devkeys.Keys, builder intentbuilder.Builder) {
// Once we get the latest changes from op-geth we can change this to
// params.DefaultBlobSchedule.
builder.L1().WithL1BlobSchedule(¶ms.BlobScheduleConfig{
Cancun: params.DefaultCancunBlobConfig,
Osaka: params.DefaultOsakaBlobConfig,
Prague: params.DefaultPragueBlobConfig,
BPO1: params.DefaultBPO1BlobConfig,
BPO2: params.DefaultBPO2BlobConfig,
BPO3: params.DefaultBPO3BlobConfig,
BPO4: params.DefaultBPO4BlobConfig,
})
}
func WithKarstAtOffset(offset *uint64) DeployerOption {
return func(p devtest.T, _ devkeys.Keys, builder intentbuilder.Builder) {
for _, l2Cfg := range builder.L2s() {
l2Cfg.WithForkAtOffset(opforks.Karst, offset)
}
}
}
func WithKarstAtGenesis(p devtest.T, _ devkeys.Keys, builder intentbuilder.Builder) {
for _, l2Cfg := range builder.L2s() {
l2Cfg.WithForkAtGenesis(opforks.Karst)
}
}
func WithJovianAtGenesis(p devtest.T, _ devkeys.Keys, builder intentbuilder.Builder) {
for _, l2Cfg := range builder.L2s() {
l2Cfg.WithForkAtGenesis(opforks.Jovian)
}
}
func WithL2GasLimit(gasLimit uint64) DeployerOption {
return func(_ devtest.T, _ devkeys.Keys, builder intentbuilder.Builder) {
for _, l2Cfg := range builder.L2s() {
l2Cfg.WithGasLimit(gasLimit)
}
}
}
func WithEcotoneAtGenesis(p devtest.T, _ devkeys.Keys, builder intentbuilder.Builder) {
for _, l2Cfg := range builder.L2s() {
l2Cfg.WithForkAtGenesis(opforks.Ecotone)
}
}
type DeployerPipelineOption func(wb *worldBuilder, intent *state.Intent, cfg *deployer.ApplyPipelineOpts)
func WithDeployerCacheDir(dirPath string) DeployerPipelineOption {
return func(_ *worldBuilder, _ *state.Intent, cfg *deployer.ApplyPipelineOpts) {
cfg.CacheDir = dirPath
}
}
// WithDAFootprintGasScalar sets the DA footprint gas scalar with which the networks identified by
// l2ChainIDs will be launched. If there are no l2ChainIDs provided, all L2 networks are set with scalar.
func WithDAFootprintGasScalar(scalar uint16, l2ChainIDs ...eth.ChainID) DeployerOption {
return func(p devtest.T, _ devkeys.Keys, builder intentbuilder.Builder) {
for _, l2 := range builder.L2s() {
if len(l2ChainIDs) == 0 || slices.Contains(l2ChainIDs, l2.ChainID()) {
l2.WithDAFootprintGasScalar(scalar)
}
}
}
}
type L2Deployment struct {
systemConfigProxyAddr common.Address
disputeGameFactoryProxy common.Address
l1StandardBridgeProxy common.Address
proxyAdmin common.Address
permissionlessDelayedWETHProxy common.Address
}
var _ stack.L2Deployment = &L2Deployment{}
func (d *L2Deployment) SystemConfigProxyAddr() common.Address {
return d.systemConfigProxyAddr
}
func (d *L2Deployment) DisputeGameFactoryProxyAddr() common.Address {
return d.disputeGameFactoryProxy
}
func (d *L2Deployment) L1StandardBridgeProxyAddr() common.Address {
return d.l1StandardBridgeProxy
}
func (d *L2Deployment) ProxyAdminAddr() common.Address {
return d.proxyAdmin
}
func (d *L2Deployment) PermissionlessDelayedWETHProxyAddr() common.Address {
return d.permissionlessDelayedWETHProxy
}
type worldBuilder struct {
p devtest.CommonT
logger log.Logger
require *testreq.Assertions
keys devkeys.Keys
// options
deployerPipelineOptions []DeployerPipelineOption
builder intentbuilder.Builder
output *state.State
outL1Genesis *core.Genesis
l2Chains []eth.ChainID
outL2Genesis map[eth.ChainID]*core.Genesis
outL2RollupCfg map[eth.ChainID]*rollup.Config
outL2Deployment map[eth.ChainID]*L2Deployment
outFullCfgSet depset.FullConfigSetMerged
outSuperchainDeployment *SuperchainDeployment
}
var (
oneEth = uint256.NewInt(1e18)
millionEth = new(uint256.Int).Mul(uint256.NewInt(1e6), oneEth)
)
func WithEmbeddedContractSources() DeployerOption {
return func(_ devtest.T, _ devkeys.Keys, builder intentbuilder.Builder) {
setContractLocators(builder, artifacts.EmbeddedLocator)
}
}
func localContractSourcesLocator(artifactsPath string) (*artifacts.Locator, error) {
if artifactsPath == "" {
paths, err := contractPaths()
if err != nil {
return nil, err
}
artifactsPath = paths.FoundryArtifacts
}
absPath, err := filepath.Abs(artifactsPath)
if err != nil {
return nil, err
}
if err := ensureDir(absPath); err != nil {
return nil, err
}
return artifacts.NewFileLocator(absPath)
}
func setContractLocators(builder intentbuilder.Builder, contractArtifacts *artifacts.Locator) {
builder.WithL1ContractsLocator(contractArtifacts)
builder.WithL2ContractsLocator(contractArtifacts)
}
// WithLocalContractSourcesAt configures the deployer to load both L1 and L2
// contract artifacts from the given local contracts-bedrock checkout or
// forge-artifacts directory.
func WithLocalContractSourcesAt(artifactsPath string) DeployerOption {
return func(p devtest.T, _ devkeys.Keys, builder intentbuilder.Builder) {
contractArtifacts, err := localContractSourcesLocator(artifactsPath)
p.Require().NoError(err)
setContractLocators(builder, contractArtifacts)
}
}
func WithLocalContractSources() DeployerOption {
return func(p devtest.T, _ devkeys.Keys, builder intentbuilder.Builder) {
contractArtifacts, err := localContractSourcesLocator("")
p.Require().NoError(err)
setContractLocators(builder, contractArtifacts)
}
}
func WithCommons(l1ChainID eth.ChainID) DeployerOption {
return func(p devtest.T, keys devkeys.Keys, builder intentbuilder.Builder) {
_, l1Config := builder.WithL1(l1ChainID)
l1StartTimestamp := uint64(time.Now().Unix()) + 1
l1Config.WithTimestamp(l1StartTimestamp)
l1Config.WithL1ForkAtGenesis(forks.Prague) // activate pectra on L1
faucetFunderAddr, err := keys.Address(devkeys.UserKey(funderMnemonicIndex))
p.Require().NoError(err, "need funder addr")
l1Config.WithPrefundedAccount(faucetFunderAddr, *eth.BillionEther.ToU256())
// We use the L1 chain ID to identify the superchain-wide roles.
addrFor := intentbuilder.RoleToAddrProvider(p, keys, l1ChainID)
_, superCfg := builder.WithSuperchain()
intentbuilder.WithDevkeySuperRoles(p, keys, l1ChainID, superCfg)
l1Config.WithPrefundedAccount(addrFor(devkeys.SuperchainProxyAdminOwner), *millionEth)
l1Config.WithPrefundedAccount(addrFor(devkeys.SuperchainProtocolVersionsOwner), *millionEth)
l1Config.WithPrefundedAccount(addrFor(devkeys.SuperchainConfigGuardianKey), *millionEth)
l1Config.WithPrefundedAccount(addrFor(devkeys.L1ProxyAdminOwnerRole), *millionEth)
}
}
func WithGuardianMatchL1PAO() DeployerOption {
return func(p devtest.T, keys devkeys.Keys, builder intentbuilder.Builder) {
_, superCfg := builder.WithSuperchain()
intentbuilder.WithOverrideGuardianToL1PAO(p, keys, superCfg.L1ChainID(), superCfg)
}
}
func WithPrefundedL2(l1ChainID, l2ChainID eth.ChainID) DeployerOption {
return func(p devtest.T, keys devkeys.Keys, builder intentbuilder.Builder) {
_, l2Config := builder.WithL2(l2ChainID)
intentbuilder.WithDevkeyVaults(p, keys, l2Config)
intentbuilder.WithDevkeyL2Roles(p, keys, l2Config)
// l2configurator L1ProxyAdminOwner must be also populated
intentbuilder.WithDevkeyL1Roles(p, keys, l2Config, l1ChainID)
{
faucetFunderAddr, err := keys.Address(devkeys.UserKey(funderMnemonicIndex))
p.Require().NoError(err, "need funder addr")
l2Config.WithPrefundedAccount(faucetFunderAddr, *eth.BillionEther.ToU256())
}
{
addrFor := intentbuilder.RoleToAddrProvider(p, keys, l2ChainID)
l1Config := l2Config.L1Config()
l1Config.WithPrefundedAccount(addrFor(devkeys.BatcherRole), *millionEth)
l1Config.WithPrefundedAccount(addrFor(devkeys.ProposerRole), *millionEth)
l1Config.WithPrefundedAccount(addrFor(devkeys.ChallengerRole), *millionEth)
l1Config.WithPrefundedAccount(addrFor(devkeys.SystemConfigOwner), *millionEth)
}
}
}
// WithDevFeatureEnabled adds a feature as enabled in the dev feature bitmap
func WithDevFeatureEnabled(flag common.Hash) DeployerOption {
return func(p devtest.T, keys devkeys.Keys, builder intentbuilder.Builder) {
currentValue := builder.GlobalOverride(devFeatureBitmapKey)
var bitmap common.Hash
if currentValue != nil {
bitmap = currentValue.(common.Hash)
}
builder.WithGlobalOverride(devFeatureBitmapKey, deployer.EnableDevFeature(bitmap, flag))
if flag == deployer.OptimismPortalInteropDevFlag {
builder.WithUseInterop(true)
}
}
}
// WithInteropAtGenesis activates interop at genesis for all known L2s
func WithInteropAtGenesis() DeployerOption {
return func(p devtest.T, keys devkeys.Keys, builder intentbuilder.Builder) {
for _, l2Cfg := range builder.L2s() {
l2Cfg.WithForkAtGenesis(opforks.Interop)
}
}
}
// WithHardforkSequentialActivation configures a deployment such that L2 chains
// activate hardforks sequentially, starting from startFork and continuing
// until (including) endFork. Each successive fork is scheduled at
// an increasing offset.
func WithHardforkSequentialActivation(startFork, endFork opforks.Name, delta *uint64) DeployerOption {
return func(p devtest.T, keys devkeys.Keys, builder intentbuilder.Builder) {
for _, l2Cfg := range builder.L2s() {
l2Cfg.WithForkAtGenesis(startFork)
activateWithOffset := false
deactivate := false
for idx, refFork := range opforks.All {
if deactivate {
l2Cfg.WithForkAtOffset(refFork, nil)
deactivate = true
continue
}
if activateWithOffset {
offset := *delta * uint64(idx)
l2Cfg.WithForkAtOffset(refFork, &offset)
}
if startFork == refFork {
activateWithOffset = true
}
if endFork == refFork {
deactivate = true
}
}
}
}
}
// WithSequencingWindow overrides the number of L1 blocks in a sequencing window, applied to all L2s.
func WithSequencingWindow(n uint64) DeployerOption {
return func(p devtest.T, keys devkeys.Keys, builder intentbuilder.Builder) {
builder.WithGlobalOverride("sequencerWindowSize", uint64(n))
}
}
func WithDeployerMatchL1PAO() DeployerPipelineOption {
return func(wb *worldBuilder, intent *state.Intent, cfg *deployer.ApplyPipelineOpts) {
l1ChainID := new(big.Int).SetUint64(intent.L1ChainID)
deployerKey, err := wb.keys.Secret(devkeys.L1ProxyAdminOwnerRole.Key(l1ChainID))
wb.require.NoError(err)
cfg.DeployerPrivateKey = deployerKey
}
}
// WithL2BlockTimes sets per-chain L2 block times. The map keys are L2 chain
// IDs and values are the desired block time in seconds for that chain.
func WithL2BlockTimes(blockTimes map[eth.ChainID]uint64) DeployerOption {
return func(_ devtest.T, _ devkeys.Keys, builder intentbuilder.Builder) {
for _, l2Cfg := range builder.L2s() {
if bt, ok := blockTimes[l2Cfg.ChainID()]; ok {
l2Cfg.WithBlockTime(bt)
}
}
}
}
// WithFinalizationPeriodSeconds overrides the number of L1 blocks in a sequencing window, applied to all L2s.
func WithFinalizationPeriodSeconds(n uint64) DeployerOption {
return func(p devtest.T, keys devkeys.Keys, builder intentbuilder.Builder) {
for _, l2Cfg := range builder.L2s() {
l2Cfg.WithFinalizationPeriodSeconds(n)
}
}
}
func WithProofMaturityDelaySeconds(n uint64) DeployerOption {
return func(p devtest.T, keys devkeys.Keys, builder intentbuilder.Builder) {
builder.WithGlobalOverride("proofMaturityDelaySeconds", uint64(n))
}
}
func WithDisputeGameFinalityDelaySeconds(seconds uint64) DeployerOption {
return func(p devtest.T, keys devkeys.Keys, builder intentbuilder.Builder) {
builder.WithGlobalOverride("disputeGameFinalityDelaySeconds", seconds)
}
}
func WithCustomGasToken(name, symbol string, initialLiquidity *big.Int, liquidityControllerOwner common.Address) DeployerOption {
return func(p devtest.T, keys devkeys.Keys, builder intentbuilder.Builder) {
for _, l2Cfg := range builder.L2s() {
l2Cfg.WithCustomGasToken(name, symbol, initialLiquidity, liquidityControllerOwner)
}
}
}
func (wb *worldBuilder) buildL1Genesis() {
wb.require.NotNil(wb.output.L1DevGenesis, "must have L1 genesis outer config")
wb.require.NotNil(wb.output.L1StateDump, "must have L1 genesis alloc")
genesisOuter := wb.output.L1DevGenesis
genesisAlloc := wb.output.L1StateDump.Data.Accounts
genesisCfg := *genesisOuter
genesisCfg.StateHash = nil
genesisCfg.Alloc = genesisAlloc
wb.outL1Genesis = &genesisCfg
}
func (wb *worldBuilder) buildL2Genesis() {
wb.outL2Genesis = make(map[eth.ChainID]*core.Genesis)
wb.outL2RollupCfg = make(map[eth.ChainID]*rollup.Config)
for _, ch := range wb.output.Chains {
l2Genesis, l2RollupCfg, err := inspect.GenesisAndRollup(wb.output, ch.ID)
wb.require.NoError(err, "need L2 genesis and rollup")
id := eth.ChainIDFromBytes32(ch.ID)
wb.outL2Genesis[id] = l2Genesis
wb.outL2RollupCfg[id] = l2RollupCfg
}
}
func (wb *worldBuilder) buildL2DeploymentOutputs() {
wb.outL2Deployment = make(map[eth.ChainID]*L2Deployment)
for _, ch := range wb.output.Chains {
chainID := eth.ChainIDFromBytes32(ch.ID)
wb.outL2Deployment[chainID] = &L2Deployment{
systemConfigProxyAddr: ch.SystemConfigProxy,
disputeGameFactoryProxy: ch.DisputeGameFactoryProxy,
l1StandardBridgeProxy: ch.L1StandardBridgeProxy,
proxyAdmin: ch.OpChainProxyAdminImpl,
permissionlessDelayedWETHProxy: ch.DelayedWethPermissionlessGameProxy,
}
}
wb.outSuperchainDeployment = &SuperchainDeployment{
protocolVersionsAddr: wb.output.SuperchainDeployment.ProtocolVersionsProxy,
superchainConfigAddr: wb.output.SuperchainDeployment.SuperchainConfigProxy,
}
}
func WithRevenueShare(enabled bool, chainFeesRecipient common.Address) DeployerOption {
return func(p devtest.T, keys devkeys.Keys, builder intentbuilder.Builder) {
for _, l2Cfg := range builder.L2s() {
l2Cfg.WithRevenueShare(enabled, chainFeesRecipient)
}
}
}
func (wb *worldBuilder) buildFullConfigSet() {
// If no chain has interop active, the dep set will be nil here,
// so we should skip building the full config set.
if wb.output.InteropDepSet == nil {
return
}
rollupConfigSet := depset.StaticRollupConfigSetFromRollupConfigMap(wb.outL2RollupCfg,
depset.StaticTimestamp(wb.outL1Genesis.Timestamp))
fullCfgSet, err := depset.NewFullConfigSetMerged(rollupConfigSet, wb.output.InteropDepSet)
wb.require.NoError(err)
wb.outFullCfgSet = fullCfgSet
}
func (wb *worldBuilder) Build() {
st := &state.State{
Version: 1,
}
// Work-around of op-deployer design issue.
// We use the same deployer key for all L1 and L2 chains we deploy here.
deployerKey, err := wb.keys.Secret(devkeys.DeployerRole.Key(big.NewInt(0)))
wb.require.NoError(err, "need deployer key")
intent, err := wb.builder.Build()
wb.require.NoError(err)
pipelineOpts := deployer.ApplyPipelineOpts{
DeploymentTarget: deployer.DeploymentTargetGenesis,
L1RPCUrl: "",
DeployerPrivateKey: deployerKey,
Intent: intent,
State: st,
Logger: wb.logger,
StateWriter: wb, // direct output back here
}
for _, opt := range wb.deployerPipelineOptions {
opt(wb, intent, &pipelineOpts)
}
err = deployer.ApplyPipeline(wb.p.Ctx(), pipelineOpts)
wb.require.NoError(err)
wb.require.NotNil(wb.output, "expected state-write to output")
for _, id := range wb.output.Chains {
chainID := eth.ChainIDFromBytes32(id.ID)
wb.l2Chains = append(wb.l2Chains, chainID)
}
wb.buildL1Genesis()
wb.buildL2Genesis()
wb.buildL2DeploymentOutputs()
wb.buildFullConfigSet()
}
// WriteState is a callback used by deployer.ApplyPipeline to write the output
func (wb *worldBuilder) WriteState(st *state.State) error {
wb.output = st
return nil
}