Skip to content

Commit

Permalink
Merge branch 'develop' into clabby/ctb/semver-assertions
Browse files Browse the repository at this point in the history
  • Loading branch information
mergify[bot] committed Jul 26, 2023
2 parents 622ae46 + 9af277e commit d6698a4
Show file tree
Hide file tree
Showing 5 changed files with 195 additions and 38 deletions.
15 changes: 8 additions & 7 deletions op-challenger/fault/cannon/cannon_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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),
}
}

Expand Down Expand Up @@ -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.
Expand Down
46 changes: 23 additions & 23 deletions op-challenger/fault/cannon/cannon_provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
})
}

Expand All @@ -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
}
84 changes: 77 additions & 7 deletions op-challenger/fault/cannon/executor.go
Original file line number Diff line number Diff line change
@@ -1,21 +1,91 @@
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
}
86 changes: 86 additions & 0 deletions op-challenger/fault/cannon/executor_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
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)
})
}
2 changes: 1 addition & 1 deletion op-challenger/fault/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down

0 comments on commit d6698a4

Please sign in to comment.