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
package pipeline
import (
"bufio"
"context"
"crypto/rand"
"fmt"
"os"
"strings"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard"
"github.com/mattn/go-isatty"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state"
"github.com/ethereum-optimism/optimism/op-chain-ops/script"
"github.com/ethereum/go-ethereum/common"
)
func IsSupportedStateVersion(version int) bool {
return version == 1
}
func InitLiveStrategy(ctx context.Context, env *Env, intent *state.Intent, st *state.State) error {
lgr := env.Logger.New("stage", "init", "strategy", "live")
lgr.Info("initializing pipeline")
if err := initCommonChecks(st); err != nil {
return err
}
opcmAddress, opcmAddrErr := standard.ManagerImplementationAddrFor(intent.L1ChainID)
hasPredeployedOPCM := opcmAddrErr == nil
isTag := intent.L1ContractsLocator.IsTag()
if isTag && hasPredeployedOPCM {
superCfg, err := standard.SuperchainFor(intent.L1ChainID)
if err != nil {
return fmt.Errorf("error getting superchain config: %w", err)
}
proxyAdmin, err := standard.ManagerOwnerAddrFor(intent.L1ChainID)
if err != nil {
return fmt.Errorf("error getting superchain proxy admin address: %w", err)
}
// Have to do this weird pointer thing below because the Superchain Registry defines its
// own Address type.
st.SuperchainDeployment = &state.SuperchainDeployment{
ProxyAdminAddress: proxyAdmin,
ProtocolVersionsProxyAddress: common.Address(*superCfg.Config.ProtocolVersionsAddr),
SuperchainConfigProxyAddress: common.Address(*superCfg.Config.SuperchainConfigAddr),
}
st.ImplementationsDeployment = &state.ImplementationsDeployment{
OpcmAddress: opcmAddress,
}
} else if isTag && !hasPredeployedOPCM {
if err := displayWarning(); err != nil {
return err
}
}
l1ChainID, err := env.L1Client.ChainID(ctx)
if err != nil {
return fmt.Errorf("failed to get L1 chain ID: %w", err)
}
if l1ChainID.Cmp(intent.L1ChainIDBig()) != 0 {
return fmt.Errorf("l1 chain ID mismatch: got %d, expected %d", l1ChainID, intent.L1ChainID)
}
deployerCode, err := env.L1Client.CodeAt(ctx, script.DeterministicDeployerAddress, nil)
if err != nil {
return fmt.Errorf("failed to get deployer code: %w", err)
}
if len(deployerCode) == 0 {
return fmt.Errorf("deterministic deployer is not deployed on this chain - please deploy it first")
}
// If the state has never been applied, we don't need to perform
// any additional checks.
if st.AppliedIntent == nil {
return nil
}
// If the state has been applied, we need to check if any immutable
// fields have changed.
if st.AppliedIntent.L1ChainID != intent.L1ChainID {
return immutableErr("L1ChainID", st.AppliedIntent.L1ChainID, intent.L1ChainID)
}
if st.AppliedIntent.FundDevAccounts != intent.FundDevAccounts {
return immutableErr("fundDevAccounts", st.AppliedIntent.FundDevAccounts, intent.FundDevAccounts)
}
// TODO: validate individual
return nil
}
func initCommonChecks(st *state.State) error {
// Ensure the state version is supported.
if !IsSupportedStateVersion(st.Version) {
return fmt.Errorf("unsupported state version: %d", st.Version)
}
if st.Create2Salt == (common.Hash{}) {
_, err := rand.Read(st.Create2Salt[:])
if err != nil {
return fmt.Errorf("failed to generate CREATE2 salt: %w", err)
}
}
return nil
}
func InitGenesisStrategy(env *Env, intent *state.Intent, st *state.State) error {
lgr := env.Logger.New("stage", "init", "strategy", "genesis")
lgr.Info("initializing pipeline")
if err := initCommonChecks(st); err != nil {
return err
}
if intent.SuperchainRoles == nil {
return fmt.Errorf("superchain roles must be set for genesis strategy")
}
// Mostly a stub for now.
return nil
}
func immutableErr(field string, was, is any) error {
return fmt.Errorf("%s is immutable: was %v, is %v", field, was, is)
}
func displayWarning() error {
warning := strings.TrimPrefix(`
####################### WARNING! WARNING WARNING! #######################
You are deploying a tagged release to a chain with no pre-deployed OPCM.
The contracts you are deploying may not be audited, or match a governance
approved release.
USE OF THIS DEPLOYMENT IS NOT RECOMMENDED FOR PRODUCTION. USE AT YOUR OWN
RISK. BUGS OR LOSS OF FUNDS MAY OCCUR. WE HOPE YOU KNOW WHAT YOU ARE
DOING.
####################### WARNING! WARNING WARNING! #######################
`, "\n")
_, _ = fmt.Fprint(os.Stderr, warning)
if isatty.IsTerminal(os.Stdout.Fd()) {
_, _ = fmt.Fprintf(os.Stderr, "Please confirm that you have read and understood the warning above [y/n]: ")
reader := bufio.NewReader(os.Stdin)
input, err := reader.ReadString('\n')
if err != nil {
return fmt.Errorf("failed to read input: %w", err)
}
input = strings.ToLower(strings.TrimSpace(input))
if input != "y" && input != "yes" {
return fmt.Errorf("aborted")
}
}
return nil
}