Skip to content

Commit 5a65359

Browse files
authored
op-program: Implement padding steps (#13741)
* op-program: Implement padding steps * op-program: Restore Head field in DerivationResult
1 parent 40e7fc7 commit 5a65359

File tree

7 files changed

+159
-74
lines changed

7 files changed

+159
-74
lines changed

op-e2e/actions/interop/interop_test.go

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -282,14 +282,16 @@ func TestInteropFaultProofs(gt *testing.T) {
282282
Step: 2,
283283
})
284284

285-
step3Expected := serializeIntermediateRoot(&types.TransitionState{
286-
SuperRoot: start.Marshal(),
287-
PendingProgress: []types.OptimisticBlock{
288-
{BlockHash: chain1End.BlockRef.Hash, OutputRoot: chain1End.OutputRoot},
289-
{BlockHash: chain2End.BlockRef.Hash, OutputRoot: chain2End.OutputRoot},
290-
},
291-
Step: 3,
292-
})
285+
paddingStep := func(step uint64) []byte {
286+
return serializeIntermediateRoot(&types.TransitionState{
287+
SuperRoot: start.Marshal(),
288+
PendingProgress: []types.OptimisticBlock{
289+
{BlockHash: chain1End.BlockRef.Hash, OutputRoot: chain1End.OutputRoot},
290+
{BlockHash: chain2End.BlockRef.Hash, OutputRoot: chain2End.OutputRoot},
291+
},
292+
Step: step,
293+
})
294+
}
293295

