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

Merge branch 'develop' into qbzzt/230711-docs-opstack-remove-shared-key

parents 6607e7d1 c2a353b5
---
'@eth-optimism/sdk': patch
---
Fixed missing indexes for multicall support
...@@ -63,6 +63,8 @@ func TestEVM(t *testing.T) { ...@@ -63,6 +63,8 @@ func TestEVM(t *testing.T) {
if strings.HasPrefix(f.Name(), "oracle") { if strings.HasPrefix(f.Name(), "oracle") {
oracle = staticOracle(t, []byte("hello world")) oracle = staticOracle(t, []byte("hello world"))
} }
// Short-circuit early for exit_group.bin
exitGroup := f.Name() == "exit_group.bin"
env, evmState := NewEVMEnv(contracts, addrs) env, evmState := NewEVMEnv(contracts, addrs)
env.Config.Tracer = tracer env.Config.Tracer = tracer
...@@ -83,6 +85,9 @@ func TestEVM(t *testing.T) { ...@@ -83,6 +85,9 @@ func TestEVM(t *testing.T) {
if us.state.PC == endAddr { if us.state.PC == endAddr {
break break
} }
if exitGroup && us.state.Exited {
break
}
insn := state.Memory.GetMemory(state.PC) insn := state.Memory.GetMemory(state.PC)
t.Logf("step: %4d pc: 0x%08x insn: 0x%08x", state.Step, state.PC, insn) t.Logf("step: %4d pc: 0x%08x insn: 0x%08x", state.Step, state.PC, insn)
...@@ -122,11 +127,17 @@ func TestEVM(t *testing.T) { ...@@ -122,11 +127,17 @@ func TestEVM(t *testing.T) {
require.Equal(t, hexutil.Bytes(uniPost).String(), hexutil.Bytes(evmPost).String(), require.Equal(t, hexutil.Bytes(uniPost).String(), hexutil.Bytes(evmPost).String(),
"mipsevm produced different state than EVM") "mipsevm produced different state than EVM")
} }
require.Equal(t, uint32(endAddr), state.PC, "must reach end") if exitGroup {
// inspect test result require.NotEqual(t, uint32(endAddr), us.state.PC, "must not reach end")
done, result := state.Memory.GetMemory(baseAddrEnd+4), state.Memory.GetMemory(baseAddrEnd+8) require.True(t, us.state.Exited, "must set exited state")
require.Equal(t, done, uint32(1), "must be done") require.Equal(t, uint8(1), us.state.ExitCode, "must exit with 1")
require.Equal(t, result, uint32(1), "must have success result") } else {
require.Equal(t, uint32(endAddr), state.PC, "must reach end")
// inspect test result
done, result := state.Memory.GetMemory(baseAddrEnd+4), state.Memory.GetMemory(baseAddrEnd+8)
require.Equal(t, done, uint32(1), "must be done")
require.Equal(t, result, uint32(1), "must have success result")
}
}) })
} }
} }
......
.section .test, "x"
.balign 4
.set noreorder
.global test
.ent test
test:
li $v0, 4045
syscall
lui $t0, 0x4000
subu $v0, $v0, $t0
sltiu $v0, $v0, 1
# save results
lui $s0, 0xbfff # Load the base address 0xbffffff0
ori $s0, 0xfff0
ori $s1, $0, 1 # Prepare the 'done' status
sw $v0, 8($s0) # Set the test result
sw $s1, 4($s0) # Set 'done'
$done:
jr $ra
nop
.end test
.section .test, "x"
.balign 4
.set noreorder
.global test
.ent test
test:
li $v0, 4120
syscall
li $t0, 0x1
subu $v0, $v0, $t0
sltiu $v0, $v0, 1
# save results
lui $s0, 0xbfff # Load the base address 0xbffffff0
ori $s0, 0xfff0
ori $s1, $0, 1 # Prepare the 'done' status
sw $v0, 8($s0) # Set the test result
sw $s1, 4($s0) # Set 'done'
$done:
jr $ra
nop
.end test
.section .test, "x"
.balign 4
.set noreorder
.global test
.ent test
test:
li $a0, 1
li $v0, 4246
syscall
# Unreachable ....
# set test result to fail.
# Test runner should short-circuit before reaching this point.
li $v0, 0
# save results
lui $s0, 0xbfff # Load the base address 0xbffffff0
ori $s0, 0xfff0
ori $s1, $0, 1 # Prepare the 'done' status
sw $v0, 8($s0) # Set the test result
sw $s1, 4($s0) # Set 'done'
$done:
jr $ra
nop
.end test
.section .test, "x"
.balign 4
.set noreorder
.global test
.ent test
test:
# fnctl(0, 3)
li $v0, 4055
li $a0, 0x0
li $a1, 0x3
syscall
sltiu $v0, $v0, 1
# save results
lui $s0, 0xbfff # Load the base address 0xbffffff0
ori $s0, 0xfff0
ori $s1, $0, 1 # Prepare the 'done' status
sw $v0, 8($s0) # Set the test result
sw $s1, 4($s0) # Set 'done'
$done:
jr $ra
nop
.end test
.section .test, "x"
.balign 4
.set noreorder
.global test
.ent test
test:
li $v0, 4090
lui $a0, 0x3000
li $a1, 4096
syscall
lui $t0, 0x3000
subu $v0, $v0, $t0
sltiu $v0, $v0, 1
# save results
lui $s0, 0xbfff # Load the base address 0xbffffff0
ori $s0, 0xfff0
ori $s1, $0, 1 # Prepare the 'done' status
sw $v0, 8($s0) # Set the test result
sw $s1, 4($s0) # Set 'done'
$done:
jr $ra
nop
.end test
...@@ -35,6 +35,9 @@ func TestState(t *testing.T) { ...@@ -35,6 +35,9 @@ func TestState(t *testing.T) {
if strings.HasPrefix(f.Name(), "oracle") { if strings.HasPrefix(f.Name(), "oracle") {
oracle = staticOracle(t, []byte("hello world")) oracle = staticOracle(t, []byte("hello world"))
} }
// Short-circuit early for exit_group.bin
exitGroup := f.Name() == "exit_group.bin"
// TODO: currently tests are compiled as flat binary objects // TODO: currently tests are compiled as flat binary objects
// We can use more standard tooling to compile them to ELF files and get remove maketests.py // We can use more standard tooling to compile them to ELF files and get remove maketests.py
fn := path.Join("open_mips_tests/test/bin", f.Name()) fn := path.Join("open_mips_tests/test/bin", f.Name())
...@@ -57,14 +60,24 @@ func TestState(t *testing.T) { ...@@ -57,14 +60,24 @@ func TestState(t *testing.T) {
if us.state.PC == endAddr { if us.state.PC == endAddr {
break break
} }
if exitGroup && us.state.Exited {
break
}
_, err := us.Step(false) _, err := us.Step(false)
require.NoError(t, err) require.NoError(t, err)
} }
require.Equal(t, uint32(endAddr), us.state.PC, "must reach end")
// inspect test result if exitGroup {
done, result := state.Memory.GetMemory(baseAddrEnd+4), state.Memory.GetMemory(baseAddrEnd+8) require.NotEqual(t, uint32(endAddr), us.state.PC, "must not reach end")
require.Equal(t, done, uint32(1), "must be done") require.True(t, us.state.Exited, "must set exited state")
require.Equal(t, result, uint32(1), "must have success result") require.Equal(t, uint8(1), us.state.ExitCode, "must exit with 1")
} else {
require.Equal(t, uint32(endAddr), us.state.PC, "must reach end")
done, result := state.Memory.GetMemory(baseAddrEnd+4), state.Memory.GetMemory(baseAddrEnd+8)
// inspect test result
require.Equal(t, done, uint32(1), "must be done")
require.Equal(t, result, uint32(1), "must have success result")
}
}) })
} }
} }
......
...@@ -4,7 +4,10 @@ ...@@ -4,7 +4,10 @@
"defaultBase": "develop" "defaultBase": "develop"
}, },
"implicitDependencies": { "implicitDependencies": {
"nx.json": "*" "nx.json": "*",
"tsconfig.json": "*",
".foundryrc": "*",
".nvmrc": "*"
}, },
"tasksRunnerOptions": { "tasksRunnerOptions": {
"default": { "default": {
......
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
...@@ -72,3 +72,25 @@ func TestBuildFaultAttackData(t *testing.T) { ...@@ -72,3 +72,25 @@ func TestBuildFaultAttackData(t *testing.T) {
require.Equal(t, data, tx.Data()) require.Equal(t, data, tx.Data())
} }
// TestBuildFaultStepData ensures that the manual ABI packing is the same as going through the bound contract.
func TestBuildFaultStepData(t *testing.T) {
_, opts, _, contract, err := setupFaultDisputeGame()
require.NoError(t, err)
responder, _ := newTestFaultResponder(t, false)
data, err := responder.buildStepTxData(StepCallData{
ClaimIndex: 2,
IsAttack: false,
StateData: []byte{0x01},
Proof: []byte{0x02},
})
require.NoError(t, err)
opts.GasLimit = 100_000
tx, err := contract.Step(opts, big.NewInt(2), false, []byte{0x01}, []byte{0x02})
require.NoError(t, err)
require.Equal(t, data, tx.Data())
}
...@@ -2,70 +2,117 @@ package fault ...@@ -2,70 +2,117 @@ package fault
import ( import (
"context" "context"
"sync" "errors"
"fmt"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
) )
type Agent struct { type Agent struct {
mu sync.Mutex
game Game
solver *Solver solver *Solver
trace TraceProvider trace TraceProvider
loader Loader
responder Responder responder Responder
maxDepth int maxDepth int
log log.Logger log log.Logger
} }
func NewAgent(game Game, maxDepth int, trace TraceProvider, responder Responder, log log.Logger) Agent { func NewAgent(loader Loader, maxDepth int, trace TraceProvider, responder Responder, log log.Logger) Agent {
return Agent{ return Agent{
game: game,
solver: NewSolver(maxDepth, trace), solver: NewSolver(maxDepth, trace),
trace: trace, trace: trace,
loader: loader,
responder: responder, responder: responder,
maxDepth: maxDepth, maxDepth: maxDepth,
log: log, log: log,
} }
} }
// AddClaim stores a claim in the local state. // Act iterates the game & performs all of the next actions.
// This function shares a lock with PerformActions. func (a *Agent) Act() error {
func (a *Agent) AddClaim(claim Claim) error { game, err := a.newGameFromContracts(context.Background())
a.mu.Lock() if err != nil {
defer a.mu.Unlock() a.log.Error("Failed to create new game", "err", err)
return a.game.Put(claim) return err
}
// Create counter claims
for _, claim := range game.Claims() {
if err := a.move(claim, game); err != nil {
log.Error("Failed to move", "err", err)
}
}
// Step on all leaf claims
for _, claim := range game.Claims() {
if err := a.step(claim, game); err != nil {
log.Error("Failed to step", "err", err)
}
}
return nil
} }
// PerformActions iterates the game & performs all of the next actions. // newGameFromContracts initializes a new game state from the state in the contract
// Note: PerformActions & AddClaim share a lock so the responder cannot func (a *Agent) newGameFromContracts(ctx context.Context) (Game, error) {
// call AddClaim on the same thread. claims, err := a.loader.FetchClaims(ctx)
func (a *Agent) PerformActions() { if err != nil {
a.mu.Lock() return nil, fmt.Errorf("failed to fetch claims: %w", err)
defer a.mu.Unlock() }
for _, claim := range a.game.Claims() { if len(claims) == 0 {
_ = a.move(claim) return nil, errors.New("no claims")
}
game := NewGameState(claims[0], uint64(a.maxDepth))
if err := game.PutAll(claims[1:]); err != nil {
return nil, fmt.Errorf("failed to load claims into the local state: %w", err)
} }
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) error { func (a *Agent) move(claim Claim, game Game) error {
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 a.game.IsDuplicate(move) { if game.IsDuplicate(move) {
log.Debug("Duplicate move") log.Debug("Duplicate move")
return nil return nil
} }
log.Info("Performing move") log.Info("Performing move")
return a.responder.Respond(context.TODO(), move) return a.responder.Respond(context.TODO(), move)
} }
// step determines & executes the next step against a leaf claim through the responder
func (a *Agent) step(claim Claim, game Game) error {
if claim.Depth() != a.maxDepth {
return nil
}
a.log.Info("Attempting step", "claim_depth", claim.Depth(), "maxDepth", a.maxDepth)
step, err := a.solver.AttemptStep(claim)
if err != nil {
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
}
a.log.Info("Performing step",
"depth", step.LeafClaim.Depth(), "index_at_depth", step.LeafClaim.IndexAtDepth(), "value", step.LeafClaim.Value,
"is_attack", step.IsAttack, "prestate_trace_index", step.PreStateTraceIndex)
callData := StepCallData{
ClaimIndex: uint64(step.LeafClaim.ContractIndex),
IsAttack: step.IsAttack,
StateData: stateData,
}
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)
......
...@@ -2,74 +2,74 @@ package fault ...@@ -2,74 +2,74 @@ package fault
import ( import (
"context" "context"
"os"
"time"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
) )
type Orchestrator struct { type Orchestrator struct {
agents []Agent agents []Agent
outputChs []chan Claim claims []Claim
responses chan Claim steps []StepCallData
// tracking when to exit
claimLen, stepLen, step int
} }
func NewOrchestrator(maxDepth uint64, traces []TraceProvider, names []string, root Claim) Orchestrator { func NewOrchestrator(maxDepth uint64, traces []TraceProvider, names []string, root Claim) Orchestrator {
o := Orchestrator{ o := Orchestrator{
responses: make(chan Claim, 100), agents: make([]Agent, len(traces)),
outputChs: make([]chan Claim, len(traces)), claims: []Claim{root},
agents: make([]Agent, len(traces)), steps: make([]StepCallData, 0),
} }
log.Info("Starting game", "root_letter", string(root.Value[31:])) log.Info("Starting game", "root_letter", string(root.Value[31:]))
for i, trace := range traces { for i, trace := range traces {
game := NewGameState(root, maxDepth) o.agents[i] = NewAgent(&o, int(maxDepth), trace, &o, log.New("role", names[i]))
o.agents[i] = NewAgent(game, int(maxDepth), trace, &o, log.New("role", names[i]))
o.outputChs[i] = make(chan Claim)
} }
return o return o
} }
func (o *Orchestrator) Respond(_ context.Context, response Claim) error { func (o *Orchestrator) Respond(_ context.Context, response Claim) error {
o.responses <- response response.ContractIndex = len(o.claims)
o.claims = append(o.claims, response)
return nil return nil
} }
func (o *Orchestrator) Step(ctx context.Context, stepData StepCallData) error { func (o *Orchestrator) Step(_ context.Context, stepData StepCallData) error {
log.Info("Step recorded", "step", stepData)
o.steps = append(o.steps, stepData)
return nil return nil
} }
func (o *Orchestrator) Start() { func (o *Orchestrator) FetchClaims(ctx context.Context) ([]Claim, error) {
for i := 0; i < len(o.agents); i++ { c := make([]Claim, len(o.claims))
go runAgent(&o.agents[i], o.outputChs[i]) copy(c, o.claims)
} return c, nil
o.responderThread()
} }
func runAgent(agent *Agent, claimCh <-chan Claim) { func (o *Orchestrator) Start() {
for { for {
agent.PerformActions() for _, a := range o.agents {
// Note: Should drain the channel here _ = a.Act()
claim := <-claimCh }
_ = agent.AddClaim(claim) if o.shouldExit() {
log.Info("exiting")
return
}
} }
} }
func (o *Orchestrator) responderThread() { func (o *Orchestrator) shouldExit() bool {
timer := time.NewTimer(200 * time.Millisecond) cl := o.claimLen
defer timer.Stop() sl := o.stepLen
for {
select { o.claimLen = len(o.claims)
case resp := <-o.responses: o.stepLen = len(o.steps)
timer.Reset(200 * time.Millisecond)
for _, ch := range o.outputChs {
// Copy it. Should be immutable, but be sure.
resp := resp
ch <- resp
}
case <-timer.C:
os.Exit(0)
}
noProgress := o.claimLen == cl && o.stepLen == sl
if noProgress {
o.step = o.step + 1
} else {
o.step = 0
} }
return noProgress && o.step == 1
} }
...@@ -104,7 +104,6 @@ func (r *faultResponder) sendTxAndWait(ctx context.Context, txData []byte) error ...@@ -104,7 +104,6 @@ func (r *faultResponder) sendTxAndWait(ctx context.Context, txData []byte) error
func (r *faultResponder) buildStepTxData(stepData StepCallData) ([]byte, error) { func (r *faultResponder) buildStepTxData(stepData StepCallData) ([]byte, error) {
return r.fdgAbi.Pack( return r.fdgAbi.Pack(
"step", "step",
big.NewInt(int64(stepData.StateIndex)),
big.NewInt(int64(stepData.ClaimIndex)), big.NewInt(int64(stepData.ClaimIndex)),
stepData.IsAttack, stepData.IsAttack,
stepData.StateData, stepData.StateData,
......
...@@ -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,14 +30,14 @@ func (s *Solver) NextMove(claim Claim) (*Claim, error) { ...@@ -31,14 +30,14 @@ func (s *Solver) NextMove(claim Claim) (*Claim, error) {
} }
type StepData struct { type StepData struct {
LeafClaim Claim LeafClaim Claim
StateClaim 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.
// An error will be returned if the claim is not at the max depth. // An error will be returned if the claim is not at the max depth.
func (s *Solver) AttemptStep(claim Claim, state Game) (StepData, error) { func (s *Solver) AttemptStep(claim Claim) (StepData, error) {
if claim.Depth() != s.gameDepth { if claim.Depth() != s.gameDepth {
return StepData{}, errors.New("cannot step on non-leaf claims") return StepData{}, errors.New("cannot step on non-leaf claims")
} }
...@@ -46,20 +45,15 @@ func (s *Solver) AttemptStep(claim Claim, state Game) (StepData, error) { ...@@ -46,20 +45,15 @@ func (s *Solver) AttemptStep(claim Claim, state Game) (StepData, error) {
if err != nil { if err != nil {
return StepData{}, err return StepData{}, err
} }
var selectorFn func(Claim) (Claim, error) index := claim.TraceIndex(s.gameDepth)
if claimCorrect { // TODO(CLI-4198): Handle case where we dispute trace index 0
selectorFn = state.PostStateClaim if !claimCorrect {
} else { index -= 1
selectorFn = state.PreStateClaim
}
stateClaim, err := selectorFn(claim)
if err != nil {
return StepData{}, err
} }
return StepData{ return StepData{
LeafClaim: claim, LeafClaim: claim,
StateClaim: stateClaim, IsAttack: !claimCorrect,
IsAttack: claimCorrect, PreStateTraceIndex: index,
}, nil }, nil
} }
...@@ -117,8 +111,9 @@ func (s *Solver) attack(claim Claim) (*Claim, error) { ...@@ -117,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
} }
...@@ -130,8 +125,9 @@ func (s *Solver) defend(claim Claim) (*Claim, error) { ...@@ -130,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),
}, },
}, },
...@@ -89,12 +90,11 @@ func TestAttemptStep(t *testing.T) { ...@@ -89,12 +90,11 @@ func TestAttemptStep(t *testing.T) {
require.NoError(t, g.Put(middle)) require.NoError(t, g.Put(middle))
require.NoError(t, g.Put(bottom)) require.NoError(t, g.Put(bottom))
step, err := solver.AttemptStep(bottom, g) step, err := solver.AttemptStep(bottom)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, bottom, step.LeafClaim) require.Equal(t, bottom, step.LeafClaim)
require.Equal(t, middle, step.StateClaim)
require.True(t, step.IsAttack) require.True(t, step.IsAttack)
_, err = solver.AttemptStep(middle, g) _, err = solver.AttemptStep(middle)
require.Error(t, err) require.Error(t, err)
} }
...@@ -14,7 +14,6 @@ var ( ...@@ -14,7 +14,6 @@ var (
// StepCallData encapsulates the data needed to perform a step. // StepCallData encapsulates the data needed to perform a step.
type StepCallData struct { type StepCallData struct {
StateIndex uint64
ClaimIndex uint64 ClaimIndex uint64
IsAttack bool IsAttack bool
StateData []byte StateData []byte
......
#!/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"
...@@ -15,7 +15,7 @@ type Metrics interface { ...@@ -15,7 +15,7 @@ type Metrics interface {
RecordL1Ref(name string, ref eth.L1BlockRef) RecordL1Ref(name string, ref eth.L1BlockRef)
RecordL2Ref(name string, ref eth.L2BlockRef) RecordL2Ref(name string, ref eth.L2BlockRef)
RecordUnsafePayloadsBuffer(length uint64, memSize uint64, next eth.BlockID) RecordUnsafePayloadsBuffer(length uint64, memSize uint64, next eth.BlockID)
RecordChannelInputBytes(inputCompresedBytes int) RecordChannelInputBytes(inputCompressedBytes int)
} }
type L1Fetcher interface { type L1Fetcher interface {
......
...@@ -21,7 +21,7 @@ type Metrics interface { ...@@ -21,7 +21,7 @@ type Metrics interface {
RecordL1Ref(name string, ref eth.L1BlockRef) RecordL1Ref(name string, ref eth.L1BlockRef)
RecordL2Ref(name string, ref eth.L2BlockRef) RecordL2Ref(name string, ref eth.L2BlockRef)
RecordChannelInputBytes(inputCompresedBytes int) RecordChannelInputBytes(inputCompressedBytes int)
RecordUnsafePayloadsBuffer(length uint64, memSize uint64, next eth.BlockID) RecordUnsafePayloadsBuffer(length uint64, memSize uint64, next eth.BlockID)
......
...@@ -11,7 +11,7 @@ type TestDerivationMetrics struct { ...@@ -11,7 +11,7 @@ type TestDerivationMetrics struct {
FnRecordL1Ref func(name string, ref eth.L1BlockRef) FnRecordL1Ref func(name string, ref eth.L1BlockRef)
FnRecordL2Ref func(name string, ref eth.L2BlockRef) FnRecordL2Ref func(name string, ref eth.L2BlockRef)
FnRecordUnsafePayloads func(length uint64, memSize uint64, next eth.BlockID) FnRecordUnsafePayloads func(length uint64, memSize uint64, next eth.BlockID)
FnRecordChannelInputBytes func(inputCompresedBytes int) FnRecordChannelInputBytes func(inputCompressedBytes int)
} }
func (t *TestDerivationMetrics) RecordL1ReorgDepth(d uint64) { func (t *TestDerivationMetrics) RecordL1ReorgDepth(d uint64) {
...@@ -38,9 +38,9 @@ func (t *TestDerivationMetrics) RecordUnsafePayloadsBuffer(length uint64, memSiz ...@@ -38,9 +38,9 @@ func (t *TestDerivationMetrics) RecordUnsafePayloadsBuffer(length uint64, memSiz
} }
} }
func (t *TestDerivationMetrics) RecordChannelInputBytes(inputCompresedBytes int) { func (t *TestDerivationMetrics) RecordChannelInputBytes(inputCompressedBytes int) {
if t.FnRecordChannelInputBytes != nil { if t.FnRecordChannelInputBytes != nil {
t.FnRecordChannelInputBytes(inputCompresedBytes) t.FnRecordChannelInputBytes(inputCompressedBytes)
} }
} }
......
...@@ -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
...@@ -1563,9 +1563,17 @@ export class CrossChainMessenger { ...@@ -1563,9 +1563,17 @@ export class CrossChainMessenger {
opts?: { opts?: {
signer?: Signer signer?: Signer
overrides?: Overrides overrides?: Overrides
} },
/**
* The index of the withdrawal if multiple are made with multicall
*/
messageIndex: number = 0
): Promise<TransactionResponse> { ): Promise<TransactionResponse> {
const tx = await this.populateTransaction.proveMessage(message, opts) const tx = await this.populateTransaction.proveMessage(
message,
opts,
messageIndex
)
return (opts?.signer || this.l1Signer).sendTransaction(tx) return (opts?.signer || this.l1Signer).sendTransaction(tx)
} }
...@@ -1584,10 +1592,18 @@ export class CrossChainMessenger { ...@@ -1584,10 +1592,18 @@ export class CrossChainMessenger {
opts?: { opts?: {
signer?: Signer signer?: Signer
overrides?: PayableOverrides overrides?: PayableOverrides
} },
/**
* The index of the withdrawal if multiple are made with multicall
*/
messageIndex = 0
): Promise<TransactionResponse> { ): Promise<TransactionResponse> {
return (opts?.signer || this.l1Signer).sendTransaction( return (opts?.signer || this.l1Signer).sendTransaction(
await this.populateTransaction.finalizeMessage(message, opts) await this.populateTransaction.finalizeMessage(
message,
opts,
messageIndex
)
) )
} }
...@@ -1810,7 +1826,6 @@ export class CrossChainMessenger { ...@@ -1810,7 +1826,6 @@ export class CrossChainMessenger {
opts?: { opts?: {
overrides?: Overrides overrides?: Overrides
}, },
/** /**
* The index of the withdrawal if multiple are made with multicall * The index of the withdrawal if multiple are made with multicall
*/ */
...@@ -1822,13 +1837,17 @@ export class CrossChainMessenger { ...@@ -1822,13 +1837,17 @@ export class CrossChainMessenger {
} }
if (this.bedrock) { if (this.bedrock) {
return this.populateTransaction.finalizeMessage(resolved, { return this.populateTransaction.finalizeMessage(
...(opts || {}), resolved,
overrides: { {
...opts?.overrides, ...(opts || {}),
gasLimit: messageGasLimit, overrides: {
...opts?.overrides,
gasLimit: messageGasLimit,
},
}, },
}) messageIndex
)
} else { } else {
const legacyL1XDM = new ethers.Contract( const legacyL1XDM = new ethers.Contract(
this.contracts.l1.L1CrossDomainMessenger.address, this.contracts.l1.L1CrossDomainMessenger.address,
...@@ -1861,7 +1880,6 @@ export class CrossChainMessenger { ...@@ -1861,7 +1880,6 @@ export class CrossChainMessenger {
opts?: { opts?: {
overrides?: PayableOverrides overrides?: PayableOverrides
}, },
/** /**
* The index of the withdrawal if multiple are made with multicall * The index of the withdrawal if multiple are made with multicall
*/ */
...@@ -1921,7 +1939,6 @@ export class CrossChainMessenger { ...@@ -1921,7 +1939,6 @@ export class CrossChainMessenger {
opts?: { opts?: {
overrides?: PayableOverrides overrides?: PayableOverrides
}, },
/** /**
* The index of the withdrawal if multiple are made with multicall * The index of the withdrawal if multiple are made with multicall
*/ */
...@@ -2229,10 +2246,18 @@ export class CrossChainMessenger { ...@@ -2229,10 +2246,18 @@ export class CrossChainMessenger {
message: MessageLike, message: MessageLike,
opts?: { opts?: {
overrides?: CallOverrides overrides?: CallOverrides
} },
/**
* The index of the withdrawal if multiple are made with multicall
*/
messageIndex = 0
): Promise<BigNumber> => { ): Promise<BigNumber> => {
return this.l1Provider.estimateGas( return this.l1Provider.estimateGas(
await this.populateTransaction.finalizeMessage(message, opts) await this.populateTransaction.finalizeMessage(
message,
opts,
messageIndex
)
) )
}, },
......
...@@ -123,6 +123,9 @@ importers: ...@@ -123,6 +123,9 @@ importers:
prettier-plugin-solidity: prettier-plugin-solidity:
specifier: ^1.0.0-beta.13 specifier: ^1.0.0-beta.13
version: 1.0.0-beta.18(prettier@2.8.1) version: 1.0.0-beta.18(prettier@2.8.1)
rimraf:
specifier: ^5.0.1
version: 5.0.1
ts-mocha: ts-mocha:
specifier: ^10.0.0 specifier: ^10.0.0
version: 10.0.0(mocha@8.4.0) version: 10.0.0(mocha@8.4.0)
...@@ -9833,7 +9836,7 @@ packages: ...@@ -9833,7 +9836,7 @@ packages:
fs.realpath: 1.0.0 fs.realpath: 1.0.0
minimatch: 8.0.4 minimatch: 8.0.4
minipass: 4.2.8 minipass: 4.2.8
path-scurry: 1.9.2 path-scurry: 1.10.0
dev: true dev: true
/global-modules@2.0.0: /global-modules@2.0.0:
...@@ -12149,11 +12152,6 @@ packages: ...@@ -12149,11 +12152,6 @@ packages:
engines: {node: '>=12'} engines: {node: '>=12'}
dev: true dev: true
/lru-cache@9.1.2:
resolution: {integrity: sha512-ERJq3FOzJTxBbFjZ7iDs+NiK4VI9Wz+RdrrAB8dio1oV+YvdPzUEE4QNiT2VD51DkIbCYRUUzCRkssXCHqSnKQ==}
engines: {node: 14 || >=16.14}
dev: true
/lru_map@0.3.3: /lru_map@0.3.3:
resolution: {integrity: sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==} resolution: {integrity: sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==}
...@@ -14531,14 +14529,6 @@ packages: ...@@ -14531,14 +14529,6 @@ packages:
minipass: 6.0.2 minipass: 6.0.2
dev: true dev: true
/path-scurry@1.9.2:
resolution: {integrity: sha512-qSDLy2aGFPm8i4rsbHd4MNyTcrzHFsLQykrtbuGRknZZCBBVXSv2tSCDN2Cg6Rt/GFRw8GoW9y9Ecw5rIPG1sg==}
engines: {node: '>=16 || 14 >=14.17'}
dependencies:
lru-cache: 9.1.2
minipass: 6.0.2
dev: true
/path-to-regexp@0.1.7: /path-to-regexp@0.1.7:
resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==}
...@@ -15807,6 +15797,14 @@ packages: ...@@ -15807,6 +15797,14 @@ packages:
glob: 9.3.5 glob: 9.3.5
dev: true dev: true
/rimraf@5.0.1:
resolution: {integrity: sha512-OfFZdwtd3lZ+XZzYP/6gTACubwFcHdLRqS9UX3UwpU2dnGQYkPFISRwvM3w9IiB2w7bW5qGo/uAwE4SmXXSKvg==}
engines: {node: '>=14'}
hasBin: true
dependencies:
glob: 10.3.1
dev: true
/ripemd160@2.0.2: /ripemd160@2.0.2:
resolution: {integrity: sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==} resolution: {integrity: sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==}
dependencies: dependencies:
......
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