Commit 9a5ff2be authored by refcell.eth's avatar refcell.eth Committed by GitHub

Merge pull request #8491 from ethereum-optimism/refcell/prestate-provider-refactor

feat(op-challenger): Prestate Provider Refactor
parents b2a4415b 7fe636a8
package alphabet
import (
"context"
"github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
)
var absolutePrestate = common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000060")
var _ types.PrestateProvider = (*AlphabetPrestateProvider)(nil)
// AlphabetPrestateProvider is a stateless [PrestateProvider] that
// uses a pre-determined, fixed pre-state hash.
type AlphabetPrestateProvider struct{}
func (ap *AlphabetPrestateProvider) AbsolutePreStateCommitment(_ context.Context) (common.Hash, error) {
hash := common.BytesToHash(crypto.Keccak256(absolutePrestate))
hash[0] = mipsevm.VMStatusUnfinished
return hash, nil
}
package alphabet
import (
"context"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require"
)
func TestAlphabetPrestateProvider_AbsolutePreStateCommitment_Succeeds(t *testing.T) {
provider := AlphabetPrestateProvider{}
hash, err := provider.AbsolutePreStateCommitment(context.Background())
require.NoError(t, err)
expected := common.HexToHash("0x03c7ae758795765c6664a5d39bf63841c71ff191e9189522bad8ebff5d4eca98")
require.Equal(t, expected, hash)
}
...@@ -15,12 +15,12 @@ import ( ...@@ -15,12 +15,12 @@ import (
var ( var (
ErrIndexTooLarge = errors.New("index is larger than the maximum index") ErrIndexTooLarge = errors.New("index is larger than the maximum index")
absolutePrestate = common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000060")
) )
// AlphabetTraceProvider is a [TraceProvider] that provides claims for specific // AlphabetTraceProvider is a [TraceProvider] that provides claims for specific
// indices in the given trace. // indices in the given trace.
type AlphabetTraceProvider struct { type AlphabetTraceProvider struct {
AlphabetPrestateProvider
state []string state []string
depth uint64 depth uint64
maxLen uint64 maxLen uint64
...@@ -68,12 +68,6 @@ func (ap *AlphabetTraceProvider) Get(ctx context.Context, i types.Position) (com ...@@ -68,12 +68,6 @@ func (ap *AlphabetTraceProvider) Get(ctx context.Context, i types.Position) (com
return alphabetStateHash(claimBytes), nil return alphabetStateHash(claimBytes), nil
} }
func (ap *AlphabetTraceProvider) AbsolutePreStateCommitment(_ context.Context) (common.Hash, error) {
hash := common.BytesToHash(crypto.Keccak256(absolutePrestate))
hash[0] = mipsevm.VMStatusUnfinished
return hash, nil
}
// BuildAlphabetPreimage constructs the claim bytes for the index and state item. // BuildAlphabetPreimage constructs the claim bytes for the index and state item.
func BuildAlphabetPreimage(i *big.Int, letter string) []byte { func BuildAlphabetPreimage(i *big.Int, letter string) []byte {
return append(i.FillBytes(make([]byte, 32)), LetterToBytes(letter)...) return append(i.FillBytes(make([]byte, 32)), LetterToBytes(letter)...)
......
package cannon
import (
"context"
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/op-challenger/config"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
)
var _ types.PrestateProvider = (*CannonPrestateProvider)(nil)
type CannonPrestateProvider struct {
prestate string
}
func NewPrestateProvider(cfg *config.Config) *CannonPrestateProvider {
return &CannonPrestateProvider{
prestate: cfg.CannonAbsolutePreState,
}
}
func (p *CannonPrestateProvider) absolutePreState() ([]byte, error) {
state, err := parseState(p.prestate)
if err != nil {
return nil, fmt.Errorf("cannot load absolute pre-state: %w", err)
}
return state.EncodeWitness(), nil
}
func (p *CannonPrestateProvider) AbsolutePreStateCommitment(_ context.Context) (common.Hash, error) {
state, err := p.absolutePreState()
if err != nil {
return common.Hash{}, fmt.Errorf("cannot load absolute pre-state: %w", err)
}
hash, err := mipsevm.StateWitness(state).StateHash()
if err != nil {
return common.Hash{}, fmt.Errorf("cannot hash absolute pre-state: %w", err)
}
return hash, nil
}
package cannon
import (
"context"
"os"
"path/filepath"
"testing"
"github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require"
)
func newCannonPrestateProvider(dataDir string, prestate string) *CannonPrestateProvider {
return &CannonPrestateProvider{
prestate: filepath.Join(dataDir, prestate),
}
}
func TestAbsolutePreStateCommitment(t *testing.T) {
dataDir := t.TempDir()
prestate := "state.json"
t.Run("StateUnavailable", func(t *testing.T) {
provider := newCannonPrestateProvider("/dir/does/not/exist", prestate)
_, err := provider.AbsolutePreStateCommitment(context.Background())
require.ErrorIs(t, err, os.ErrNotExist)
})
t.Run("InvalidStateFile", func(t *testing.T) {
setupPreState(t, dataDir, "invalid.json")
provider := newCannonPrestateProvider(dataDir, prestate)
_, err := provider.AbsolutePreStateCommitment(context.Background())
require.ErrorContains(t, err, "invalid mipsevm state")
})
t.Run("ExpectedAbsolutePreState", func(t *testing.T) {
setupPreState(t, dataDir, "state.json")
provider := newCannonPrestateProvider(dataDir, prestate)
actual, err := provider.AbsolutePreStateCommitment(context.Background())
require.NoError(t, err)
state := mipsevm.State{
Memory: mipsevm.NewMemory(),
PreimageKey: common.HexToHash("cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"),
PreimageOffset: 0,
PC: 0,
NextPC: 1,
LO: 0,
HI: 0,
Heap: 0,
ExitCode: 0,
Exited: false,
Step: 0,
Registers: [32]uint32{},
}
expected, err := state.EncodeWitness().StateHash()
require.NoError(t, err)
require.Equal(t, expected, actual)
})
}
func setupPreState(t *testing.T, dataDir string, filename string) {
srcDir := filepath.Join("test_data")
path := filepath.Join(srcDir, filename)
file, err := testData.ReadFile(path)
require.NoErrorf(t, err, "reading %v", path)
err = os.WriteFile(filepath.Join(dataDir, "state.json"), file, 0o644)
require.NoErrorf(t, err, "writing %v", path)
}
...@@ -216,58 +216,6 @@ func TestGetStepData(t *testing.T) { ...@@ -216,58 +216,6 @@ func TestGetStepData(t *testing.T) {
}) })
} }
func TestAbsolutePreStateCommitment(t *testing.T) {
dataDir := t.TempDir()
prestate := "state.json"
t.Run("StateUnavailable", func(t *testing.T) {
provider, _ := setupWithTestData(t, "/dir/does/not/exist", prestate)
_, err := provider.AbsolutePreStateCommitment(context.Background())
require.ErrorIs(t, err, os.ErrNotExist)
})
t.Run("InvalidStateFile", func(t *testing.T) {
setupPreState(t, dataDir, "invalid.json")
provider, _ := setupWithTestData(t, dataDir, prestate)
_, err := provider.AbsolutePreStateCommitment(context.Background())
require.ErrorContains(t, err, "invalid mipsevm state")
})
t.Run("ExpectedAbsolutePreState", func(t *testing.T) {
setupPreState(t, dataDir, "state.json")
provider, _ := setupWithTestData(t, dataDir, prestate)
actual, err := provider.AbsolutePreStateCommitment(context.Background())
require.NoError(t, err)
state := mipsevm.State{
Memory: mipsevm.NewMemory(),
PreimageKey: common.HexToHash("cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"),
PreimageOffset: 0,
PC: 0,
NextPC: 1,
LO: 0,
HI: 0,
Heap: 0,
ExitCode: 0,
Exited: false,
Step: 0,
Registers: [32]uint32{},
}
expected, err := state.EncodeWitness().StateHash()
require.NoError(t, err)
require.Equal(t, expected, actual)
})
}
func setupPreState(t *testing.T, dataDir string, filename string) {
srcDir := filepath.Join("test_data")
path := filepath.Join(srcDir, filename)
file, err := testData.ReadFile(path)
require.NoErrorf(t, err, "reading %v", path)
err = os.WriteFile(filepath.Join(dataDir, "state.json"), file, 0o644)
require.NoErrorf(t, err, "writing %v", path)
}
func setupTestData(t *testing.T) (string, string) { func setupTestData(t *testing.T) (string, string) {
srcDir := filepath.Join("test_data", "proofs") srcDir := filepath.Join("test_data", "proofs")
entries, err := testData.ReadDir(srcDir) entries, err := testData.ReadDir(srcDir)
......
package outputs
import (
"context"
"fmt"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
)
var _ types.PrestateProvider = (*OutputPrestateProvider)(nil)
type OutputPrestateProvider struct {
prestateBlock uint64
rollupClient OutputRollupClient
}
func NewPrestateProvider(ctx context.Context, logger log.Logger, rollupClient OutputRollupClient, prestateBlock uint64) *OutputPrestateProvider {
return &OutputPrestateProvider{
prestateBlock: prestateBlock,
rollupClient: rollupClient,
}
}
func (o *OutputPrestateProvider) AbsolutePreStateCommitment(ctx context.Context) (hash common.Hash, err error) {
return o.outputAtBlock(ctx, o.prestateBlock)
}
func (o *OutputPrestateProvider) outputAtBlock(ctx context.Context, block uint64) (common.Hash, error) {
output, err := o.rollupClient.OutputAtBlock(ctx, block)
if err != nil {
return common.Hash{}, fmt.Errorf("failed to fetch output at block %v: %w", block, err)
}
return common.Hash(output.OutputRoot), nil
}
package outputs
import (
"context"
"testing"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/stretchr/testify/require"
)
func newOutputPrestateProvider(t *testing.T, prestateBlock uint64) (*OutputPrestateProvider, *stubRollupClient) {
rollupClient := stubRollupClient{
outputs: map[uint64]*eth.OutputResponse{
prestateBlock: {
OutputRoot: eth.Bytes32(prestateOutputRoot),
},
101: {
OutputRoot: eth.Bytes32(firstOutputRoot),
},
poststateBlock: {
OutputRoot: eth.Bytes32(poststateOutputRoot),
},
},
}
return &OutputPrestateProvider{
rollupClient: &rollupClient,
prestateBlock: prestateBlock,
}, &rollupClient
}
func TestAbsolutePreStateCommitment(t *testing.T) {
var prestateBlock = uint64(100)
t.Run("FailedToFetchOutput", func(t *testing.T) {
provider, rollupClient := newOutputPrestateProvider(t, prestateBlock)
rollupClient.errorsOnPrestateFetch = true
_, err := provider.AbsolutePreStateCommitment(context.Background())
require.ErrorIs(t, err, errNoOutputAtBlock)
})
t.Run("ReturnsCorrectPrestateOutput", func(t *testing.T) {
provider, _ := newOutputPrestateProvider(t, prestateBlock)
value, err := provider.AbsolutePreStateCommitment(context.Background())
require.NoError(t, err)
require.Equal(t, value, prestateOutputRoot)
})
}
...@@ -27,6 +27,7 @@ type OutputRollupClient interface { ...@@ -27,6 +27,7 @@ type OutputRollupClient interface {
// OutputTraceProvider is a [types.TraceProvider] implementation that uses // OutputTraceProvider is a [types.TraceProvider] implementation that uses
// output roots for given L2 Blocks as a trace. // output roots for given L2 Blocks as a trace.
type OutputTraceProvider struct { type OutputTraceProvider struct {
types.PrestateProvider
logger log.Logger logger log.Logger
rollupClient OutputRollupClient rollupClient OutputRollupClient
prestateBlock uint64 prestateBlock uint64
...@@ -39,16 +40,18 @@ func NewTraceProvider(ctx context.Context, logger log.Logger, rollupRpc string, ...@@ -39,16 +40,18 @@ func NewTraceProvider(ctx context.Context, logger log.Logger, rollupRpc string,
if err != nil { if err != nil {
return nil, err return nil, err
} }
return NewTraceProviderFromInputs(logger, rollupClient, gameDepth, prestateBlock, poststateBlock), nil prestateProvider := NewPrestateProvider(ctx, logger, rollupClient, prestateBlock)
return NewTraceProviderFromInputs(logger, prestateProvider, rollupClient, gameDepth, prestateBlock, poststateBlock), nil
} }
func NewTraceProviderFromInputs(logger log.Logger, rollupClient OutputRollupClient, gameDepth, prestateBlock, poststateBlock uint64) *OutputTraceProvider { func NewTraceProviderFromInputs(logger log.Logger, prestateProvider types.PrestateProvider, rollupClient OutputRollupClient, gameDepth, prestateBlock, poststateBlock uint64) *OutputTraceProvider {
return &OutputTraceProvider{ return &OutputTraceProvider{
logger: logger, PrestateProvider: prestateProvider,
rollupClient: rollupClient, logger: logger,
prestateBlock: prestateBlock, rollupClient: rollupClient,
poststateBlock: poststateBlock, prestateBlock: prestateBlock,
gameDepth: gameDepth, poststateBlock: poststateBlock,
gameDepth: gameDepth,
} }
} }
...@@ -72,11 +75,6 @@ func (o *OutputTraceProvider) Get(ctx context.Context, pos types.Position) (comm ...@@ -72,11 +75,6 @@ func (o *OutputTraceProvider) Get(ctx context.Context, pos types.Position) (comm
return o.outputAtBlock(ctx, outputBlock) return o.outputAtBlock(ctx, outputBlock)
} }
// AbsolutePreStateCommitment returns the absolute prestate at the configured prestateBlock.
func (o *OutputTraceProvider) AbsolutePreStateCommitment(ctx context.Context) (hash common.Hash, err error) {
return o.outputAtBlock(ctx, o.prestateBlock)
}
// GetStepData is not supported in the [OutputTraceProvider]. // GetStepData is not supported in the [OutputTraceProvider].
func (o *OutputTraceProvider) GetStepData(_ context.Context, _ types.Position) (prestate []byte, proofData []byte, preimageData *types.PreimageOracleData, err error) { func (o *OutputTraceProvider) GetStepData(_ context.Context, _ types.Position) (prestate []byte, proofData []byte, preimageData *types.PreimageOracleData, err error) {
return nil, nil, nil, ErrGetStepData return nil, nil, nil, ErrGetStepData
......
...@@ -96,22 +96,6 @@ func TestGetBlockNumber(t *testing.T) { ...@@ -96,22 +96,6 @@ func TestGetBlockNumber(t *testing.T) {
}) })
} }
func TestAbsolutePreStateCommitment(t *testing.T) {
t.Run("FailedToFetchOutput", func(t *testing.T) {
provider, rollupClient := setupWithTestData(t, prestateBlock, poststateBlock)
rollupClient.errorsOnPrestateFetch = true
_, err := provider.AbsolutePreStateCommitment(context.Background())
require.ErrorIs(t, err, errNoOutputAtBlock)
})
t.Run("ReturnsCorrectPrestateOutput", func(t *testing.T) {
provider, _ := setupWithTestData(t, prestateBlock, poststateBlock)
value, err := provider.AbsolutePreStateCommitment(context.Background())
require.NoError(t, err)
require.Equal(t, value, prestateOutputRoot)
})
}
func TestGetStepData(t *testing.T) { func TestGetStepData(t *testing.T) {
provider, _ := setupWithTestData(t, prestateBlock, poststateBlock) provider, _ := setupWithTestData(t, prestateBlock, poststateBlock)
_, _, _, err := provider.GetStepData(context.Background(), types.NewPosition(1, common.Big0)) _, _, _, err := provider.GetStepData(context.Background(), types.NewPosition(1, common.Big0))
......
...@@ -131,7 +131,10 @@ func setupAdapterTest(t *testing.T, topDepth int) (split.ProviderCreator, *captu ...@@ -131,7 +131,10 @@ func setupAdapterTest(t *testing.T, topDepth int) (split.ProviderCreator, *captu
}, },
}, },
} }
topProvider := NewTraceProviderFromInputs(testlog.Logger(t, log.LvlInfo), rollupClient, uint64(topDepth), prestateBlock, poststateBlock) prestateProvider := &stubPrestateProvider{
absolutePrestate: prestateOutputRoot,
}
topProvider := NewTraceProviderFromInputs(testlog.Logger(t, log.LvlInfo), prestateProvider, rollupClient, uint64(topDepth), prestateBlock, poststateBlock)
adapter := OutputRootSplitAdapter(topProvider, creator.Create) adapter := OutputRootSplitAdapter(topProvider, creator.Create)
return adapter, creator return adapter, creator
} }
...@@ -206,3 +209,15 @@ func TestCreateLocalContext(t *testing.T) { ...@@ -206,3 +209,15 @@ func TestCreateLocalContext(t *testing.T) {
}) })
} }
} }
type stubPrestateProvider struct {
errorsOnAbsolutePrestateFetch bool
absolutePrestate common.Hash
}
func (s *stubPrestateProvider) AbsolutePreStateCommitment(_ context.Context) (common.Hash, error) {
if s.errorsOnAbsolutePrestateFetch {
return common.Hash{}, errNoOutputAtBlock
}
return s.absolutePrestate, nil
}
...@@ -67,8 +67,16 @@ type TraceAccessor interface { ...@@ -67,8 +67,16 @@ type TraceAccessor interface {
GetStepData(ctx context.Context, game Game, ref Claim, pos Position) (prestate []byte, proofData []byte, preimageData *PreimageOracleData, err error) GetStepData(ctx context.Context, game Game, ref Claim, pos Position) (prestate []byte, proofData []byte, preimageData *PreimageOracleData, err error)
} }
// PrestateProvider defines an interface to request the absolute prestate.
type PrestateProvider interface {
// AbsolutePreStateCommitment is the commitment of the pre-image value of the trace that transitions to the trace value at index 0
AbsolutePreStateCommitment(ctx context.Context) (hash common.Hash, err error)
}
// TraceProvider is a generic way to get a claim value at a specific step in the trace. // TraceProvider is a generic way to get a claim value at a specific step in the trace.
type TraceProvider interface { type TraceProvider interface {
PrestateProvider
// Get returns the claim value at the requested index. // Get returns the claim value at the requested index.
// Get(i) = Keccak256(GetPreimage(i)) // Get(i) = Keccak256(GetPreimage(i))
Get(ctx context.Context, i Position) (common.Hash, error) Get(ctx context.Context, i Position) (common.Hash, error)
...@@ -78,9 +86,6 @@ type TraceProvider interface { ...@@ -78,9 +86,6 @@ type TraceProvider interface {
// and any pre-image data that needs to be loaded into the oracle prior to execution (may be nil) // and any pre-image data that needs to be loaded into the oracle prior to execution (may be nil)
// The prestate returned from GetStepData for trace 10 should be the pre-image of the claim from trace 9 // The prestate returned from GetStepData for trace 10 should be the pre-image of the claim from trace 9
GetStepData(ctx context.Context, i Position) (prestate []byte, proofData []byte, preimageData *PreimageOracleData, err error) GetStepData(ctx context.Context, i Position) (prestate []byte, proofData []byte, preimageData *PreimageOracleData, err error)
// AbsolutePreStateCommitment is the commitment of the pre-image value of the trace that transitions to the trace value at index 0
AbsolutePreStateCommitment(ctx context.Context) (hash common.Hash, err error)
} }
// ClaimData is the core of a claim. It must be unique inside a specific game. // ClaimData is the core of a claim. It must be unique inside a specific game.
......
...@@ -183,7 +183,8 @@ func (h *FactoryHelper) StartOutputCannonGame(ctx context.Context, l2Node string ...@@ -183,7 +183,8 @@ func (h *FactoryHelper) StartOutputCannonGame(ctx context.Context, l2Node string
h.require.NoError(err, "Failed to load l2 block number") h.require.NoError(err, "Failed to load l2 block number")
splitDepth, err := game.SPLITDEPTH(&bind.CallOpts{Context: ctx}) splitDepth, err := game.SPLITDEPTH(&bind.CallOpts{Context: ctx})
h.require.NoError(err, "Failed to load split depth") h.require.NoError(err, "Failed to load split depth")
provider := outputs.NewTraceProviderFromInputs(logger, rollupClient, splitDepth.Uint64(), prestateBlock.Uint64(), poststateBlock.Uint64()) prestateProvider := outputs.NewPrestateProvider(ctx, logger, rollupClient, prestateBlock.Uint64())
provider := outputs.NewTraceProviderFromInputs(logger, prestateProvider, rollupClient, splitDepth.Uint64(), prestateBlock.Uint64(), poststateBlock.Uint64())
return &OutputCannonGameHelper{ return &OutputCannonGameHelper{
OutputGameHelper: OutputGameHelper{ OutputGameHelper: OutputGameHelper{
...@@ -231,7 +232,8 @@ func (h *FactoryHelper) StartOutputAlphabetGame(ctx context.Context, l2Node stri ...@@ -231,7 +232,8 @@ func (h *FactoryHelper) StartOutputAlphabetGame(ctx context.Context, l2Node stri
h.require.NoError(err, "Failed to load l2 block number") h.require.NoError(err, "Failed to load l2 block number")
splitDepth, err := game.SPLITDEPTH(&bind.CallOpts{Context: ctx}) splitDepth, err := game.SPLITDEPTH(&bind.CallOpts{Context: ctx})
h.require.NoError(err, "Failed to load split depth") h.require.NoError(err, "Failed to load split depth")
provider := outputs.NewTraceProviderFromInputs(logger, rollupClient, splitDepth.Uint64(), prestateBlock.Uint64(), poststateBlock.Uint64()) prestateProvider := outputs.NewPrestateProvider(ctx, logger, rollupClient, prestateBlock.Uint64())
provider := outputs.NewTraceProviderFromInputs(logger, prestateProvider, rollupClient, splitDepth.Uint64(), prestateBlock.Uint64(), poststateBlock.Uint64())
return &OutputAlphabetGameHelper{ return &OutputAlphabetGameHelper{
OutputGameHelper: OutputGameHelper{ OutputGameHelper: OutputGameHelper{
......
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