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) {
Step: 2,
})
step3Expected := serializeIntermediateRoot(&types.TransitionState{
paddingStep := func(step uint64) []byte {
return serializeIntermediateRoot(&types.TransitionState{
SuperRoot: start.Marshal(),
PendingProgress: []types.OptimisticBlock{
{BlockHash: chain1End.BlockRef.Hash, OutputRoot: chain1End.OutputRoot},
{BlockHash: chain2End.BlockRef.Hash, OutputRoot: chain2End.OutputRoot},
},
Step: 3,
Step: step,
})
}
tests := []*transitionTest{
{
......@@ -321,17 +323,30 @@ func TestInteropFaultProofs(gt *testing.T) {
expectValid: true,
},
{
name: "PaddingStep",
name: "FirstPaddingStep",
startTimestamp: startTimestamp,
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,
skip: true,
},
{
name: "Consolidate",
startTimestamp: startTimestamp,
agreedClaim: step3Expected,
agreedClaim: paddingStep(1023),
disputedClaim: end.Marshal(),
expectValid: true,
skip: true,
......
......@@ -11,8 +11,8 @@ import (
var ErrClaimNotValid = errors.New("invalid claim")
func ValidateClaim(log log.Logger, l2Head eth.L2BlockRef, claimedOutputRoot eth.Bytes32, outputRoot eth.Bytes32) error {
log.Info("Validating claim", "head", l2Head, "output", outputRoot, "claim", claimedOutputRoot)
func ValidateClaim(log log.Logger, claimedOutputRoot eth.Bytes32, outputRoot eth.Bytes32) error {
log.Info("Validating claim", "output", outputRoot, "claim", claimedOutputRoot)
if claimedOutputRoot != outputRoot {
return fmt.Errorf("%w: claim: %v actual: %v", ErrClaimNotValid, claimedOutputRoot, outputRoot)
}
......
......@@ -15,18 +15,16 @@ func TestValidateClaim(t *testing.T) {
t.Run("Valid", func(t *testing.T) {
expected := eth.Bytes32{0x11}
actual := eth.Bytes32{0x11}
l2Head := eth.L2BlockRef{Number: 42}
logger := testlog.Logger(t, log.LevelError)
err := ValidateClaim(logger, l2Head, expected, actual)
err := ValidateClaim(logger, expected, actual)
require.NoError(t, err)
})
t.Run("Invalid", func(t *testing.T) {
expected := eth.Bytes32{0x11}
actual := eth.Bytes32{0x22}
l2Head := eth.L2BlockRef{Number: 42}
logger := testlog.Logger(t, log.LevelError)
err := ValidateClaim(logger, l2Head, expected, actual)
err := ValidateClaim(logger, expected, actual)
require.ErrorIs(t, err, ErrClaimNotValid)
})
}
......@@ -55,6 +55,9 @@ func runInteropProgram(logger log.Logger, bootInfo *boot.BootInfoInterop, l1Prei
return fmt.Errorf("%w: %v", ErrIncorrectOutputRootType, super.Version())
}
superRoot := super.(*eth.SuperV1)
expectedPendingProgress := transitionState.PendingProgress
if transitionState.Step < uint64(len(superRoot.Chains)) {
chainAgreedPrestate := superRoot.Chains[transitionState.Step]
rollupCfg, err := bootInfo.Configs.RollupConfig(chainAgreedPrestate.ChainID)
if err != nil {
......@@ -82,15 +85,14 @@ func runInteropProgram(logger log.Logger, bootInfo *boot.BootInfoInterop, l1Prei
return err
}
newPendingProgress := make([]types.OptimisticBlock, len(transitionState.PendingProgress)+1)
copy(newPendingProgress, transitionState.PendingProgress)
newPendingProgress[len(newPendingProgress)-1] = types.OptimisticBlock{
expectedPendingProgress = append(expectedPendingProgress, types.OptimisticBlock{
BlockHash: derivationResult.BlockHash,
OutputRoot: derivationResult.OutputRoot,
})
}
finalState := &types.TransitionState{
SuperRoot: transitionState.SuperRoot,
PendingProgress: newPendingProgress,
PendingProgress: expectedPendingProgress,
Step: transitionState.Step + 1,
}
expected, err := finalState.Hash()
......@@ -100,7 +102,7 @@ func runInteropProgram(logger log.Logger, bootInfo *boot.BootInfoInterop, l1Prei
if !validateClaim {
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 {
......
......@@ -2,6 +2,7 @@ package interop
import (
"fmt"
"math/big"
"testing"
"github.com/ethereum-optimism/optimism/op-node/chaincfg"
......@@ -21,46 +22,114 @@ import (
"github.com/stretchr/testify/require"
)
func TestDeriveBlockForFirstChainFromSuperchainRoot(t *testing.T) {
logger := testlog.Logger(t, log.LevelError)
rollupCfg := chaincfg.OPSepolia()
chainCfg := chainconfig.OPSepoliaChainConfig()
chain1Output := &eth.OutputV0{}
func setupTwoChains() (*staticConfigSource, *eth.SuperV1, stubTasks) {
rollupCfg1 := chaincfg.OPSepolia()
chainCfg1 := chainconfig.OPSepoliaChainConfig()
rollupCfg2 := *chaincfg.OPSepolia()
rollupCfg2.L2ChainID = new(big.Int).SetUint64(42)
chainCfg2 := *chainconfig.OPSepoliaChainConfig()
chainCfg2.ChainID = rollupCfg2.L2ChainID
agreedSuperRoot := &eth.SuperV1{
Timestamp: rollupCfg.Genesis.L2Time + 1234,
Chains: []eth.ChainIDAndOutput{{ChainID: rollupCfg.L2ChainID.Uint64(), Output: eth.OutputRoot(chain1Output)}},
Timestamp: rollupCfg1.Genesis.L2Time + 1234,
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))
l2PreimageOracle, _ := test.NewStubOracle(t)
l2PreimageOracle.TransitionStates[outputRootHash] = &types.TransitionState{SuperRoot: agreedSuperRoot.Marshal()}
tasks := stubTasks{
l2SafeHead: eth.L2BlockRef{
Number: 56,
Hash: common.Hash{0x11},
expectedIntermediateRoot := &types.TransitionState{
SuperRoot: agreedSuperRoot.Marshal(),
PendingProgress: []types.OptimisticBlock{
{BlockHash: tasksStub.blockHash, OutputRoot: tasksStub.outputRoot},
},
blockHash: common.Hash{0x22},
outputRoot: eth.Bytes32{0x66},
Step: 1,
}
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(),
PendingProgress: []types.OptimisticBlock{
{BlockHash: tasks.blockHash, OutputRoot: tasks.outputRoot},
{BlockHash: common.Hash{0xaa}, OutputRoot: eth.Bytes32{6: 22}},
},
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()
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{
AgreedPrestate: outputRootHash,
AgreedPrestate: agreedPrestate,
ClaimTimestamp: agreedSuperRoot.Timestamp + 1,
Claim: expectedClaim,
Configs: &staticConfigSource{
rollupCfgs: []*rollup.Config{rollupCfg},
chainConfigs: []*params.ChainConfig{chainCfg},
},
Configs: configSource,
}
err = runInteropProgram(logger, bootInfo, nil, l2PreimageOracle, true, &tasks)
err := runInteropProgram(logger, bootInfo, nil, l2PreimageOracle, true, &tasks)
require.NoError(t, err)
}
......
......@@ -25,5 +25,5 @@ func RunPreInteropProgram(logger log.Logger, bootInfo *boot.BootInfo, l1Preimage
if err != nil {
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(
}
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)
result, err := d.RunComplete()
if err != nil {
return DerivationResult{}, fmt.Errorf("failed to run program to completion: %w", err)
}
logger.Info("Derivation complete", "head", result)
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