Commit 5a65359f authored by Adrian Sutton's avatar Adrian Sutton Committed by GitHub

op-program: Implement padding steps (#13741)

* op-program: Implement padding steps

* op-program: Restore Head field in DerivationResult
parent 40e7fc7b
...@@ -282,14 +282,16 @@ func TestInteropFaultProofs(gt *testing.T) { ...@@ -282,14 +282,16 @@ func TestInteropFaultProofs(gt *testing.T) {
Step: 2, Step: 2,
}) })
step3Expected := serializeIntermediateRoot(&types.TransitionState{ paddingStep := func(step uint64) []byte {
return serializeIntermediateRoot(&types.TransitionState{
SuperRoot: start.Marshal(), SuperRoot: start.Marshal(),
PendingProgress: []types.OptimisticBlock{ PendingProgress: []types.OptimisticBlock{
{BlockHash: chain1End.BlockRef.Hash, OutputRoot: chain1End.OutputRoot}, {BlockHash: chain1End.BlockRef.Hash, OutputRoot: chain1End.OutputRoot},
{BlockHash: chain2End.BlockRef.Hash, OutputRoot: chain2End.OutputRoot}, {BlockHash: chain2End.BlockRef.Hash, OutputRoot: chain2End.OutputRoot},
}, },
Step: 3, Step: step,
}) })
}
tests := []*transitionTest{ tests := []*transitionTest{
{ {
...@@ -321,17 +323,30 @@ func TestInteropFaultProofs(gt *testing.T) { ...@@ -321,17 +323,30 @@ func TestInteropFaultProofs(gt *testing.T) {
expectValid: true, expectValid: true,
}, },
{ {
name: "PaddingStep", name: "FirstPaddingStep",
startTimestamp: startTimestamp, startTimestamp: startTimestamp,
agreedClaim: step2Expected, agreedClaim: step2Expected,
disputedClaim: step3Expected, disputedClaim: paddingStep(3),
expectValid: true,
},
{
name: "SecondPaddingStep",
startTimestamp: startTimestamp,
agreedClaim: paddingStep(3),
disputedClaim: paddingStep(4),
expectValid: true,
},
{
name: "LastPaddingStep",
startTimestamp: startTimestamp,
agreedClaim: paddingStep(1022),
disputedClaim: paddingStep(1023),
expectValid: true, expectValid: true,
skip: true,
}, },
{ {
name: "Consolidate", name: "Consolidate",
startTimestamp: startTimestamp, startTimestamp: startTimestamp,
agreedClaim: step3Expected, agreedClaim: paddingStep(1023),
disputedClaim: end.Marshal(), disputedClaim: end.Marshal(),
expectValid: true, expectValid: true,
skip: true, skip: true,
......
...@@ -11,8 +11,8 @@ import ( ...@@ -11,8 +11,8 @@ import (
var ErrClaimNotValid = errors.New("invalid claim") var ErrClaimNotValid = errors.New("invalid claim")
func ValidateClaim(log log.Logger, l2Head eth.L2BlockRef, claimedOutputRoot eth.Bytes32, outputRoot eth.Bytes32) error { func ValidateClaim(log log.Logger, claimedOutputRoot eth.Bytes32, outputRoot eth.Bytes32) error {
log.Info("Validating claim", "head", l2Head, "output", outputRoot, "claim", claimedOutputRoot) log.Info("Validating claim", "output", outputRoot, "claim", claimedOutputRoot)
if claimedOutputRoot != outputRoot { if claimedOutputRoot != outputRoot {
return fmt.Errorf("%w: claim: %v actual: %v", ErrClaimNotValid, claimedOutputRoot, outputRoot) return fmt.Errorf("%w: claim: %v actual: %v", ErrClaimNotValid, claimedOutputRoot, outputRoot)
} }
......
...@@ -15,18 +15,16 @@ func TestValidateClaim(t *testing.T) { ...@@ -15,18 +15,16 @@ func TestValidateClaim(t *testing.T) {
t.Run("Valid", func(t *testing.T) { t.Run("Valid", func(t *testing.T) {
expected := eth.Bytes32{0x11} expected := eth.Bytes32{0x11}
actual := eth.Bytes32{0x11} actual := eth.Bytes32{0x11}
l2Head := eth.L2BlockRef{Number: 42}
logger := testlog.Logger(t, log.LevelError) logger := testlog.Logger(t, log.LevelError)
err := ValidateClaim(logger, l2Head, expected, actual) err := ValidateClaim(logger, expected, actual)
require.NoError(t, err) require.NoError(t, err)
}) })
t.Run("Invalid", func(t *testing.T) { t.Run("Invalid", func(t *testing.T) {
expected := eth.Bytes32{0x11} expected := eth.Bytes32{0x11}
actual := eth.Bytes32{0x22} actual := eth.Bytes32{0x22}
l2Head := eth.L2BlockRef{Number: 42}
logger := testlog.Logger(t, log.LevelError) logger := testlog.Logger(t, log.LevelError)
err := ValidateClaim(logger, l2Head, expected, actual) err := ValidateClaim(logger, expected, actual)
require.ErrorIs(t, err, ErrClaimNotValid) require.ErrorIs(t, err, ErrClaimNotValid)
}) })
} }
...@@ -55,6 +55,9 @@ func runInteropProgram(logger log.Logger, bootInfo *boot.BootInfoInterop, l1Prei ...@@ -55,6 +55,9 @@ func runInteropProgram(logger log.Logger, bootInfo *boot.BootInfoInterop, l1Prei
return fmt.Errorf("%w: %v", ErrIncorrectOutputRootType, super.Version()) return fmt.Errorf("%w: %v", ErrIncorrectOutputRootType, super.Version())
} }
superRoot := super.(*eth.SuperV1) superRoot := super.(*eth.SuperV1)
expectedPendingProgress := transitionState.PendingProgress
if transitionState.Step < uint64(len(superRoot.Chains)) {
chainAgreedPrestate := superRoot.Chains[transitionState.Step] chainAgreedPrestate := superRoot.Chains[transitionState.Step]
rollupCfg, err := bootInfo.Configs.RollupConfig(chainAgreedPrestate.ChainID) rollupCfg, err := bootInfo.Configs.RollupConfig(chainAgreedPrestate.ChainID)
if err != nil { if err != nil {
...@@ -82,15 +85,14 @@ func runInteropProgram(logger log.Logger, bootInfo *boot.BootInfoInterop, l1Prei ...@@ -82,15 +85,14 @@ func runInteropProgram(logger log.Logger, bootInfo *boot.BootInfoInterop, l1Prei
return err return err
} }
newPendingProgress := make([]types.OptimisticBlock, len(transitionState.PendingProgress)+1) expectedPendingProgress = append(expectedPendingProgress, types.OptimisticBlock{
copy(newPendingProgress, transitionState.PendingProgress)
newPendingProgress[len(newPendingProgress)-1] = types.OptimisticBlock{
BlockHash: derivationResult.BlockHash, BlockHash: derivationResult.BlockHash,
OutputRoot: derivationResult.OutputRoot, OutputRoot: derivationResult.OutputRoot,
})
} }
finalState := &types.TransitionState{ finalState := &types.TransitionState{
SuperRoot: transitionState.SuperRoot, SuperRoot: transitionState.SuperRoot,
PendingProgress: newPendingProgress, PendingProgress: expectedPendingProgress,
Step: transitionState.Step + 1, Step: transitionState.Step + 1,
} }
expected, err := finalState.Hash() expected, err := finalState.Hash()
...@@ -100,7 +102,7 @@ func runInteropProgram(logger log.Logger, bootInfo *boot.BootInfoInterop, l1Prei ...@@ -100,7 +102,7 @@ func runInteropProgram(logger log.Logger, bootInfo *boot.BootInfoInterop, l1Prei
if !validateClaim { if !validateClaim {
return nil return nil
} }
return claim.ValidateClaim(logger, derivationResult.Head, eth.Bytes32(bootInfo.Claim), eth.Bytes32(expected)) return claim.ValidateClaim(logger, eth.Bytes32(bootInfo.Claim), eth.Bytes32(expected))
} }
type interopTaskExecutor struct { type interopTaskExecutor struct {
......
...@@ -2,6 +2,7 @@ package interop ...@@ -2,6 +2,7 @@ package interop
import ( import (
"fmt" "fmt"
"math/big"
"testing" "testing"
"github.com/ethereum-optimism/optimism/op-node/chaincfg" "github.com/ethereum-optimism/optimism/op-node/chaincfg"
...@@ -21,46 +22,114 @@ import ( ...@@ -21,46 +22,114 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestDeriveBlockForFirstChainFromSuperchainRoot(t *testing.T) { func setupTwoChains() (*staticConfigSource, *eth.SuperV1, stubTasks) {
logger := testlog.Logger(t, log.LevelError) rollupCfg1 := chaincfg.OPSepolia()
rollupCfg := chaincfg.OPSepolia() chainCfg1 := chainconfig.OPSepoliaChainConfig()
chainCfg := chainconfig.OPSepoliaChainConfig()
chain1Output := &eth.OutputV0{} rollupCfg2 := *chaincfg.OPSepolia()
rollupCfg2.L2ChainID = new(big.Int).SetUint64(42)
chainCfg2 := *chainconfig.OPSepoliaChainConfig()
chainCfg2.ChainID = rollupCfg2.L2ChainID
agreedSuperRoot := &eth.SuperV1{ agreedSuperRoot := &eth.SuperV1{
Timestamp: rollupCfg.Genesis.L2Time + 1234, Timestamp: rollupCfg1.Genesis.L2Time + 1234,
Chains: []eth.ChainIDAndOutput{{ChainID: rollupCfg.L2ChainID.Uint64(), Output: eth.OutputRoot(chain1Output)}}, Chains: []eth.ChainIDAndOutput{
{ChainID: rollupCfg1.L2ChainID.Uint64(), Output: eth.OutputRoot(&eth.OutputV0{BlockHash: common.Hash{0x11}})},
{ChainID: rollupCfg2.L2ChainID.Uint64(), Output: eth.OutputRoot(&eth.OutputV0{BlockHash: common.Hash{0x22}})},
},
}
configSource := &staticConfigSource{
rollupCfgs: []*rollup.Config{rollupCfg1, &rollupCfg2},
chainConfigs: []*params.ChainConfig{chainCfg1, &chainCfg2},
} }
tasksStub := stubTasks{
blockHash: common.Hash{0x22},
outputRoot: eth.Bytes32{0x66},
}
return configSource, agreedSuperRoot, tasksStub
}
func TestDeriveBlockForFirstChainFromSuperchainRoot(t *testing.T) {
logger := testlog.Logger(t, log.LevelError)
configSource, agreedSuperRoot, tasksStub := setupTwoChains()
outputRootHash := common.Hash(eth.SuperRoot(agreedSuperRoot)) outputRootHash := common.Hash(eth.SuperRoot(agreedSuperRoot))
l2PreimageOracle, _ := test.NewStubOracle(t) l2PreimageOracle, _ := test.NewStubOracle(t)
l2PreimageOracle.TransitionStates[outputRootHash] = &types.TransitionState{SuperRoot: agreedSuperRoot.Marshal()} l2PreimageOracle.TransitionStates[outputRootHash] = &types.TransitionState{SuperRoot: agreedSuperRoot.Marshal()}
tasks := stubTasks{
l2SafeHead: eth.L2BlockRef{ expectedIntermediateRoot := &types.TransitionState{
Number: 56, SuperRoot: agreedSuperRoot.Marshal(),
Hash: common.Hash{0x11}, PendingProgress: []types.OptimisticBlock{
{BlockHash: tasksStub.blockHash, OutputRoot: tasksStub.outputRoot},
}, },
blockHash: common.Hash{0x22}, Step: 1,
outputRoot: eth.Bytes32{0x66},
} }
expectedIntermediateRoot := &types.TransitionState{
expectedClaim, err := expectedIntermediateRoot.Hash()
require.NoError(t, err)
verifyResult(t, logger, tasksStub, configSource, l2PreimageOracle, agreedSuperRoot, outputRootHash, expectedClaim)
}
func TestDeriveBlockForSecondChainFromTransitionState(t *testing.T) {
logger := testlog.Logger(t, log.LevelError)
configSource, agreedSuperRoot, tasksStub := setupTwoChains()
agreedTransitionState := &types.TransitionState{
SuperRoot: agreedSuperRoot.Marshal(), SuperRoot: agreedSuperRoot.Marshal(),
PendingProgress: []types.OptimisticBlock{ PendingProgress: []types.OptimisticBlock{
{BlockHash: tasks.blockHash, OutputRoot: tasks.outputRoot}, {BlockHash: common.Hash{0xaa}, OutputRoot: eth.Bytes32{6: 22}},
}, },
Step: 1, Step: 1,
} }
outputRootHash, err := agreedTransitionState.Hash()
require.NoError(t, err)
l2PreimageOracle, _ := test.NewStubOracle(t)
l2PreimageOracle.TransitionStates[outputRootHash] = agreedTransitionState
expectedIntermediateRoot := &types.TransitionState{
SuperRoot: agreedSuperRoot.Marshal(),
PendingProgress: []types.OptimisticBlock{
{BlockHash: common.Hash{0xaa}, OutputRoot: eth.Bytes32{6: 22}},
{BlockHash: tasksStub.blockHash, OutputRoot: tasksStub.outputRoot},
},
Step: 2,
}
expectedClaim, err := expectedIntermediateRoot.Hash() expectedClaim, err := expectedIntermediateRoot.Hash()
require.NoError(t, err) require.NoError(t, err)
verifyResult(t, logger, tasksStub, configSource, l2PreimageOracle, agreedSuperRoot, outputRootHash, expectedClaim)
}
func TestNoOpStep(t *testing.T) {
logger := testlog.Logger(t, log.LevelError)
configSource, agreedSuperRoot, tasksStub := setupTwoChains()
agreedTransitionState := &types.TransitionState{
SuperRoot: agreedSuperRoot.Marshal(),
PendingProgress: []types.OptimisticBlock{
{BlockHash: common.Hash{0xaa}, OutputRoot: eth.Bytes32{6: 22}},
{BlockHash: tasksStub.blockHash, OutputRoot: tasksStub.outputRoot},
},
Step: 2,
}
outputRootHash, err := agreedTransitionState.Hash()
require.NoError(t, err)
l2PreimageOracle, _ := test.NewStubOracle(t)
l2PreimageOracle.TransitionStates[outputRootHash] = agreedTransitionState
expectedIntermediateRoot := *agreedTransitionState // Copy agreed state
expectedIntermediateRoot.Step = 3
expectedClaim, err := expectedIntermediateRoot.Hash()
require.NoError(t, err)
verifyResult(t, logger, tasksStub, configSource, l2PreimageOracle, agreedSuperRoot, outputRootHash, expectedClaim)
}
func verifyResult(t *testing.T, logger log.Logger, tasks stubTasks, configSource *staticConfigSource, l2PreimageOracle *test.StubBlockOracle, agreedSuperRoot *eth.SuperV1, agreedPrestate common.Hash, expectedClaim common.Hash) {
bootInfo := &boot.BootInfoInterop{ bootInfo := &boot.BootInfoInterop{
AgreedPrestate: outputRootHash, AgreedPrestate: agreedPrestate,
ClaimTimestamp: agreedSuperRoot.Timestamp + 1, ClaimTimestamp: agreedSuperRoot.Timestamp + 1,
Claim: expectedClaim, Claim: expectedClaim,
Configs: &staticConfigSource{ Configs: configSource,
rollupCfgs: []*rollup.Config{rollupCfg},
chainConfigs: []*params.ChainConfig{chainCfg},
},
} }
err = runInteropProgram(logger, bootInfo, nil, l2PreimageOracle, true, &tasks) err := runInteropProgram(logger, bootInfo, nil, l2PreimageOracle, true, &tasks)
require.NoError(t, err) require.NoError(t, err)
} }
......
...@@ -25,5 +25,5 @@ func RunPreInteropProgram(logger log.Logger, bootInfo *boot.BootInfo, l1Preimage ...@@ -25,5 +25,5 @@ func RunPreInteropProgram(logger log.Logger, bootInfo *boot.BootInfo, l1Preimage
if err != nil { if err != nil {
return err return err
} }
return claim.ValidateClaim(logger, result.Head, eth.Bytes32(bootInfo.L2Claim), result.OutputRoot) return claim.ValidateClaim(logger, eth.Bytes32(bootInfo.L2Claim), result.OutputRoot)
} }
...@@ -45,12 +45,13 @@ func RunDerivation( ...@@ -45,12 +45,13 @@ func RunDerivation(
} }
l2Source := l2.NewOracleEngine(cfg, logger, engineBackend) l2Source := l2.NewOracleEngine(cfg, logger, engineBackend)
logger.Info("Starting derivation") logger.Info("Starting derivation", "chainID", cfg.L2ChainID)
d := cldr.NewDriver(logger, cfg, l1Source, l1BlobsSource, l2Source, l2ClaimBlockNum) d := cldr.NewDriver(logger, cfg, l1Source, l1BlobsSource, l2Source, l2ClaimBlockNum)
result, err := d.RunComplete() result, err := d.RunComplete()
if err != nil { if err != nil {
return DerivationResult{}, fmt.Errorf("failed to run program to completion: %w", err) return DerivationResult{}, fmt.Errorf("failed to run program to completion: %w", err)
} }
logger.Info("Derivation complete", "head", result)
return loadOutputRoot(l2ClaimBlockNum, result, l2Source) return loadOutputRoot(l2ClaimBlockNum, result, l2Source)
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment