Commit 40750a58 authored by Adrian Sutton's avatar Adrian Sutton Committed by GitHub

op-challenger: Support binary and JSON snapshots (#11754)

* op-challenger: Support loading json or binary cannon states.

* op-challenger: Use binary cannon snapshots

* op-challenger: Support downloading prestates in multiple formats.

* op-challenger: Verify newly downloaded snapshots.

* op-e2e: Update test to handle binary snapshots correctly.

* op-e2e: Remove unused parameter

* op-challenger: Add more varied data to the test cannon state.

* op-challenger: Add more varied data to the test cannon state.
parent 9170e93e
...@@ -139,13 +139,14 @@ func NewConfig( ...@@ -139,13 +139,14 @@ func NewConfig(
Datadir: datadir, Datadir: datadir,
Cannon: vm.Config{ Cannon: vm.Config{
VmType: types.TraceTypeCannon, VmType: types.TraceTypeCannon,
L1: l1EthRpc, L1: l1EthRpc,
L1Beacon: l1BeaconApi, L1Beacon: l1BeaconApi,
L2: l2EthRpc, L2: l2EthRpc,
SnapshotFreq: DefaultCannonSnapshotFreq, SnapshotFreq: DefaultCannonSnapshotFreq,
InfoFreq: DefaultCannonInfoFreq, InfoFreq: DefaultCannonInfoFreq,
DebugInfo: true, DebugInfo: true,
BinarySnapshots: true,
}, },
Asterisc: vm.Config{ Asterisc: vm.Config{
VmType: types.TraceTypeAsterisc, VmType: types.TraceTypeAsterisc,
......
...@@ -578,6 +578,7 @@ func NewConfigFromCLI(ctx *cli.Context, logger log.Logger) (*config.Config, erro ...@@ -578,6 +578,7 @@ func NewConfigFromCLI(ctx *cli.Context, logger log.Logger) (*config.Config, erro
SnapshotFreq: ctx.Uint(CannonSnapshotFreqFlag.Name), SnapshotFreq: ctx.Uint(CannonSnapshotFreqFlag.Name),
InfoFreq: ctx.Uint(CannonInfoFreqFlag.Name), InfoFreq: ctx.Uint(CannonInfoFreqFlag.Name),
DebugInfo: true, DebugInfo: true,
BinarySnapshots: true,
}, },
CannonAbsolutePreState: ctx.String(CannonPreStateFlag.Name), CannonAbsolutePreState: ctx.String(CannonPreStateFlag.Name),
CannonAbsolutePreStateBaseURL: cannonPrestatesURL, CannonAbsolutePreStateBaseURL: cannonPrestatesURL,
......
...@@ -48,16 +48,18 @@ type RegisterTask struct { ...@@ -48,16 +48,18 @@ type RegisterTask struct {
} }
func NewCannonRegisterTask(gameType faultTypes.GameType, cfg *config.Config, m caching.Metrics, serverExecutor vm.OracleServerExecutor) *RegisterTask { func NewCannonRegisterTask(gameType faultTypes.GameType, cfg *config.Config, m caching.Metrics, serverExecutor vm.OracleServerExecutor) *RegisterTask {
stateConverter := cannon.NewStateConverter()
return &RegisterTask{ return &RegisterTask{
gameType: gameType, gameType: gameType,
getPrestateProvider: cachePrestates( getPrestateProvider: cachePrestates(
gameType, gameType,
stateConverter,
m, m,
cfg.CannonAbsolutePreStateBaseURL, cfg.CannonAbsolutePreStateBaseURL,
cfg.CannonAbsolutePreState, cfg.CannonAbsolutePreState,
filepath.Join(cfg.Datadir, "cannon-prestates"), filepath.Join(cfg.Datadir, "cannon-prestates"),
func(path string) faultTypes.PrestateProvider { func(path string) faultTypes.PrestateProvider {
return vm.NewPrestateProvider(path, cannon.NewStateConverter()) return vm.NewPrestateProvider(path, stateConverter)
}), }),
newTraceAccessor: func( newTraceAccessor: func(
logger log.Logger, logger log.Logger,
...@@ -78,16 +80,18 @@ func NewCannonRegisterTask(gameType faultTypes.GameType, cfg *config.Config, m c ...@@ -78,16 +80,18 @@ func NewCannonRegisterTask(gameType faultTypes.GameType, cfg *config.Config, m c
} }
func NewAsteriscRegisterTask(gameType faultTypes.GameType, cfg *config.Config, m caching.Metrics, serverExecutor vm.OracleServerExecutor) *RegisterTask { func NewAsteriscRegisterTask(gameType faultTypes.GameType, cfg *config.Config, m caching.Metrics, serverExecutor vm.OracleServerExecutor) *RegisterTask {
stateConverter := asterisc.NewStateConverter()
return &RegisterTask{ return &RegisterTask{
gameType: gameType, gameType: gameType,
getPrestateProvider: cachePrestates( getPrestateProvider: cachePrestates(
gameType, gameType,
stateConverter,
m, m,
cfg.AsteriscAbsolutePreStateBaseURL, cfg.AsteriscAbsolutePreStateBaseURL,
cfg.AsteriscAbsolutePreState, cfg.AsteriscAbsolutePreState,
filepath.Join(cfg.Datadir, "asterisc-prestates"), filepath.Join(cfg.Datadir, "asterisc-prestates"),
func(path string) faultTypes.PrestateProvider { func(path string) faultTypes.PrestateProvider {
return vm.NewPrestateProvider(path, asterisc.NewStateConverter()) return vm.NewPrestateProvider(path, stateConverter)
}), }),
newTraceAccessor: func( newTraceAccessor: func(
logger log.Logger, logger log.Logger,
...@@ -108,16 +112,18 @@ func NewAsteriscRegisterTask(gameType faultTypes.GameType, cfg *config.Config, m ...@@ -108,16 +112,18 @@ func NewAsteriscRegisterTask(gameType faultTypes.GameType, cfg *config.Config, m
} }
func NewAsteriscKonaRegisterTask(gameType faultTypes.GameType, cfg *config.Config, m caching.Metrics, serverExecutor vm.OracleServerExecutor) *RegisterTask { func NewAsteriscKonaRegisterTask(gameType faultTypes.GameType, cfg *config.Config, m caching.Metrics, serverExecutor vm.OracleServerExecutor) *RegisterTask {
stateConverter := asterisc.NewStateConverter()
return &RegisterTask{ return &RegisterTask{
gameType: gameType, gameType: gameType,
getPrestateProvider: cachePrestates( getPrestateProvider: cachePrestates(
gameType, gameType,
stateConverter,
m, m,
cfg.AsteriscKonaAbsolutePreStateBaseURL, cfg.AsteriscKonaAbsolutePreStateBaseURL,
cfg.AsteriscKonaAbsolutePreState, cfg.AsteriscKonaAbsolutePreState,
filepath.Join(cfg.Datadir, "asterisc-kona-prestates"), filepath.Join(cfg.Datadir, "asterisc-kona-prestates"),
func(path string) faultTypes.PrestateProvider { func(path string) faultTypes.PrestateProvider {
return vm.NewPrestateProvider(path, asterisc.NewStateConverter()) return vm.NewPrestateProvider(path, stateConverter)
}), }),
newTraceAccessor: func( newTraceAccessor: func(
logger log.Logger, logger log.Logger,
...@@ -162,13 +168,14 @@ func NewAlphabetRegisterTask(gameType faultTypes.GameType) *RegisterTask { ...@@ -162,13 +168,14 @@ func NewAlphabetRegisterTask(gameType faultTypes.GameType) *RegisterTask {
func cachePrestates( func cachePrestates(
gameType faultTypes.GameType, gameType faultTypes.GameType,
stateConverter vm.StateConverter,
m caching.Metrics, m caching.Metrics,
prestateBaseURL *url.URL, prestateBaseURL *url.URL,
preStatePath string, preStatePath string,
prestateDir string, prestateDir string,
newPrestateProvider func(path string) faultTypes.PrestateProvider, newPrestateProvider func(path string) faultTypes.PrestateProvider,
) func(prestateHash common.Hash) (faultTypes.PrestateProvider, error) { ) func(prestateHash common.Hash) (faultTypes.PrestateProvider, error) {
prestateSource := prestates.NewPrestateSource(prestateBaseURL, preStatePath, prestateDir) prestateSource := prestates.NewPrestateSource(prestateBaseURL, preStatePath, prestateDir, stateConverter)
prestateProviderCache := prestates.NewPrestateProviderCache(m, fmt.Sprintf("prestates-%v", gameType), func(prestateHash common.Hash) (faultTypes.PrestateProvider, error) { prestateProviderCache := prestates.NewPrestateProviderCache(m, fmt.Sprintf("prestates-%v", gameType), func(prestateHash common.Hash) (faultTypes.PrestateProvider, error) {
prestatePath, err := prestateSource.PrestatePath(prestateHash) prestatePath, err := prestateSource.PrestatePath(prestateHash)
if err != nil { if err != nil {
......
...@@ -28,6 +28,7 @@ type AsteriscTraceProvider struct { ...@@ -28,6 +28,7 @@ type AsteriscTraceProvider struct {
gameDepth types.Depth gameDepth types.Depth
preimageLoader *utils.PreimageLoader preimageLoader *utils.PreimageLoader
stateConverter vm.StateConverter stateConverter vm.StateConverter
cfg vm.Config
types.PrestateProvider types.PrestateProvider
...@@ -48,6 +49,7 @@ func NewTraceProvider(logger log.Logger, m vm.Metricer, cfg vm.Config, vmCfg vm. ...@@ -48,6 +49,7 @@ func NewTraceProvider(logger log.Logger, m vm.Metricer, cfg vm.Config, vmCfg vm.
}), }),
PrestateProvider: prestateProvider, PrestateProvider: prestateProvider,
stateConverter: NewStateConverter(), stateConverter: NewStateConverter(),
cfg: cfg,
} }
} }
...@@ -122,7 +124,7 @@ func (p *AsteriscTraceProvider) loadProof(ctx context.Context, i uint64) (*utils ...@@ -122,7 +124,7 @@ func (p *AsteriscTraceProvider) loadProof(ctx context.Context, i uint64) (*utils
file, err = ioutil.OpenDecompressed(path) file, err = ioutil.OpenDecompressed(path)
if errors.Is(err, os.ErrNotExist) { if errors.Is(err, os.ErrNotExist) {
// Expected proof wasn't generated, check if we reached the end of execution // Expected proof wasn't generated, check if we reached the end of execution
proof, step, exited, err := p.stateConverter.ConvertStateToProof(filepath.Join(p.dir, vm.FinalState)) proof, step, exited, err := p.stateConverter.ConvertStateToProof(vm.FinalStatePath(p.dir, p.cfg.BinarySnapshots))
if err != nil { if err != nil {
return nil, err return nil, err
} }
...@@ -171,6 +173,7 @@ func NewTraceProviderForTest(logger log.Logger, m vm.Metricer, cfg *config.Confi ...@@ -171,6 +173,7 @@ func NewTraceProviderForTest(logger log.Logger, m vm.Metricer, cfg *config.Confi
return kvstore.NewFileKV(vm.PreimageDir(dir)) return kvstore.NewFileKV(vm.PreimageDir(dir))
}), }),
stateConverter: NewStateConverter(), stateConverter: NewStateConverter(),
cfg: cfg.Asterisc,
} }
return &AsteriscTraceProviderForTest{p} return &AsteriscTraceProviderForTest{p}
} }
...@@ -181,7 +184,7 @@ func (p *AsteriscTraceProviderForTest) FindStep(ctx context.Context, start uint6 ...@@ -181,7 +184,7 @@ func (p *AsteriscTraceProviderForTest) FindStep(ctx context.Context, start uint6
return 0, fmt.Errorf("generate asterisc trace (until preimage read): %w", err) return 0, fmt.Errorf("generate asterisc trace (until preimage read): %w", err)
} }
// Load the step from the state asterisc finished with // Load the step from the state asterisc finished with
_, step, exited, err := p.stateConverter.ConvertStateToProof(filepath.Join(p.dir, vm.FinalState)) _, step, exited, err := p.stateConverter.ConvertStateToProof(vm.FinalStatePath(p.dir, p.cfg.BinarySnapshots))
if err != nil { if err != nil {
return 0, fmt.Errorf("failed to load final state: %w", err) return 0, fmt.Errorf("failed to load final state: %w", err)
} }
......
...@@ -243,7 +243,7 @@ func (e *stubGenerator) GenerateProof(ctx context.Context, dir string, i uint64) ...@@ -243,7 +243,7 @@ func (e *stubGenerator) GenerateProof(ctx context.Context, dir string, i uint64)
var err error var err error
if e.finalState != nil && e.finalState.Step <= i { if e.finalState != nil && e.finalState.Step <= i {
// Requesting a trace index past the end of the trace // Requesting a trace index past the end of the trace
proofFile = filepath.Join(dir, vm.FinalState) proofFile = vm.FinalStatePath(dir, false)
data, err = json.Marshal(e.finalState) data, err = json.Marshal(e.finalState)
if err != nil { if err != nil {
return err return err
......
...@@ -29,6 +29,7 @@ type CannonTraceProvider struct { ...@@ -29,6 +29,7 @@ type CannonTraceProvider struct {
gameDepth types.Depth gameDepth types.Depth
preimageLoader *utils.PreimageLoader preimageLoader *utils.PreimageLoader
stateConverter vm.StateConverter stateConverter vm.StateConverter
cfg vm.Config
types.PrestateProvider types.PrestateProvider
...@@ -49,6 +50,7 @@ func NewTraceProvider(logger log.Logger, m vm.Metricer, cfg vm.Config, vmCfg vm. ...@@ -49,6 +50,7 @@ func NewTraceProvider(logger log.Logger, m vm.Metricer, cfg vm.Config, vmCfg vm.
}), }),
PrestateProvider: prestateProvider, PrestateProvider: prestateProvider,
stateConverter: &StateConverter{}, stateConverter: &StateConverter{},
cfg: cfg,
} }
} }
...@@ -122,7 +124,7 @@ func (p *CannonTraceProvider) loadProof(ctx context.Context, i uint64) (*utils.P ...@@ -122,7 +124,7 @@ func (p *CannonTraceProvider) loadProof(ctx context.Context, i uint64) (*utils.P
// Try opening the file again now and it should exist. // Try opening the file again now and it should exist.
file, err = ioutil.OpenDecompressed(path) file, err = ioutil.OpenDecompressed(path)
if errors.Is(err, os.ErrNotExist) { if errors.Is(err, os.ErrNotExist) {
proof, stateStep, exited, err := p.stateConverter.ConvertStateToProof(filepath.Join(p.dir, vm.FinalState)) proof, stateStep, exited, err := p.stateConverter.ConvertStateToProof(vm.FinalStatePath(p.dir, p.cfg.BinarySnapshots))
if err != nil { if err != nil {
return nil, fmt.Errorf("cannot create proof from final state: %w", err) return nil, fmt.Errorf("cannot create proof from final state: %w", err)
} }
...@@ -170,6 +172,7 @@ func NewTraceProviderForTest(logger log.Logger, m vm.Metricer, cfg *config.Confi ...@@ -170,6 +172,7 @@ func NewTraceProviderForTest(logger log.Logger, m vm.Metricer, cfg *config.Confi
return kvstore.NewFileKV(vm.PreimageDir(dir)) return kvstore.NewFileKV(vm.PreimageDir(dir))
}), }),
stateConverter: NewStateConverter(), stateConverter: NewStateConverter(),
cfg: cfg.Cannon,
} }
return &CannonTraceProviderForTest{p} return &CannonTraceProviderForTest{p}
} }
...@@ -180,7 +183,8 @@ func (p *CannonTraceProviderForTest) FindStep(ctx context.Context, start uint64, ...@@ -180,7 +183,8 @@ func (p *CannonTraceProviderForTest) FindStep(ctx context.Context, start uint64,
return 0, fmt.Errorf("generate cannon trace (until preimage read): %w", err) return 0, fmt.Errorf("generate cannon trace (until preimage read): %w", err)
} }
// Load the step from the state cannon finished with // Load the step from the state cannon finished with
_, step, exited, err := p.stateConverter.ConvertStateToProof(filepath.Join(p.dir, vm.FinalState))
_, step, exited, err := p.stateConverter.ConvertStateToProof(vm.FinalStatePath(p.dir, p.cfg.BinarySnapshots))
if err != nil { if err != nil {
return 0, fmt.Errorf("failed to load final state: %w", err) return 0, fmt.Errorf("failed to load final state: %w", err)
} }
......
...@@ -261,7 +261,7 @@ func (e *stubGenerator) GenerateProof(ctx context.Context, dir string, i uint64) ...@@ -261,7 +261,7 @@ func (e *stubGenerator) GenerateProof(ctx context.Context, dir string, i uint64)
var err error var err error
if e.finalState != nil && e.finalState.Step <= i { if e.finalState != nil && e.finalState.Step <= i {
// Requesting a trace index past the end of the trace // Requesting a trace index past the end of the trace
proofFile = filepath.Join(dir, vm.FinalState) proofFile = vm.FinalStatePath(dir, false)
data, err = json.Marshal(e.finalState) data, err = json.Marshal(e.finalState)
if err != nil { if err != nil {
return err return err
......
package cannon package cannon
import ( import (
"encoding/json"
"fmt" "fmt"
"io"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/singlethreaded" "github.com/ethereum-optimism/optimism/cannon/mipsevm/singlethreaded"
"github.com/ethereum-optimism/optimism/cannon/serialize"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/utils" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/utils"
"github.com/ethereum-optimism/optimism/op-service/ioutil"
) )
type StateConverter struct { type StateConverter struct {
...@@ -36,18 +34,5 @@ func (c *StateConverter) ConvertStateToProof(statePath string) (*utils.ProofData ...@@ -36,18 +34,5 @@ func (c *StateConverter) ConvertStateToProof(statePath string) (*utils.ProofData
} }
func parseState(path string) (*singlethreaded.State, error) { func parseState(path string) (*singlethreaded.State, error) {
file, err := ioutil.OpenDecompressed(path) return serialize.Load[singlethreaded.State](path)
if err != nil {
return nil, fmt.Errorf("cannot open state file (%v): %w", path, err)
}
return parseStateFromReader(file)
}
func parseStateFromReader(in io.ReadCloser) (*singlethreaded.State, error) {
defer in.Close()
var state singlethreaded.State
if err := json.NewDecoder(in).Decode(&state); err != nil {
return nil, fmt.Errorf("invalid mipsevm state: %w", err)
}
return &state, nil
} }
...@@ -8,6 +8,7 @@ import ( ...@@ -8,6 +8,7 @@ import (
"path/filepath" "path/filepath"
"testing" "testing"
"github.com/ethereum-optimism/optimism/cannon/serialize"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/singlethreaded" "github.com/ethereum-optimism/optimism/cannon/mipsevm/singlethreaded"
...@@ -48,4 +49,30 @@ func TestLoadState(t *testing.T) { ...@@ -48,4 +49,30 @@ func TestLoadState(t *testing.T) {
require.NoError(t, json.Unmarshal(testState, &expected)) require.NoError(t, json.Unmarshal(testState, &expected))
require.Equal(t, &expected, state) require.Equal(t, &expected, state)
}) })
t.Run("Binary", func(t *testing.T) {
var expected singlethreaded.State
require.NoError(t, json.Unmarshal(testState, &expected))
dir := t.TempDir()
path := filepath.Join(dir, "state.bin")
require.NoError(t, serialize.Write[*singlethreaded.State](path, &expected, 0644))
state, err := parseState(path)
require.NoError(t, err)
require.Equal(t, &expected, state)
})
t.Run("BinaryGzip", func(t *testing.T) {
var expected singlethreaded.State
require.NoError(t, json.Unmarshal(testState, &expected))
dir := t.TempDir()
path := filepath.Join(dir, "state.bin.gz")
require.NoError(t, serialize.Write[*singlethreaded.State](path, &expected, 0644))
state, err := parseState(path)
require.NoError(t, err)
require.Equal(t, &expected, state)
})
} }
{ {
"memory": [], "memory": [{"index":10,"data":"eJzswAENAAAAwiD7p7bHBwMAAADyHgAA//8QAAAB"}],
"preimageKey": "0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", "preimageKey": "0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
"preimageOffset": 0, "preimageOffset": 42,
"pc": 0, "pc": 94,
"nextPC": 1, "nextPC": 1,
"lo": 0, "lo": 3,
"hi": 0, "hi": 5,
"heap": 0, "heap": 4,
"exit": 0, "exit": 1,
"exited": false, "exited": true,
"step": 0, "step": 8849,
"registers": [] "registers": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2147471360,0,0]
} }
...@@ -9,43 +9,64 @@ import ( ...@@ -9,43 +9,64 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/vm"
"github.com/ethereum-optimism/optimism/op-service/ioutil" "github.com/ethereum-optimism/optimism/op-service/ioutil"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
) )
var ( var (
ErrPrestateUnavailable = errors.New("prestate unavailable") ErrPrestateUnavailable = errors.New("prestate unavailable")
// supportedFileTypes lists, in preferred order, the prestate file types to attempt to download
supportedFileTypes = []string{".bin.gz", ".json.gz", ".json"}
) )
type MultiPrestateProvider struct { type MultiPrestateProvider struct {
baseUrl *url.URL baseUrl *url.URL
dataDir string dataDir string
stateConverter vm.StateConverter
} }
func NewMultiPrestateProvider(baseUrl *url.URL, dataDir string) *MultiPrestateProvider { func NewMultiPrestateProvider(baseUrl *url.URL, dataDir string, stateConverter vm.StateConverter) *MultiPrestateProvider {
return &MultiPrestateProvider{ return &MultiPrestateProvider{
baseUrl: baseUrl, baseUrl: baseUrl,
dataDir: dataDir, dataDir: dataDir,
stateConverter: stateConverter,
} }
} }
func (m *MultiPrestateProvider) PrestatePath(hash common.Hash) (string, error) { func (m *MultiPrestateProvider) PrestatePath(hash common.Hash) (string, error) {
path := filepath.Join(m.dataDir, hash.Hex()+".json.gz") // First try to find a previously downloaded prestate
if _, err := os.Stat(path); errors.Is(err, os.ErrNotExist) { for _, fileType := range supportedFileTypes {
if err := m.fetchPrestate(hash, path); err != nil { path := filepath.Join(m.dataDir, hash.Hex()+fileType)
return "", fmt.Errorf("failed to fetch prestate: %w", err) if _, err := os.Stat(path); errors.Is(err, os.ErrNotExist) {
continue // File doesn't exist, try the next file type
} else if err != nil {
return "", fmt.Errorf("error checking for existing prestate %v in file %v: %w", hash, path, err)
}
return path, nil // Found an existing file so use it
}
// Didn't find any available files, try to download one
var combinedErr error // Keep a track of each download attempt so we can report them if none work
for _, fileType := range supportedFileTypes {
path := filepath.Join(m.dataDir, hash.Hex()+fileType)
if err := m.fetchPrestate(hash, fileType, path); errors.Is(err, ErrPrestateUnavailable) {
combinedErr = errors.Join(combinedErr, err)
continue // Didn't find prestate in this format, try the next
} else if err != nil {
return "", fmt.Errorf("error downloading prestate %v to file %v: %w", hash, path, err)
} }
} else if err != nil { return path, nil // Successfully downloaded a prestate so use it
return "", fmt.Errorf("error checking for existing prestate %v: %w", hash, err)
} }
return path, nil return "", errors.Join(ErrPrestateUnavailable, combinedErr)
} }
func (m *MultiPrestateProvider) fetchPrestate(hash common.Hash, dest string) error { func (m *MultiPrestateProvider) fetchPrestate(hash common.Hash, fileType string, dest string) error {
if err := os.MkdirAll(m.dataDir, 0755); err != nil { if err := os.MkdirAll(m.dataDir, 0755); err != nil {
return fmt.Errorf("error creating prestate dir: %w", err) return fmt.Errorf("error creating prestate dir: %w", err)
} }
prestateUrl := m.baseUrl.JoinPath(hash.Hex() + ".json") prestateUrl := m.baseUrl.JoinPath(hash.Hex() + fileType)
resp, err := http.Get(prestateUrl.String()) resp, err := http.Get(prestateUrl.String())
if err != nil { if err != nil {
return fmt.Errorf("failed to fetch prestate from %v: %w", prestateUrl, err) return fmt.Errorf("failed to fetch prestate from %v: %w", prestateUrl, err)
...@@ -54,7 +75,8 @@ func (m *MultiPrestateProvider) fetchPrestate(hash common.Hash, dest string) err ...@@ -54,7 +75,8 @@ func (m *MultiPrestateProvider) fetchPrestate(hash common.Hash, dest string) err
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
return fmt.Errorf("%w from url %v: status %v", ErrPrestateUnavailable, prestateUrl, resp.StatusCode) return fmt.Errorf("%w from url %v: status %v", ErrPrestateUnavailable, prestateUrl, resp.StatusCode)
} }
out, err := ioutil.NewAtomicWriterCompressed(dest, 0o644) tmpFile := dest + ".tmp" + fileType // Preserve the file type extension so compression is applied correctly
out, err := ioutil.NewAtomicWriterCompressed(tmpFile, 0o644)
if err != nil { if err != nil {
return fmt.Errorf("failed to open atomic writer for %v: %w", dest, err) return fmt.Errorf("failed to open atomic writer for %v: %w", dest, err)
} }
...@@ -68,5 +90,15 @@ func (m *MultiPrestateProvider) fetchPrestate(hash common.Hash, dest string) err ...@@ -68,5 +90,15 @@ func (m *MultiPrestateProvider) fetchPrestate(hash common.Hash, dest string) err
if err := out.Close(); err != nil { if err := out.Close(); err != nil {
return fmt.Errorf("failed to close file %v: %w", dest, err) return fmt.Errorf("failed to close file %v: %w", dest, err)
} }
// Verify the prestate actually matches the expected hash before moving it into the final destination
proof, _, _, err := m.stateConverter.ConvertStateToProof(dest)
if err != nil || proof.ClaimValue != hash {
// Treat invalid prestates as unavailable. Often servers return a 404 page with 200 status code
_ = os.Remove(tmpFile) // Best effort attempt to clean up the temporary file
return fmt.Errorf("invalid prestate from url: %v, ignoring: %w", prestateUrl, errors.Join(ErrPrestateUnavailable, err))
}
if err := os.Rename(tmpFile, dest); err != nil {
return fmt.Errorf("failed to move temp file to final destination: %w", err)
}
return nil return nil
} }
package prestates package prestates
import ( import (
"errors"
"io" "io"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"net/url" "net/url"
"os" "os"
"path/filepath" "path/filepath"
"strings"
"testing" "testing"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/utils"
"github.com/ethereum-optimism/optimism/op-service/ioutil" "github.com/ethereum-optimism/optimism/op-service/ioutil"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
...@@ -20,8 +23,8 @@ func TestDownloadPrestate(t *testing.T) { ...@@ -20,8 +23,8 @@ func TestDownloadPrestate(t *testing.T) {
_, _ = w.Write([]byte(r.URL.Path)) _, _ = w.Write([]byte(r.URL.Path))
})) }))
defer server.Close() defer server.Close()
provider := NewMultiPrestateProvider(parseURL(t, server.URL), dir)
hash := common.Hash{0xaa} hash := common.Hash{0xaa}
provider := NewMultiPrestateProvider(parseURL(t, server.URL), dir, &stubStateConverter{hash: hash})
path, err := provider.PrestatePath(hash) path, err := provider.PrestatePath(hash)
require.NoError(t, err) require.NoError(t, err)
in, err := ioutil.OpenDecompressed(path) in, err := ioutil.OpenDecompressed(path)
...@@ -29,7 +32,7 @@ func TestDownloadPrestate(t *testing.T) { ...@@ -29,7 +32,7 @@ func TestDownloadPrestate(t *testing.T) {
defer in.Close() defer in.Close()
content, err := io.ReadAll(in) content, err := io.ReadAll(in)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, "/"+hash.Hex()+".json", string(content)) require.Equal(t, "/"+hash.Hex()+".bin.gz", string(content))
} }
func TestCreateDirectory(t *testing.T) { func TestCreateDirectory(t *testing.T) {
...@@ -39,8 +42,8 @@ func TestCreateDirectory(t *testing.T) { ...@@ -39,8 +42,8 @@ func TestCreateDirectory(t *testing.T) {
_, _ = w.Write([]byte(r.URL.Path)) _, _ = w.Write([]byte(r.URL.Path))
})) }))
defer server.Close() defer server.Close()
provider := NewMultiPrestateProvider(parseURL(t, server.URL), dir)
hash := common.Hash{0xaa} hash := common.Hash{0xaa}
provider := NewMultiPrestateProvider(parseURL(t, server.URL), dir, &stubStateConverter{hash: hash})
path, err := provider.PrestatePath(hash) path, err := provider.PrestatePath(hash)
require.NoError(t, err) require.NoError(t, err)
in, err := ioutil.OpenDecompressed(path) in, err := ioutil.OpenDecompressed(path)
...@@ -48,13 +51,13 @@ func TestCreateDirectory(t *testing.T) { ...@@ -48,13 +51,13 @@ func TestCreateDirectory(t *testing.T) {
defer in.Close() defer in.Close()
content, err := io.ReadAll(in) content, err := io.ReadAll(in)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, "/"+hash.Hex()+".json", string(content)) require.Equal(t, "/"+hash.Hex()+".bin.gz", string(content))
} }
func TestExistingPrestate(t *testing.T) { func TestExistingPrestate(t *testing.T) {
dir := t.TempDir() dir := t.TempDir()
provider := NewMultiPrestateProvider(parseURL(t, "http://127.0.0.1:1"), dir)
hash := common.Hash{0xaa} hash := common.Hash{0xaa}
provider := NewMultiPrestateProvider(parseURL(t, "http://127.0.0.1:1"), dir, &stubStateConverter{hash: hash})
expectedFile := filepath.Join(dir, hash.Hex()+".json.gz") expectedFile := filepath.Join(dir, hash.Hex()+".json.gz")
err := ioutil.WriteCompressedBytes(expectedFile, []byte("expected content"), os.O_WRONLY|os.O_CREATE, 0o644) err := ioutil.WriteCompressedBytes(expectedFile, []byte("expected content"), os.O_WRONLY|os.O_CREATE, 0o644)
require.NoError(t, err) require.NoError(t, err)
...@@ -72,16 +75,84 @@ func TestExistingPrestate(t *testing.T) { ...@@ -72,16 +75,84 @@ func TestExistingPrestate(t *testing.T) {
func TestMissingPrestate(t *testing.T) { func TestMissingPrestate(t *testing.T) {
dir := t.TempDir() dir := t.TempDir()
var requests []string
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
requests = append(requests, r.URL.Path)
w.WriteHeader(404) w.WriteHeader(404)
})) }))
defer server.Close() defer server.Close()
provider := NewMultiPrestateProvider(parseURL(t, server.URL), dir)
hash := common.Hash{0xaa} hash := common.Hash{0xaa}
provider := NewMultiPrestateProvider(parseURL(t, server.URL), dir, &stubStateConverter{hash: hash})
path, err := provider.PrestatePath(hash) path, err := provider.PrestatePath(hash)
require.ErrorIs(t, err, ErrPrestateUnavailable) require.ErrorIs(t, err, ErrPrestateUnavailable)
_, err = os.Stat(path) _, err = os.Stat(path)
require.ErrorIs(t, err, os.ErrNotExist) require.ErrorIs(t, err, os.ErrNotExist)
expectedRequests := []string{
"/" + hash.Hex() + ".bin.gz",
"/" + hash.Hex() + ".json.gz",
"/" + hash.Hex() + ".json",
}
require.Equal(t, expectedRequests, requests)
}
func TestStorePrestateWithCorrectExtension(t *testing.T) {
extensions := []string{".bin.gz", ".json.gz", ".json"}
for _, ext := range extensions {
ext := ext
t.Run(ext, func(t *testing.T) {
dir := t.TempDir()
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !strings.HasSuffix(r.URL.Path, ext) {
w.WriteHeader(404)
return
}
_, _ = w.Write([]byte("content"))
}))
defer server.Close()
hash := common.Hash{0xaa}
provider := NewMultiPrestateProvider(parseURL(t, server.URL), dir, &stubStateConverter{hash: hash})
path, err := provider.PrestatePath(hash)
require.NoError(t, err)
require.Truef(t, strings.HasSuffix(path, ext), "Expected path %v to have extension %v", path, ext)
in, err := ioutil.OpenDecompressed(path)
require.NoError(t, err)
defer in.Close()
content, err := io.ReadAll(in)
require.NoError(t, err)
require.Equal(t, "content", string(content))
})
}
}
func TestDetectInvalidPrestate(t *testing.T) {
dir := t.TempDir()
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, _ = w.Write([]byte("content"))
}))
defer server.Close()
hash := common.Hash{0xaa}
provider := NewMultiPrestateProvider(parseURL(t, server.URL), dir, &stubStateConverter{hash: hash, err: errors.New("boom")})
_, err := provider.PrestatePath(hash)
require.ErrorIs(t, err, ErrPrestateUnavailable)
entries, err := os.ReadDir(dir)
require.NoError(t, err)
require.Empty(t, entries, "should not leave any files in temp dir")
}
func TestDetectPrestateWithWrongHash(t *testing.T) {
dir := t.TempDir()
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, _ = w.Write([]byte("content"))
}))
defer server.Close()
hash := common.Hash{0xaa}
actualHash := common.Hash{0xbb}
provider := NewMultiPrestateProvider(parseURL(t, server.URL), dir, &stubStateConverter{hash: actualHash})
_, err := provider.PrestatePath(hash)
require.ErrorIs(t, err, ErrPrestateUnavailable)
entries, err := os.ReadDir(dir)
require.NoError(t, err)
require.Empty(t, entries, "should not leave any files in temp dir")
} }
func parseURL(t *testing.T, str string) *url.URL { func parseURL(t *testing.T, str string) *url.URL {
...@@ -89,3 +160,12 @@ func parseURL(t *testing.T, str string) *url.URL { ...@@ -89,3 +160,12 @@ func parseURL(t *testing.T, str string) *url.URL {
require.NoError(t, err) require.NoError(t, err)
return parsed return parsed
} }
type stubStateConverter struct {
err error
hash common.Hash
}
func (s *stubStateConverter) ConvertStateToProof(path string) (*utils.ProofData, uint64, bool, error) {
return &utils.ProofData{ClaimValue: s.hash}, 0, false, s.err
}
package prestates package prestates
import "net/url" import (
"net/url"
func NewPrestateSource(baseURL *url.URL, path string, localDir string) PrestateSource { "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/vm"
)
func NewPrestateSource(baseURL *url.URL, path string, localDir string, stateConverter vm.StateConverter) PrestateSource {
if baseURL != nil { if baseURL != nil {
return NewMultiPrestateProvider(baseURL, localDir) return NewMultiPrestateProvider(baseURL, localDir, stateConverter)
} else { } else {
return NewSinglePrestateSource(path) return NewSinglePrestateSource(path)
} }
......
...@@ -23,7 +23,6 @@ const ( ...@@ -23,7 +23,6 @@ const (
) )
var ( var (
ErrInvalidScalarValue = errors.New("invalid scalar value")
ErrInvalidBlobKeyPreimage = errors.New("invalid blob key preimage") ErrInvalidBlobKeyPreimage = errors.New("invalid blob key preimage")
) )
......
...@@ -28,11 +28,12 @@ type Metricer interface { ...@@ -28,11 +28,12 @@ type Metricer interface {
type Config struct { type Config struct {
// VM Configuration // VM Configuration
VmType types.TraceType VmType types.TraceType
VmBin string // Path to the vm executable to run when generating trace data VmBin string // Path to the vm executable to run when generating trace data
SnapshotFreq uint // Frequency of snapshots to create when executing (in VM instructions) SnapshotFreq uint // Frequency of snapshots to create when executing (in VM instructions)
InfoFreq uint // Frequency of progress log messages (in VM instructions) InfoFreq uint // Frequency of progress log messages (in VM instructions)
DebugInfo bool DebugInfo bool // Whether to record debug info from the execution
BinarySnapshots bool // Whether to use binary snapshots instead of JSON
// Host Configuration // Host Configuration
L1 string L1 string
...@@ -82,13 +83,13 @@ func (e *Executor) GenerateProof(ctx context.Context, dir string, i uint64) erro ...@@ -82,13 +83,13 @@ func (e *Executor) GenerateProof(ctx context.Context, dir string, i uint64) erro
// The proof is stored at the specified directory. // The proof is stored at the specified directory.
func (e *Executor) DoGenerateProof(ctx context.Context, dir string, begin uint64, end uint64, extraVmArgs ...string) error { func (e *Executor) DoGenerateProof(ctx context.Context, dir string, begin uint64, end uint64, extraVmArgs ...string) error {
snapshotDir := filepath.Join(dir, SnapsDir) snapshotDir := filepath.Join(dir, SnapsDir)
start, err := e.selectSnapshot(e.logger, snapshotDir, e.absolutePreState, begin) start, err := e.selectSnapshot(e.logger, snapshotDir, e.absolutePreState, begin, e.cfg.BinarySnapshots)
if err != nil { if err != nil {
return fmt.Errorf("find starting snapshot: %w", err) return fmt.Errorf("find starting snapshot: %w", err)
} }
proofDir := filepath.Join(dir, utils.ProofsDir) proofDir := filepath.Join(dir, utils.ProofsDir)
dataDir := PreimageDir(dir) dataDir := PreimageDir(dir)
lastGeneratedState := filepath.Join(dir, FinalState) lastGeneratedState := FinalStatePath(dir, e.cfg.BinarySnapshots)
args := []string{ args := []string{
"run", "run",
"--input", start, "--input", start,
...@@ -98,7 +99,11 @@ func (e *Executor) DoGenerateProof(ctx context.Context, dir string, begin uint64 ...@@ -98,7 +99,11 @@ func (e *Executor) DoGenerateProof(ctx context.Context, dir string, begin uint64
"--proof-at", "=" + strconv.FormatUint(end, 10), "--proof-at", "=" + strconv.FormatUint(end, 10),
"--proof-fmt", filepath.Join(proofDir, "%d.json.gz"), "--proof-fmt", filepath.Join(proofDir, "%d.json.gz"),
"--snapshot-at", "%" + strconv.FormatUint(uint64(e.cfg.SnapshotFreq), 10), "--snapshot-at", "%" + strconv.FormatUint(uint64(e.cfg.SnapshotFreq), 10),
"--snapshot-fmt", filepath.Join(snapshotDir, "%d.json.gz"), }
if e.cfg.BinarySnapshots {
args = append(args, "--snapshot-fmt", filepath.Join(snapshotDir, "%d.bin.gz"))
} else {
args = append(args, "--snapshot-fmt", filepath.Join(snapshotDir, "%d.json.gz"))
} }
if end < math.MaxUint64 { if end < math.MaxUint64 {
args = append(args, "--stop-at", "="+strconv.FormatUint(end+1, 10)) args = append(args, "--stop-at", "="+strconv.FormatUint(end+1, 10))
......
...@@ -43,7 +43,7 @@ func TestGenerateProof(t *testing.T) { ...@@ -43,7 +43,7 @@ func TestGenerateProof(t *testing.T) {
captureExec := func(t *testing.T, cfg Config, proofAt uint64) (string, string, map[string]string) { captureExec := func(t *testing.T, cfg Config, proofAt uint64) (string, string, map[string]string) {
m := &stubVmMetrics{} m := &stubVmMetrics{}
executor := NewExecutor(testlog.Logger(t, log.LevelInfo), m, cfg, NewOpProgramServerExecutor(), prestate, inputs) executor := NewExecutor(testlog.Logger(t, log.LevelInfo), m, cfg, NewOpProgramServerExecutor(), prestate, inputs)
executor.selectSnapshot = func(logger log.Logger, dir string, absolutePreState string, i uint64) (string, error) { executor.selectSnapshot = func(logger log.Logger, dir string, absolutePreState string, i uint64, binary bool) (string, error) {
return input, nil return input, nil
} }
var binary string var binary string
...@@ -82,7 +82,7 @@ func TestGenerateProof(t *testing.T) { ...@@ -82,7 +82,7 @@ func TestGenerateProof(t *testing.T) {
require.Equal(t, input, args["--input"]) require.Equal(t, input, args["--input"])
require.Contains(t, args, "--meta") require.Contains(t, args, "--meta")
require.Equal(t, "", args["--meta"]) require.Equal(t, "", args["--meta"])
require.Equal(t, filepath.Join(dir, FinalState), args["--output"]) require.Equal(t, FinalStatePath(dir, cfg.BinarySnapshots), args["--output"])
require.Equal(t, "=150000000", args["--proof-at"]) require.Equal(t, "=150000000", args["--proof-at"])
require.Equal(t, "=150000001", args["--stop-at"]) require.Equal(t, "=150000001", args["--stop-at"])
require.Equal(t, "%500", args["--snapshot-at"]) require.Equal(t, "%500", args["--snapshot-at"])
...@@ -128,6 +128,20 @@ func TestGenerateProof(t *testing.T) { ...@@ -128,6 +128,20 @@ func TestGenerateProof(t *testing.T) {
// so expect that it will be omitted. We'll ultimately want asterisc to execute until the program exits. // so expect that it will be omitted. We'll ultimately want asterisc to execute until the program exits.
require.NotContains(t, args, "--stop-at") require.NotContains(t, args, "--stop-at")
}) })
t.Run("BinarySnapshots", func(t *testing.T) {
cfg.Network = "mainnet"
cfg.BinarySnapshots = true
_, _, args := captureExec(t, cfg, 100)
require.Equal(t, filepath.Join(dir, SnapsDir, "%d.bin.gz"), args["--snapshot-fmt"])
})
t.Run("JsonSnapshots", func(t *testing.T) {
cfg.Network = "mainnet"
cfg.BinarySnapshots = false
_, _, args := captureExec(t, cfg, 100)
require.Equal(t, filepath.Join(dir, SnapsDir, "%d.json.gz"), args["--snapshot-fmt"])
})
} }
type stubVmMetrics struct { type stubVmMetrics struct {
......
...@@ -14,16 +14,26 @@ import ( ...@@ -14,16 +14,26 @@ import (
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
) )
type SnapshotSelect func(logger log.Logger, dir string, absolutePreState string, i uint64) (string, error) type SnapshotSelect func(logger log.Logger, dir string, absolutePreState string, i uint64, binary bool) (string, error)
type CmdExecutor func(ctx context.Context, l log.Logger, binary string, args ...string) error type CmdExecutor func(ctx context.Context, l log.Logger, binary string, args ...string) error
const ( const (
SnapsDir = "snapshots" SnapsDir = "snapshots"
PreimagesDir = "preimages" PreimagesDir = "preimages"
FinalState = "final.json.gz" finalStateJson = "final.json.gz"
finalStateBinary = "final.bin.gz"
) )
var snapshotNameRegexp = regexp.MustCompile(`^[0-9]+\.json.gz$`) func FinalStatePath(dir string, binarySnapshots bool) string {
filename := finalStateJson
if binarySnapshots {
filename = finalStateBinary
}
return filepath.Join(dir, filename)
}
var snapshotJsonNameRegexp = regexp.MustCompile(`^[0-9]+\.json\.gz$`)
var snapshotBinaryNameRegexp = regexp.MustCompile(`^[0-9]+\.bin\.gz$`)
func PreimageDir(dir string) string { func PreimageDir(dir string) string {
return filepath.Join(dir, PreimagesDir) return filepath.Join(dir, PreimagesDir)
...@@ -43,7 +53,13 @@ func RunCmd(ctx context.Context, l log.Logger, binary string, args ...string) er ...@@ -43,7 +53,13 @@ func RunCmd(ctx context.Context, l log.Logger, binary string, args ...string) er
// FindStartingSnapshot finds the closest snapshot before the specified traceIndex in snapDir. // FindStartingSnapshot finds the closest snapshot before the specified traceIndex in snapDir.
// If no suitable snapshot can be found it returns absolutePreState. // If no suitable snapshot can be found it returns absolutePreState.
func FindStartingSnapshot(logger log.Logger, snapDir string, absolutePreState string, traceIndex uint64) (string, error) { func FindStartingSnapshot(logger log.Logger, snapDir string, absolutePreState string, traceIndex uint64, binarySnapshots bool) (string, error) {
suffix := ".json.gz"
nameRegexp := snapshotJsonNameRegexp
if binarySnapshots {
suffix = ".bin.gz"
nameRegexp = snapshotBinaryNameRegexp
}
// Find the closest snapshot to start from // Find the closest snapshot to start from
entries, err := os.ReadDir(snapDir) entries, err := os.ReadDir(snapDir)
if err != nil { if err != nil {
...@@ -59,11 +75,11 @@ func FindStartingSnapshot(logger log.Logger, snapDir string, absolutePreState st ...@@ -59,11 +75,11 @@ func FindStartingSnapshot(logger log.Logger, snapDir string, absolutePreState st
continue continue
} }
name := entry.Name() name := entry.Name()
if !snapshotNameRegexp.MatchString(name) { if !nameRegexp.MatchString(name) {
logger.Warn("Unexpected file in snapshots dir", "parent", snapDir, "child", entry.Name()) logger.Warn("Unexpected file in snapshots dir", "parent", snapDir, "child", entry.Name())
continue continue
} }
index, err := strconv.ParseUint(name[0:len(name)-len(".json.gz")], 10, 64) index, err := strconv.ParseUint(name[0:len(name)-len(suffix)], 10, 64)
if err != nil { if err != nil {
logger.Error("Unable to parse trace index of snapshot file", "parent", snapDir, "child", entry.Name()) logger.Error("Unable to parse trace index of snapshot file", "parent", snapDir, "child", entry.Name())
continue continue
...@@ -75,7 +91,7 @@ func FindStartingSnapshot(logger log.Logger, snapDir string, absolutePreState st ...@@ -75,7 +91,7 @@ func FindStartingSnapshot(logger log.Logger, snapDir string, absolutePreState st
if bestSnap == 0 { if bestSnap == 0 {
return absolutePreState, nil return absolutePreState, nil
} }
startFrom := fmt.Sprintf("%v/%v.json.gz", snapDir, bestSnap) startFrom := fmt.Sprintf("%v/%v%v", snapDir, bestSnap, suffix)
return startFrom, nil return startFrom, nil
} }
...@@ -29,37 +29,41 @@ func createTraceProvider( ...@@ -29,37 +29,41 @@ func createTraceProvider(
switch traceType { switch traceType {
case types.TraceTypeCannon: case types.TraceTypeCannon:
vmConfig := vm.NewOpProgramServerExecutor() vmConfig := vm.NewOpProgramServerExecutor()
prestate, err := getPrestate(prestateHash, cfg.CannonAbsolutePreStateBaseURL, cfg.CannonAbsolutePreState, dir) stateConverter := cannon.NewStateConverter()
prestate, err := getPrestate(prestateHash, cfg.CannonAbsolutePreStateBaseURL, cfg.CannonAbsolutePreState, dir, stateConverter)
if err != nil { if err != nil {
return nil, err return nil, err
} }
prestateProvider := vm.NewPrestateProvider(prestate, cannon.NewStateConverter()) prestateProvider := vm.NewPrestateProvider(prestate, stateConverter)
return cannon.NewTraceProvider(logger, m, cfg.Cannon, vmConfig, prestateProvider, prestate, localInputs, dir, 42), nil return cannon.NewTraceProvider(logger, m, cfg.Cannon, vmConfig, prestateProvider, prestate, localInputs, dir, 42), nil
case types.TraceTypeAsterisc: case types.TraceTypeAsterisc:
vmConfig := vm.NewOpProgramServerExecutor() vmConfig := vm.NewOpProgramServerExecutor()
prestate, err := getPrestate(prestateHash, cfg.AsteriscAbsolutePreStateBaseURL, cfg.AsteriscAbsolutePreState, dir) stateConverter := asterisc.NewStateConverter()
prestate, err := getPrestate(prestateHash, cfg.AsteriscAbsolutePreStateBaseURL, cfg.AsteriscAbsolutePreState, dir, stateConverter)
if err != nil { if err != nil {
return nil, err return nil, err
} }
prestateProvider := vm.NewPrestateProvider(prestate, asterisc.NewStateConverter()) prestateProvider := vm.NewPrestateProvider(prestate, stateConverter)
return asterisc.NewTraceProvider(logger, m, cfg.Asterisc, vmConfig, prestateProvider, prestate, localInputs, dir, 42), nil return asterisc.NewTraceProvider(logger, m, cfg.Asterisc, vmConfig, prestateProvider, prestate, localInputs, dir, 42), nil
case types.TraceTypeAsteriscKona: case types.TraceTypeAsteriscKona:
vmConfig := vm.NewKonaServerExecutor() vmConfig := vm.NewKonaServerExecutor()
prestate, err := getPrestate(prestateHash, cfg.AsteriscKonaAbsolutePreStateBaseURL, cfg.AsteriscKonaAbsolutePreState, dir) stateConverter := asterisc.NewStateConverter()
prestate, err := getPrestate(prestateHash, cfg.AsteriscKonaAbsolutePreStateBaseURL, cfg.AsteriscKonaAbsolutePreState, dir, stateConverter)
if err != nil { if err != nil {
return nil, err return nil, err
} }
prestateProvider := vm.NewPrestateProvider(prestate, asterisc.NewStateConverter()) prestateProvider := vm.NewPrestateProvider(prestate, stateConverter)
return asterisc.NewTraceProvider(logger, m, cfg.AsteriscKona, vmConfig, prestateProvider, prestate, localInputs, dir, 42), nil return asterisc.NewTraceProvider(logger, m, cfg.AsteriscKona, vmConfig, prestateProvider, prestate, localInputs, dir, 42), nil
} }
return nil, errors.New("invalid trace type") return nil, errors.New("invalid trace type")
} }
func getPrestate(prestateHash common.Hash, prestateBaseUrl *url.URL, prestatePath string, dataDir string) (string, error) { func getPrestate(prestateHash common.Hash, prestateBaseUrl *url.URL, prestatePath string, dataDir string, stateConverter vm.StateConverter) (string, error) {
prestateSource := prestates.NewPrestateSource( prestateSource := prestates.NewPrestateSource(
prestateBaseUrl, prestateBaseUrl,
prestatePath, prestatePath,
filepath.Join(dataDir, "prestates")) filepath.Join(dataDir, "prestates"),
stateConverter)
prestate, err := prestateSource.PrestatePath(prestateHash) prestate, err := prestateSource.PrestatePath(prestateHash)
if err != nil { if err != nil {
......
...@@ -93,7 +93,7 @@ func TestBenchmarkCannon_FPP(t *testing.T) { ...@@ -93,7 +93,7 @@ func TestBenchmarkCannon_FPP(t *testing.T) {
L2BlockNumber: l2ClaimBlockNumber, L2BlockNumber: l2ClaimBlockNumber,
} }
debugfile := path.Join(t.TempDir(), "debug.json") debugfile := path.Join(t.TempDir(), "debug.json")
runCannon(t, ctx, sys, inputs, "sequencer", "--debug-info", debugfile) runCannon(t, ctx, sys, inputs, "--debug-info", debugfile)
data, err := os.ReadFile(debugfile) data, err := os.ReadFile(debugfile)
require.NoError(t, err) require.NoError(t, err)
var debuginfo mipsevm.DebugInfo var debuginfo mipsevm.DebugInfo
......
...@@ -2,13 +2,12 @@ package faultproofs ...@@ -2,13 +2,12 @@ package faultproofs
import ( import (
"context" "context"
"encoding/json"
"fmt"
"math" "math"
"math/big" "math/big"
"path/filepath" "path/filepath"
"testing" "testing"
"github.com/ethereum-optimism/optimism/cannon/serialize"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
...@@ -25,7 +24,6 @@ import ( ...@@ -25,7 +24,6 @@ import (
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/challenger" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/challenger"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/disputegame" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/disputegame"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait"
"github.com/ethereum-optimism/optimism/op-service/ioutil"
"github.com/ethereum-optimism/optimism/op-service/testlog" "github.com/ethereum-optimism/optimism/op-service/testlog"
) )
...@@ -130,7 +128,7 @@ func TestPrecompiles(t *testing.T) { ...@@ -130,7 +128,7 @@ func TestPrecompiles(t *testing.T) {
L2OutputRoot: common.Hash(l2OutputRoot), L2OutputRoot: common.Hash(l2OutputRoot),
L2BlockNumber: l2ClaimBlockNumber, L2BlockNumber: l2ClaimBlockNumber,
} }
runCannon(t, ctx, sys, inputs, "sequencer") runCannon(t, ctx, sys, inputs)
}) })
t.Run("DisputePrecompile-"+test.name, func(t *testing.T) { t.Run("DisputePrecompile-"+test.name, func(t *testing.T) {
...@@ -242,10 +240,10 @@ func TestGranitePrecompiles(t *testing.T) { ...@@ -242,10 +240,10 @@ func TestGranitePrecompiles(t *testing.T) {
L2OutputRoot: common.Hash(l2OutputRoot), L2OutputRoot: common.Hash(l2OutputRoot),
L2BlockNumber: l2ClaimBlockNumber, L2BlockNumber: l2ClaimBlockNumber,
} }
runCannon(t, ctx, sys, inputs, "sequencer") runCannon(t, ctx, sys, inputs)
} }
func runCannon(t *testing.T, ctx context.Context, sys *op_e2e.System, inputs utils.LocalGameInputs, l2Node string, extraVmArgs ...string) { func runCannon(t *testing.T, ctx context.Context, sys *op_e2e.System, inputs utils.LocalGameInputs, extraVmArgs ...string) {
l1Endpoint := sys.NodeEndpoint("l1").RPC() l1Endpoint := sys.NodeEndpoint("l1").RPC()
l1Beacon := sys.L1BeaconEndpoint().RestHTTP() l1Beacon := sys.L1BeaconEndpoint().RestHTTP()
rollupEndpoint := sys.RollupEndpoint("sequencer").RPC() rollupEndpoint := sys.RollupEndpoint("sequencer").RPC()
...@@ -263,23 +261,9 @@ func runCannon(t *testing.T, ctx context.Context, sys *op_e2e.System, inputs uti ...@@ -263,23 +261,9 @@ func runCannon(t *testing.T, ctx context.Context, sys *op_e2e.System, inputs uti
err := executor.DoGenerateProof(ctx, proofsDir, math.MaxUint, math.MaxUint, extraVmArgs...) err := executor.DoGenerateProof(ctx, proofsDir, math.MaxUint, math.MaxUint, extraVmArgs...)
require.NoError(t, err, "failed to generate proof") require.NoError(t, err, "failed to generate proof")
state, err := parseState(filepath.Join(proofsDir, "final.json.gz")) state, err := serialize.Load[singlethreaded.State](vm.FinalStatePath(proofsDir, cfg.Cannon.BinarySnapshots))
require.NoError(t, err, "failed to parse state") require.NoError(t, err, "failed to parse state")
require.True(t, state.Exited, "cannon did not exit") require.True(t, state.Exited, "cannon did not exit")
require.Zero(t, state.ExitCode, "cannon failed with exit code %d", state.ExitCode) require.Zero(t, state.ExitCode, "cannon failed with exit code %d", state.ExitCode)
t.Logf("Completed in %d steps", state.Step) t.Logf("Completed in %d steps", state.Step)
} }
func parseState(path string) (*singlethreaded.State, error) {
file, err := ioutil.OpenDecompressed(path)
if err != nil {
return nil, fmt.Errorf("cannot open state file (%v): %w", path, err)
}
defer file.Close()
var state singlethreaded.State
err = json.NewDecoder(file).Decode(&state)
if err != nil {
return nil, fmt.Errorf("invalid mipsevm state (%v): %w", path, err)
}
return &state, nil
}
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