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

Merge branch 'develop' into inphi/fpvm-tv

parents 27cb0b54 0564afcb
package op_challenger package op_challenger
import ( import (
"fmt"
"time"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-challenger/config" "github.com/ethereum-optimism/optimism/op-challenger/config"
"github.com/ethereum-optimism/optimism/op-challenger/fault"
"github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum-optimism/optimism/op-service/txmgr/metrics"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
) )
// Main is the programmatic entry-point for running op-challenger // Main is the programmatic entry-point for running op-challenger
func Main(logger log.Logger, cfg *config.Config) error { func Main(logger log.Logger, cfg *config.Config) error {
client, err := ethclient.Dial(cfg.L1EthRpc)
if err != nil {
return fmt.Errorf("failed to dial L1: %w", err)
}
txMgr, err := txmgr.NewSimpleTxManager("challenger", logger, &metrics.NoopTxMetrics{}, cfg.TxMgrConfig)
if err != nil {
return fmt.Errorf("failed to create the transaction manager: %w", err)
}
contract, err := bindings.NewFaultDisputeGameCaller(cfg.GameAddress, client)
if err != nil {
return fmt.Errorf("failed to bind the fault dispute game contract: %w", err)
}
loader := fault.NewLoader(logger, contract)
responder, err := fault.NewFaultResponder(logger, txMgr, cfg.GameAddress)
if err != nil {
return fmt.Errorf("failed to create the responder: %w", err)
}
gameDepth := 4
trace := fault.NewAlphabetProvider(cfg.AlphabetTrace, uint64(gameDepth))
agent := fault.NewAgent(loader, gameDepth, trace, responder, logger)
logger.Info("Fault game started") logger.Info("Fault game started")
return nil
for {
logger.Info("Performing action")
_ = agent.Act()
time.Sleep(300 * time.Millisecond)
}
} }
#!/bin/bash
set -euo pipefail
DIR=$(cd $(dirname "${BASH_SOURCE[0]}") && pwd)
cd "$DIR"
DISPUTE_GAME_PROXY="0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e"
CHARLIE_ADDRESS="0xF45B7537828CB2fffBC69996B054c2Aaf36DC778"
CHARLIE_KEY="74feb147d72bfae943e6b4e483410933d9e447d5dc47d52432dcc2c1454dabb7"
MALLORY_ADDRESS="0x4641c704a6c743f73ee1f36C7568Fbf4b80681e4"
MALLORY_KEY="28d7045146193f5f4eeb151c4843544b1b0d30a7ac1680c845a416fac65a7715"
FAULT_GAME_ADDRESS="0x8daf17a20c9dba35f005b6324f493785d239719d"
./bin/op-challenger --l1-eth-rpc http://localhost:8545 --alphabet "abcdefgh" --game-address $FAULT_GAME_ADDRESS --private-key $CHARLIE_KEY --num-confirmations 1
...@@ -3,6 +3,7 @@ package fault ...@@ -3,6 +3,7 @@ package fault
import ( import (
"context" "context"
"errors" "errors"
"fmt"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
) )
...@@ -36,11 +37,15 @@ func (a *Agent) Act() error { ...@@ -36,11 +37,15 @@ func (a *Agent) Act() error {
} }
// Create counter claims // Create counter claims
for _, claim := range game.Claims() { for _, claim := range game.Claims() {
_ = a.move(claim, game) if err := a.move(claim, game); err != nil {
log.Error("Failed to move", "err", err)
}
} }
// Step on all leaf claims // Step on all leaf claims
for _, claim := range game.Claims() { for _, claim := range game.Claims() {
_ = a.step(claim, game) if err := a.step(claim, game); err != nil {
log.Error("Failed to step", "err", err)
}
} }
return nil return nil
} }
...@@ -49,34 +54,33 @@ func (a *Agent) Act() error { ...@@ -49,34 +54,33 @@ func (a *Agent) Act() error {
func (a *Agent) newGameFromContracts(ctx context.Context) (Game, error) { func (a *Agent) newGameFromContracts(ctx context.Context) (Game, error) {
claims, err := a.loader.FetchClaims(ctx) claims, err := a.loader.FetchClaims(ctx)
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("failed to fetch claims: %w", err)
} }
if len(claims) == 0 { if len(claims) == 0 {
return nil, errors.New("no claims") return nil, errors.New("no claims")
} }
game := NewGameState(claims[0], uint64(a.maxDepth)) game := NewGameState(claims[0], uint64(a.maxDepth))
if err := game.PutAll(claims[1:]); err != nil { if err := game.PutAll(claims[1:]); err != nil {
return nil, err return nil, fmt.Errorf("failed to load claims into the local state: %w", err)
} }
return game, nil return game, nil
} }
// move determines & executes the next move given a claim pair // move determines & executes the next move given a claim
func (a *Agent) move(claim Claim, game Game) error { func (a *Agent) move(claim Claim, game Game) error {
a.log.Info("Fetching claims")
nextMove, err := a.solver.NextMove(claim) nextMove, err := a.solver.NextMove(claim)
if err != nil { if err != nil {
a.log.Warn("Failed to execute the next move", "err", err) a.log.Warn("Failed to execute the next move", "err", err)
return err return err
} }
if nextMove == nil { if nextMove == nil {
a.log.Info("No next move") a.log.Debug("No next move")
return nil return nil
} }
move := *nextMove move := *nextMove
log := a.log.New("is_defend", move.DefendsParent(), "depth", move.Depth(), "index_at_depth", move.IndexAtDepth(), "value", move.Value, log := a.log.New("is_defend", move.DefendsParent(), "depth", move.Depth(), "index_at_depth", move.IndexAtDepth(),
"letter", string(move.Value[31:]), "trace_index", move.Value[30], "value", move.Value, "trace_index", move.TraceIndex(a.maxDepth),
"parent_letter", string(claim.Value[31:]), "parent_trace_index", claim.Value[30]) "parent_value", claim.Value, "parent_trace_index", claim.TraceIndex(a.maxDepth))
if game.IsDuplicate(move) { if game.IsDuplicate(move) {
log.Debug("Duplicate move") log.Debug("Duplicate move")
return nil return nil
...@@ -85,25 +89,30 @@ func (a *Agent) move(claim Claim, game Game) error { ...@@ -85,25 +89,30 @@ func (a *Agent) move(claim Claim, game Game) error {
return a.responder.Respond(context.TODO(), move) return a.responder.Respond(context.TODO(), move)
} }
// step attempts to execute the step through the responder // step determines & executes the next step against a leaf claim through the responder
func (a *Agent) step(claim Claim, game Game) error { func (a *Agent) step(claim Claim, game Game) error {
if claim.Depth() != a.maxDepth { if claim.Depth() != a.maxDepth {
return nil return nil
} }
a.log.Info("Attempting step", "claim_depth", claim.Depth(), "maxDepth", a.maxDepth) a.log.Info("Attempting step", "claim_depth", claim.Depth(), "maxDepth", a.maxDepth)
step, err := a.solver.AttemptStep(claim) step, err := a.solver.AttemptStep(claim)
if err != nil { if err != nil {
a.log.Info("Failed to get a step", "err", err) a.log.Warn("Failed to get a step", "err", err)
return err
}
stateData, err := a.trace.GetPreimage(step.PreStateTraceIndex)
if err != nil {
a.log.Warn("Failed to get a state data", "err", err)
return err return err
} }
a.log.Info("Performing step", a.log.Info("Performing step",
"depth", step.LeafClaim.Depth(), "index_at_depth", step.LeafClaim.IndexAtDepth(), "value", step.LeafClaim.Value, "depth", step.LeafClaim.Depth(), "index_at_depth", step.LeafClaim.IndexAtDepth(), "value", step.LeafClaim.Value,
"is_attack", step.IsAttack) "is_attack", step.IsAttack, "prestate_trace_index", step.PreStateTraceIndex)
callData := StepCallData{ callData := StepCallData{
ClaimIndex: uint64(step.LeafClaim.ContractIndex), ClaimIndex: uint64(step.LeafClaim.ContractIndex),
IsAttack: step.IsAttack, IsAttack: step.IsAttack,
StateData: stateData,
} }
return a.responder.Step(context.TODO(), callData) return a.responder.Step(context.TODO(), callData)
} }
...@@ -5,6 +5,7 @@ import ( ...@@ -5,6 +5,7 @@ import (
"strings" "strings"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
) )
// AlphabetProvider is a [TraceProvider] that provides claims for specific // AlphabetProvider is a [TraceProvider] that provides claims for specific
...@@ -32,7 +33,7 @@ func (ap *AlphabetProvider) GetPreimage(i uint64) ([]byte, error) { ...@@ -32,7 +33,7 @@ func (ap *AlphabetProvider) GetPreimage(i uint64) ([]byte, error) {
if i >= uint64(len(ap.state)) { if i >= uint64(len(ap.state)) {
return ap.GetPreimage(uint64(len(ap.state)) - 1) return ap.GetPreimage(uint64(len(ap.state)) - 1)
} }
return buildAlphabetClaimBytes(i, ap.state[i]), nil return BuildAlphabetPreimage(i, ap.state[i]), nil
} }
// Get returns the claim value at the given index in the trace. // Get returns the claim value at the given index in the trace.
...@@ -41,17 +42,25 @@ func (ap *AlphabetProvider) Get(i uint64) (common.Hash, error) { ...@@ -41,17 +42,25 @@ func (ap *AlphabetProvider) Get(i uint64) (common.Hash, error) {
if err != nil { if err != nil {
return common.Hash{}, err return common.Hash{}, err
} }
return common.BytesToHash(claimBytes), nil return crypto.Keccak256Hash(claimBytes), nil
} }
// buildAlphabetClaimBytes constructs the claim bytes for the index and state item. // BuildAlphabetPreimage constructs the claim bytes for the index and state item.
func buildAlphabetClaimBytes(i uint64, letter string) []byte { func BuildAlphabetPreimage(i uint64, letter string) []byte {
return append(IndexToBytes(i), []byte(letter)...) return append(IndexToBytes(i), LetterToBytes(letter)...)
} }
// IndexToBytes converts an index to a byte slice big endian // IndexToBytes converts an index to a byte slice big endian
func IndexToBytes(i uint64) []byte { func IndexToBytes(i uint64) []byte {
big := new(big.Int) big := new(big.Int)
big.SetUint64(i) big.SetUint64(i)
return big.Bytes() out := make([]byte, 32)
return big.FillBytes(out)
}
// LetterToBytes converts a letter to a 32 byte array
func LetterToBytes(letter string) []byte {
out := make([]byte, 32)
out[31] = byte(letter[0])
return out
} }
...@@ -20,15 +20,15 @@ func TestAlphabetProvider_Get_ClaimsByTraceIndex(t *testing.T) { ...@@ -20,15 +20,15 @@ func TestAlphabetProvider_Get_ClaimsByTraceIndex(t *testing.T) {
}{ }{
{ {
7, 7,
common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000768"), alphabetClaim(7, "h"),
}, },
{ {
3, 3,
common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000364"), alphabetClaim(3, "d"),
}, },
{ {
5, 5,
common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000566"), alphabetClaim(5, "f"),
}, },
} }
...@@ -54,7 +54,7 @@ func FuzzIndexToBytes(f *testing.F) { ...@@ -54,7 +54,7 @@ func FuzzIndexToBytes(f *testing.F) {
// returns the correct pre-image for a index. // returns the correct pre-image for a index.
func TestGetPreimage_Succeeds(t *testing.T) { func TestGetPreimage_Succeeds(t *testing.T) {
ap := NewAlphabetProvider("abc", 2) ap := NewAlphabetProvider("abc", 2)
expected := append(IndexToBytes(uint64(0)), []byte("a")...) expected := BuildAlphabetPreimage(0, "a'")
retrieved, err := ap.GetPreimage(uint64(0)) retrieved, err := ap.GetPreimage(uint64(0))
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, expected, retrieved) require.Equal(t, expected, retrieved)
...@@ -73,8 +73,7 @@ func TestGet_Succeeds(t *testing.T) { ...@@ -73,8 +73,7 @@ func TestGet_Succeeds(t *testing.T) {
ap := NewAlphabetProvider("abc", 2) ap := NewAlphabetProvider("abc", 2)
claim, err := ap.Get(0) claim, err := ap.Get(0)
require.NoError(t, err) require.NoError(t, err)
concatenated := append(IndexToBytes(0), []byte("a")...) expected := alphabetClaim(0, "a")
expected := common.BytesToHash(concatenated)
require.Equal(t, expected, claim) require.Equal(t, expected, claim)
} }
...@@ -92,7 +91,6 @@ func TestGet_Extends(t *testing.T) { ...@@ -92,7 +91,6 @@ func TestGet_Extends(t *testing.T) {
ap := NewAlphabetProvider("abc", 2) ap := NewAlphabetProvider("abc", 2)
claim, err := ap.Get(3) claim, err := ap.Get(3)
require.NoError(t, err) require.NoError(t, err)
concatenated := append(IndexToBytes(2), []byte("c")...) expected := alphabetClaim(2, "c")
expected := common.BytesToHash(concatenated)
require.Equal(t, expected, claim) require.Equal(t, expected, claim)
} }
...@@ -36,9 +36,8 @@ type Game interface { ...@@ -36,9 +36,8 @@ type Game interface {
} }
type extendedClaim struct { type extendedClaim struct {
self Claim self Claim
contractIndex int children []ClaimData
children []ClaimData
} }
// gameState is a struct that represents the state of a dispute game. // gameState is a struct that represents the state of a dispute game.
...@@ -54,9 +53,8 @@ type gameState struct { ...@@ -54,9 +53,8 @@ type gameState struct {
func NewGameState(root Claim, depth uint64) *gameState { func NewGameState(root Claim, depth uint64) *gameState {
claims := make(map[ClaimData]*extendedClaim) claims := make(map[ClaimData]*extendedClaim)
claims[root.ClaimData] = &extendedClaim{ claims[root.ClaimData] = &extendedClaim{
self: root, self: root,
contractIndex: 0, children: make([]ClaimData, 0),
children: make([]ClaimData, 0),
} }
return &gameState{ return &gameState{
root: root.ClaimData, root: root.ClaimData,
...@@ -87,9 +85,8 @@ func (g *gameState) Put(claim Claim) error { ...@@ -87,9 +85,8 @@ func (g *gameState) Put(claim Claim) error {
parent.children = append(parent.children, claim.ClaimData) parent.children = append(parent.children, claim.ClaimData)
} }
g.claims[claim.ClaimData] = &extendedClaim{ g.claims[claim.ClaimData] = &extendedClaim{
self: claim, self: claim,
contractIndex: claim.ContractIndex, children: make([]ClaimData, 0),
children: make([]ClaimData, 0),
} }
return nil return nil
} }
......
...@@ -28,18 +28,14 @@ type Loader interface { ...@@ -28,18 +28,14 @@ type Loader interface {
// loader pulls in fault dispute game claim data periodically and over subscriptions. // loader pulls in fault dispute game claim data periodically and over subscriptions.
type loader struct { type loader struct {
log log.Logger log log.Logger
state Game
claimFetcher ClaimFetcher claimFetcher ClaimFetcher
} }
// NewLoader creates a new [loader]. // NewLoader creates a new [loader].
func NewLoader(log log.Logger, state Game, claimFetcher ClaimFetcher) *loader { func NewLoader(log log.Logger, claimFetcher ClaimFetcher) *loader {
return &loader{ return &loader{
log: log, log: log,
state: state,
claimFetcher: claimFetcher, claimFetcher: claimFetcher,
} }
} }
...@@ -60,6 +56,8 @@ func (l *loader) fetchClaim(ctx context.Context, arrIndex uint64) (Claim, error) ...@@ -60,6 +56,8 @@ func (l *loader) fetchClaim(ctx context.Context, arrIndex uint64) (Claim, error)
Value: fetchedClaim.Claim, Value: fetchedClaim.Claim,
Position: NewPositionFromGIndex(fetchedClaim.Position.Uint64()), Position: NewPositionFromGIndex(fetchedClaim.Position.Uint64()),
}, },
ContractIndex: int(arrIndex),
ParentContractIndex: int(fetchedClaim.ParentIndex),
} }
if !claim.IsRootPosition() { if !claim.IsRootPosition() {
......
...@@ -15,46 +15,8 @@ import ( ...@@ -15,46 +15,8 @@ import (
var ( var (
mockClaimDataError = fmt.Errorf("claim data errored") mockClaimDataError = fmt.Errorf("claim data errored")
mockClaimLenError = fmt.Errorf("claim len errored") mockClaimLenError = fmt.Errorf("claim len errored")
mockPutError = fmt.Errorf("put errored")
) )
type mockGameState struct {
putCalled int
putErrors bool
}
func (m *mockGameState) Put(claim Claim) error {
m.putCalled++
if m.putErrors {
return mockPutError
}
return nil
}
func (m *mockGameState) PutAll(claims []Claim) error {
m.putCalled += len(claims)
if m.putErrors {
return mockPutError
}
return nil
}
func (m *mockGameState) Claims() []Claim {
return []Claim{}
}
func (m *mockGameState) IsDuplicate(claim Claim) bool {
return false
}
func (m *mockGameState) PreStateClaim(claim Claim) (Claim, error) {
panic("unimplemented")
}
func (m *mockGameState) PostStateClaim(claim Claim) (Claim, error) {
panic("unimplemented")
}
type mockClaimFetcher struct { type mockClaimFetcher struct {
claimDataError bool claimDataError bool
claimLenError bool claimLenError bool
...@@ -126,7 +88,7 @@ func TestLoader_FetchClaims_Succeeds(t *testing.T) { ...@@ -126,7 +88,7 @@ func TestLoader_FetchClaims_Succeeds(t *testing.T) {
log := testlog.Logger(t, log.LvlError) log := testlog.Logger(t, log.LvlError)
mockClaimFetcher := newMockClaimFetcher() mockClaimFetcher := newMockClaimFetcher()
expectedClaims := mockClaimFetcher.returnClaims expectedClaims := mockClaimFetcher.returnClaims
loader := NewLoader(log, &mockGameState{}, mockClaimFetcher) loader := NewLoader(log, mockClaimFetcher)
claims, err := loader.FetchClaims(context.Background()) claims, err := loader.FetchClaims(context.Background())
require.NoError(t, err) require.NoError(t, err)
require.ElementsMatch(t, []Claim{ require.ElementsMatch(t, []Claim{
...@@ -139,6 +101,7 @@ func TestLoader_FetchClaims_Succeeds(t *testing.T) { ...@@ -139,6 +101,7 @@ func TestLoader_FetchClaims_Succeeds(t *testing.T) {
Value: expectedClaims[0].Claim, Value: expectedClaims[0].Claim,
Position: NewPositionFromGIndex(expectedClaims[0].Position.Uint64()), Position: NewPositionFromGIndex(expectedClaims[0].Position.Uint64()),
}, },
ContractIndex: 0,
}, },
{ {
ClaimData: ClaimData{ ClaimData: ClaimData{
...@@ -149,6 +112,7 @@ func TestLoader_FetchClaims_Succeeds(t *testing.T) { ...@@ -149,6 +112,7 @@ func TestLoader_FetchClaims_Succeeds(t *testing.T) {
Value: expectedClaims[0].Claim, Value: expectedClaims[0].Claim,
Position: NewPositionFromGIndex(expectedClaims[1].Position.Uint64()), Position: NewPositionFromGIndex(expectedClaims[1].Position.Uint64()),
}, },
ContractIndex: 1,
}, },
{ {
ClaimData: ClaimData{ ClaimData: ClaimData{
...@@ -159,6 +123,7 @@ func TestLoader_FetchClaims_Succeeds(t *testing.T) { ...@@ -159,6 +123,7 @@ func TestLoader_FetchClaims_Succeeds(t *testing.T) {
Value: expectedClaims[0].Claim, Value: expectedClaims[0].Claim,
Position: NewPositionFromGIndex(expectedClaims[2].Position.Uint64()), Position: NewPositionFromGIndex(expectedClaims[2].Position.Uint64()),
}, },
ContractIndex: 2,
}, },
}, claims) }, claims)
} }
...@@ -169,7 +134,7 @@ func TestLoader_FetchClaims_ClaimDataErrors(t *testing.T) { ...@@ -169,7 +134,7 @@ func TestLoader_FetchClaims_ClaimDataErrors(t *testing.T) {
log := testlog.Logger(t, log.LvlError) log := testlog.Logger(t, log.LvlError)
mockClaimFetcher := newMockClaimFetcher() mockClaimFetcher := newMockClaimFetcher()
mockClaimFetcher.claimDataError = true mockClaimFetcher.claimDataError = true
loader := NewLoader(log, &mockGameState{}, mockClaimFetcher) loader := NewLoader(log, mockClaimFetcher)
claims, err := loader.FetchClaims(context.Background()) claims, err := loader.FetchClaims(context.Background())
require.ErrorIs(t, err, mockClaimDataError) require.ErrorIs(t, err, mockClaimDataError)
require.Empty(t, claims) require.Empty(t, claims)
...@@ -181,7 +146,7 @@ func TestLoader_FetchClaims_ClaimLenErrors(t *testing.T) { ...@@ -181,7 +146,7 @@ func TestLoader_FetchClaims_ClaimLenErrors(t *testing.T) {
log := testlog.Logger(t, log.LvlError) log := testlog.Logger(t, log.LvlError)
mockClaimFetcher := newMockClaimFetcher() mockClaimFetcher := newMockClaimFetcher()
mockClaimFetcher.claimLenError = true mockClaimFetcher.claimLenError = true
loader := NewLoader(log, &mockGameState{}, mockClaimFetcher) loader := NewLoader(log, mockClaimFetcher)
claims, err := loader.FetchClaims(context.Background()) claims, err := loader.FetchClaims(context.Background())
require.ErrorIs(t, err, mockClaimLenError) require.ErrorIs(t, err, mockClaimLenError)
require.Empty(t, claims) require.Empty(t, claims)
......
...@@ -9,7 +9,6 @@ import ( ...@@ -9,7 +9,6 @@ import (
// Solver uses a [TraceProvider] to determine the moves to make in a dispute game. // Solver uses a [TraceProvider] to determine the moves to make in a dispute game.
type Solver struct { type Solver struct {
TraceProvider TraceProvider
gameDepth int gameDepth int
} }
...@@ -31,8 +30,9 @@ func (s *Solver) NextMove(claim Claim) (*Claim, error) { ...@@ -31,8 +30,9 @@ func (s *Solver) NextMove(claim Claim) (*Claim, error) {
} }
type StepData struct { type StepData struct {
LeafClaim Claim LeafClaim Claim
IsAttack bool IsAttack bool
PreStateTraceIndex uint64
} }
// AttemptStep determines what step should occur for a given leaf claim. // AttemptStep determines what step should occur for a given leaf claim.
...@@ -45,9 +45,15 @@ func (s *Solver) AttemptStep(claim Claim) (StepData, error) { ...@@ -45,9 +45,15 @@ func (s *Solver) AttemptStep(claim Claim) (StepData, error) {
if err != nil { if err != nil {
return StepData{}, err return StepData{}, err
} }
index := claim.TraceIndex(s.gameDepth)
// TODO(CLI-4198): Handle case where we dispute trace index 0
if !claimCorrect {
index -= 1
}
return StepData{ return StepData{
LeafClaim: claim, LeafClaim: claim,
IsAttack: claimCorrect, IsAttack: !claimCorrect,
PreStateTraceIndex: index,
}, nil }, nil
} }
...@@ -105,8 +111,9 @@ func (s *Solver) attack(claim Claim) (*Claim, error) { ...@@ -105,8 +111,9 @@ func (s *Solver) attack(claim Claim) (*Claim, error) {
return nil, err return nil, err
} }
return &Claim{ return &Claim{
ClaimData: ClaimData{Value: value, Position: position}, ClaimData: ClaimData{Value: value, Position: position},
Parent: claim.ClaimData, Parent: claim.ClaimData,
ParentContractIndex: claim.ContractIndex,
}, nil }, nil
} }
...@@ -118,8 +125,9 @@ func (s *Solver) defend(claim Claim) (*Claim, error) { ...@@ -118,8 +125,9 @@ func (s *Solver) defend(claim Claim) (*Claim, error) {
return nil, err return nil, err
} }
return &Claim{ return &Claim{
ClaimData: ClaimData{Value: value, Position: position}, ClaimData: ClaimData{Value: value, Position: position},
Parent: claim.ClaimData, Parent: claim.ClaimData,
ParentContractIndex: claim.ContractIndex,
}, nil }, nil
} }
......
...@@ -4,9 +4,14 @@ import ( ...@@ -4,9 +4,14 @@ import (
"testing" "testing"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func alphabetClaim(index uint64, letter string) common.Hash {
return crypto.Keccak256Hash(BuildAlphabetPreimage(index, letter))
}
// TestSolver_NextMove_Opponent tests the [Solver] NextMove function // TestSolver_NextMove_Opponent tests the [Solver] NextMove function
// with an [fault.AlphabetProvider] as the [TraceProvider]. // with an [fault.AlphabetProvider] as the [TraceProvider].
func TestSolver_NextMove_Opponent(t *testing.T) { func TestSolver_NextMove_Opponent(t *testing.T) {
...@@ -18,55 +23,51 @@ func TestSolver_NextMove_Opponent(t *testing.T) { ...@@ -18,55 +23,51 @@ func TestSolver_NextMove_Opponent(t *testing.T) {
// The following claims are created using the state: "abcdexyz". // The following claims are created using the state: "abcdexyz".
// The responses are the responses we expect from the solver. // The responses are the responses we expect from the solver.
indices := []struct { indices := []struct {
traceIndex int claim Claim
claim Claim response ClaimData
response ClaimData
}{ }{
{ {
7,
Claim{ Claim{
ClaimData: ClaimData{ ClaimData: ClaimData{
Value: common.HexToHash("0x000000000000000000000000000000000000000000000000000000000000077a"), Value: alphabetClaim(7, "z"),
Position: NewPosition(0, 0), Position: NewPosition(0, 0),
}, },
// Root claim has no parent // Root claim has no parent
}, },
ClaimData{ ClaimData{
Value: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000364"), Value: alphabetClaim(3, "d"),
Position: NewPosition(1, 0), Position: NewPosition(1, 0),
}, },
}, },
{ {
3,
Claim{ Claim{
ClaimData: ClaimData{ ClaimData: ClaimData{
Value: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000364"), Value: alphabetClaim(3, "d"),
Position: NewPosition(1, 0), Position: NewPosition(1, 0),
}, },
Parent: ClaimData{ Parent: ClaimData{
Value: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000768"), Value: alphabetClaim(7, "h"),
Position: NewPosition(0, 0), Position: NewPosition(0, 0),
}, },
}, },
ClaimData{ ClaimData{
Value: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000566"), Value: alphabetClaim(5, "f"),
Position: NewPosition(2, 2), Position: NewPosition(2, 2),
}, },
}, },
{ {
5,
Claim{ Claim{
ClaimData: ClaimData{ ClaimData: ClaimData{
Value: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000578"), Value: alphabetClaim(5, "x"),
Position: NewPosition(2, 2), Position: NewPosition(2, 2),
}, },
Parent: ClaimData{ Parent: ClaimData{
Value: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000768"), Value: alphabetClaim(7, "h"),
Position: NewPosition(1, 1), Position: NewPosition(1, 1),
}, },
}, },
ClaimData{ ClaimData{
Value: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000465"), Value: alphabetClaim(4, "e"),
Position: NewPosition(3, 4), Position: NewPosition(3, 4),
}, },
}, },
......
#!/bin/bash
set -euo pipefail
DIR=$(cd $(dirname "${BASH_SOURCE[0]}") && pwd)
cd "$DIR"
# build the challenger to keep it up to date
make
cd ..
make devnet-clean
make devnet-up-deploy
DEVNET_SPONSOR="ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
DISPUTE_GAME_PROXY="0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e"
CHARLIE_ADDRESS="0xF45B7537828CB2fffBC69996B054c2Aaf36DC778"
CHARLIE_KEY="74feb147d72bfae943e6b4e483410933d9e447d5dc47d52432dcc2c1454dabb7"
MALLORY_ADDRESS="0x4641c704a6c743f73ee1f36C7568Fbf4b80681e4"
MALLORY_KEY="28d7045146193f5f4eeb151c4843544b1b0d30a7ac1680c845a416fac65a7715"
echo "----------------------------------------------------------------"
echo " - Fetching balance of the sponsor"
echo " - Balance: $(cast balance 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266)"
echo "----------------------------------------------------------------"
echo "Funding Charlie"
cast send $CHARLIE_ADDRESS --value 5ether --private-key $DEVNET_SPONSOR
echo "Funding Mallory"
cast send $MALLORY_ADDRESS --value 5ether --private-key $DEVNET_SPONSOR
# Fault game type = 0
GAME_TYPE=0
# Root claim commits to the entire trace.
# Alphabet game claim construction: keccak256(abi.encode(trace_index, trace[trace_index]))
ROOT_CLAIM=$(cast keccak $(cast abi-encode "f(uint256,uint256)" 15 122))
# Extra data is a dynamic `bytes` type that contains the L2 Block Number of the output proposal that the root claim disagrees with
# Doesn't matter right now since we're not deleting outputs, so just set it to 1
EXTRA_DATA=$(cast to-bytes32 1)
echo "Initializing the game"
cast call --private-key $MALLORY_KEY $DISPUTE_GAME_PROXY "create(uint8,bytes32,bytes)" $GAME_TYPE $ROOT_CLAIM $EXTRA_DATA
cast send --private-key $MALLORY_KEY $DISPUTE_GAME_PROXY "create(uint8,bytes32,bytes)" $GAME_TYPE $ROOT_CLAIM $EXTRA_DATA
#!/bin/bash
set -euo pipefail
DIR=$(cd $(dirname "${BASH_SOURCE[0]}") && pwd)
cd "$DIR"
DISPUTE_GAME_PROXY="0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e"
CHARLIE_ADDRESS="0xF45B7537828CB2fffBC69996B054c2Aaf36DC778"
CHARLIE_KEY="74feb147d72bfae943e6b4e483410933d9e447d5dc47d52432dcc2c1454dabb7"
MALLORY_ADDRESS="0x4641c704a6c743f73ee1f36C7568Fbf4b80681e4"
MALLORY_KEY="28d7045146193f5f4eeb151c4843544b1b0d30a7ac1680c845a416fac65a7715"
FAULT_GAME_ADDRESS="0x8daf17a20c9dba35f005b6324f493785d239719d"
./bin/op-challenger --l1-eth-rpc http://localhost:8545 --alphabet "abcdexyz" --game-address $FAULT_GAME_ADDRESS --private-key $MALLORY_KEY --num-confirmations 1
#!/bin/bash
set -euo pipefail
DISPUTE_GAME_PROXY="0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e"
FAULT_GAME_ADDRESS="0x8daf17a20c9dba35f005b6324f493785d239719d"
dir=$(cd $(dirname "${BASH_SOURCE[0]}") && pwd)
cd "$dir"
cd ../packages/contracts-bedrock
forge script scripts/FaultDisputeGameViz.s.sol --sig "remote(address)" $FAULT_GAME_ADDRESS --fork-url http://localhost:8545
mv dispute_game.svg "$dir"
...@@ -2,7 +2,7 @@ FROM ethereum/client-go:v1.11.2 ...@@ -2,7 +2,7 @@ FROM ethereum/client-go:v1.11.2
RUN apk add --no-cache jq RUN apk add --no-cache jq
COPY entrypoint.sh /entrypoint.sh COPY entrypoint-l1.sh /entrypoint.sh
VOLUME ["/db"] VOLUME ["/db"]
......
...@@ -2,7 +2,7 @@ FROM us-docker.pkg.dev/oplabs-tools-artifacts/images/op-geth:optimism ...@@ -2,7 +2,7 @@ FROM us-docker.pkg.dev/oplabs-tools-artifacts/images/op-geth:optimism
RUN apk add --no-cache jq RUN apk add --no-cache jq
COPY entrypoint.sh /entrypoint.sh COPY entrypoint-l2.sh /entrypoint.sh
VOLUME ["/db"] VOLUME ["/db"]
......
#!/bin/sh
set -exu
VERBOSITY=${GETH_VERBOSITY:-3}
GETH_DATA_DIR=/db
GETH_CHAINDATA_DIR="$GETH_DATA_DIR/geth/chaindata"
GENESIS_FILE_PATH="${GENESIS_FILE_PATH:-/genesis.json}"
CHAIN_ID=$(cat "$GENESIS_FILE_PATH" | jq -r .config.chainId)
RPC_PORT="${RPC_PORT:-8545}"
WS_PORT="${WS_PORT:-8546}"
if [ ! -d "$GETH_CHAINDATA_DIR" ]; then
echo "$GETH_CHAINDATA_DIR missing, running init"
echo "Initializing genesis."
geth --verbosity="$VERBOSITY" init \
--datadir="$GETH_DATA_DIR" \
"$GENESIS_FILE_PATH"
else
echo "$GETH_CHAINDATA_DIR exists."
fi
# Warning: Archive mode is required, otherwise old trie nodes will be
# pruned within minutes of starting the devnet.
exec geth \
--datadir="$GETH_DATA_DIR" \
--verbosity="$VERBOSITY" \
--http \
--http.corsdomain="*" \
--http.vhosts="*" \
--http.addr=0.0.0.0 \
--http.port="$RPC_PORT" \
--http.api=web3,debug,eth,txpool,net,engine \
--ws \
--ws.addr=0.0.0.0 \
--ws.port="$WS_PORT" \
--ws.origins="*" \
--ws.api=debug,eth,txpool,net,engine \
--syncmode=full \
--nodiscover \
--maxpeers=0 \
--networkid=$CHAIN_ID \
--authrpc.addr="0.0.0.0" \
--authrpc.port="8551" \
--authrpc.vhosts="*" \
--authrpc.jwtsecret=/config/jwt-secret.txt \
--gcmode=archive \
--metrics \
--metrics.addr=0.0.0.0 \
--metrics.port=6060 \
"$@"
...@@ -6,10 +6,10 @@ WORKDIR /opt ...@@ -6,10 +6,10 @@ WORKDIR /opt
ENV DEBIAN_FRONTEND=noninteractive ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && \ RUN apt-get update && \
apt-get install -y curl build-essential git && \ apt-get install -y curl build-essential git && \
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > rustup.sh && \ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > rustup.sh && \
chmod +x ./rustup.sh && \ chmod +x ./rustup.sh && \
./rustup.sh -y ./rustup.sh -y
COPY ./.foundryrc ./.foundryrc COPY ./.foundryrc ./.foundryrc
...@@ -21,10 +21,10 @@ RUN git clone https://github.com/foundry-rs/foundry.git ./foundry \ ...@@ -21,10 +21,10 @@ RUN git clone https://github.com/foundry-rs/foundry.git ./foundry \
WORKDIR /opt/foundry WORKDIR /opt/foundry
RUN source $HOME/.profile && \ RUN source $HOME/.profile && \
cargo build --release && \ cargo build --release && \
strip /opt/foundry/target/release/forge && \ strip /opt/foundry/target/release/forge && \
strip /opt/foundry/target/release/cast && \ strip /opt/foundry/target/release/cast && \
strip /opt/foundry/target/release/anvil strip /opt/foundry/target/release/anvil
FROM ethereum/client-go:alltools-v1.10.25 as geth FROM ethereum/client-go:alltools-v1.10.25 as geth
...@@ -105,3 +105,8 @@ RUN echo "downloading mockery tool" && \ ...@@ -105,3 +105,8 @@ RUN echo "downloading mockery tool" && \
cp mockery-tmp-dir/mockery /usr/local/bin/mockery && \ cp mockery-tmp-dir/mockery /usr/local/bin/mockery && \
chmod +x /usr/local/bin/mockery && \ chmod +x /usr/local/bin/mockery && \
rm -rf mockery-tmp-dir rm -rf mockery-tmp-dir
RUN echo "installing mips binutils" && \
apt-get install -y binutils-mips-linux-gnu python3 python3-pip && \
pip3 install capstone pyelftools
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