294296
tests := []*transitionTest{
295297
{
@@ -321,17 +323,30 @@ func TestInteropFaultProofs(gt *testing.T) {
321323
expectValid: true,
322324
},
323325
{
324-
name: "PaddingStep",
326+
name: "FirstPaddingStep",
325327
startTimestamp: startTimestamp,
326328
agreedClaim: step2Expected,
327-
disputedClaim: step3Expected,
329+
disputedClaim: paddingStep(3),
330+
expectValid: true,
331+
},
332+
{
333+
name: "SecondPaddingStep",
334+
startTimestamp: startTimestamp,
335+
agreedClaim: paddingStep(3),
336+
disputedClaim: paddingStep(4),
337+
expectValid: true,
338+
},
339+
{
340+
name: "LastPaddingStep",
341+
startTimestamp: startTimestamp,
342+
agreedClaim: paddingStep(1022),
343+
disputedClaim: paddingStep(1023),
328344
expectValid: true,
329-
skip: true,
330345
},
331346
{
332347
name: "Consolidate",
333348
startTimestamp: startTimestamp,
334-
agreedClaim: step3Expected,
349+
agreedClaim: paddingStep(1023),
335350
disputedClaim: end.Marshal(),
336351
expectValid: true,
337352
skip: true,

op-program/client/claim/validate.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ import (
1111

1212
var ErrClaimNotValid = errors.New("invalid claim")
1313

14-
func ValidateClaim(log log.Logger, l2Head eth.L2BlockRef, claimedOutputRoot eth.Bytes32, outputRoot eth.Bytes32) error {
15-
log.Info("Validating claim", "head", l2Head, "output", outputRoot, "claim", claimedOutputRoot)
14+
func ValidateClaim(log log.Logger, claimedOutputRoot eth.Bytes32, outputRoot eth.Bytes32) error {
15+
log.Info("Validating claim", "output", outputRoot, "claim", claimedOutputRoot)
1616
if claimedOutputRoot != outputRoot {
1717
return fmt.Errorf("%w: claim: %v actual: %v", ErrClaimNotValid, claimedOutputRoot, outputRoot)
1818
}

op-program/client/claim/validate_test.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,16 @@ func TestValidateClaim(t *testing.T) {
1515
t.Run("Valid", func(t *testing.T) {
1616
expected := eth.Bytes32{0x11}
1717
actual := eth.Bytes32{0x11}
18-
l2Head := eth.L2BlockRef{Number: 42}
1918
logger := testlog.Logger(t, log.LevelError)
20-
err := ValidateClaim(logger, l2Head, expected, actual)
19+
err := ValidateClaim(logger, expected, actual)
2120
require.NoError(t, err)
2221
})
2322

2423
t.Run("Invalid", func(t *testing.T) {
2524
expected := eth.Bytes32{0x11}
2625
actual := eth.Bytes32{0x22}
27-
l2Head := eth.L2BlockRef{Number: 42}
2826
logger := testlog.Logger(t, log.LevelError)
29-
err := ValidateClaim(logger, l2Head, expected, actual)
27+
err := ValidateClaim(logger, expected, actual)
3028
require.ErrorIs(t, err, ErrClaimNotValid)
3129
})
3230
}

op-program/client/interop/interop.go

Lines changed: 35 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -55,42 +55,44 @@ func runInteropProgram(logger log.Logger, bootInfo *boot.BootInfoInterop, l1Prei
5555
return fmt.Errorf("%w: %v", ErrIncorrectOutputRootType, super.Version())
5656
}
5757
superRoot := super.(*eth.SuperV1)
58-
chainAgreedPrestate := superRoot.Chains[transitionState.Step]
59-
rollupCfg, err := bootInfo.Configs.RollupConfig(chainAgreedPrestate.ChainID)
60-
if err != nil {
61-
return fmt.Errorf("no rollup config available for chain ID %v: %w", chainAgreedPrestate.ChainID, err)
62-
}
63-
l2ChainConfig, err := bootInfo.Configs.ChainConfig(chainAgreedPrestate.ChainID)
64-
if err != nil {
65-
return fmt.Errorf("no chain config available for chain ID %v: %w", chainAgreedPrestate.ChainID, err)
66-
}
67-
claimedBlockNumber, err := rollupCfg.TargetBlockNumber(superRoot.Timestamp + 1)
68-
if err != nil {
69-
return err
70-
}
71-
derivationResult, err := tasks.RunDerivation(
72-
logger,
73-
rollupCfg,
74-
l2ChainConfig,
75-
bootInfo.L1Head,
76-
chainAgreedPrestate.Output,
77-
claimedBlockNumber,
78-
l1PreimageOracle,
79-
l2PreimageOracle,
80-
)
81-
if err != nil {
82-
return err
83-
}
8458

85-
newPendingProgress := make([]types.OptimisticBlock, len(transitionState.PendingProgress)+1)
86-
copy(newPendingProgress, transitionState.PendingProgress)
87-
newPendingProgress[len(newPendingProgress)-1] = types.OptimisticBlock{
88-
BlockHash: derivationResult.BlockHash,
89-
OutputRoot: derivationResult.OutputRoot,
59+
expectedPendingProgress := transitionState.PendingProgress
60+
if transitionState.Step < uint64(len(superRoot.Chains)) {
61+
chainAgreedPrestate := superRoot.Chains[transitionState.Step]
62+
rollupCfg, err := bootInfo.Configs.RollupConfig(chainAgreedPrestate.ChainID)
63+
if err != nil {
64+
return fmt.Errorf("no rollup config available for chain ID %v: %w", chainAgreedPrestate.ChainID, err)
65+
}
66+
l2ChainConfig, err := bootInfo.Configs.ChainConfig(chainAgreedPrestate.ChainID)
67+
if err != nil {
68+
return fmt.Errorf("no chain config available for chain ID %v: %w", chainAgreedPrestate.ChainID, err)
69+
}
70+
claimedBlockNumber, err := rollupCfg.TargetBlockNumber(superRoot.Timestamp + 1)
71+
if err != nil {
72+
return err
73+
}
74+
derivationResult, err := tasks.RunDerivation(
75+
logger,
76+
rollupCfg,
77+
l2ChainConfig,
78+
bootInfo.L1Head,
79+
chainAgreedPrestate.Output,
80+
claimedBlockNumber,
81+
l1PreimageOracle,
82+
l2PreimageOracle,
83+
)
84+
if err != nil {
85+
return err
86+
}
87+
88+
expectedPendingProgress = append(expectedPendingProgress, types.OptimisticBlock{
89+
BlockHash: derivationResult.BlockHash,
90+
OutputRoot: derivationResult.OutputRoot,
91+
})
9092
}
9193
finalState := &types.TransitionState{
9294
SuperRoot: transitionState.SuperRoot,
93-
PendingProgress: newPendingProgress,
95+
PendingProgress: expectedPendingProgress,
9496
Step: transitionState.Step + 1,
9597
}
9698
expected, err := finalState.Hash()
@@ -100,7 +102,7 @@ func runInteropProgram(logger log.Logger, bootInfo *boot.BootInfoInterop, l1Prei
100102
if !validateClaim {
101103
return nil
102104
}
103-
return claim.ValidateClaim(logger, derivationResult.Head, eth.Bytes32(bootInfo.Claim), eth.Bytes32(expected))
105+
return claim.ValidateClaim(logger, eth.Bytes32(bootInfo.Claim), eth.Bytes32(expected))
104106
}
105107

106108
type interopTaskExecutor struct {

op-program/client/interop/interop_test.go

Lines changed: 90 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package interop
22

33
import (
44
"fmt"
5+
"math/big"
56
"testing"
67

78
"github.com/ethereum-optimism/optimism/op-node/chaincfg"
@@ -21,46 +22,114 @@ import (
2122
"github.com/stretchr/testify/require"
2223
)
2324

24-
func TestDeriveBlockForFirstChainFromSuperchainRoot(t *testing.T) {
25-
logger := testlog.Logger(t, log.LevelError)
26-
rollupCfg := chaincfg.OPSepolia()
27-
chainCfg := chainconfig.OPSepoliaChainConfig()
28-
chain1Output := &eth.OutputV0{}
25+
func setupTwoChains() (*staticConfigSource, *eth.SuperV1, stubTasks) {
26+
rollupCfg1 := chaincfg.OPSepolia()
27+
chainCfg1 := chainconfig.OPSepoliaChainConfig()
28+
29+
rollupCfg2 := *chaincfg.OPSepolia()
30+
rollupCfg2.L2ChainID = new(big.Int).SetUint64(42)
31+
chainCfg2 := *chainconfig.OPSepoliaChainConfig()
32+
chainCfg2.ChainID = rollupCfg2.L2ChainID
33+
2934
agreedSuperRoot := &eth.SuperV1{
30-
Timestamp: rollupCfg.Genesis.L2Time + 1234,
31-
Chains: []eth.ChainIDAndOutput{{ChainID: rollupCfg.L2ChainID.Uint64(), Output: eth.OutputRoot(chain1Output)}},
35+
Timestamp: rollupCfg1.Genesis.L2Time + 1234,
36+
Chains: []eth.ChainIDAndOutput{
37+
{ChainID: rollupCfg1.L2ChainID.Uint64(), Output: eth.OutputRoot(&eth.OutputV0{BlockHash: common.Hash{0x11}})},
38+
{ChainID: rollupCfg2.L2ChainID.Uint64(), Output: eth.OutputRoot(&eth.OutputV0{BlockHash: common.Hash{0x22}})},
39+
},
40+
}
41+
configSource := &staticConfigSource{
42+
rollupCfgs: []*rollup.Config{rollupCfg1, &rollupCfg2},
43+
chainConfigs: []*params.ChainConfig{chainCfg1, &chainCfg2},
3244
}
45+
tasksStub := stubTasks{
46+
blockHash: common.Hash{0x22},
47+
outputRoot: eth.Bytes32{0x66},
48+
}
49+
return configSource, agreedSuperRoot, tasksStub
50+
}
51+
52+
func TestDeriveBlockForFirstChainFromSuperchainRoot(t *testing.T) {
53+
logger := testlog.Logger(t, log.LevelError)
54+
configSource, agreedSuperRoot, tasksStub := setupTwoChains()
55+
3356
outputRootHash := common.Hash(eth.SuperRoot(agreedSuperRoot))
3457
l2PreimageOracle, _ := test.NewStubOracle(t)
3558
l2PreimageOracle.TransitionStates[outputRootHash] = &types.TransitionState{SuperRoot: agreedSuperRoot.Marshal()}
36-
tasks := stubTasks{
37-
l2SafeHead: eth.L2BlockRef{
38-
Number: 56,
39-
Hash: common.Hash{0x11},
59+
60+
expectedIntermediateRoot := &types.TransitionState{
61+
SuperRoot: agreedSuperRoot.Marshal(),
62+
PendingProgress: []types.OptimisticBlock{
63+
{BlockHash: tasksStub.blockHash, OutputRoot: tasksStub.outputRoot},
4064
},
41-
blockHash: common.Hash{0x22},
42-
outputRoot: eth.Bytes32{0x66},
65+
Step: 1,
4366
}
44-
expectedIntermediateRoot := &types.TransitionState{
67+
68+
expectedClaim, err := expectedIntermediateRoot.Hash()
69+
require.NoError(t, err)
70+
71+
verifyResult(t, logger, tasksStub, configSource, l2PreimageOracle, agreedSuperRoot, outputRootHash, expectedClaim)
72+
}
73+
74+
func TestDeriveBlockForSecondChainFromTransitionState(t *testing.T) {
75+
logger := testlog.Logger(t, log.LevelError)
76+
configSource, agreedSuperRoot, tasksStub := setupTwoChains()
77+
agreedTransitionState := &types.TransitionState{
4578
SuperRoot: agreedSuperRoot.Marshal(),
4679
PendingProgress: []types.OptimisticBlock{
47-
{BlockHash: tasks.blockHash, OutputRoot: tasks.outputRoot},
80+
{BlockHash: common.Hash{0xaa}, OutputRoot: eth.Bytes32{6: 22}},
4881
},
4982
Step: 1,
5083
}
84+
outputRootHash, err := agreedTransitionState.Hash()
85+
require.NoError(t, err)
86+
l2PreimageOracle, _ := test.NewStubOracle(t)
87+
l2PreimageOracle.TransitionStates[outputRootHash] = agreedTransitionState
88+
expectedIntermediateRoot := &types.TransitionState{
89+
SuperRoot: agreedSuperRoot.Marshal(),
90+
PendingProgress: []types.OptimisticBlock{
91+
{BlockHash: common.Hash{0xaa}, OutputRoot: eth.Bytes32{6: 22}},
92+
{BlockHash: tasksStub.blockHash, OutputRoot: tasksStub.outputRoot},
93+
},
94+
Step: 2,
95+
}
5196

5297
expectedClaim, err := expectedIntermediateRoot.Hash()
5398
require.NoError(t, err)
99+
verifyResult(t, logger, tasksStub, configSource, l2PreimageOracle, agreedSuperRoot, outputRootHash, expectedClaim)
100+
}
101+
102+
func TestNoOpStep(t *testing.T) {
103+
logger := testlog.Logger(t, log.LevelError)
104+
configSource, agreedSuperRoot, tasksStub := setupTwoChains()
105+
agreedTransitionState := &types.TransitionState{
106+
SuperRoot: agreedSuperRoot.Marshal(),
107+
PendingProgress: []types.OptimisticBlock{
108+
{BlockHash: common.Hash{0xaa}, OutputRoot: eth.Bytes32{6: 22}},
109+
{BlockHash: tasksStub.blockHash, OutputRoot: tasksStub.outputRoot},
110+
},
111+
Step: 2,
112+
}
113+
outputRootHash, err := agreedTransitionState.Hash()
114+
require.NoError(t, err)
115+
l2PreimageOracle, _ := test.NewStubOracle(t)
116+
l2PreimageOracle.TransitionStates[outputRootHash] = agreedTransitionState
117+
expectedIntermediateRoot := *agreedTransitionState // Copy agreed state
118+
expectedIntermediateRoot.Step = 3
119+
120+
expectedClaim, err := expectedIntermediateRoot.Hash()
121+
require.NoError(t, err)
122+
verifyResult(t, logger, tasksStub, configSource, l2PreimageOracle, agreedSuperRoot, outputRootHash, expectedClaim)
123+
}
124+
125+
func verifyResult(t *testing.T, logger log.Logger, tasks stubTasks, configSource *staticConfigSource, l2PreimageOracle *test.StubBlockOracle, agreedSuperRoot *eth.SuperV1, agreedPrestate common.Hash, expectedClaim common.Hash) {
54126
bootInfo := &boot.BootInfoInterop{
55-
AgreedPrestate: outputRootHash,
127+
AgreedPrestate: agreedPrestate,
56128
ClaimTimestamp: agreedSuperRoot.Timestamp + 1,
57129
Claim: expectedClaim,
58-
Configs: &staticConfigSource{
59-
rollupCfgs: []*rollup.Config{rollupCfg},
60-
chainConfigs: []*params.ChainConfig{chainCfg},
61-
},
130+
Configs: configSource,
62131
}
63-
err = runInteropProgram(logger, bootInfo, nil, l2PreimageOracle, true, &tasks)
132+
err := runInteropProgram(logger, bootInfo, nil, l2PreimageOracle, true, &tasks)
64133
require.NoError(t, err)
65134
}
66135

op-program/client/preinterop.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,5 @@ func RunPreInteropProgram(logger log.Logger, bootInfo *boot.BootInfo, l1Preimage
2525
if err != nil {
2626
return err
2727
}
28-
return claim.ValidateClaim(logger, result.Head, eth.Bytes32(bootInfo.L2Claim), result.OutputRoot)
28+
return claim.ValidateClaim(logger, eth.Bytes32(bootInfo.L2Claim), result.OutputRoot)
2929
}

op-program/client/tasks/derive.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,13 @@ func RunDerivation(
4545
}
4646
l2Source := l2.NewOracleEngine(cfg, logger, engineBackend)
4747

48-
logger.Info("Starting derivation")
48+
logger.Info("Starting derivation", "chainID", cfg.L2ChainID)
4949
d := cldr.NewDriver(logger, cfg, l1Source, l1BlobsSource, l2Source, l2ClaimBlockNum)
5050
result, err := d.RunComplete()
5151
if err != nil {
5252
return DerivationResult{}, fmt.Errorf("failed to run program to completion: %w", err)
5353
}
54+
logger.Info("Derivation complete", "head", result)
5455
return loadOutputRoot(l2ClaimBlockNum, result, l2Source)
5556
}
5657

0 commit comments

Comments
 (0)