Commit 258a480d authored by protolambda's avatar protolambda Committed by GitHub

op-program: encapsulate ValidateClaim (#10960)

parent ea4d1fdb
...@@ -6,20 +6,22 @@ import ( ...@@ -6,20 +6,22 @@ import (
"testing" "testing"
"time" "time"
"github.com/stretchr/testify/require"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum-optimism/optimism/op-chain-ops/genesis" "github.com/ethereum-optimism/optimism/op-chain-ops/genesis"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/geth" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/geth"
"github.com/ethereum-optimism/optimism/op-program/client/driver" "github.com/ethereum-optimism/optimism/op-program/client/claim"
opp "github.com/ethereum-optimism/optimism/op-program/host" opp "github.com/ethereum-optimism/optimism/op-program/host"
oppconf "github.com/ethereum-optimism/optimism/op-program/host/config" oppconf "github.com/ethereum-optimism/optimism/op-program/host/config"
"github.com/ethereum-optimism/optimism/op-service/client" "github.com/ethereum-optimism/optimism/op-service/client"
"github.com/ethereum-optimism/optimism/op-service/sources" "github.com/ethereum-optimism/optimism/op-service/sources"
"github.com/ethereum-optimism/optimism/op-service/testlog" "github.com/ethereum-optimism/optimism/op-service/testlog"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rpc"
"github.com/stretchr/testify/require"
) )
func TestVerifyL2OutputRoot(t *testing.T) { func TestVerifyL2OutputRoot(t *testing.T) {
...@@ -320,7 +322,7 @@ func testFaultProofProgramScenario(t *testing.T, ctx context.Context, sys *Syste ...@@ -320,7 +322,7 @@ func testFaultProofProgramScenario(t *testing.T, ctx context.Context, sys *Syste
if s.Detached { if s.Detached {
require.Error(t, err, "exit status 1") require.Error(t, err, "exit status 1")
} else { } else {
require.ErrorIs(t, err, driver.ErrClaimNotValid) require.ErrorIs(t, err, claim.ErrClaimNotValid)
} }
} }
......
package claim
import (
"context"
"errors"
"fmt"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/op-service/eth"
)
var ErrClaimNotValid = errors.New("invalid claim")
type L2Source interface {
L2BlockRefByLabel(ctx context.Context, label eth.BlockLabel) (eth.L2BlockRef, error)
L2OutputRoot(uint64) (eth.Bytes32, error)
}
func ValidateClaim(log log.Logger, l2ClaimBlockNum uint64, claimedOutputRoot eth.Bytes32, src L2Source) error {
l2Head, err := src.L2BlockRefByLabel(context.Background(), eth.Safe)
if err != nil {
return fmt.Errorf("cannot retrieve safe head: %w", err)
}
outputRoot, err := src.L2OutputRoot(min(l2ClaimBlockNum, l2Head.Number))
if err != nil {
return fmt.Errorf("calculate L2 output root: %w", err)
}
log.Info("Validating claim", "head", l2Head, "output", outputRoot, "claim", claimedOutputRoot)
if claimedOutputRoot != outputRoot {
return fmt.Errorf("%w: claim: %v actual: %v", ErrClaimNotValid, claimedOutputRoot, outputRoot)
}
return nil
}
package claim
import (
"context"
"errors"
"testing"
"github.com/stretchr/testify/require"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-service/testlog"
)
type mockL2 struct {
safeL2 eth.L2BlockRef
safeL2Err error
outputRoot eth.Bytes32
outputRootErr error
requestedOutputRoot uint64
}
func (m *mockL2) L2BlockRefByLabel(ctx context.Context, label eth.BlockLabel) (eth.L2BlockRef, error) {
if label != eth.Safe {
panic("unexpected usage")
}
if m.safeL2Err != nil {
return eth.L2BlockRef{}, m.safeL2Err
}
return m.safeL2, nil
}
func (m *mockL2) L2OutputRoot(u uint64) (eth.Bytes32, error) {
m.requestedOutputRoot = u
if m.outputRootErr != nil {
return eth.Bytes32{}, m.outputRootErr
}
return m.outputRoot, nil
}
var _ L2Source = (*mockL2)(nil)
func TestValidateClaim(t *testing.T) {
t.Run("Valid", func(t *testing.T) {
expected := eth.Bytes32{0x11}
l2 := &mockL2{
outputRoot: expected,
}
logger := testlog.Logger(t, log.LevelError)
err := ValidateClaim(logger, uint64(0), expected, l2)
require.NoError(t, err)
})
t.Run("Valid-PriorToSafeHead", func(t *testing.T) {
expected := eth.Bytes32{0x11}
l2 := &mockL2{
outputRoot: expected,
safeL2: eth.L2BlockRef{
Number: 10,
},
}
logger := testlog.Logger(t, log.LevelError)
err := ValidateClaim(logger, uint64(20), expected, l2)
require.NoError(t, err)
require.Equal(t, uint64(10), l2.requestedOutputRoot)
})
t.Run("Invalid", func(t *testing.T) {
l2 := &mockL2{
outputRoot: eth.Bytes32{0x22},
}
logger := testlog.Logger(t, log.LevelError)
err := ValidateClaim(logger, uint64(0), eth.Bytes32{0x11}, l2)
require.ErrorIs(t, err, ErrClaimNotValid)
})
t.Run("Invalid-PriorToSafeHead", func(t *testing.T) {
l2 := &mockL2{
outputRoot: eth.Bytes32{0x22},
safeL2: eth.L2BlockRef{Number: 10},
}
logger := testlog.Logger(t, log.LevelError)
err := ValidateClaim(logger, uint64(20), eth.Bytes32{0x55}, l2)
require.ErrorIs(t, err, ErrClaimNotValid)
require.Equal(t, uint64(10), l2.requestedOutputRoot)
})
t.Run("Error-safe-head", func(t *testing.T) {
expectedErr := errors.New("boom")
l2 := &mockL2{
outputRoot: eth.Bytes32{0x11},
safeL2: eth.L2BlockRef{Number: 10},
safeL2Err: expectedErr,
}
logger := testlog.Logger(t, log.LevelError)
err := ValidateClaim(logger, uint64(0), eth.Bytes32{0x11}, l2)
require.ErrorIs(t, err, expectedErr)
})
t.Run("Error-output-root", func(t *testing.T) {
expectedErr := errors.New("boom")
l2 := &mockL2{
outputRoot: eth.Bytes32{0x11},
outputRootErr: expectedErr,
safeL2: eth.L2BlockRef{Number: 10},
}
logger := testlog.Logger(t, log.LevelError)
err := ValidateClaim(logger, uint64(0), eth.Bytes32{0x11}, l2)
require.ErrorIs(t, err, expectedErr)
})
}
...@@ -19,8 +19,6 @@ import ( ...@@ -19,8 +19,6 @@ import (
"github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-service/eth"
) )
var ErrClaimNotValid = errors.New("invalid claim")
type Derivation interface { type Derivation interface {
Step(ctx context.Context) error Step(ctx context.Context) error
} }
...@@ -154,16 +152,3 @@ func (d *Driver) Step(ctx context.Context) error { ...@@ -154,16 +152,3 @@ func (d *Driver) Step(ctx context.Context) error {
func (d *Driver) SafeHead() eth.L2BlockRef { func (d *Driver) SafeHead() eth.L2BlockRef {
return d.deriver.SafeL2Head() return d.deriver.SafeL2Head()
} }
func (d *Driver) ValidateClaim(l2ClaimBlockNum uint64, claimedOutputRoot eth.Bytes32) error {
l2Head := d.SafeHead()
outputRoot, err := d.l2OutputRoot(min(l2ClaimBlockNum, l2Head.Number))
if err != nil {
return fmt.Errorf("calculate L2 output root: %w", err)
}
d.logger.Info("Validating claim", "head", l2Head, "output", outputRoot, "claim", claimedOutputRoot)
if claimedOutputRoot != outputRoot {
return fmt.Errorf("%w: claim: %v actual: %v", ErrClaimNotValid, claimedOutputRoot, outputRoot)
}
return nil
}
...@@ -69,63 +69,6 @@ func TestNoError(t *testing.T) { ...@@ -69,63 +69,6 @@ func TestNoError(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
} }
func TestValidateClaim(t *testing.T) {
t.Run("Valid", func(t *testing.T) {
driver := createDriver(t, io.EOF)
expected := eth.Bytes32{0x11}
driver.l2OutputRoot = func(_ uint64) (eth.Bytes32, error) {
return expected, nil
}
err := driver.ValidateClaim(uint64(0), expected)
require.NoError(t, err)
})
t.Run("Valid-PriorToSafeHead", func(t *testing.T) {
driver := createDriverWithNextBlock(t, io.EOF, 10)
expected := eth.Bytes32{0x11}
requestedOutputRoot := uint64(0)
driver.l2OutputRoot = func(blockNum uint64) (eth.Bytes32, error) {
requestedOutputRoot = blockNum
return expected, nil
}
err := driver.ValidateClaim(uint64(20), expected)
require.NoError(t, err)
require.Equal(t, uint64(10), requestedOutputRoot)
})
t.Run("Invalid", func(t *testing.T) {
driver := createDriver(t, io.EOF)
driver.l2OutputRoot = func(_ uint64) (eth.Bytes32, error) {
return eth.Bytes32{0x22}, nil
}
err := driver.ValidateClaim(uint64(0), eth.Bytes32{0x11})
require.ErrorIs(t, err, ErrClaimNotValid)
})
t.Run("Invalid-PriorToSafeHead", func(t *testing.T) {
driver := createDriverWithNextBlock(t, io.EOF, 10)
expected := eth.Bytes32{0x11}
requestedOutputRoot := uint64(0)
driver.l2OutputRoot = func(blockNum uint64) (eth.Bytes32, error) {
requestedOutputRoot = blockNum
return expected, nil
}
err := driver.ValidateClaim(uint64(20), eth.Bytes32{0x55})
require.ErrorIs(t, err, ErrClaimNotValid)
require.Equal(t, uint64(10), requestedOutputRoot)
})
t.Run("Error", func(t *testing.T) {
driver := createDriver(t, io.EOF)
expectedErr := errors.New("boom")
driver.l2OutputRoot = func(_ uint64) (eth.Bytes32, error) {
return eth.Bytes32{}, expectedErr
}
err := driver.ValidateClaim(uint64(0), eth.Bytes32{0x11})
require.ErrorIs(t, err, expectedErr)
})
}
func createDriver(t *testing.T, derivationResult error) *Driver { func createDriver(t *testing.T, derivationResult error) *Driver {
return createDriverWithNextBlock(t, derivationResult, 0) return createDriverWithNextBlock(t, derivationResult, 0)
} }
......
...@@ -13,6 +13,7 @@ import ( ...@@ -13,6 +13,7 @@ import (
"github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-node/rollup"
preimage "github.com/ethereum-optimism/optimism/op-preimage" preimage "github.com/ethereum-optimism/optimism/op-preimage"
"github.com/ethereum-optimism/optimism/op-program/client/claim"
cldr "github.com/ethereum-optimism/optimism/op-program/client/driver" cldr "github.com/ethereum-optimism/optimism/op-program/client/driver"
"github.com/ethereum-optimism/optimism/op-program/client/l1" "github.com/ethereum-optimism/optimism/op-program/client/l1"
"github.com/ethereum-optimism/optimism/op-program/client/l2" "github.com/ethereum-optimism/optimism/op-program/client/l2"
...@@ -26,7 +27,7 @@ func Main(logger log.Logger) { ...@@ -26,7 +27,7 @@ func Main(logger log.Logger) {
log.Info("Starting fault proof program client") log.Info("Starting fault proof program client")
preimageOracle := CreatePreimageChannel() preimageOracle := CreatePreimageChannel()
preimageHinter := CreateHinterChannel() preimageHinter := CreateHinterChannel()
if err := RunProgram(logger, preimageOracle, preimageHinter); errors.Is(err, cldr.ErrClaimNotValid) { if err := RunProgram(logger, preimageOracle, preimageHinter); errors.Is(err, claim.ErrClaimNotValid) {
log.Error("Claim is invalid", "err", err) log.Error("Claim is invalid", "err", err)
os.Exit(1) os.Exit(1)
} else if err != nil { } else if err != nil {
...@@ -79,7 +80,7 @@ func runDerivation(logger log.Logger, cfg *rollup.Config, l2Cfg *params.ChainCon ...@@ -79,7 +80,7 @@ func runDerivation(logger log.Logger, cfg *rollup.Config, l2Cfg *params.ChainCon
return err return err
} }
} }
return d.ValidateClaim(l2ClaimBlockNum, eth.Bytes32(l2Claim)) return claim.ValidateClaim(logger, l2ClaimBlockNum, eth.Bytes32(l2Claim), l2Source)
} }
func CreateHinterChannel() oppio.FileChannel { func CreateHinterChannel() oppio.FileChannel {
......
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