Commit d6698a41 authored by mergify[bot]'s avatar mergify[bot] Committed by GitHub

Merge branch 'develop' into clabby/ctb/semver-assertions

parents 622ae465 9af277e9
......@@ -7,6 +7,7 @@ import (
"os"
"path/filepath"
"github.com/ethereum-optimism/optimism/op-challenger/config"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/log"
......@@ -22,20 +23,20 @@ type proofData struct {
ProofData hexutil.Bytes `json:"proof-data"`
}
type Executor interface {
type ProofGenerator interface {
// GenerateProof executes cannon to generate a proof at the specified trace index in dataDir.
GenerateProof(dataDir string, proofAt uint64) error
}
type CannonTraceProvider struct {
dir string
executor Executor
dir string
generator ProofGenerator
}
func NewCannonTraceProvider(logger log.Logger, dataDir string) *CannonTraceProvider {
func NewCannonTraceProvider(logger log.Logger, cfg *config.Config) *CannonTraceProvider {
return &CannonTraceProvider{
dir: dataDir,
executor: newExecutor(logger),
dir: cfg.CannonDatadir,
generator: NewExecutor(logger, cfg),
}
}
......@@ -76,7 +77,7 @@ func (p *CannonTraceProvider) loadProof(i uint64) (*proofData, error) {
path := filepath.Join(p.dir, proofsDir, fmt.Sprintf("%d.json", i))
file, err := os.Open(path)
if errors.Is(err, os.ErrNotExist) {
if err := p.executor.GenerateProof(p.dir, i); err != nil {
if err := p.generator.GenerateProof(p.dir, i); err != nil {
return nil, fmt.Errorf("generate cannon trace with proof at %v: %w", i, err)
}
// Try opening the file again now and it should exist.
......
......@@ -17,73 +17,73 @@ var testData embed.FS
func TestGet(t *testing.T) {
dataDir := setupTestData(t)
t.Run("ExistingProof", func(t *testing.T) {
provider, executor := setupWithTestData(dataDir)
provider, generator := setupWithTestData(dataDir)
value, err := provider.Get(0)
require.NoError(t, err)
require.Equal(t, common.HexToHash("0x45fd9aa59768331c726e719e76aa343e73123af888804604785ae19506e65e87"), value)
require.Empty(t, executor.generated)
require.Empty(t, generator.generated)
})
t.Run("ProofUnavailable", func(t *testing.T) {
provider, executor := setupWithTestData(dataDir)
provider, generator := setupWithTestData(dataDir)
_, err := provider.Get(7)
require.ErrorIs(t, err, os.ErrNotExist)
require.Contains(t, executor.generated, 7, "should have tried to generate the proof")
require.Contains(t, generator.generated, 7, "should have tried to generate the proof")
})
t.Run("MissingPostHash", func(t *testing.T) {
provider, executor := setupWithTestData(dataDir)
provider, generator := setupWithTestData(dataDir)
_, err := provider.Get(1)
require.ErrorContains(t, err, "missing post hash")
require.Empty(t, executor.generated)
require.Empty(t, generator.generated)
})
t.Run("IgnoreUnknownFields", func(t *testing.T) {
provider, executor := setupWithTestData(dataDir)
provider, generator := setupWithTestData(dataDir)
value, err := provider.Get(2)
require.NoError(t, err)
expected := common.HexToHash("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")
require.Equal(t, expected, value)
require.Empty(t, executor.generated)
require.Empty(t, generator.generated)
})
}
func TestGetPreimage(t *testing.T) {
dataDir := setupTestData(t)
t.Run("ExistingProof", func(t *testing.T) {
provider, executor := setupWithTestData(dataDir)
provider, generator := setupWithTestData(dataDir)
value, proof, err := provider.GetPreimage(0)
require.NoError(t, err)
expected := common.Hex2Bytes("b8f068de604c85ea0e2acd437cdb47add074a2d70b81d018390c504b71fe26f400000000000000000000000000000000000000000000000000000000000000000000000000")
require.Equal(t, expected, value)
expectedProof := common.Hex2Bytes("08028e3c0000000000000000000000003c01000a24210b7c00200008000000008fa40004")
require.Equal(t, expectedProof, proof)
require.Empty(t, executor.generated)
require.Empty(t, generator.generated)
})
t.Run("ProofUnavailable", func(t *testing.T) {
provider, executor := setupWithTestData(dataDir)
provider, generator := setupWithTestData(dataDir)
_, _, err := provider.GetPreimage(7)
require.ErrorIs(t, err, os.ErrNotExist)
require.Contains(t, executor.generated, 7, "should have tried to generate the proof")
require.Contains(t, generator.generated, 7, "should have tried to generate the proof")
})
t.Run("MissingStateData", func(t *testing.T) {
provider, executor := setupWithTestData(dataDir)
provider, generator := setupWithTestData(dataDir)
_, _, err := provider.GetPreimage(1)
require.ErrorContains(t, err, "missing state data")
require.Empty(t, executor.generated)
require.Empty(t, generator.generated)
})
t.Run("IgnoreUnknownFields", func(t *testing.T) {
provider, executor := setupWithTestData(dataDir)
provider, generator := setupWithTestData(dataDir)
value, proof, err := provider.GetPreimage(2)
require.NoError(t, err)
expected := common.Hex2Bytes("cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc")
require.Equal(t, expected, value)
expectedProof := common.Hex2Bytes("dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd")
require.Equal(t, expectedProof, proof)
require.Empty(t, executor.generated)
require.Empty(t, generator.generated)
})
}
......@@ -103,19 +103,19 @@ func setupTestData(t *testing.T) string {
return dataDir
}
func setupWithTestData(dataDir string) (*CannonTraceProvider, *stubExecutor) {
executor := &stubExecutor{}
func setupWithTestData(dataDir string) (*CannonTraceProvider, *stubGenerator) {
generator := &stubGenerator{}
return &CannonTraceProvider{
dir: dataDir,
executor: executor,
}, executor
dir: dataDir,
generator: generator,
}, generator
}
type stubExecutor struct {
type stubGenerator struct {
generated []int // Using int makes assertions easier
}
func (e *stubExecutor) GenerateProof(dir string, i uint64) error {
func (e *stubGenerator) GenerateProof(dir string, i uint64) error {
e.generated = append(e.generated, int(i))
return nil
}
package cannon
import (
"errors"
"fmt"
"os"
"path/filepath"
"regexp"
"strconv"
"github.com/ethereum-optimism/optimism/op-challenger/config"
"github.com/ethereum/go-ethereum/log"
)
type executor struct {
logger log.Logger
const snapsDir = "snapshots"
var snapshotNameRegexp = regexp.MustCompile(`^[0-9]+\.json$`)
const snapshotFrequency = 10_000
type snapshotSelect func(logger log.Logger, dir string, absolutePreState string, i uint64) (string, error)
type Executor struct {
logger log.Logger
l1 string
l2 string
cannon string
absolutePreState string
dataDir string
selectSnapshot snapshotSelect
}
func newExecutor(logger log.Logger) Executor {
return &executor{
logger: logger,
func NewExecutor(logger log.Logger, cfg *config.Config) *Executor {
return &Executor{
logger: logger,
l1: cfg.L1EthRpc,
l2: cfg.CannonL2,
cannon: cfg.CannonBin,
absolutePreState: cfg.CannonAbsolutePreState,
dataDir: cfg.CannonDatadir,
selectSnapshot: findStartingSnapshot,
}
}
func (e *executor) GenerateProof(dir string, i uint64) error {
return fmt.Errorf("please execute cannon with --proof-at %v --proof-fmt %v/%v/%%d.json", i, dir, proofsDir)
func (e *Executor) GenerateProof(dir string, i uint64) error {
start, err := e.selectSnapshot(e.logger, filepath.Join(e.dataDir, snapsDir), e.absolutePreState, i)
if err != nil {
return fmt.Errorf("find starting snapshot: %w", err)
}
return fmt.Errorf("please execute cannon with --input %v --proof-at %v --proof-fmt %v/%v/%%d.json --snapshot-at %%%d --snapshot-fmt '%v/%v/%%d.json",
start, i, dir, proofsDir, snapshotFrequency, dir, snapsDir)
}
// findStartingSnapshot finds the closest snapshot before the specified traceIndex in snapDir.
// If no suitable snapshot can be found it returns absolutePreState.
func findStartingSnapshot(logger log.Logger, snapDir string, absolutePreState string, traceIndex uint64) (string, error) {
// Find the closest snapshot to start from
entries, err := os.ReadDir(snapDir)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
return absolutePreState, nil
}
return "", fmt.Errorf("list snapshots in %v: %w", snapDir, err)
}
bestSnap := uint64(0)
for _, entry := range entries {
if entry.IsDir() {
logger.Warn("Unexpected directory in snapshots dir: %v/%v", snapDir, entry.Name())
continue
}
name := entry.Name()
if !snapshotNameRegexp.MatchString(name) {
logger.Warn("Unexpected file in snapshots dir: %v/%v", snapDir, entry.Name())
continue
}
index, err := strconv.ParseUint(name[0:len(name)-len(".json")], 10, 64)
if err != nil {
logger.Error("Unable to parse trace index of snapshot file: %v/%v", snapDir, entry.Name())
continue
}
if index > bestSnap && index < traceIndex {
bestSnap = index
}
}
if bestSnap == 0 {
return absolutePreState, nil
}
startFrom := fmt.Sprintf("%v/%v.json", snapDir, bestSnap)
return startFrom, nil
}
package cannon
import (
"fmt"
"os"
"path/filepath"
"testing"
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require"
)
const execTestCannonPrestate = "/foo/pre.json"
func TestFindStartingSnapshot(t *testing.T) {
logger := testlog.Logger(t, log.LvlInfo)
withSnapshots := func(t *testing.T, files ...string) string {
dir := t.TempDir()
for _, file := range files {
require.NoError(t, os.WriteFile(fmt.Sprintf("%v/%v", dir, file), nil, 0o644))
}
return dir
}
t.Run("UsePrestateWhenSnapshotsDirDoesNotExist", func(t *testing.T) {
dir := t.TempDir()
snapshot, err := findStartingSnapshot(logger, filepath.Join(dir, "doesNotExist"), execTestCannonPrestate, 1200)
require.NoError(t, err)
require.Equal(t, execTestCannonPrestate, snapshot)
})
t.Run("UsePrestateWhenSnapshotsDirEmpty", func(t *testing.T) {
dir := withSnapshots(t)
snapshot, err := findStartingSnapshot(logger, dir, execTestCannonPrestate, 1200)
require.NoError(t, err)
require.Equal(t, execTestCannonPrestate, snapshot)
})
t.Run("UsePrestateWhenNoSnapshotBeforeTraceIndex", func(t *testing.T) {
dir := withSnapshots(t, "100.json", "200.json")
snapshot, err := findStartingSnapshot(logger, dir, execTestCannonPrestate, 99)
require.NoError(t, err)
require.Equal(t, execTestCannonPrestate, snapshot)
snapshot, err = findStartingSnapshot(logger, dir, execTestCannonPrestate, 100)
require.NoError(t, err)
require.Equal(t, execTestCannonPrestate, snapshot)
})
t.Run("UseClosestAvailableSnapshot", func(t *testing.T) {
dir := withSnapshots(t, "100.json", "123.json", "250.json")
snapshot, err := findStartingSnapshot(logger, dir, execTestCannonPrestate, 101)
require.NoError(t, err)
require.Equal(t, filepath.Join(dir, "100.json"), snapshot)
snapshot, err = findStartingSnapshot(logger, dir, execTestCannonPrestate, 123)
require.NoError(t, err)
require.Equal(t, filepath.Join(dir, "100.json"), snapshot)
snapshot, err = findStartingSnapshot(logger, dir, execTestCannonPrestate, 124)
require.NoError(t, err)
require.Equal(t, filepath.Join(dir, "123.json"), snapshot)
snapshot, err = findStartingSnapshot(logger, dir, execTestCannonPrestate, 256)
require.NoError(t, err)
require.Equal(t, filepath.Join(dir, "250.json"), snapshot)
})
t.Run("IgnoreDirectories", func(t *testing.T) {
dir := withSnapshots(t, "100.json")
require.NoError(t, os.Mkdir(filepath.Join(dir, "120.json"), 0o777))
snapshot, err := findStartingSnapshot(logger, dir, execTestCannonPrestate, 150)
require.NoError(t, err)
require.Equal(t, filepath.Join(dir, "100.json"), snapshot)
})
t.Run("IgnoreUnexpectedFiles", func(t *testing.T) {
dir := withSnapshots(t, ".file", "100.json", "foo", "bar.json")
snapshot, err := findStartingSnapshot(logger, dir, execTestCannonPrestate, 150)
require.NoError(t, err)
require.Equal(t, filepath.Join(dir, "100.json"), snapshot)
})
}
......@@ -56,7 +56,7 @@ func NewService(ctx context.Context, logger log.Logger, cfg *config.Config) (*se
var trace types.TraceProvider
switch cfg.TraceType {
case config.TraceTypeCannon:
trace = cannon.NewCannonTraceProvider(logger, cfg.CannonDatadir)
trace = cannon.NewCannonTraceProvider(logger, cfg)
case config.TraceTypeAlphabet:
trace = alphabet.NewAlphabetProvider(cfg.AlphabetTrace, uint64(cfg.GameDepth))
default:
......
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