Commit 2bf094a8 authored by Adrian Sutton's avatar Adrian Sutton Committed by GitHub

Merge pull request #8043 from ethereum-optimism/aj/simple-trace-accessor

op-challenger: Introduce TraceAccessor
parents f292f095 8c219fcb
...@@ -39,7 +39,7 @@ type Agent struct { ...@@ -39,7 +39,7 @@ type Agent struct {
log log.Logger log log.Logger
} }
func NewAgent(m metrics.Metricer, loader ClaimLoader, maxDepth int, trace types.TraceProvider, responder Responder, updater types.OracleUpdater, agreeWithProposedOutput bool, log log.Logger) *Agent { func NewAgent(m metrics.Metricer, loader ClaimLoader, maxDepth int, trace types.TraceAccessor, responder Responder, updater types.OracleUpdater, agreeWithProposedOutput bool, log log.Logger) *Agent {
return &Agent{ return &Agent{
metrics: m, metrics: m,
solver: solver.NewGameSolver(maxDepth, trace), solver: solver.NewGameSolver(maxDepth, trace),
......
...@@ -5,6 +5,7 @@ import ( ...@@ -5,6 +5,7 @@ import (
"errors" "errors"
"testing" "testing"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
...@@ -112,10 +113,10 @@ func setupTestAgent(t *testing.T, agreeWithProposedOutput bool) (*Agent, *stubCl ...@@ -112,10 +113,10 @@ func setupTestAgent(t *testing.T, agreeWithProposedOutput bool) (*Agent, *stubCl
logger := testlog.Logger(t, log.LvlInfo) logger := testlog.Logger(t, log.LvlInfo)
claimLoader := &stubClaimLoader{} claimLoader := &stubClaimLoader{}
depth := 4 depth := 4
trace := alphabet.NewTraceProvider("abcd", uint64(depth)) provider := alphabet.NewTraceProvider("abcd", uint64(depth))
responder := &stubResponder{} responder := &stubResponder{}
updater := &stubUpdater{} updater := &stubUpdater{}
agent := NewAgent(metrics.NoopMetrics, claimLoader, depth, trace, responder, updater, agreeWithProposedOutput, logger) agent := NewAgent(metrics.NoopMetrics, claimLoader, depth, trace.NewSimpleTraceAccessor(provider), responder, updater, agreeWithProposedOutput, logger)
return agent, claimLoader, responder return agent, claimLoader, responder
} }
......
...@@ -8,6 +8,7 @@ import ( ...@@ -8,6 +8,7 @@ import (
"github.com/ethereum-optimism/optimism/op-challenger/config" "github.com/ethereum-optimism/optimism/op-challenger/config"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/responder" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/responder"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
gameTypes "github.com/ethereum-optimism/optimism/op-challenger/game/types" gameTypes "github.com/ethereum-optimism/optimism/op-challenger/game/types"
"github.com/ethereum-optimism/optimism/op-challenger/metrics" "github.com/ethereum-optimism/optimism/op-challenger/metrics"
...@@ -90,8 +91,10 @@ func NewGamePlayer( ...@@ -90,8 +91,10 @@ func NewGamePlayer(
return nil, fmt.Errorf("failed to create the responder: %w", err) return nil, fmt.Errorf("failed to create the responder: %w", err)
} }
accessor := trace.NewSimpleTraceAccessor(provider)
agent := NewAgent(m, loader, int(gameDepth), accessor, responder, updater, cfg.AgreeWithProposedOutput, logger)
return &GamePlayer{ return &GamePlayer{
act: NewAgent(m, loader, int(gameDepth), provider, responder, updater, cfg.AgreeWithProposedOutput, logger).Act, act: agent.Act,
agreeWithProposedOutput: cfg.AgreeWithProposedOutput, agreeWithProposedOutput: cfg.AgreeWithProposedOutput,
loader: loader, loader: loader,
logger: logger, logger: logger,
......
...@@ -12,7 +12,7 @@ type GameSolver struct { ...@@ -12,7 +12,7 @@ type GameSolver struct {
claimSolver *claimSolver claimSolver *claimSolver
} }
func NewGameSolver(gameDepth int, trace types.TraceProvider) *GameSolver { func NewGameSolver(gameDepth int, trace types.TraceAccessor) *GameSolver {
return &GameSolver{ return &GameSolver{
claimSolver: newClaimSolver(gameDepth, trace), claimSolver: newClaimSolver(gameDepth, trace),
} }
......
...@@ -6,6 +6,7 @@ import ( ...@@ -6,6 +6,7 @@ import (
"testing" "testing"
faulttest "github.com/ethereum-optimism/optimism/op-challenger/game/fault/test" faulttest "github.com/ethereum-optimism/optimism/op-challenger/game/fault/test"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
...@@ -113,7 +114,7 @@ func TestCalculateNextActions(t *testing.T) { ...@@ -113,7 +114,7 @@ func TestCalculateNextActions(t *testing.T) {
i, claim.Position.ToGIndex(), claim.Position.TraceIndex(maxDepth), claim.ParentContractIndex, claim.Countered, claim.Value) i, claim.Position.ToGIndex(), claim.Position.TraceIndex(maxDepth), claim.ParentContractIndex, claim.Countered, claim.Value)
} }
solver := NewGameSolver(maxDepth, claimBuilder.CorrectTraceProvider()) solver := NewGameSolver(maxDepth, trace.NewSimpleTraceAccessor(claimBuilder.CorrectTraceProvider()))
actions, err := solver.CalculateNextActions(context.Background(), game) actions, err := solver.CalculateNextActions(context.Background(), game)
require.NoError(t, err) require.NoError(t, err)
for i, action := range actions { for i, action := range actions {
......
...@@ -7,7 +7,6 @@ import ( ...@@ -7,7 +7,6 @@ import (
"fmt" "fmt"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum/go-ethereum/common"
) )
var ( var (
...@@ -18,14 +17,14 @@ var ( ...@@ -18,14 +17,14 @@ var (
// claimSolver uses a [TraceProvider] to determine the moves to make in a dispute game. // claimSolver uses a [TraceProvider] to determine the moves to make in a dispute game.
type claimSolver struct { type claimSolver struct {
trace types.TraceProvider trace types.TraceAccessor
gameDepth int gameDepth int
} }
// newClaimSolver creates a new [claimSolver] using the provided [TraceProvider]. // newClaimSolver creates a new [claimSolver] using the provided [TraceProvider].
func newClaimSolver(gameDepth int, traceProvider types.TraceProvider) *claimSolver { func newClaimSolver(gameDepth int, trace types.TraceAccessor) *claimSolver {
return &claimSolver{ return &claimSolver{
traceProvider, trace,
gameDepth, gameDepth,
} }
} }
...@@ -53,14 +52,14 @@ func (s *claimSolver) NextMove(ctx context.Context, claim types.Claim, game type ...@@ -53,14 +52,14 @@ func (s *claimSolver) NextMove(ctx context.Context, claim types.Claim, game type
} }
} }
agree, err := s.agreeWithClaim(ctx, claim.ClaimData) agree, err := s.agreeWithClaim(ctx, game, claim)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if agree { if agree {
return s.defend(ctx, claim) return s.defend(ctx, game, claim)
} else { } else {
return s.attack(ctx, claim) return s.attack(ctx, game, claim)
} }
} }
...@@ -93,27 +92,24 @@ func (s *claimSolver) AttemptStep(ctx context.Context, game types.Game, claim ty ...@@ -93,27 +92,24 @@ func (s *claimSolver) AttemptStep(ctx context.Context, game types.Game, claim ty
return StepData{}, ErrStepIgnoreInvalidPath return StepData{}, ErrStepIgnoreInvalidPath
} }
claimCorrect, err := s.agreeWithClaim(ctx, claim.ClaimData) claimCorrect, err := s.agreeWithClaim(ctx, game, claim)
if err != nil { if err != nil {
return StepData{}, err return StepData{}, err
} }
var preState []byte
var proofData []byte
var oracleData *types.PreimageOracleData
var position types.Position
if !claimCorrect { if !claimCorrect {
// Attack the claim by executing step index, so we need to get the pre-state of that index // Attack the claim by executing step index, so we need to get the pre-state of that index
preState, proofData, oracleData, err = s.trace.GetStepData(ctx, claim.Position) position = claim.Position
if err != nil {
return StepData{}, err
}
} else { } else {
// We agree with the claim so Defend and use this claim as the starting point to // Defend and use this claim as the starting point to execute the step after.
// execute the step after. Thus we need the pre-state of the next step. // Thus, we need the pre-state of the next step.
preState, proofData, oracleData, err = s.trace.GetStepData(ctx, claim.MoveRight()) position = claim.Position.MoveRight()
if err != nil { }
return StepData{}, err
} preState, proofData, oracleData, err := s.trace.GetStepData(ctx, game, claim, position)
if err != nil {
return StepData{}, err
} }
return StepData{ return StepData{
...@@ -126,9 +122,9 @@ func (s *claimSolver) AttemptStep(ctx context.Context, game types.Game, claim ty ...@@ -126,9 +122,9 @@ func (s *claimSolver) AttemptStep(ctx context.Context, game types.Game, claim ty
} }
// attack returns a response that attacks the claim. // attack returns a response that attacks the claim.
func (s *claimSolver) attack(ctx context.Context, claim types.Claim) (*types.Claim, error) { func (s *claimSolver) attack(ctx context.Context, game types.Game, claim types.Claim) (*types.Claim, error) {
position := claim.Attack() position := claim.Attack()
value, err := s.traceAtPosition(ctx, position) value, err := s.trace.Get(ctx, game, claim, position)
if err != nil { if err != nil {
return nil, fmt.Errorf("attack claim: %w", err) return nil, fmt.Errorf("attack claim: %w", err)
} }
...@@ -139,12 +135,12 @@ func (s *claimSolver) attack(ctx context.Context, claim types.Claim) (*types.Cla ...@@ -139,12 +135,12 @@ func (s *claimSolver) attack(ctx context.Context, claim types.Claim) (*types.Cla
} }
// defend returns a response that defends the claim. // defend returns a response that defends the claim.
func (s *claimSolver) defend(ctx context.Context, claim types.Claim) (*types.Claim, error) { func (s *claimSolver) defend(ctx context.Context, game types.Game, claim types.Claim) (*types.Claim, error) {
if claim.IsRoot() { if claim.IsRoot() {
return nil, nil return nil, nil
} }
position := claim.Defend() position := claim.Defend()
value, err := s.traceAtPosition(ctx, position) value, err := s.trace.Get(ctx, game, claim, position)
if err != nil { if err != nil {
return nil, fmt.Errorf("defend claim: %w", err) return nil, fmt.Errorf("defend claim: %w", err)
} }
...@@ -155,19 +151,14 @@ func (s *claimSolver) defend(ctx context.Context, claim types.Claim) (*types.Cla ...@@ -155,19 +151,14 @@ func (s *claimSolver) defend(ctx context.Context, claim types.Claim) (*types.Cla
} }
// agreeWithClaim returns true if the claim is correct according to the internal [TraceProvider]. // agreeWithClaim returns true if the claim is correct according to the internal [TraceProvider].
func (s *claimSolver) agreeWithClaim(ctx context.Context, claim types.ClaimData) (bool, error) { func (s *claimSolver) agreeWithClaim(ctx context.Context, game types.Game, claim types.Claim) (bool, error) {
ourValue, err := s.traceAtPosition(ctx, claim.Position) ourValue, err := s.trace.Get(ctx, game, claim, claim.Position)
return bytes.Equal(ourValue[:], claim.Value[:]), err return bytes.Equal(ourValue[:], claim.Value[:]), err
} }
// traceAtPosition returns the [common.Hash] from internal [TraceProvider] at the given [Position].
func (s *claimSolver) traceAtPosition(ctx context.Context, p types.Position) (common.Hash, error) {
return s.trace.Get(ctx, p)
}
// agreeWithClaimPath returns true if the every other claim in the path to root is correct according to the internal [TraceProvider]. // agreeWithClaimPath returns true if the every other claim in the path to root is correct according to the internal [TraceProvider].
func (s *claimSolver) agreeWithClaimPath(ctx context.Context, game types.Game, claim types.Claim) (bool, error) { func (s *claimSolver) agreeWithClaimPath(ctx context.Context, game types.Game, claim types.Claim) (bool, error) {
agree, err := s.agreeWithClaim(ctx, claim.ClaimData) agree, err := s.agreeWithClaim(ctx, game, claim)
if err != nil { if err != nil {
return false, err return false, err
} }
......
...@@ -6,6 +6,7 @@ import ( ...@@ -6,6 +6,7 @@ import (
"testing" "testing"
faulttest "github.com/ethereum-optimism/optimism/op-challenger/game/fault/test" faulttest "github.com/ethereum-optimism/optimism/op-challenger/game/fault/test"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
...@@ -161,7 +162,7 @@ func TestAttemptStep(t *testing.T) { ...@@ -161,7 +162,7 @@ func TestAttemptStep(t *testing.T) {
t.Run(tableTest.name, func(t *testing.T) { t.Run(tableTest.name, func(t *testing.T) {
builder := claimBuilder.GameBuilder(tableTest.agreeWithOutputRoot, !tableTest.agreeWithOutputRoot) builder := claimBuilder.GameBuilder(tableTest.agreeWithOutputRoot, !tableTest.agreeWithOutputRoot)
tableTest.setupGame(builder) tableTest.setupGame(builder)
alphabetSolver := newClaimSolver(maxDepth, claimBuilder.CorrectTraceProvider()) alphabetSolver := newClaimSolver(maxDepth, trace.NewSimpleTraceAccessor(claimBuilder.CorrectTraceProvider()))
game := builder.Game game := builder.Game
claims := game.Claims() claims := game.Claims()
lastClaim := claims[len(claims)-1] lastClaim := claims[len(claims)-1]
......
package trace
import (
"context"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum/go-ethereum/common"
)
type ProviderCreator func(ctx context.Context, pre types.Claim, post types.Claim) (types.TraceProvider, error)
func NewSimpleTraceAccessor(trace types.TraceProvider) *Accessor {
selector := func(_ context.Context, _ types.Game, _ types.Claim, _ types.Position) (types.TraceProvider, error) {
return trace, nil
}
return &Accessor{selector}
}
type Accessor struct {
selector func(ctx context.Context, game types.Game, ref types.Claim, pos types.Position) (types.TraceProvider, error)
}
func (t *Accessor) Get(ctx context.Context, game types.Game, ref types.Claim, pos types.Position) (common.Hash, error) {
provider, err := t.selector(ctx, game, ref, pos)
if err != nil {
return common.Hash{}, err
}
return provider.Get(ctx, pos)
}
func (t *Accessor) GetStepData(ctx context.Context, game types.Game, ref types.Claim, pos types.Position) (prestate []byte, proofData []byte, preimageData *types.PreimageOracleData, err error) {
provider, err := t.selector(ctx, game, ref, pos)
if err != nil {
return nil, nil, nil, err
}
return provider.GetStepData(ctx, pos)
}
var _ types.TraceAccessor = (*Accessor)(nil)
package trace
import (
"context"
"fmt"
"math/big"
"testing"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/test"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/alphabet"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/stretchr/testify/require"
)
func TestAccessor_UsesSelector(t *testing.T) {
ctx := context.Background()
depth := uint64(4)
provider1 := test.NewAlphabetWithProofProvider(t, int(depth), nil)
provider2 := alphabet.NewTraceProvider("qrstuv", depth)
claim := types.Claim{}
game := types.NewGameState(true, []types.Claim{claim}, depth)
pos1 := types.NewPositionFromGIndex(big.NewInt(4))
pos2 := types.NewPositionFromGIndex(big.NewInt(6))
accessor := &Accessor{
selector: func(ctx context.Context, actualGame types.Game, ref types.Claim, pos types.Position) (types.TraceProvider, error) {
require.Equal(t, game, actualGame)
require.Equal(t, claim, ref)
if pos == pos1 {
return provider1, nil
} else if pos == pos2 {
return provider2, nil
}
return nil, fmt.Errorf("incorrect position requested: %v", pos)
},
}
t.Run("Get", func(t *testing.T) {
actual, err := accessor.Get(ctx, game, claim, pos1)
require.NoError(t, err)
expected, err := provider1.Get(ctx, pos1)
require.NoError(t, err)
require.Equal(t, expected, actual)
actual, err = accessor.Get(ctx, game, claim, pos2)
require.NoError(t, err)
expected, err = provider2.Get(ctx, pos2)
require.NoError(t, err)
require.Equal(t, expected, actual)
})
t.Run("GetStepData", func(t *testing.T) {
actualPrestate, actualProofData, actualPreimageData, err := accessor.GetStepData(ctx, game, claim, pos1)
require.NoError(t, err)
expectedPrestate, expectedProofData, expectedPreimageData, err := provider1.GetStepData(ctx, pos1)
require.NoError(t, err)
require.Equal(t, expectedPrestate, actualPrestate)
require.Equal(t, expectedProofData, actualProofData)
require.Equal(t, expectedPreimageData, actualPreimageData)
actualPrestate, actualProofData, actualPreimageData, err = accessor.GetStepData(ctx, game, claim, pos2)
require.NoError(t, err)
expectedPrestate, expectedProofData, expectedPreimageData, err = provider2.GetStepData(ctx, pos2)
require.NoError(t, err)
require.Equal(t, expectedPrestate, actualPrestate)
require.Equal(t, expectedProofData, actualProofData)
require.Equal(t, expectedPreimageData, actualPreimageData)
})
}
...@@ -57,6 +57,11 @@ type OracleUpdater interface { ...@@ -57,6 +57,11 @@ type OracleUpdater interface {
UpdateOracle(ctx context.Context, data *PreimageOracleData) error UpdateOracle(ctx context.Context, data *PreimageOracleData) error
} }
type TraceAccessor interface {
Get(ctx context.Context, game Game, ref Claim, pos Position) (common.Hash, error)
GetStepData(ctx context.Context, game Game, ref Claim, pos Position) (prestate []byte, proofData []byte, preimageData *PreimageOracleData, 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 {
// Get returns the claim value at the requested index. // Get returns the claim value at the requested index.
......
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