Skip to content

Commit 9a99e0a

Browse files
Bedrock: use Engine API authentication (ethereum-optimism#2591)
* feat: new --l2.jwt-secret flag to authenticate engine API comms with l2, remove l2.eth flag, update e2e tests * feat: update bedrock ops with jwt secret flag Co-authored-by: Matthew Slipper <[email protected]>
1 parent c17e3d6 commit 9a99e0a

File tree

12 files changed

+310
-122
lines changed

12 files changed

+310
-122
lines changed

op-e2e/geth.go

+9-17
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ import (
66
"errors"
77
"fmt"
88
"math/big"
9-
"strconv"
10-
"strings"
119
"time"
1210

1311
"github.com/ethereum/go-ethereum"
@@ -98,36 +96,30 @@ func initL1Geth(cfg *SystemConfig, wallet *hdwallet.Wallet, genesis *core.Genesi
9896
}
9997
nodeConfig := &node.Config{
10098
Name: "l1-geth",
101-
WSHost: cfg.L1WsAddr,
102-
WSPort: cfg.L1WsPort,
99+
WSHost: "127.0.0.1",
100+
WSPort: 0,
103101
WSModules: []string{"debug", "admin", "eth", "txpool", "net", "rpc", "web3", "personal", "engine"},
104102
HTTPModules: []string{"debug", "admin", "eth", "txpool", "net", "rpc", "web3", "personal", "engine"},
105103
}
106104

107105
return createGethNode(false, nodeConfig, ethConfig, []*ecdsa.PrivateKey{pk})
108106
}
109107

110-
func initL2Geth(name, addr string, l2ChainID *big.Int, genesis *core.Genesis) (*node.Node, *eth.Ethereum, error) {
108+
// init a geth node.
109+
func initL2Geth(name string, l2ChainID *big.Int, genesis *core.Genesis, jwtPath string) (*node.Node, *eth.Ethereum, error) {
111110
ethConfig := &ethconfig.Config{
112111
NetworkId: l2ChainID.Uint64(),
113112
Genesis: genesis,
114113
}
115-
// Parsing ws://127.0.0.1:9091 for "127.0.0.1" and "9091"
116-
s := strings.Split(addr, ":")
117-
_, host, ok := strings.Cut(s[1], "//")
118-
if !ok {
119-
return nil, nil, fmt.Errorf("could not find ws host in %s", addr)
120-
}
121-
port, err := strconv.ParseInt(s[2], 10, 32)
122-
if err != nil {
123-
return nil, nil, fmt.Errorf("failed to parse port from address: %w", err)
124-
}
125114
nodeConfig := &node.Config{
126115
Name: fmt.Sprintf("l2-geth-%v", name),
127-
WSHost: host,
128-
WSPort: int(port),
116+
WSHost: "127.0.0.1",
117+
WSPort: 0,
118+
AuthAddr: "127.0.0.1",
119+
AuthPort: 0,
129120
WSModules: []string{"debug", "admin", "eth", "txpool", "net", "rpc", "web3", "personal", "engine"},
130121
HTTPModules: []string{"debug", "admin", "eth", "txpool", "net", "rpc", "web3", "personal", "engine"},
122+
JWTSecret: jwtPath,
131123
}
132124
return createGethNode(true, nodeConfig, ethConfig, nil)
133125
}

op-e2e/setup.go

+29-11
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"github.com/ethereum/go-ethereum/log"
2929
"github.com/ethereum/go-ethereum/node"
3030
"github.com/ethereum/go-ethereum/params"
31+
"github.com/ethereum/go-ethereum/rpc"
3132
mocknet "github.com/libp2p/go-libp2p/p2p/net/mock"
3233
hdwallet "github.com/miguelmota/go-ethereum-hdwallet"
3334
)
@@ -75,10 +76,12 @@ type SystemConfig struct {
7576
L2OOCfg L2OOContractConfig
7677
DepositCFG DepositContractConfig
7778

78-
L1WsAddr string
79-
L1WsPort int
80-
L1ChainID *big.Int
81-
L2ChainID *big.Int
79+
L1ChainID *big.Int
80+
L2ChainID *big.Int
81+
82+
JWTFilePath string
83+
JWTSecret [32]byte
84+
8285
Nodes map[string]*rollupNode.Config // Per node config. Don't use populate rollup.Config
8386
Loggers map[string]log.Logger
8487
RollupConfig rollup.Config // Shared rollup configs
@@ -293,8 +296,9 @@ func (cfg SystemConfig) start() (*System, error) {
293296
}
294297
sys.nodes["l1"] = l1Node
295298
sys.backends["l1"] = l1Backend
296-
for name, l2Cfg := range cfg.Nodes {
297-
node, backend, err := initL2Geth(name, l2Cfg.L2EngineAddrs[0], cfg.L2ChainID, l2Genesis)
299+
300+
for name := range cfg.Nodes {
301+
node, backend, err := initL2Geth(name, cfg.L2ChainID, l2Genesis, cfg.JWTFilePath)
298302
if err != nil {
299303
return nil, err
300304
}
@@ -324,14 +328,28 @@ func (cfg SystemConfig) start() (*System, error) {
324328
}
325329
}
326330

331+
// Configure connections to L1 and L2 for rollup nodes.
332+
// TODO: refactor testing to use in-process rpc connections instead of websockets.
333+
for name, rollupCfg := range cfg.Nodes {
334+
rollupCfg.L1 = &rollupNode.L1EndpointConfig{
335+
L1NodeAddr: l1Node.WSEndpoint(),
336+
L1TrustRPC: false,
337+
}
338+
rollupCfg.L2s = &rollupNode.L2EndpointsConfig{
339+
L2EngineAddrs: []string{sys.nodes[name].WSAuthEndpoint()},
340+
L2EngineJWTSecrets: [][32]byte{cfg.JWTSecret},
341+
}
342+
}
343+
327344
// Geth Clients
328345
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
329346
defer cancel()
330-
l1Client, err := ethclient.DialContext(ctx, fmt.Sprintf("ws://%s:%d", cfg.L1WsAddr, cfg.L1WsPort))
347+
l1Srv, err := l1Node.RPCHandler()
331348
if err != nil {
332349
didErrAfterStart = true
333350
return nil, err
334351
}
352+
l1Client := ethclient.NewClient(rpc.DialInProc(l1Srv))
335353
sys.Clients["l1"] = l1Client
336354
for name, node := range sys.nodes {
337355
client, err := ethclient.DialContext(ctx, node.WSEndpoint())
@@ -512,8 +530,8 @@ func (cfg SystemConfig) start() (*System, error) {
512530

513531
// L2Output Submitter
514532
sys.l2OutputSubmitter, err = l2os.NewL2OutputSubmitter(l2os.Config{
515-
L1EthRpc: "ws://127.0.0.1:9090",
516-
L2EthRpc: sys.cfg.Nodes["sequencer"].L2NodeAddr,
533+
L1EthRpc: sys.nodes["l1"].WSEndpoint(),
534+
L2EthRpc: sys.nodes["sequencer"].WSEndpoint(),
517535
RollupRpc: rollupEndpoint,
518536
L2OOAddress: sys.L2OOContractAddr.String(),
519537
PollInterval: 50 * time.Millisecond,
@@ -544,8 +562,8 @@ func (cfg SystemConfig) start() (*System, error) {
544562

545563
// Batch Submitter
546564
sys.batchSubmitter, err = bss.NewBatchSubmitter(bss.Config{
547-
L1EthRpc: "ws://127.0.0.1:9090",
548-
L2EthRpc: sys.cfg.Nodes["sequencer"].L2NodeAddr,
565+
L1EthRpc: sys.nodes["l1"].WSEndpoint(),
566+
L2EthRpc: sys.nodes["sequencer"].WSEndpoint(),
549567
RollupRpc: rollupEndpoint,
550568
MinL1TxSize: 1,
551569
MaxL1TxSize: 120000,

op-e2e/system_test.go

+18-14
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import (
55
"flag"
66
"fmt"
77
"math/big"
8+
"os"
9+
"path"
810
"testing"
911
"time"
1012

@@ -24,6 +26,7 @@ import (
2426
"github.com/ethereum/go-ethereum/accounts/abi/bind"
2527
"github.com/ethereum/go-ethereum/accounts/keystore"
2628
"github.com/ethereum/go-ethereum/common"
29+
"github.com/ethereum/go-ethereum/common/hexutil"
2730
"github.com/ethereum/go-ethereum/core/types"
2831
"github.com/ethereum/go-ethereum/crypto"
2932
"github.com/ethereum/go-ethereum/ethclient"
@@ -59,8 +62,18 @@ const (
5962

6063
var (
6164
batchInboxAddress = common.Address{0xff, 0x02}
65+
testingJWTSecret = [32]byte{123}
6266
)
6367

68+
func writeDefaultJWT(t *testing.T) string {
69+
// Sadly the geth node config cannot load JWT secret from memory, it has to be a file
70+
jwtPath := path.Join(t.TempDir(), "jwt_secret")
71+
if err := os.WriteFile(jwtPath, []byte(hexutil.Encode(testingJWTSecret[:])), 0600); err != nil {
72+
t.Fatalf("failed to prepare jwt file for geth: %v", err)
73+
}
74+
return jwtPath
75+
}
76+
6477
func defaultSystemConfig(t *testing.T) SystemConfig {
6578
return SystemConfig{
6679
Mnemonic: "squirrel green gallery layer logic title habit chase clog actress language enrich body plate fun pledge gap abuse mansion define either blast alien witness",
@@ -86,23 +99,14 @@ func defaultSystemConfig(t *testing.T) SystemConfig {
8699
CliqueSignerDerivationPath: cliqueSignerHDPath,
87100
L1InfoPredeployAddress: derive.L1InfoPredeployAddr,
88101
L1BlockTime: 2,
89-
L1WsAddr: "127.0.0.1",
90-
L1WsPort: 9090,
91102
L1ChainID: big.NewInt(900),
92103
L2ChainID: big.NewInt(901),
104+
JWTFilePath: writeDefaultJWT(t),
105+
JWTSecret: testingJWTSecret,
93106
Nodes: map[string]*rollupNode.Config{
94-
"verifier": {
95-
L1NodeAddr: "ws://127.0.0.1:9090",
96-
L2EngineAddrs: []string{"ws://127.0.0.1:9091"},
97-
L2NodeAddr: "ws://127.0.0.1:9091",
98-
L1TrustRPC: false,
99-
},
107+
"verifier": {},
100108
"sequencer": {
101-
L1NodeAddr: "ws://127.0.0.1:9090",
102-
L2EngineAddrs: []string{"ws://127.0.0.1:9092"},
103-
L2NodeAddr: "ws://127.0.0.1:9092",
104-
L1TrustRPC: false,
105-
Sequencer: true,
109+
Sequencer: true,
106110
// Submitter PrivKey is set in system start for rollup nodes where sequencer = true
107111
RPC: node.RPCConfig{
108112
ListenAddr: "127.0.0.1",
@@ -786,7 +790,7 @@ func TestWithdrawals(t *testing.T) {
786790
header, err = l2Seq.HeaderByNumber(ctx, blockNumber)
787791
require.Nil(t, err)
788792

789-
rpc, err := rpc.Dial(cfg.Nodes["sequencer"].L2NodeAddr)
793+
rpc, err := rpc.Dial(sys.nodes["sequencer"].WSEndpoint())
790794
require.Nil(t, err)
791795
l2client := withdrawals.NewClient(rpc)
792796

op-node/cmd/main.go

+7-5
Original file line numberDiff line numberDiff line change
@@ -63,14 +63,16 @@ func main() {
6363

6464
func RollupNodeMain(ctx *cli.Context) error {
6565
log.Info("Initializing Rollup Node")
66-
cfg, err := opnode.NewConfig(ctx)
66+
logCfg, err := opnode.NewLogConfig(ctx)
6767
if err != nil {
68-
log.Error("Unable to create the rollup node config", "error", err)
68+
log.Error("Unable to create the log config", "error", err)
6969
return err
7070
}
71-
logCfg, err := opnode.NewLogConfig(ctx)
71+
log := logCfg.NewLogger()
72+
73+
cfg, err := opnode.NewConfig(ctx, log)
7274
if err != nil {
73-
log.Error("Unable to create the log config", "error", err)
75+
log.Error("Unable to create the rollup node config", "error", err)
7476
return err
7577
}
7678
snapshotLog, err := opnode.NewSnapshotLogger(ctx)
@@ -79,7 +81,7 @@ func RollupNodeMain(ctx *cli.Context) error {
7981
return err
8082
}
8183

82-
n, err := node.New(context.Background(), cfg, logCfg.NewLogger(), snapshotLog, VersionWithMeta)
84+
n, err := node.New(context.Background(), cfg, log, snapshotLog, VersionWithMeta)
8385
if err != nil {
8486
log.Error("Unable to create the rollup node", "error", err)
8587
return err

op-node/flags/flags.go

+9-8
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,6 @@ var (
3131
Required: true,
3232
EnvVar: prefixEnvVar("ROLLUP_CONFIG"),
3333
}
34-
L2EthNodeAddr = cli.StringFlag{
35-
Name: "l2.eth",
36-
Usage: "Address of L2 User JSON-RPC endpoint to use (eth namespace required)",
37-
Required: true,
38-
EnvVar: prefixEnvVar("L2_ETH_RPC"),
39-
}
4034
RPCListenAddr = cli.StringFlag{
4135
Name: "rpc.addr",
4236
Usage: "RPC listening address",
@@ -56,7 +50,14 @@ var (
5650
Usage: "Trust the L1 RPC, sync faster at risk of malicious/buggy RPC providing bad or inconsistent L1 data",
5751
EnvVar: prefixEnvVar("L1_TRUST_RPC"),
5852
}
59-
53+
L2EngineJWTSecret = cli.StringSliceFlag{
54+
Name: "l2.jwt-secret",
55+
Usage: "Paths to JWT secret keys, one per L2 endpoint, in the same order as the provided l2 addresses. " +
56+
"Keys are 32 bytes, hex encoded in a file. A new key per endpoint will be generated if left empty.",
57+
Required: false,
58+
Value: &cli.StringSlice{},
59+
EnvVar: prefixEnvVar("L2_ENGINE_AUTH"),
60+
}
6061
SequencingEnabledFlag = cli.BoolFlag{
6162
Name: "sequencing.enabled",
6263
Usage: "enable sequencing",
@@ -92,13 +93,13 @@ var requiredFlags = []cli.Flag{
9293
L1NodeAddr,
9394
L2EngineAddrs,
9495
RollupConfig,
95-
L2EthNodeAddr,
9696
RPCListenAddr,
9797
RPCListenPort,
9898
}
9999

100100
var optionalFlags = append([]cli.Flag{
101101
L1TrustRPC,
102+
L2EngineJWTSecret,
102103
SequencingEnabledFlag,
103104
LogLevelFlag,
104105
LogFormatFlag,

0 commit comments

Comments
 (0)