Skip to content

Commit 4c58f23

Browse files
authored
Add key rotation tests (#224)
1 parent cd1a33f commit 4c58f23

File tree

3 files changed

+194
-22
lines changed

3 files changed

+194
-22
lines changed

espresso/devnet-tests/devnet_tools.go

Lines changed: 75 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package devnet_tests
33
import (
44
"bytes"
55
"context"
6+
"encoding/hex"
67
"fmt"
78
"math/big"
89
"os"
@@ -13,16 +14,20 @@ import (
1314
"testing"
1415
"time"
1516

17+
env "github.com/ethereum-optimism/optimism/espresso/environment"
18+
"github.com/ethereum-optimism/optimism/op-e2e/bindings"
19+
"github.com/ethereum-optimism/optimism/op-e2e/config/secrets"
1620
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait"
1721
"github.com/ethereum-optimism/optimism/op-e2e/system/helpers"
22+
"github.com/ethereum-optimism/optimism/op-node/rollup"
23+
opclient "github.com/ethereum-optimism/optimism/op-service/client"
24+
"github.com/ethereum-optimism/optimism/op-service/sources"
25+
"github.com/ethereum/go-ethereum/accounts/abi/bind"
1826
"github.com/ethereum/go-ethereum/common"
1927
"github.com/ethereum/go-ethereum/core/types"
2028
"github.com/ethereum/go-ethereum/crypto"
2129
"github.com/ethereum/go-ethereum/ethclient"
2230
"github.com/ethereum/go-ethereum/log"
23-
24-
env "github.com/ethereum-optimism/optimism/espresso/environment"
25-
"github.com/ethereum-optimism/optimism/op-e2e/config/secrets"
2631
)
2732

2833
type Devnet struct {
@@ -31,8 +36,11 @@ type Devnet struct {
3136
outageTime time.Duration
3237
successTime time.Duration
3338

34-
L2Seq *ethclient.Client
35-
L2Verif *ethclient.Client
39+
L1 *ethclient.Client
40+
L2Seq *ethclient.Client
41+
L2SeqRollup *sources.RollupClient
42+
L2Verif *ethclient.Client
43+
L2VerifRollup *sources.RollupClient
3644
}
3745

3846
func NewDevnet(ctx context.Context, t *testing.T) *Devnet {
@@ -70,6 +78,10 @@ func (d *Devnet) Up(verbose bool) (err error) {
7078
d.ctx,
7179
"docker", "compose", "up", "-d",
7280
)
81+
cmd.Env = append(
82+
cmd.Env,
83+
fmt.Sprintf("OP_BATCHER_PRIVATE_KEY=%s", hex.EncodeToString(crypto.FromECDSA(d.secrets.Batcher))),
84+
)
7385
buf := new(bytes.Buffer)
7486
cmd.Stderr = buf
7587
if err := cmd.Run(); err != nil {
@@ -112,10 +124,22 @@ func (d *Devnet) Up(verbose bool) (err error) {
112124
if err != nil {
113125
return err
114126
}
127+
d.L2SeqRollup, err = d.rollupClient("op-node-sequencer", 9545)
128+
if err != nil {
129+
return err
130+
}
115131
d.L2Verif, err = d.serviceClient("op-geth-verifier", 8546)
116132
if err != nil {
117133
return err
118134
}
135+
d.L2VerifRollup, err = d.rollupClient("op-node-verifier", 9546)
136+
if err != nil {
137+
return err
138+
}
139+
d.L1, err = d.serviceClient("l1-geth", 8545)
140+
if err != nil {
141+
return err
142+
}
119143

120144
return nil
121145
}
@@ -148,6 +172,28 @@ func (d *Devnet) ServiceRestart(service string) error {
148172
return nil
149173
}
150174

175+
func (d *Devnet) RollupConfig(ctx context.Context) (*rollup.Config, error) {
176+
return d.L2SeqRollup.RollupConfig(ctx)
177+
}
178+
179+
func (d *Devnet) SystemConfig(ctx context.Context) (*bindings.SystemConfig, *bind.TransactOpts, error) {
180+
config, err := d.RollupConfig(ctx)
181+
if err != nil {
182+
return nil, nil, err
183+
}
184+
contract, err := bindings.NewSystemConfig(config.L1SystemConfigAddress, d.L1)
185+
if err != nil {
186+
return nil, nil, err
187+
}
188+
189+
owner, err := bind.NewKeyedTransactorWithChainID(d.secrets.Deployer, config.L1ChainID)
190+
if err != nil {
191+
return nil, nil, err
192+
}
193+
194+
return contract, owner, nil
195+
}
196+
151197
// Submits a transaction and waits until it is confirmed by the sequencer (but not necessarily the verifier).
152198
func (d *Devnet) SubmitL2Tx(applyTxOpts helpers.TxOptsFn) (*types.Receipt, error) {
153199
ctx, cancel := context.WithTimeout(d.ctx, 2*time.Minute)
@@ -238,6 +284,15 @@ func (d *Devnet) RunL2Tx(applyTxOpts helpers.TxOptsFn) error {
238284
return d.VerifyL2Tx(receipt)
239285
}
240286

287+
func (d *Devnet) SendL1Tx(ctx context.Context, tx *types.Transaction) (*types.Receipt, error) {
288+
err := d.L1.SendTransaction(ctx, tx)
289+
if err != nil {
290+
return nil, err
291+
}
292+
293+
return wait.ForReceiptOK(ctx, d.L1, tx.Hash())
294+
}
295+
241296
type BurnReceipt struct {
242297
InitialBurnBalance *big.Int
243298
BurnAmount *big.Int
@@ -354,9 +409,23 @@ func (d *Devnet) serviceClient(service string, port uint16) (*ethclient.Client,
354409
if err != nil {
355410
return nil, fmt.Errorf("could not get %s port: %w", service, err)
356411
}
357-
client, err := ethclient.DialContext(d.ctx, fmt.Sprintf("http://localhost:%d", port))
412+
client, err := ethclient.DialContext(d.ctx, fmt.Sprintf("http://127.0.0.1:%d", port))
358413
if err != nil {
359414
return nil, fmt.Errorf("could not open %s RPC client: %w", service, err)
360415
}
361416
return client, nil
362417
}
418+
419+
func (d *Devnet) rollupClient(service string, port uint16) (*sources.RollupClient, error) {
420+
port, err := d.hostPort(service, port)
421+
if err != nil {
422+
return nil, fmt.Errorf("could not get %s port: %w", service, err)
423+
}
424+
rpc, err := opclient.NewRPC(d.ctx, log.Root(), fmt.Sprintf("http://127.0.0.1:%d", port), opclient.WithDialAttempts(10))
425+
if err != nil {
426+
return nil, fmt.Errorf("could not open %s RPC client: %w", service, err)
427+
}
428+
429+
client := sources.NewRollupClient(rpc)
430+
return client, nil
431+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package devnet_tests
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
"github.com/ethereum-optimism/optimism/op-batcher/bindings"
8+
"github.com/ethereum-optimism/optimism/op-service/eth"
9+
"github.com/ethereum/go-ethereum/accounts/abi/bind"
10+
"github.com/stretchr/testify/require"
11+
)
12+
13+
func TestRotateBatcherKey(t *testing.T) {
14+
ctx, cancel := context.WithCancel(context.Background())
15+
defer cancel()
16+
17+
d := NewDevnet(ctx, t)
18+
19+
// We're going to change batcher key to Bob's, verify that it won't be a no-op
20+
require.NotEqual(t, d.secrets.Batcher, d.secrets.Bob)
21+
22+
require.NoError(t, d.Up(testing.Verbose()))
23+
defer func() {
24+
require.NoError(t, d.Down())
25+
}()
26+
27+
// Send a transaction just to check that everything has started up ok.
28+
require.NoError(t, d.RunSimpleL2Burn())
29+
30+
// Shut down the batcher
31+
require.NoError(t, d.ServiceDown("op-batcher"))
32+
d.SleepOutageDuration()
33+
34+
// Change the batch sender key to Bob
35+
contract, owner, err := d.SystemConfig(ctx)
36+
require.NoError(t, err)
37+
38+
tx, err := contract.SetBatcherHash(owner, eth.AddressAsLeftPaddedHash(d.secrets.Addresses().Bob))
39+
require.NoError(t, err)
40+
41+
_, err = d.SendL1Tx(ctx, tx)
42+
require.NoError(t, err)
43+
44+
d.secrets.Batcher = d.secrets.Bob
45+
46+
// Restart the batcher
47+
require.NoError(t, d.ServiceUp("op-batcher"))
48+
d.SleepOutageDuration()
49+
50+
// Send a transaction to check the L2 still runs
51+
require.NoError(t, d.RunSimpleL2Burn())
52+
}
53+
54+
func TestChangeBatchInboxOwner(t *testing.T) {
55+
ctx, cancel := context.WithCancel(context.Background())
56+
defer cancel()
57+
58+
d := NewDevnet(ctx, t)
59+
60+
require.NoError(t, d.Up(testing.Verbose()))
61+
defer func() {
62+
require.NoError(t, d.Down())
63+
}()
64+
65+
// Send a transaction just to check that everything has started up ok.
66+
require.NoError(t, d.RunSimpleL2Burn())
67+
68+
config, err := d.RollupConfig(ctx)
69+
require.NoError(t, err)
70+
71+
// Change the BatchAuthenticator's owner
72+
batchAuthenticator, err := bindings.NewBatchAuthenticator(config.BatchAuthenticatorAddress, d.L1)
73+
require.NoError(t, err)
74+
tx, err := batchAuthenticator.TransferOwnership(&bind.TransactOpts{}, d.secrets.Addresses().Bob)
75+
require.NoError(t, err)
76+
_, err = d.SendL1Tx(ctx, tx)
77+
require.NoError(t, err)
78+
79+
// Ensure the owner has been changed
80+
newOwner, err := batchAuthenticator.Owner(&bind.CallOpts{})
81+
require.NoError(t, err)
82+
require.Equal(t, newOwner, d.secrets.Addresses().Bob)
83+
84+
// Check that everything still functions
85+
require.NoError(t, d.RunSimpleL2Burn())
86+
}

espresso/docker-compose.yml

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ services:
8080
l1-genesis:
8181
condition: service_completed_successfully
8282
healthcheck:
83-
test: [ "CMD", "curl", "-f", "http://localhost:${L1_HTTP_PORT}" ]
83+
test: ["CMD", "curl", "-f", "http://localhost:${L1_HTTP_PORT}"]
8484
interval: 3s
8585
timeout: 2s
8686
retries: 40
@@ -171,6 +171,11 @@ services:
171171
context: ../
172172
dockerfile: espresso/docker/op-stack/Dockerfile
173173
target: op-node-target
174+
healthcheck:
175+
test: ["CMD", "curl", "-f", "http://localhost:${ROLLUP_PORT}"]
176+
interval: 3s
177+
timeout: 2s
178+
retries: 40
174179
image: op-node-sequencer:espresso
175180
depends_on:
176181
l2-rollup:
@@ -205,6 +210,11 @@ services:
205210
context: ../
206211
dockerfile: espresso/docker/op-stack/Dockerfile
207212
target: op-node-target
213+
healthcheck:
214+
test: ["CMD", "curl", "-f", "http://localhost:${VERIFIER_PORT}"]
215+
interval: 3s
216+
timeout: 2s
217+
retries: 40
208218
image: op-node-verifier:espresso
209219
depends_on:
210220
l2-rollup:
@@ -237,6 +247,11 @@ services:
237247
context: ../
238248
dockerfile: espresso/docker/op-stack/Dockerfile
239249
target: op-node-target
250+
healthcheck:
251+
test: ["CMD", "curl", "-f", "http://localhost:${CAFF_PORT}"]
252+
interval: 3s
253+
timeout: 2s
254+
retries: 40
240255
image: caff-node:espresso
241256
depends_on:
242257
op-geth-caff-node:
@@ -307,9 +322,8 @@ services:
307322
command:
308323
- op-batcher
309324
- --espresso-light-client-addr=0x703848f4c85f18e3acd8196c8ec91eb0b7bd0797
310-
- --testing-espresso-batcher-private-key=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 # Default value for testing
311-
- --mnemonic=test test test test test test test test test test test junk # Arbitrary value for testing
312-
- --hd-path=m/44'/60'/0'/0/0 # Arbitrary value for testing
325+
- --testing-espresso-batcher-private-key=${OP_TESTING_BATCHER_PRIVATE_KEY:-0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80} # Default value for testing
326+
- --private-key=${OP_BATCHER_PRIVATE_KEY:-"0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"} # Arbitrary value for testing
313327
- --throttle-threshold=0
314328
- --max-channel-duration=2
315329
- --target-num-frames=1
@@ -343,7 +357,11 @@ services:
343357
target: op-batcher-enclave-target
344358
image: op-batcher-tee:espresso
345359
healthcheck:
346-
test: ["CMD-SHELL", "test -f /tmp/enclave-tools.pid && kill -0 $(cat /tmp/enclave-tools.pid) 2>/dev/null || exit 1"]
360+
test:
361+
[
362+
"CMD-SHELL",
363+
"test -f /tmp/enclave-tools.pid && kill -0 $(cat /tmp/enclave-tools.pid) 2>/dev/null || exit 1",
364+
]
347365
interval: 30s
348366
timeout: 10s
349367
retries: 3
@@ -380,16 +398,15 @@ services:
380398
- /dev/nitro_enclaves:/dev/nitro_enclaves
381399
restart: "no"
382400
command:
383-
- sh
384-
- -c
385-
- |
386-
export DEPLOYMENT_MODE=local
387-
export L1_RPC_URL="http://127.0.0.1:${L1_HTTP_PORT}"
388-
export L2_RPC_URL="http://127.0.0.1:${OP_HTTP_PORT}"
389-
export ROLLUP_RPC_URL="http://127.0.0.1:${ROLLUP_PORT}"
390-
export ESPRESSO_URL1="http://127.0.0.1:${ESPRESSO_SEQUENCER_API_PORT}"
391-
/source/espresso/docker/op-batcher-tee/run-enclave.sh
392-
401+
- sh
402+
- -c
403+
- |
404+
export DEPLOYMENT_MODE=local
405+
export L1_RPC_URL="http://127.0.0.1:${L1_HTTP_PORT}"
406+
export L2_RPC_URL="http://127.0.0.1:${OP_HTTP_PORT}"
407+
export ROLLUP_RPC_URL="http://127.0.0.1:${ROLLUP_PORT}"
408+
export ESPRESSO_URL1="http://127.0.0.1:${ESPRESSO_SEQUENCER_API_PORT}"
409+
/source/espresso/docker/op-batcher-tee/run-enclave.sh
393410
394411
op-proposer:
395412
profiles: ["default"]
@@ -443,7 +460,7 @@ services:
443460
ESPRESSO_SEQUENCER_STORAGE_PATH: /data/espresso
444461
ESPRESSO_SEQUENCER_L1_PROVIDER: http://l1-geth:${L1_HTTP_PORT}
445462
ESPRESSO_DEPLOYER_ACCOUNT_INDEX: 0
446-
ESPRESSO_DEV_NODE_EPOCH_HEIGHT: '4294967295'
463+
ESPRESSO_DEV_NODE_EPOCH_HEIGHT: "4294967295"
447464
ESPRESSO_SEQUENCER_ETH_MNEMONIC: "giant issue aisle success illegal bike spike question tent bar rely arctic volcano long crawl hungry vocal artwork sniff fantasy very lucky have athlete"
448465

449466
volumes:

0 commit comments

Comments
 (0)