Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
N
nebula
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
exchain
nebula
Commits
86b91fc6
Unverified
Commit
86b91fc6
authored
Sep 11, 2023
by
mergify[bot]
Committed by
GitHub
Sep 11, 2023
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'develop' into deps/rm-dead-deps
parents
b46e162e
23854294
Changes
20
Show whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
567 additions
and
400 deletions
+567
-400
README.md
docs/fault-proof-alpha/README.md
+5
-1
README.md
op-challenger/README.md
+1
-1
agent.go
op-challenger/game/fault/agent.go
+30
-78
agent_test.go
op-challenger/game/fault/agent_test.go
+2
-5
abi_test.go
op-challenger/game/fault/responder/abi_test.go
+1
-7
responder.go
op-challenger/game/fault/responder/responder.go
+30
-46
responder_test.go
op-challenger/game/fault/responder/responder_test.go
+61
-49
game_solver.go
op-challenger/game/fault/solver/game_solver.go
+107
-0
game_solver_test.go
op-challenger/game/fault/solver/game_solver_test.go
+187
-0
solver.go
op-challenger/game/fault/solver/solver.go
+11
-11
solver_test.go
op-challenger/game/fault/solver/solver_test.go
+7
-8
claim_builder.go
op-challenger/game/fault/test/claim_builder.go
+35
-2
game_builder.go
op-challenger/game/fault/test/game_builder.go
+76
-0
init_game.sh
op-challenger/scripts/alphabet/init_game.sh
+2
-0
pnpm-lock.yaml
pnpm-lock.yaml
+6
-16
README.md
specs/README.md
+2
-1
bond-manager.md
specs/bond-manager.md
+3
-3
challenger.md
specs/challenger.md
+0
-94
dispute-game.md
specs/dispute-game.md
+0
-77
fault-proof.md
specs/fault-proof.md
+1
-1
No files found.
docs/fault-proof-alpha/README.md
View file @
86b91fc6
...
@@ -9,7 +9,11 @@ finalized and may change without notice.
...
@@ -9,7 +9,11 @@ finalized and may change without notice.
### Contents
### Contents
*
Overview
*
Specifications
*
[
Generic Fault Proof System
](
../../specs/fault-proof.md
)
*
[
Generic Dispute Game Interface
](
../../specs/dispute-game-interface.md
)
*
[
Fault Dispute Game
](
../../specs/fault-dispute-game.md
)
*
[
Cannon VM
](
../../specs/cannon-fault-proof-vm.md
)
*
[
Deployment Details
](
./deployments.md
)
*
[
Deployment Details
](
./deployments.md
)
*
[
Manual Usage
](
./manual.md
)
*
[
Manual Usage
](
./manual.md
)
*
[
Creating Traces with Cannon
](
./cannon.md
)
*
[
Creating Traces with Cannon
](
./cannon.md
)
...
...
op-challenger/README.md
View file @
86b91fc6
...
@@ -3,7 +3,7 @@
...
@@ -3,7 +3,7 @@
The
`op-challenger`
is a modular
**op-stack**
challenge agent
The
`op-challenger`
is a modular
**op-stack**
challenge agent
written in golang for dispute games including, but not limited to, attestation games, fault
written in golang for dispute games including, but not limited to, attestation games, fault
games, and validity games. To learn more about dispute games, visit the
games, and validity games. To learn more about dispute games, visit the
[
dispute game specs
](
../specs/dispute-game
.md
)
.
[
fault proof specs
](
../specs/fault-proof
.md
)
.
## Quickstart
## Quickstart
...
...
op-challenger/game/fault/agent.go
View file @
86b91fc6
...
@@ -17,8 +17,7 @@ import (
...
@@ -17,8 +17,7 @@ import (
type
Responder
interface
{
type
Responder
interface
{
CallResolve
(
ctx
context
.
Context
)
(
gameTypes
.
GameStatus
,
error
)
CallResolve
(
ctx
context
.
Context
)
(
gameTypes
.
GameStatus
,
error
)
Resolve
(
ctx
context
.
Context
)
error
Resolve
(
ctx
context
.
Context
)
error
Respond
(
ctx
context
.
Context
,
response
types
.
Claim
)
error
PerformAction
(
ctx
context
.
Context
,
action
solver
.
Action
)
error
Step
(
ctx
context
.
Context
,
stepData
types
.
StepCallData
)
error
}
}
type
ClaimLoader
interface
{
type
ClaimLoader
interface
{
...
@@ -27,7 +26,7 @@ type ClaimLoader interface {
...
@@ -27,7 +26,7 @@ type ClaimLoader interface {
type
Agent
struct
{
type
Agent
struct
{
metrics
metrics
.
Metricer
metrics
metrics
.
Metricer
solver
*
solver
.
Solver
solver
*
solver
.
Game
Solver
loader
ClaimLoader
loader
ClaimLoader
responder
Responder
responder
Responder
updater
types
.
OracleUpdater
updater
types
.
OracleUpdater
...
@@ -39,7 +38,7 @@ type Agent struct {
...
@@ -39,7 +38,7 @@ type Agent struct {
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
.
TraceProvider
,
responder
Responder
,
updater
types
.
OracleUpdater
,
agreeWithProposedOutput
bool
,
log
log
.
Logger
)
*
Agent
{
return
&
Agent
{
return
&
Agent
{
metrics
:
m
,
metrics
:
m
,
solver
:
solver
.
NewSolver
(
maxDepth
,
trace
),
solver
:
solver
.
New
Game
Solver
(
maxDepth
,
trace
),
loader
:
loader
,
loader
:
loader
,
responder
:
responder
,
responder
:
responder
,
updater
:
updater
,
updater
:
updater
,
...
@@ -58,16 +57,34 @@ func (a *Agent) Act(ctx context.Context) error {
...
@@ -58,16 +57,34 @@ func (a *Agent) Act(ctx context.Context) error {
if
err
!=
nil
{
if
err
!=
nil
{
return
fmt
.
Errorf
(
"create game from contracts: %w"
,
err
)
return
fmt
.
Errorf
(
"create game from contracts: %w"
,
err
)
}
}
// Create counter claims
for
_
,
claim
:=
range
game
.
Claims
()
{
// Calculate the actions to take
if
err
:=
a
.
move
(
ctx
,
claim
,
game
);
err
!=
nil
&&
!
errors
.
Is
(
err
,
types
.
ErrGameDepthReached
)
{
actions
,
err
:=
a
.
solver
.
CalculateNextActions
(
ctx
,
game
)
log
.
Error
(
"Failed to move"
,
"err"
,
err
)
if
err
!=
nil
{
log
.
Error
(
"Failed to calculate all required moves"
,
"err"
,
err
)
}
// Perform the actions
for
_
,
action
:=
range
actions
{
log
:=
a
.
log
.
New
(
"action"
,
action
.
Type
,
"is_attack"
,
action
.
IsAttack
,
"parent"
,
action
.
ParentIdx
,
"value"
,
action
.
Value
)
if
action
.
OracleData
!=
nil
{
a
.
log
.
Info
(
"Updating oracle data"
,
"oracleKey"
,
action
.
OracleData
.
OracleKey
,
"oracleData"
,
action
.
OracleData
.
OracleData
)
if
err
:=
a
.
updater
.
UpdateOracle
(
ctx
,
action
.
OracleData
);
err
!=
nil
{
return
fmt
.
Errorf
(
"failed to load oracle data: %w"
,
err
)
}
}
}
switch
action
.
Type
{
case
solver
.
ActionTypeMove
:
a
.
metrics
.
RecordGameMove
()
case
solver
.
ActionTypeStep
:
a
.
metrics
.
RecordGameStep
()
}
}
// Step on all leaf claims
log
.
Info
(
"Performing action"
)
for
_
,
claim
:=
range
game
.
Claims
()
{
err
:=
a
.
responder
.
PerformAction
(
ctx
,
action
)
if
err
:=
a
.
step
(
ctx
,
claim
,
game
);
err
!=
nil
{
if
err
!=
nil
{
log
.
Error
(
"
Failed to step
"
,
"err"
,
err
)
log
.
Error
(
"
Action failed
"
,
"err"
,
err
)
}
}
}
}
return
nil
return
nil
...
@@ -118,68 +135,3 @@ func (a *Agent) newGameFromContracts(ctx context.Context) (types.Game, error) {
...
@@ -118,68 +135,3 @@ func (a *Agent) newGameFromContracts(ctx context.Context) (types.Game, error) {
}
}
return
game
,
nil
return
game
,
nil
}
}
// move determines & executes the next move given a claim
func
(
a
*
Agent
)
move
(
ctx
context
.
Context
,
claim
types
.
Claim
,
game
types
.
Game
)
error
{
nextMove
,
err
:=
a
.
solver
.
NextMove
(
ctx
,
claim
,
game
.
AgreeWithClaimLevel
(
claim
))
if
err
!=
nil
{
return
fmt
.
Errorf
(
"execute next move: %w"
,
err
)
}
if
nextMove
==
nil
{
a
.
log
.
Debug
(
"No next move"
)
return
nil
}
move
:=
*
nextMove
log
:=
a
.
log
.
New
(
"is_defend"
,
move
.
DefendsParent
(),
"depth"
,
move
.
Depth
(),
"index_at_depth"
,
move
.
IndexAtDepth
(),
"value"
,
move
.
Value
,
"trace_index"
,
move
.
TraceIndex
(
a
.
maxDepth
),
"parent_value"
,
claim
.
Value
,
"parent_trace_index"
,
claim
.
TraceIndex
(
a
.
maxDepth
))
if
game
.
IsDuplicate
(
move
)
{
log
.
Debug
(
"Skipping duplicate move"
)
return
nil
}
a
.
metrics
.
RecordGameMove
()
log
.
Info
(
"Performing move"
)
return
a
.
responder
.
Respond
(
ctx
,
move
)
}
// step determines & executes the next step against a leaf claim through the responder
func
(
a
*
Agent
)
step
(
ctx
context
.
Context
,
claim
types
.
Claim
,
game
types
.
Game
)
error
{
if
claim
.
Depth
()
!=
a
.
maxDepth
{
return
nil
}
agreeWithClaimLevel
:=
game
.
AgreeWithClaimLevel
(
claim
)
if
agreeWithClaimLevel
{
a
.
log
.
Debug
(
"Agree with leaf claim, skipping step"
,
"claim_depth"
,
claim
.
Depth
(),
"maxDepth"
,
a
.
maxDepth
)
return
nil
}
if
claim
.
Countered
{
a
.
log
.
Debug
(
"Step already executed against claim"
,
"depth"
,
claim
.
Depth
(),
"index_at_depth"
,
claim
.
IndexAtDepth
(),
"value"
,
claim
.
Value
)
return
nil
}
a
.
log
.
Info
(
"Attempting step"
,
"claim_depth"
,
claim
.
Depth
(),
"maxDepth"
,
a
.
maxDepth
)
step
,
err
:=
a
.
solver
.
AttemptStep
(
ctx
,
claim
,
agreeWithClaimLevel
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"attempt step: %w"
,
err
)
}
if
step
.
OracleData
!=
nil
{
a
.
log
.
Info
(
"Updating oracle data"
,
"oracleKey"
,
step
.
OracleData
.
OracleKey
,
"oracleData"
,
step
.
OracleData
.
OracleData
)
if
err
:=
a
.
updater
.
UpdateOracle
(
ctx
,
step
.
OracleData
);
err
!=
nil
{
return
fmt
.
Errorf
(
"failed to load oracle data: %w"
,
err
)
}
}
a
.
log
.
Info
(
"Performing step"
,
"is_attack"
,
step
.
IsAttack
,
"depth"
,
step
.
LeafClaim
.
Depth
(),
"index_at_depth"
,
step
.
LeafClaim
.
IndexAtDepth
(),
"value"
,
step
.
LeafClaim
.
Value
)
a
.
metrics
.
RecordGameStep
()
callData
:=
types
.
StepCallData
{
ClaimIndex
:
uint64
(
step
.
LeafClaim
.
ContractIndex
),
IsAttack
:
step
.
IsAttack
,
StateData
:
step
.
PreState
,
Proof
:
step
.
ProofData
,
}
return
a
.
responder
.
Step
(
ctx
,
callData
)
}
op-challenger/game/fault/agent_test.go
View file @
86b91fc6
...
@@ -5,6 +5,7 @@ import (
...
@@ -5,6 +5,7 @@ import (
"errors"
"errors"
"testing"
"testing"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/solver"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/test"
"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/trace/alphabet"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
...
@@ -144,11 +145,7 @@ func (s *stubResponder) Resolve(ctx context.Context) error {
...
@@ -144,11 +145,7 @@ func (s *stubResponder) Resolve(ctx context.Context) error {
return
s
.
resolveErr
return
s
.
resolveErr
}
}
func
(
s
*
stubResponder
)
Respond
(
ctx
context
.
Context
,
response
types
.
Claim
)
error
{
func
(
s
*
stubResponder
)
PerformAction
(
ctx
context
.
Context
,
response
solver
.
Action
)
error
{
panic
(
"Not implemented"
)
}
func
(
s
*
stubResponder
)
Step
(
ctx
context
.
Context
,
stepData
types
.
StepCallData
)
error
{
panic
(
"Not implemented"
)
panic
(
"Not implemented"
)
}
}
...
...
op-challenger/game/fault/responder/abi_test.go
View file @
86b91fc6
...
@@ -5,7 +5,6 @@ import (
...
@@ -5,7 +5,6 @@ import (
"testing"
"testing"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common"
...
@@ -94,12 +93,7 @@ func TestBuildFaultStepData(t *testing.T) {
...
@@ -94,12 +93,7 @@ func TestBuildFaultStepData(t *testing.T) {
resp
,
_
:=
newTestFaultResponder
(
t
)
resp
,
_
:=
newTestFaultResponder
(
t
)
data
,
err
:=
resp
.
buildStepTxData
(
types
.
StepCallData
{
data
,
err
:=
resp
.
buildStepTxData
(
2
,
false
,
[]
byte
{
0x01
},
[]
byte
{
0x02
})
ClaimIndex
:
2
,
IsAttack
:
false
,
StateData
:
[]
byte
{
0x01
},
Proof
:
[]
byte
{
0x02
},
})
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
opts
.
GasLimit
=
100
_000
opts
.
GasLimit
=
100
_000
...
...
op-challenger/game/fault/responder/responder.go
View file @
86b91fc6
...
@@ -5,7 +5,7 @@ import (
...
@@ -5,7 +5,7 @@ import (
"math/big"
"math/big"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/
types
"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/
solver
"
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-service/txmgr"
"github.com/ethereum-optimism/optimism/op-service/txmgr"
...
@@ -16,8 +16,8 @@ import (
...
@@ -16,8 +16,8 @@ import (
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/log"
)
)
//
f
aultResponder implements the [Responder] interface to send onchain transactions.
//
F
aultResponder implements the [Responder] interface to send onchain transactions.
type
f
aultResponder
struct
{
type
F
aultResponder
struct
{
log
log
.
Logger
log
log
.
Logger
txMgr
txmgr
.
TxManager
txMgr
txmgr
.
TxManager
...
@@ -26,13 +26,13 @@ type faultResponder struct {
...
@@ -26,13 +26,13 @@ type faultResponder struct {
fdgAbi
*
abi
.
ABI
fdgAbi
*
abi
.
ABI
}
}
// NewFaultResponder returns a new [
f
aultResponder].
// NewFaultResponder returns a new [
F
aultResponder].
func
NewFaultResponder
(
logger
log
.
Logger
,
txManagr
txmgr
.
TxManager
,
fdgAddr
common
.
Address
)
(
*
f
aultResponder
,
error
)
{
func
NewFaultResponder
(
logger
log
.
Logger
,
txManagr
txmgr
.
TxManager
,
fdgAddr
common
.
Address
)
(
*
F
aultResponder
,
error
)
{
fdgAbi
,
err
:=
bindings
.
FaultDisputeGameMetaData
.
GetAbi
()
fdgAbi
,
err
:=
bindings
.
FaultDisputeGameMetaData
.
GetAbi
()
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
err
return
nil
,
err
}
}
return
&
f
aultResponder
{
return
&
F
aultResponder
{
log
:
logger
,
log
:
logger
,
txMgr
:
txManagr
,
txMgr
:
txManagr
,
fdgAddr
:
fdgAddr
,
fdgAddr
:
fdgAddr
,
...
@@ -41,7 +41,7 @@ func NewFaultResponder(logger log.Logger, txManagr txmgr.TxManager, fdgAddr comm
...
@@ -41,7 +41,7 @@ func NewFaultResponder(logger log.Logger, txManagr txmgr.TxManager, fdgAddr comm
}
}
// buildFaultDefendData creates the transaction data for the Defend function.
// buildFaultDefendData creates the transaction data for the Defend function.
func
(
r
*
f
aultResponder
)
buildFaultDefendData
(
parentContractIndex
int
,
pivot
[
32
]
byte
)
([]
byte
,
error
)
{
func
(
r
*
F
aultResponder
)
buildFaultDefendData
(
parentContractIndex
int
,
pivot
[
32
]
byte
)
([]
byte
,
error
)
{
return
r
.
fdgAbi
.
Pack
(
return
r
.
fdgAbi
.
Pack
(
"defend"
,
"defend"
,
big
.
NewInt
(
int64
(
parentContractIndex
)),
big
.
NewInt
(
int64
(
parentContractIndex
)),
...
@@ -50,7 +50,7 @@ func (r *faultResponder) buildFaultDefendData(parentContractIndex int, pivot [32
...
@@ -50,7 +50,7 @@ func (r *faultResponder) buildFaultDefendData(parentContractIndex int, pivot [32
}
}
// buildFaultAttackData creates the transaction data for the Attack function.
// buildFaultAttackData creates the transaction data for the Attack function.
func
(
r
*
f
aultResponder
)
buildFaultAttackData
(
parentContractIndex
int
,
pivot
[
32
]
byte
)
([]
byte
,
error
)
{
func
(
r
*
F
aultResponder
)
buildFaultAttackData
(
parentContractIndex
int
,
pivot
[
32
]
byte
)
([]
byte
,
error
)
{
return
r
.
fdgAbi
.
Pack
(
return
r
.
fdgAbi
.
Pack
(
"attack"
,
"attack"
,
big
.
NewInt
(
int64
(
parentContractIndex
)),
big
.
NewInt
(
int64
(
parentContractIndex
)),
...
@@ -59,30 +59,13 @@ func (r *faultResponder) buildFaultAttackData(parentContractIndex int, pivot [32
...
@@ -59,30 +59,13 @@ func (r *faultResponder) buildFaultAttackData(parentContractIndex int, pivot [32
}
}
// buildResolveData creates the transaction data for the Resolve function.
// buildResolveData creates the transaction data for the Resolve function.
func
(
r
*
f
aultResponder
)
buildResolveData
()
([]
byte
,
error
)
{
func
(
r
*
F
aultResponder
)
buildResolveData
()
([]
byte
,
error
)
{
return
r
.
fdgAbi
.
Pack
(
"resolve"
)
return
r
.
fdgAbi
.
Pack
(
"resolve"
)
}
}
// BuildTx builds the transaction for the [faultResponder].
func
(
r
*
faultResponder
)
BuildTx
(
ctx
context
.
Context
,
response
types
.
Claim
)
([]
byte
,
error
)
{
if
response
.
DefendsParent
()
{
txData
,
err
:=
r
.
buildFaultDefendData
(
response
.
ParentContractIndex
,
response
.
ValueBytes
())
if
err
!=
nil
{
return
nil
,
err
}
return
txData
,
nil
}
else
{
txData
,
err
:=
r
.
buildFaultAttackData
(
response
.
ParentContractIndex
,
response
.
ValueBytes
())
if
err
!=
nil
{
return
nil
,
err
}
return
txData
,
nil
}
}
// CallResolve determines if the resolve function on the fault dispute game contract
// CallResolve determines if the resolve function on the fault dispute game contract
// would succeed. Returns the game status if the call would succeed, errors otherwise.
// would succeed. Returns the game status if the call would succeed, errors otherwise.
func
(
r
*
f
aultResponder
)
CallResolve
(
ctx
context
.
Context
)
(
gameTypes
.
GameStatus
,
error
)
{
func
(
r
*
F
aultResponder
)
CallResolve
(
ctx
context
.
Context
)
(
gameTypes
.
GameStatus
,
error
)
{
txData
,
err
:=
r
.
buildResolveData
()
txData
,
err
:=
r
.
buildResolveData
()
if
err
!=
nil
{
if
err
!=
nil
{
return
gameTypes
.
GameStatusInProgress
,
err
return
gameTypes
.
GameStatusInProgress
,
err
...
@@ -102,7 +85,7 @@ func (r *faultResponder) CallResolve(ctx context.Context) (gameTypes.GameStatus,
...
@@ -102,7 +85,7 @@ func (r *faultResponder) CallResolve(ctx context.Context) (gameTypes.GameStatus,
}
}
// Resolve executes a resolve transaction to resolve a fault dispute game.
// Resolve executes a resolve transaction to resolve a fault dispute game.
func
(
r
*
f
aultResponder
)
Resolve
(
ctx
context
.
Context
)
error
{
func
(
r
*
F
aultResponder
)
Resolve
(
ctx
context
.
Context
)
error
{
txData
,
err
:=
r
.
buildResolveData
()
txData
,
err
:=
r
.
buildResolveData
()
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
...
@@ -111,9 +94,19 @@ func (r *faultResponder) Resolve(ctx context.Context) error {
...
@@ -111,9 +94,19 @@ func (r *faultResponder) Resolve(ctx context.Context) error {
return
r
.
sendTxAndWait
(
ctx
,
txData
)
return
r
.
sendTxAndWait
(
ctx
,
txData
)
}
}
// Respond takes a [Claim] and executes the response action.
func
(
r
*
FaultResponder
)
PerformAction
(
ctx
context
.
Context
,
action
solver
.
Action
)
error
{
func
(
r
*
faultResponder
)
Respond
(
ctx
context
.
Context
,
response
types
.
Claim
)
error
{
var
txData
[]
byte
txData
,
err
:=
r
.
BuildTx
(
ctx
,
response
)
var
err
error
switch
action
.
Type
{
case
solver
.
ActionTypeMove
:
if
action
.
IsAttack
{
txData
,
err
=
r
.
buildFaultAttackData
(
action
.
ParentIdx
,
action
.
Value
)
}
else
{
txData
,
err
=
r
.
buildFaultDefendData
(
action
.
ParentIdx
,
action
.
Value
)
}
case
solver
.
ActionTypeStep
:
txData
,
err
=
r
.
buildStepTxData
(
uint64
(
action
.
ParentIdx
),
action
.
IsAttack
,
action
.
PreState
,
action
.
ProofData
)
}
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
...
@@ -122,7 +115,7 @@ func (r *faultResponder) Respond(ctx context.Context, response types.Claim) erro
...
@@ -122,7 +115,7 @@ func (r *faultResponder) Respond(ctx context.Context, response types.Claim) erro
// sendTxAndWait sends a transaction through the [txmgr] and waits for a receipt.
// sendTxAndWait sends a transaction through the [txmgr] and waits for a receipt.
// This sets the tx GasLimit to 0, performing gas estimation online through the [txmgr].
// This sets the tx GasLimit to 0, performing gas estimation online through the [txmgr].
func
(
r
*
f
aultResponder
)
sendTxAndWait
(
ctx
context
.
Context
,
txData
[]
byte
)
error
{
func
(
r
*
F
aultResponder
)
sendTxAndWait
(
ctx
context
.
Context
,
txData
[]
byte
)
error
{
receipt
,
err
:=
r
.
txMgr
.
Send
(
ctx
,
txmgr
.
TxCandidate
{
receipt
,
err
:=
r
.
txMgr
.
Send
(
ctx
,
txmgr
.
TxCandidate
{
To
:
&
r
.
fdgAddr
,
To
:
&
r
.
fdgAddr
,
TxData
:
txData
,
TxData
:
txData
,
...
@@ -140,21 +133,12 @@ func (r *faultResponder) sendTxAndWait(ctx context.Context, txData []byte) error
...
@@ -140,21 +133,12 @@ func (r *faultResponder) sendTxAndWait(ctx context.Context, txData []byte) error
}
}
// buildStepTxData creates the transaction data for the step function.
// buildStepTxData creates the transaction data for the step function.
func
(
r
*
faultResponder
)
buildStepTxData
(
stepData
types
.
StepCallData
)
([]
byte
,
error
)
{
func
(
r
*
FaultResponder
)
buildStepTxData
(
claimIdx
uint64
,
isAttack
bool
,
stateData
[]
byte
,
proof
[]
byte
)
([]
byte
,
error
)
{
return
r
.
fdgAbi
.
Pack
(
return
r
.
fdgAbi
.
Pack
(
"step"
,
"step"
,
big
.
NewInt
(
int64
(
stepData
.
ClaimInde
x
)),
big
.
NewInt
(
int64
(
claimId
x
)),
stepData
.
I
sAttack
,
i
sAttack
,
st
epData
.
St
ateData
,
stateData
,
stepData
.
P
roof
,
p
roof
,
)
)
}
}
// Step accepts step data and executes the step on the fault dispute game contract.
func
(
r
*
faultResponder
)
Step
(
ctx
context
.
Context
,
stepData
types
.
StepCallData
)
error
{
txData
,
err
:=
r
.
buildStepTxData
(
stepData
)
if
err
!=
nil
{
return
err
}
return
r
.
sendTxAndWait
(
ctx
,
txData
)
}
op-challenger/game/fault/responder/responder_test.go
View file @
86b91fc6
...
@@ -7,7 +7,7 @@ import (
...
@@ -7,7 +7,7 @@ import (
"testing"
"testing"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/
types
"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/
solver
"
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-node/testlog"
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum-optimism/optimism/op-service/txmgr"
...
@@ -74,73 +74,98 @@ func TestResolve(t *testing.T) {
...
@@ -74,73 +74,98 @@ func TestResolve(t *testing.T) {
}
}
// TestRespond tests the [Responder.Respond] method.
// TestRespond tests the [Responder.Respond] method.
func
Test
Respond
(
t
*
testing
.
T
)
{
func
Test
PerformAction
(
t
*
testing
.
T
)
{
t
.
Run
(
"send fails"
,
func
(
t
*
testing
.
T
)
{
t
.
Run
(
"send fails"
,
func
(
t
*
testing
.
T
)
{
responder
,
mockTxMgr
:=
newTestFaultResponder
(
t
)
responder
,
mockTxMgr
:=
newTestFaultResponder
(
t
)
mockTxMgr
.
sendFails
=
true
mockTxMgr
.
sendFails
=
true
err
:=
responder
.
Respond
(
context
.
Background
(),
generateMockResponseClaim
())
err
:=
responder
.
PerformAction
(
context
.
Background
(),
solver
.
Action
{
Type
:
solver
.
ActionTypeMove
,
ParentIdx
:
123
,
IsAttack
:
true
,
Value
:
common
.
Hash
{
0xaa
},
})
require
.
ErrorIs
(
t
,
err
,
mockSendError
)
require
.
ErrorIs
(
t
,
err
,
mockSendError
)
require
.
Equal
(
t
,
0
,
mockTxMgr
.
sends
)
require
.
Equal
(
t
,
0
,
mockTxMgr
.
sends
)
})
})
t
.
Run
(
"sends response"
,
func
(
t
*
testing
.
T
)
{
t
.
Run
(
"sends response"
,
func
(
t
*
testing
.
T
)
{
responder
,
mockTxMgr
:=
newTestFaultResponder
(
t
)
responder
,
mockTxMgr
:=
newTestFaultResponder
(
t
)
err
:=
responder
.
Respond
(
context
.
Background
(),
generateMockResponseClaim
())
err
:=
responder
.
PerformAction
(
context
.
Background
(),
solver
.
Action
{
Type
:
solver
.
ActionTypeMove
,
ParentIdx
:
123
,
IsAttack
:
true
,
Value
:
common
.
Hash
{
0xaa
},
})
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
1
,
mockTxMgr
.
sends
)
require
.
Equal
(
t
,
1
,
mockTxMgr
.
sends
)
})
})
}
// TestBuildTx tests the [Responder.BuildTx] method.
func
TestBuildTx
(
t
*
testing
.
T
)
{
t
.
Run
(
"attack"
,
func
(
t
*
testing
.
T
)
{
t
.
Run
(
"attack"
,
func
(
t
*
testing
.
T
)
{
responder
,
_
:=
newTestFaultResponder
(
t
)
responder
,
mockTxMgr
:=
newTestFaultResponder
(
t
)
responseClaim
:=
generateMockResponseClaim
()
action
:=
solver
.
Action
{
responseClaim
.
ParentContractIndex
=
7
Type
:
solver
.
ActionTypeMove
,
tx
,
err
:=
responder
.
BuildTx
(
context
.
Background
(),
responseClaim
)
ParentIdx
:
123
,
IsAttack
:
true
,
Value
:
common
.
Hash
{
0xaa
},
}
err
:=
responder
.
PerformAction
(
context
.
Background
(),
action
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
// Pack the tx data manually.
// Pack the tx data manually.
fdgAbi
,
err
:=
bindings
.
FaultDisputeGameMetaData
.
GetAbi
()
fdgAbi
,
err
:=
bindings
.
FaultDisputeGameMetaData
.
GetAbi
()
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
parent
:=
big
.
NewInt
(
int64
(
7
))
expected
,
err
:=
fdgAbi
.
Pack
(
"attack"
,
big
.
NewInt
(
int64
(
action
.
ParentIdx
)),
action
.
Value
)
claim
:=
responseClaim
.
ValueBytes
()
expected
,
err
:=
fdgAbi
.
Pack
(
"attack"
,
parent
,
claim
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
expected
,
tx
)
require
.
Len
(
t
,
mockTxMgr
.
sent
,
1
)
require
.
Equal
(
t
,
expected
,
mockTxMgr
.
sent
[
0
]
.
TxData
)
})
})
t
.
Run
(
"defend"
,
func
(
t
*
testing
.
T
)
{
t
.
Run
(
"defend"
,
func
(
t
*
testing
.
T
)
{
responder
,
_
:=
newTestFaultResponder
(
t
)
responder
,
mockTxMgr
:=
newTestFaultResponder
(
t
)
responseClaim
:=
types
.
Claim
{
action
:=
solver
.
Action
{
ClaimData
:
types
.
ClaimData
{
Type
:
solver
.
ActionTypeMove
,
Value
:
common
.
Hash
{
0x01
},
ParentIdx
:
123
,
Position
:
types
.
NewPositionFromGIndex
(
3
),
IsAttack
:
false
,
},
Value
:
common
.
Hash
{
0xaa
},
Parent
:
types
.
ClaimData
{
Value
:
common
.
Hash
{
0x02
},
Position
:
types
.
NewPositionFromGIndex
(
6
),
},
ContractIndex
:
0
,
ParentContractIndex
:
7
,
}
}
tx
,
err
:=
responder
.
BuildTx
(
context
.
Background
(),
responseClaim
)
err
:=
responder
.
PerformAction
(
context
.
Background
(),
action
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
// Pack the tx data manually.
// Pack the tx data manually.
fdgAbi
,
err
:=
bindings
.
FaultDisputeGameMetaData
.
GetAbi
()
fdgAbi
,
err
:=
bindings
.
FaultDisputeGameMetaData
.
GetAbi
()
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
parent
:=
big
.
NewInt
(
int64
(
7
))
expected
,
err
:=
fdgAbi
.
Pack
(
"defend"
,
big
.
NewInt
(
int64
(
action
.
ParentIdx
)),
action
.
Value
)
claim
:=
responseClaim
.
ValueBytes
()
require
.
NoError
(
t
,
err
)
expected
,
err
:=
fdgAbi
.
Pack
(
"defend"
,
parent
,
claim
)
require
.
Len
(
t
,
mockTxMgr
.
sent
,
1
)
require
.
Equal
(
t
,
expected
,
mockTxMgr
.
sent
[
0
]
.
TxData
)
})
t
.
Run
(
"step"
,
func
(
t
*
testing
.
T
)
{
responder
,
mockTxMgr
:=
newTestFaultResponder
(
t
)
action
:=
solver
.
Action
{
Type
:
solver
.
ActionTypeStep
,
ParentIdx
:
123
,
IsAttack
:
true
,
PreState
:
[]
byte
{
1
,
2
,
3
},
ProofData
:
[]
byte
{
4
,
5
,
6
},
}
err
:=
responder
.
PerformAction
(
context
.
Background
(),
action
)
require
.
NoError
(
t
,
err
)
// Pack the tx data manually.
fdgAbi
,
err
:=
bindings
.
FaultDisputeGameMetaData
.
GetAbi
()
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
expected
,
tx
)
expected
,
err
:=
fdgAbi
.
Pack
(
"step"
,
big
.
NewInt
(
int64
(
action
.
ParentIdx
)),
true
,
action
.
PreState
,
action
.
ProofData
)
require
.
NoError
(
t
,
err
)
require
.
Len
(
t
,
mockTxMgr
.
sent
,
1
)
require
.
Equal
(
t
,
expected
,
mockTxMgr
.
sent
[
0
]
.
TxData
)
})
})
}
}
func
newTestFaultResponder
(
t
*
testing
.
T
)
(
*
f
aultResponder
,
*
mockTxManager
)
{
func
newTestFaultResponder
(
t
*
testing
.
T
)
(
*
F
aultResponder
,
*
mockTxManager
)
{
log
:=
testlog
.
Logger
(
t
,
log
.
LvlError
)
log
:=
testlog
.
Logger
(
t
,
log
.
LvlError
)
mockTxMgr
:=
&
mockTxManager
{}
mockTxMgr
:=
&
mockTxManager
{}
responder
,
err
:=
NewFaultResponder
(
log
,
mockTxMgr
,
mockFdgAddress
)
responder
,
err
:=
NewFaultResponder
(
log
,
mockTxMgr
,
mockFdgAddress
)
...
@@ -151,6 +176,7 @@ func newTestFaultResponder(t *testing.T) (*faultResponder, *mockTxManager) {
...
@@ -151,6 +176,7 @@ func newTestFaultResponder(t *testing.T) (*faultResponder, *mockTxManager) {
type
mockTxManager
struct
{
type
mockTxManager
struct
{
from
common
.
Address
from
common
.
Address
sends
int
sends
int
sent
[]
txmgr
.
TxCandidate
calls
int
calls
int
sendFails
bool
sendFails
bool
callFails
bool
callFails
bool
...
@@ -162,6 +188,7 @@ func (m *mockTxManager) Send(ctx context.Context, candidate txmgr.TxCandidate) (
...
@@ -162,6 +188,7 @@ func (m *mockTxManager) Send(ctx context.Context, candidate txmgr.TxCandidate) (
return
nil
,
mockSendError
return
nil
,
mockSendError
}
}
m
.
sends
++
m
.
sends
++
m
.
sent
=
append
(
m
.
sent
,
candidate
)
return
ethtypes
.
NewReceipt
(
return
ethtypes
.
NewReceipt
(
[]
byte
{},
[]
byte
{},
false
,
false
,
...
@@ -189,18 +216,3 @@ func (m *mockTxManager) BlockNumber(ctx context.Context) (uint64, error) {
...
@@ -189,18 +216,3 @@ func (m *mockTxManager) BlockNumber(ctx context.Context) (uint64, error) {
func
(
m
*
mockTxManager
)
From
()
common
.
Address
{
func
(
m
*
mockTxManager
)
From
()
common
.
Address
{
return
m
.
from
return
m
.
from
}
}
func
generateMockResponseClaim
()
types
.
Claim
{
return
types
.
Claim
{
ClaimData
:
types
.
ClaimData
{
Value
:
common
.
Hash
{
0x01
},
Position
:
types
.
NewPositionFromGIndex
(
2
),
},
Parent
:
types
.
ClaimData
{
Value
:
common
.
Hash
{
0x02
},
Position
:
types
.
NewPositionFromGIndex
(
1
),
},
ContractIndex
:
0
,
ParentContractIndex
:
0
,
}
}
op-challenger/game/fault/solver/game_solver.go
0 → 100644
View file @
86b91fc6
package
solver
import
(
"context"
"errors"
"fmt"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum/go-ethereum/common"
)
type
ActionType
string
const
(
ActionTypeMove
ActionType
=
"move"
ActionTypeStep
ActionType
=
"step"
)
func
(
a
ActionType
)
String
()
string
{
return
string
(
a
)
}
type
Action
struct
{
Type
ActionType
ParentIdx
int
IsAttack
bool
// Moves
Value
common
.
Hash
// Steps
PreState
[]
byte
ProofData
[]
byte
OracleData
*
types
.
PreimageOracleData
}
type
GameSolver
struct
{
claimSolver
*
claimSolver
gameDepth
int
}
func
NewGameSolver
(
gameDepth
int
,
trace
types
.
TraceProvider
)
*
GameSolver
{
return
&
GameSolver
{
claimSolver
:
newClaimSolver
(
gameDepth
,
trace
),
gameDepth
:
gameDepth
,
}
}
func
(
s
*
GameSolver
)
CalculateNextActions
(
ctx
context
.
Context
,
game
types
.
Game
)
([]
Action
,
error
)
{
var
errs
[]
error
var
actions
[]
Action
for
_
,
claim
:=
range
game
.
Claims
()
{
var
action
*
Action
var
err
error
if
claim
.
Depth
()
==
s
.
gameDepth
{
action
,
err
=
s
.
calculateStep
(
ctx
,
game
,
claim
)
}
else
{
action
,
err
=
s
.
calculateMove
(
ctx
,
game
,
claim
)
}
if
err
!=
nil
{
errs
=
append
(
errs
,
err
)
continue
}
if
action
==
nil
{
continue
}
actions
=
append
(
actions
,
*
action
)
}
return
actions
,
errors
.
Join
(
errs
...
)
}
func
(
s
*
GameSolver
)
calculateStep
(
ctx
context
.
Context
,
game
types
.
Game
,
claim
types
.
Claim
)
(
*
Action
,
error
)
{
if
claim
.
Countered
{
return
nil
,
nil
}
if
game
.
AgreeWithClaimLevel
(
claim
)
{
return
nil
,
nil
}
step
,
err
:=
s
.
claimSolver
.
AttemptStep
(
ctx
,
claim
,
game
.
AgreeWithClaimLevel
(
claim
))
if
err
!=
nil
{
return
nil
,
err
}
return
&
Action
{
Type
:
ActionTypeStep
,
ParentIdx
:
step
.
LeafClaim
.
ContractIndex
,
IsAttack
:
step
.
IsAttack
,
PreState
:
step
.
PreState
,
ProofData
:
step
.
ProofData
,
OracleData
:
step
.
OracleData
,
},
nil
}
func
(
s
*
GameSolver
)
calculateMove
(
ctx
context
.
Context
,
game
types
.
Game
,
claim
types
.
Claim
)
(
*
Action
,
error
)
{
move
,
err
:=
s
.
claimSolver
.
NextMove
(
ctx
,
claim
,
game
.
AgreeWithClaimLevel
(
claim
))
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"failed to calculate next move for claim index %v: %w"
,
claim
.
ContractIndex
,
err
)
}
if
move
==
nil
||
game
.
IsDuplicate
(
*
move
)
{
return
nil
,
nil
}
return
&
Action
{
Type
:
ActionTypeMove
,
IsAttack
:
!
move
.
DefendsParent
(),
ParentIdx
:
move
.
ParentContractIndex
,
Value
:
move
.
Value
,
},
nil
}
op-challenger/game/fault/solver/game_solver_test.go
0 → 100644
View file @
86b91fc6
package
solver
import
(
"context"
"encoding/hex"
"testing"
faulttest
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/test"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require"
)
type
actionMaker
func
(
game
types
.
Game
)
Action
func
TestCalculateNextActions
(
t
*
testing
.
T
)
{
maxDepth
:=
4
claimBuilder
:=
faulttest
.
NewAlphabetClaimBuilder
(
t
,
maxDepth
)
attackClaim
:=
func
(
parentIdx
int
)
actionMaker
{
return
func
(
game
types
.
Game
)
Action
{
parentClaim
:=
game
.
Claims
()[
parentIdx
]
return
Action
{
Type
:
ActionTypeMove
,
ParentIdx
:
parentIdx
,
IsAttack
:
true
,
Value
:
claimBuilder
.
CorrectClaimAtPosition
(
parentClaim
.
Position
.
Attack
()),
}
}
}
defendClaim
:=
func
(
parentIdx
int
)
actionMaker
{
return
func
(
game
types
.
Game
)
Action
{
parentClaim
:=
game
.
Claims
()[
parentIdx
]
return
Action
{
Type
:
ActionTypeMove
,
ParentIdx
:
parentIdx
,
IsAttack
:
false
,
Value
:
claimBuilder
.
CorrectClaimAtPosition
(
parentClaim
.
Position
.
Defend
()),
}
}
}
stepAttack
:=
func
(
parentIdx
int
)
actionMaker
{
return
func
(
game
types
.
Game
)
Action
{
parentClaim
:=
game
.
Claims
()[
parentIdx
]
traceIdx
:=
parentClaim
.
Position
.
TraceIndex
(
maxDepth
)
return
Action
{
Type
:
ActionTypeStep
,
ParentIdx
:
parentIdx
,
IsAttack
:
true
,
PreState
:
claimBuilder
.
CorrectPreState
(
traceIdx
),
ProofData
:
claimBuilder
.
CorrectProofData
(
traceIdx
),
OracleData
:
claimBuilder
.
CorrectOracleData
(
traceIdx
),
}
}
}
stepDefend
:=
func
(
parentIdx
int
)
actionMaker
{
return
func
(
game
types
.
Game
)
Action
{
parentClaim
:=
game
.
Claims
()[
parentIdx
]
traceIdx
:=
parentClaim
.
Position
.
TraceIndex
(
maxDepth
)
+
1
return
Action
{
Type
:
ActionTypeStep
,
ParentIdx
:
parentIdx
,
IsAttack
:
false
,
PreState
:
claimBuilder
.
CorrectPreState
(
traceIdx
),
ProofData
:
claimBuilder
.
CorrectProofData
(
traceIdx
),
OracleData
:
claimBuilder
.
CorrectOracleData
(
traceIdx
),
}
}
}
tests
:=
[]
struct
{
name
string
agreeWithOutputRoot
bool
rootClaimCorrect
bool
setupGame
func
(
builder
*
faulttest
.
GameBuilder
)
expectedActions
[]
actionMaker
}{
{
name
:
"AttackRootClaim"
,
agreeWithOutputRoot
:
true
,
setupGame
:
func
(
builder
*
faulttest
.
GameBuilder
)
{},
expectedActions
:
[]
actionMaker
{
attackClaim
(
0
),
},
},
{
name
:
"DoNotAttackRootClaimWhenDisagreeWithOutputRoot"
,
agreeWithOutputRoot
:
false
,
setupGame
:
func
(
builder
*
faulttest
.
GameBuilder
)
{},
expectedActions
:
nil
,
},
{
// Note: The fault dispute game contract should prevent a correct root claim from actually being posted
// But for completeness, test we ignore it so we don't get sucked into playing an unwinnable game.
name
:
"DoNotAttackCorrectRootClaim_AgreeWithOutputRoot"
,
agreeWithOutputRoot
:
true
,
rootClaimCorrect
:
true
,
setupGame
:
func
(
builder
*
faulttest
.
GameBuilder
)
{},
expectedActions
:
nil
,
},
{
// Note: The fault dispute game contract should prevent a correct root claim from actually being posted
// But for completeness, test we ignore it so we don't get sucked into playing an unwinnable game.
name
:
"DoNotAttackCorrectRootClaim_DisagreeWithOutputRoot"
,
agreeWithOutputRoot
:
false
,
rootClaimCorrect
:
true
,
setupGame
:
func
(
builder
*
faulttest
.
GameBuilder
)
{},
expectedActions
:
nil
,
},
{
name
:
"DoNotPerformDuplicateMoves"
,
agreeWithOutputRoot
:
true
,
setupGame
:
func
(
builder
*
faulttest
.
GameBuilder
)
{
// Expected move has already been made.
builder
.
Seq
()
.
AttackCorrect
()
},
expectedActions
:
nil
,
},
{
name
:
"RespondToAllClaimsAtDisagreeingLevel"
,
agreeWithOutputRoot
:
true
,
setupGame
:
func
(
builder
*
faulttest
.
GameBuilder
)
{
honestClaim
:=
builder
.
Seq
()
.
AttackCorrect
()
// 1
honestClaim
.
AttackCorrect
()
// 2
honestClaim
.
DefendCorrect
()
// 3
honestClaim
.
Attack
(
common
.
Hash
{
0xaa
})
// 4
honestClaim
.
Attack
(
common
.
Hash
{
0xbb
})
// 5
honestClaim
.
Defend
(
common
.
Hash
{
0xcc
})
// 6
honestClaim
.
Defend
(
common
.
Hash
{
0xdd
})
// 7
},
expectedActions
:
[]
actionMaker
{
// Defend the correct claims
defendClaim
(
2
),
defendClaim
(
3
),
// Attack the incorrect claims
attackClaim
(
4
),
attackClaim
(
5
),
attackClaim
(
6
),
attackClaim
(
7
),
},
},
{
name
:
"StepAtMaxDepth"
,
agreeWithOutputRoot
:
true
,
setupGame
:
func
(
builder
*
faulttest
.
GameBuilder
)
{
lastHonestClaim
:=
builder
.
Seq
()
.
AttackCorrect
()
.
// 1 - Honest
AttackCorrect
()
.
// 2 - Dishonest
DefendCorrect
()
// 3 - Honest
lastHonestClaim
.
AttackCorrect
()
// 4 - Dishonest
lastHonestClaim
.
Attack
(
common
.
Hash
{
0xdd
})
// 5 - Dishonest
},
expectedActions
:
[]
actionMaker
{
stepDefend
(
4
),
stepAttack
(
5
),
},
},
}
for
_
,
test
:=
range
tests
{
test
:=
test
t
.
Run
(
test
.
name
,
func
(
t
*
testing
.
T
)
{
builder
:=
claimBuilder
.
GameBuilder
(
test
.
agreeWithOutputRoot
,
test
.
rootClaimCorrect
)
test
.
setupGame
(
builder
)
game
:=
builder
.
Game
for
i
,
claim
:=
range
game
.
Claims
()
{
t
.
Logf
(
"Claim %v: Pos: %v ParentIdx: %v, Countered: %v, Value: %v"
,
i
,
claim
.
Position
.
ToGIndex
(),
claim
.
ParentContractIndex
,
claim
.
Countered
,
claim
.
Value
)
}
solver
:=
NewGameSolver
(
maxDepth
,
claimBuilder
.
CorrectTraceProvider
())
actions
,
err
:=
solver
.
CalculateNextActions
(
context
.
Background
(),
game
)
require
.
NoError
(
t
,
err
)
for
i
,
action
:=
range
actions
{
t
.
Logf
(
"Move %v: Type: %v, ParentIdx: %v, Attack: %v, Value: %v, PreState: %v, ProofData: %v"
,
i
,
action
.
Type
,
action
.
ParentIdx
,
action
.
IsAttack
,
action
.
Value
,
hex
.
EncodeToString
(
action
.
PreState
),
hex
.
EncodeToString
(
action
.
ProofData
))
}
require
.
Len
(
t
,
actions
,
len
(
test
.
expectedActions
))
for
i
,
action
:=
range
test
.
expectedActions
{
require
.
Containsf
(
t
,
actions
,
action
(
game
),
"Expected claim %v missing"
,
i
)
}
})
}
}
op-challenger/game/fault/solver/solver.go
View file @
86b91fc6
...
@@ -15,22 +15,22 @@ var (
...
@@ -15,22 +15,22 @@ var (
ErrStepAgreedClaim
=
errors
.
New
(
"cannot step on claims we agree with"
)
ErrStepAgreedClaim
=
errors
.
New
(
"cannot step on claims we agree with"
)
)
)
// Solver uses a [TraceProvider] to determine the moves to make in a dispute game.
//
claim
Solver uses a [TraceProvider] to determine the moves to make in a dispute game.
type
Solver
struct
{
type
claim
Solver
struct
{
trace
types
.
TraceProvider
trace
types
.
TraceProvider
gameDepth
int
gameDepth
int
}
}
//
NewSolver creates a new [
Solver] using the provided [TraceProvider].
//
newClaimSolver creates a new [claim
Solver] using the provided [TraceProvider].
func
NewSolver
(
gameDepth
int
,
traceProvider
types
.
TraceProvider
)
*
Solver
{
func
newClaimSolver
(
gameDepth
int
,
traceProvider
types
.
TraceProvider
)
*
claim
Solver
{
return
&
Solver
{
return
&
claim
Solver
{
traceProvider
,
traceProvider
,
gameDepth
,
gameDepth
,
}
}
}
}
// NextMove returns the next move to make given the current state of the game.
// NextMove returns the next move to make given the current state of the game.
func
(
s
*
Solver
)
NextMove
(
ctx
context
.
Context
,
claim
types
.
Claim
,
agreeWithClaimLevel
bool
)
(
*
types
.
Claim
,
error
)
{
func
(
s
*
claim
Solver
)
NextMove
(
ctx
context
.
Context
,
claim
types
.
Claim
,
agreeWithClaimLevel
bool
)
(
*
types
.
Claim
,
error
)
{
if
agreeWithClaimLevel
{
if
agreeWithClaimLevel
{
return
nil
,
nil
return
nil
,
nil
}
}
...
@@ -58,7 +58,7 @@ type StepData struct {
...
@@ -58,7 +58,7 @@ type StepData struct {
// 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
(
ctx
context
.
Context
,
claim
types
.
Claim
,
agreeWithClaimLevel
bool
)
(
StepData
,
error
)
{
func
(
s
*
claim
Solver
)
AttemptStep
(
ctx
context
.
Context
,
claim
types
.
Claim
,
agreeWithClaimLevel
bool
)
(
StepData
,
error
)
{
if
claim
.
Depth
()
!=
s
.
gameDepth
{
if
claim
.
Depth
()
!=
s
.
gameDepth
{
return
StepData
{},
ErrStepNonLeafNode
return
StepData
{},
ErrStepNonLeafNode
}
}
...
@@ -100,7 +100,7 @@ func (s *Solver) AttemptStep(ctx context.Context, claim types.Claim, agreeWithCl
...
@@ -100,7 +100,7 @@ func (s *Solver) AttemptStep(ctx context.Context, claim types.Claim, agreeWithCl
}
}
// attack returns a response that attacks the claim.
// attack returns a response that attacks the claim.
func
(
s
*
Solver
)
attack
(
ctx
context
.
Context
,
claim
types
.
Claim
)
(
*
types
.
Claim
,
error
)
{
func
(
s
*
claim
Solver
)
attack
(
ctx
context
.
Context
,
claim
types
.
Claim
)
(
*
types
.
Claim
,
error
)
{
position
:=
claim
.
Attack
()
position
:=
claim
.
Attack
()
value
,
err
:=
s
.
traceAtPosition
(
ctx
,
position
)
value
,
err
:=
s
.
traceAtPosition
(
ctx
,
position
)
if
err
!=
nil
{
if
err
!=
nil
{
...
@@ -114,7 +114,7 @@ func (s *Solver) attack(ctx context.Context, claim types.Claim) (*types.Claim, e
...
@@ -114,7 +114,7 @@ func (s *Solver) attack(ctx context.Context, claim types.Claim) (*types.Claim, e
}
}
// defend returns a response that defends the claim.
// defend returns a response that defends the claim.
func
(
s
*
Solver
)
defend
(
ctx
context
.
Context
,
claim
types
.
Claim
)
(
*
types
.
Claim
,
error
)
{
func
(
s
*
claim
Solver
)
defend
(
ctx
context
.
Context
,
claim
types
.
Claim
)
(
*
types
.
Claim
,
error
)
{
if
claim
.
IsRoot
()
{
if
claim
.
IsRoot
()
{
return
nil
,
nil
return
nil
,
nil
}
}
...
@@ -131,13 +131,13 @@ func (s *Solver) defend(ctx context.Context, claim types.Claim) (*types.Claim, e
...
@@ -131,13 +131,13 @@ func (s *Solver) defend(ctx context.Context, claim types.Claim) (*types.Claim, e
}
}
// 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
*
Solver
)
agreeWithClaim
(
ctx
context
.
Context
,
claim
types
.
ClaimData
)
(
bool
,
error
)
{
func
(
s
*
claim
Solver
)
agreeWithClaim
(
ctx
context
.
Context
,
claim
types
.
ClaimData
)
(
bool
,
error
)
{
ourValue
,
err
:=
s
.
traceAtPosition
(
ctx
,
claim
.
Position
)
ourValue
,
err
:=
s
.
traceAtPosition
(
ctx
,
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].
// traceAtPosition returns the [common.Hash] from internal [TraceProvider] at the given [Position].
func
(
s
*
Solver
)
traceAtPosition
(
ctx
context
.
Context
,
p
types
.
Position
)
(
common
.
Hash
,
error
)
{
func
(
s
*
claim
Solver
)
traceAtPosition
(
ctx
context
.
Context
,
p
types
.
Position
)
(
common
.
Hash
,
error
)
{
index
:=
p
.
TraceIndex
(
s
.
gameDepth
)
index
:=
p
.
TraceIndex
(
s
.
gameDepth
)
hash
,
err
:=
s
.
trace
.
Get
(
ctx
,
index
)
hash
,
err
:=
s
.
trace
.
Get
(
ctx
,
index
)
return
hash
,
err
return
hash
,
err
...
...
op-challenger/game/fault/solver/solver_test.go
View file @
86b91fc6
package
solver
_test
package
solver
import
(
import
(
"context"
"context"
"errors"
"errors"
"testing"
"testing"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/solver"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/test"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/test"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/require"
...
@@ -84,7 +83,7 @@ func TestNextMove(t *testing.T) {
...
@@ -84,7 +83,7 @@ func TestNextMove(t *testing.T) {
for
_
,
test
:=
range
tests
{
for
_
,
test
:=
range
tests
{
test
:=
test
test
:=
test
t
.
Run
(
test
.
name
,
func
(
t
*
testing
.
T
)
{
t
.
Run
(
test
.
name
,
func
(
t
*
testing
.
T
)
{
solver
:=
solver
.
New
Solver
(
maxDepth
,
builder
.
CorrectTraceProvider
())
solver
:=
newClaim
Solver
(
maxDepth
,
builder
.
CorrectTraceProvider
())
move
,
err
:=
solver
.
NextMove
(
context
.
Background
(),
test
.
claim
,
test
.
agreeWithLevel
)
move
,
err
:=
solver
.
NextMove
(
context
.
Background
(),
test
.
claim
,
test
.
agreeWithLevel
)
if
test
.
expectedErr
==
nil
{
if
test
.
expectedErr
==
nil
{
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
...
@@ -174,19 +173,19 @@ func TestAttemptStep(t *testing.T) {
...
@@ -174,19 +173,19 @@ func TestAttemptStep(t *testing.T) {
{
{
name
:
"CannotStepNonLeaf"
,
name
:
"CannotStepNonLeaf"
,
claim
:
builder
.
Seq
(
false
)
.
Attack
(
false
)
.
Get
(),
claim
:
builder
.
Seq
(
false
)
.
Attack
(
false
)
.
Get
(),
expectedErr
:
solver
.
ErrStepNonLeafNode
,
expectedErr
:
ErrStepNonLeafNode
,
},
},
{
{
name
:
"CannotStepAgreedNode"
,
name
:
"CannotStepAgreedNode"
,
claim
:
builder
.
Seq
(
false
)
.
Attack
(
false
)
.
Get
(),
claim
:
builder
.
Seq
(
false
)
.
Attack
(
false
)
.
Get
(),
agreeWithLevel
:
true
,
agreeWithLevel
:
true
,
expectedErr
:
solver
.
ErrStepNonLeafNode
,
expectedErr
:
ErrStepNonLeafNode
,
},
},
{
{
name
:
"CannotStepAgreedNode"
,
name
:
"CannotStepAgreedNode"
,
claim
:
builder
.
Seq
(
false
)
.
Attack
(
false
)
.
Get
(),
claim
:
builder
.
Seq
(
false
)
.
Attack
(
false
)
.
Get
(),
agreeWithLevel
:
true
,
agreeWithLevel
:
true
,
expectedErr
:
solver
.
ErrStepNonLeafNode
,
expectedErr
:
ErrStepNonLeafNode
,
},
},
}
}
...
@@ -198,7 +197,7 @@ func TestAttemptStep(t *testing.T) {
...
@@ -198,7 +197,7 @@ func TestAttemptStep(t *testing.T) {
alphabetProvider
=
test
.
NewAlphabetWithProofProvider
(
t
,
maxDepth
,
errProvider
)
alphabetProvider
=
test
.
NewAlphabetWithProofProvider
(
t
,
maxDepth
,
errProvider
)
}
}
builder
=
test
.
NewClaimBuilder
(
t
,
maxDepth
,
alphabetProvider
)
builder
=
test
.
NewClaimBuilder
(
t
,
maxDepth
,
alphabetProvider
)
alphabetSolver
:=
solver
.
New
Solver
(
maxDepth
,
builder
.
CorrectTraceProvider
())
alphabetSolver
:=
newClaim
Solver
(
maxDepth
,
builder
.
CorrectTraceProvider
())
step
,
err
:=
alphabetSolver
.
AttemptStep
(
ctx
,
tableTest
.
claim
,
tableTest
.
agreeWithLevel
)
step
,
err
:=
alphabetSolver
.
AttemptStep
(
ctx
,
tableTest
.
claim
,
tableTest
.
agreeWithLevel
)
if
tableTest
.
expectedErr
==
nil
{
if
tableTest
.
expectedErr
==
nil
{
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
...
@@ -212,7 +211,7 @@ func TestAttemptStep(t *testing.T) {
...
@@ -212,7 +211,7 @@ func TestAttemptStep(t *testing.T) {
require
.
Equal
(
t
,
tableTest
.
expectedOracleData
.
OracleOffset
,
step
.
OracleData
.
OracleOffset
)
require
.
Equal
(
t
,
tableTest
.
expectedOracleData
.
OracleOffset
,
step
.
OracleData
.
OracleOffset
)
}
else
{
}
else
{
require
.
ErrorIs
(
t
,
err
,
tableTest
.
expectedErr
)
require
.
ErrorIs
(
t
,
err
,
tableTest
.
expectedErr
)
require
.
Equal
(
t
,
solver
.
StepData
{},
step
)
require
.
Equal
(
t
,
StepData
{},
step
)
}
}
})
})
}
}
...
...
op-challenger/game/fault/test/claim_builder.go
View file @
86b91fc6
...
@@ -38,6 +38,13 @@ func (c *ClaimBuilder) CorrectClaim(idx uint64) common.Hash {
...
@@ -38,6 +38,13 @@ func (c *ClaimBuilder) CorrectClaim(idx uint64) common.Hash {
return
value
return
value
}
}
// CorrectClaimAtPosition returns the canonical claim at a specified position
func
(
c
*
ClaimBuilder
)
CorrectClaimAtPosition
(
pos
types
.
Position
)
common
.
Hash
{
value
,
err
:=
c
.
correct
.
Get
(
context
.
Background
(),
pos
.
TraceIndex
(
c
.
maxDepth
))
c
.
require
.
NoError
(
err
)
return
value
}
// CorrectPreState returns the pre-state (not hashed) required to execute the valid step at the specified trace index
// CorrectPreState returns the pre-state (not hashed) required to execute the valid step at the specified trace index
func
(
c
*
ClaimBuilder
)
CorrectPreState
(
idx
uint64
)
[]
byte
{
func
(
c
*
ClaimBuilder
)
CorrectPreState
(
idx
uint64
)
[]
byte
{
preimage
,
_
,
_
,
err
:=
c
.
correct
.
GetStepData
(
context
.
Background
(),
idx
)
preimage
,
_
,
_
,
err
:=
c
.
correct
.
GetStepData
(
context
.
Background
(),
idx
)
...
@@ -103,6 +110,19 @@ func (c *ClaimBuilder) AttackClaim(claim types.Claim, correct bool) types.Claim
...
@@ -103,6 +110,19 @@ func (c *ClaimBuilder) AttackClaim(claim types.Claim, correct bool) types.Claim
Position
:
pos
,
Position
:
pos
,
},
},
Parent
:
claim
.
ClaimData
,
Parent
:
claim
.
ClaimData
,
ParentContractIndex
:
claim
.
ContractIndex
,
}
}
func
(
c
*
ClaimBuilder
)
AttackClaimWithValue
(
claim
types
.
Claim
,
value
common
.
Hash
)
types
.
Claim
{
pos
:=
claim
.
Position
.
Attack
()
return
types
.
Claim
{
ClaimData
:
types
.
ClaimData
{
Value
:
value
,
Position
:
pos
,
},
Parent
:
claim
.
ClaimData
,
ParentContractIndex
:
claim
.
ContractIndex
,
}
}
}
}
...
@@ -114,5 +134,18 @@ func (c *ClaimBuilder) DefendClaim(claim types.Claim, correct bool) types.Claim
...
@@ -114,5 +134,18 @@ func (c *ClaimBuilder) DefendClaim(claim types.Claim, correct bool) types.Claim
Position
:
pos
,
Position
:
pos
,
},
},
Parent
:
claim
.
ClaimData
,
Parent
:
claim
.
ClaimData
,
ParentContractIndex
:
claim
.
ContractIndex
,
}
}
func
(
c
*
ClaimBuilder
)
DefendClaimWithValue
(
claim
types
.
Claim
,
value
common
.
Hash
)
types
.
Claim
{
pos
:=
claim
.
Position
.
Defend
()
return
types
.
Claim
{
ClaimData
:
types
.
ClaimData
{
Value
:
value
,
Position
:
pos
,
},
Parent
:
claim
.
ClaimData
,
ParentContractIndex
:
claim
.
ContractIndex
,
}
}
}
}
op-challenger/game/fault/test/game_builder.go
0 → 100644
View file @
86b91fc6
package
test
import
(
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum/go-ethereum/common"
)
type
GameBuilder
struct
{
builder
*
ClaimBuilder
Game
types
.
Game
}
func
(
c
*
ClaimBuilder
)
GameBuilder
(
agreeWithOutputRoot
bool
,
rootCorrect
bool
)
*
GameBuilder
{
return
&
GameBuilder
{
builder
:
c
,
Game
:
types
.
NewGameState
(
agreeWithOutputRoot
,
c
.
CreateRootClaim
(
rootCorrect
),
uint64
(
c
.
maxDepth
)),
}
}
type
GameBuilderSeq
struct
{
builder
*
ClaimBuilder
lastClaim
types
.
Claim
game
types
.
Game
}
func
(
g
*
GameBuilder
)
Seq
()
*
GameBuilderSeq
{
return
&
GameBuilderSeq
{
builder
:
g
.
builder
,
game
:
g
.
Game
,
lastClaim
:
g
.
Game
.
Claims
()[
0
],
}
}
func
(
s
*
GameBuilderSeq
)
AttackCorrect
()
*
GameBuilderSeq
{
claim
:=
s
.
builder
.
AttackClaim
(
s
.
lastClaim
,
true
)
claim
.
ContractIndex
=
len
(
s
.
game
.
Claims
())
s
.
builder
.
require
.
NoError
(
s
.
game
.
Put
(
claim
))
return
&
GameBuilderSeq
{
builder
:
s
.
builder
,
game
:
s
.
game
,
lastClaim
:
claim
,
}
}
func
(
s
*
GameBuilderSeq
)
Attack
(
value
common
.
Hash
)
*
GameBuilderSeq
{
claim
:=
s
.
builder
.
AttackClaimWithValue
(
s
.
lastClaim
,
value
)
claim
.
ContractIndex
=
len
(
s
.
game
.
Claims
())
s
.
builder
.
require
.
NoError
(
s
.
game
.
Put
(
claim
))
return
&
GameBuilderSeq
{
builder
:
s
.
builder
,
game
:
s
.
game
,
lastClaim
:
claim
,
}
}
func
(
s
*
GameBuilderSeq
)
DefendCorrect
()
*
GameBuilderSeq
{
claim
:=
s
.
builder
.
DefendClaim
(
s
.
lastClaim
,
true
)
claim
.
ContractIndex
=
len
(
s
.
game
.
Claims
())
s
.
builder
.
require
.
NoError
(
s
.
game
.
Put
(
claim
))
return
&
GameBuilderSeq
{
builder
:
s
.
builder
,
game
:
s
.
game
,
lastClaim
:
claim
,
}
}
func
(
s
*
GameBuilderSeq
)
Defend
(
value
common
.
Hash
)
*
GameBuilderSeq
{
claim
:=
s
.
builder
.
DefendClaimWithValue
(
s
.
lastClaim
,
value
)
claim
.
ContractIndex
=
len
(
s
.
game
.
Claims
())
s
.
builder
.
require
.
NoError
(
s
.
game
.
Put
(
claim
))
return
&
GameBuilderSeq
{
builder
:
s
.
builder
,
game
:
s
.
game
,
lastClaim
:
claim
,
}
}
op-challenger/scripts/alphabet/init_game.sh
View file @
86b91fc6
...
@@ -62,5 +62,7 @@ done
...
@@ -62,5 +62,7 @@ done
# Root claim commits to the entire trace.
# Root claim commits to the entire trace.
# Alphabet game claim construction: keccak256(abi.encode(trace_index, trace[trace_index]))
# Alphabet game claim construction: keccak256(abi.encode(trace_index, trace[trace_index]))
ROOT_CLAIM
=
$(
cast keccak
$(
cast abi-encode
"f(uint256,uint256)"
15 122
))
ROOT_CLAIM
=
$(
cast keccak
$(
cast abi-encode
"f(uint256,uint256)"
15 122
))
# Replace the first byte of the claim with the invalid vm status indicator
ROOT_CLAIM
=
"0x01
${
ROOT_CLAIM
:4:60
}
"
GAME_TYPE
=
255
${
SOURCE_DIR
}
/../create_game.sh http://localhost:8545
"
${
DISPUTE_GAME_FACTORY
}
"
"
${
ROOT_CLAIM
}
"
--private-key
"
${
DEVNET_SPONSOR
}
"
GAME_TYPE
=
255
${
SOURCE_DIR
}
/../create_game.sh http://localhost:8545
"
${
DISPUTE_GAME_FACTORY
}
"
"
${
ROOT_CLAIM
}
"
--private-key
"
${
DEVNET_SPONSOR
}
"
pnpm-lock.yaml
View file @
86b91fc6
...
@@ -943,7 +943,7 @@ packages:
...
@@ -943,7 +943,7 @@ packages:
p-limit
:
2.3.0
p-limit
:
2.3.0
preferred-pm
:
3.0.3
preferred-pm
:
3.0.3
resolve-from
:
5.0.0
resolve-from
:
5.0.0
semver
:
5.7.
1
semver
:
5.7.
2
spawndamnit
:
2.0.0
spawndamnit
:
2.0.0
term-size
:
2.2.1
term-size
:
2.2.1
tty-table
:
4.1.6
tty-table
:
4.1.6
...
@@ -6846,7 +6846,7 @@ packages:
...
@@ -6846,7 +6846,7 @@ packages:
resolve
:
1.22.2
resolve
:
1.22.2
sass
:
1.52.1
sass
:
1.52.1
scss-parser
:
1.0.5
scss-parser
:
1.0.5
semver
:
7.5.
3
semver
:
7.5.
4
yargs
:
16.2.0
yargs
:
16.2.0
transitivePeerDependencies
:
transitivePeerDependencies
:
-
supports-color
-
supports-color
...
@@ -7668,7 +7668,7 @@ packages:
...
@@ -7668,7 +7668,7 @@ packages:
jsdoc-type-pratt-parser
:
1.1.1
jsdoc-type-pratt-parser
:
1.1.1
lodash
:
4.17.21
lodash
:
4.17.21
regextras
:
0.8.0
regextras
:
0.8.0
semver
:
7.5.
3
semver
:
7.5.
4
spdx-expression-parse
:
3.0.1
spdx-expression-parse
:
3.0.1
transitivePeerDependencies
:
transitivePeerDependencies
:
-
supports-color
-
supports-color
...
@@ -7686,7 +7686,7 @@ packages:
...
@@ -7686,7 +7686,7 @@ packages:
ignore
:
5.2.4
ignore
:
5.2.4
minimatch
:
3.1.2
minimatch
:
3.1.2
resolve
:
1.22.2
resolve
:
1.22.2
semver
:
6.3.
0
semver
:
6.3.
1
dev
:
true
dev
:
true
/eslint-plugin-prefer-arrow@1.2.3(eslint@8.47.0)
:
/eslint-plugin-prefer-arrow@1.2.3(eslint@8.47.0)
:
...
@@ -7767,7 +7767,7 @@ packages:
...
@@ -7767,7 +7767,7 @@ packages:
read-pkg-up
:
7.0.1
read-pkg-up
:
7.0.1
regexp-tree
:
0.1.24
regexp-tree
:
0.1.24
safe-regex
:
2.1.1
safe-regex
:
2.1.1
semver
:
7.5.
3
semver
:
7.5.
4
strip-indent
:
3.0.0
strip-indent
:
3.0.0
dev
:
true
dev
:
true
...
@@ -12176,7 +12176,7 @@ packages:
...
@@ -12176,7 +12176,7 @@ packages:
emoji-regex
:
9.2.2
emoji-regex
:
9.2.2
escape-string-regexp
:
4.0.0
escape-string-regexp
:
4.0.0
prettier
:
2.8.8
prettier
:
2.8.8
semver
:
7.5.
3
semver
:
7.5.
4
solidity-comments-extractor
:
0.0.7
solidity-comments-extractor
:
0.0.7
string-width
:
4.2.3
string-width
:
4.2.3
dev
:
true
dev
:
true
...
@@ -12961,20 +12961,10 @@ packages:
...
@@ -12961,20 +12961,10 @@ packages:
resolution
:
{
integrity
:
sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==
}
resolution
:
{
integrity
:
sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==
}
dev
:
true
dev
:
true
/semver@5.7.1
:
resolution
:
{
integrity
:
sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
}
hasBin
:
true
dev
:
false
/semver@5.7.2
:
/semver@5.7.2
:
resolution
:
{
integrity
:
sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==
}
resolution
:
{
integrity
:
sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==
}
hasBin
:
true
hasBin
:
true
/semver@6.3.0
:
resolution
:
{
integrity
:
sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
}
hasBin
:
true
dev
:
true
/semver@6.3.1
:
/semver@6.3.1
:
resolution
:
{
integrity
:
sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
}
resolution
:
{
integrity
:
sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
}
hasBin
:
true
hasBin
:
true
...
...
specs/README.md
View file @
86b91fc6
...
@@ -29,8 +29,9 @@ that maintains 1:1 compatibility with Ethereum.
...
@@ -29,8 +29,9 @@ that maintains 1:1 compatibility with Ethereum.
Specifications of new features in active development.
Specifications of new features in active development.
-
[
Fault Proof
](
./fault-proof.md
)
-
[
Fault Proof
](
./fault-proof.md
)
-
[
Dispute Game
](
./dispute-game.md
)
-
[
Dispute Game Interface
](
./dispute-game-interface.md
)
-
[
Dispute Game Interface
](
./dispute-game-interface.md
)
-
[
Fault Dispute Game
](
./fault-dispute-game.md
)
-
[
Cannon VM
](
./cannon-fault-proof-vm.md
)
## Design Goals
## Design Goals
...
...
specs/bond-manager.md
View file @
86b91fc6
...
@@ -20,14 +20,14 @@ be attached to an output proposal. In this case, the bond will be paid in ether.
...
@@ -20,14 +20,14 @@ be attached to an output proposal. In this case, the bond will be paid in ether.
By requiring a bond to be posted with an output proposal, spam and invalid outputs
By requiring a bond to be posted with an output proposal, spam and invalid outputs
are disincentivized. Explicitly, if invalid outputs are proposed, challenge agents
are disincentivized. Explicitly, if invalid outputs are proposed, challenge agents
can delete the invalid output via a
[
dispute-game
](
./dispute-game.md
)
and seize the
can delete the invalid output via a
[
dispute-game
](
./dispute-game
-interface
.md
)
and seize the
proposer's bond. So, posting invalid outputs is directly disincentivized in this way
proposer's bond. So, posting invalid outputs is directly disincentivized in this way
since the proposer would lose their bond if the challenge agents seize it.
since the proposer would lose their bond if the challenge agents seize it.
Concretely, outputs will be permissionlessly proposed to the
`L2OutputOracle`
contract.
Concretely, outputs will be permissionlessly proposed to the
`L2OutputOracle`
contract.
When submitting an output proposal, the ether value is sent as the bond. This bond is
When submitting an output proposal, the ether value is sent as the bond. This bond is
then held by a bond manager contract. The bond manager contract is responsible for
then held by a bond manager contract. The bond manager contract is responsible for
both the
[
dispute-games
](
./dispute-game.md
)
and the
`L2OutputOracle`
(further detailed
both the
[
dispute-games
](
./dispute-game
-interface
.md
)
and the
`L2OutputOracle`
(further detailed
in
[
proposals
](
./proposals.md
)
).
in
[
proposals
](
./proposals.md
)
).
The bond manager will need to handle bond logic for a variety of different
The bond manager will need to handle bond logic for a variety of different
...
@@ -136,7 +136,7 @@ instead tied to the address of the output proposer.
...
@@ -136,7 +136,7 @@ instead tied to the address of the output proposer.
## Bond Manager Implementation
## Bond Manager Implementation
Initially, the bond manager will only be used by the
`L2OutputOracle`
contract
Initially, the bond manager will only be used by the
`L2OutputOracle`
contract
for output proposals in the attestation
[
dispute game
](
./dispute-game.md
)
. Since
for output proposals in the attestation
[
dispute game
](
./dispute-game
-interface
.md
)
. Since
the attestation dispute game has a permissioned set of attestors, there are no
the attestation dispute game has a permissioned set of attestors, there are no
intermediate steps in the game that would require bonds.
intermediate steps in the game that would require bonds.
...
...
specs/challenger.md
deleted
100644 → 0
View file @
b46e162e
# Challenger Specification
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
**Table of Contents**
-
[
Description
](
#description
)
-
[
Terminology
](
#terminology
)
-
[
Event and Response Lifecycle
](
#event-and-response-lifecycle
)
-
[
`GameType.FAULT`
](
#gametypefault
)
-
[
`GameType.ATTESTATION`
](
#gametypeattestation
)
-
[
`GameType.VALIDITY`
](
#gametypevalidity
)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
## Description
The Challenger is an off-chain agent that listens for faulty claims made about the state of
the L2 on the data availability layer. It is responsible for challenging these incorrect claims
and ensuring the correctness of all finalized claims on the settlement layer.
The Challenger agent is intended to be ran as a permissionless service by participants of the network
alongside a
[
rollup-node
](
./rollup-node.md
)
. Challenger agents will be rewarded in the form of the
bond attached to the claims they disprove.
## Terminology
-
**data availability layer**
- In the context of this document, the data availability layer is the
generic term for the location where claims about the state of the layer two are made. In the context
of Optimism, this is Ethereum Mainnet.
-
**settlement layer**
- In the context of this document, the settlement layer is the location of the
bridge as well as where funds deposited to the rollup reside. In the context of Optimism, this is
Ethereum Mainnet.
-
**L2**
- In the context of this document, the layer two of the Optimistic Rollup. In the context
of Optimism, this is the Optimism Mainnet.
-
**rollup-node**
- In the context of this document, the rollup node describes the
[
rollup-node specification
](
./rollup-node.md
)
. In the context of Optimism, this is the implementation
of the
[
rollup-node specification
](
./rollup-node.md
)
, the
`op-node`
.
## Event and Response Lifecycle
The Challenger agent is expected to be able to listen for and respond to several different events
on the data availability layer. These events and responses are parameterized depending on the type
of dispute game being played, and the Challenger listens to different events and responds uniquely
to each of the different game types. For specification of dispute game types, see the
[
Dispute Game Interfaces specification
](
./dispute-game-interface.md
)
and
[
Dispute Game specification
](
./dispute-game.md
)
.
### `GameType.FAULT`
> **Warning**
> The `FAULT` game type is not yet implemented. In the first iteration of Optimism's decentralization effort,
> challengers will respond to `ATTESTATION` games only.
**Events and Responses**
*TODO*
### `GameType.ATTESTATION`
**Events and Responses**
-
[
`L2OutputOracle.OutputProposed`
](
../packages/contracts-bedrock/src/L1/L2OutputOracle.sol#L57-70
)
The
`L2OutputOracle`
contract emits this event when a new output is proposed on the data availability
layer. Each time an output is proposed, the Challenger should check to see if the output is equal
the output given by the
`optimism_outputAtBlock`
endpoint of their
`rollup-node`
.
-
If it is, the Challenger should do nothing to challenge this output proposal.
-
If it is not, the Challenger should respond by creating a new
`DisputeGame`
with the
`DisputeGameType.ATTESTATION`
`gameType`
, the correct output root as the
`rootClaim`
, and the abi-encoded
`l2BlockNumber`
of the correct output root as the
`extraData`
.
!
[
Attestation `OutputProposed` Diagram
](
./assets/challenger_attestation_output_proposed.png
)
-
`DisputeGameFactory.DisputeGameCreated`
A new dispute game has been created and is ready to be reviewed. The
Challenger agent should listen for this event and check if the
`rootClaim`
of the
`AttestationDisputeGame`
created by the
`DisputeGameFactory`
is equal to the output root of their
`rollup-node`
at the game's
`l2BlockNumber`
.
-
If it is, the Challenger should sign the
[
EIP-712 typeHash
](
./dispute-game.md
)
of the struct containing the
`AttestationDisputeGame`
's
`rootClaim`
and
`l2BlockNumber`
. The Challenger should then submit the abi-encoded
signature to the
`AttestationDisputeGame`
's
`challenge`
function.
-
If it is not, the Challenger should do nothing in support of this dispute game.
!
[
Attestation `DisputeGameCreated` Diagram
](
./assets/challenger_attestation_dispute_game_created.png
)
A full diagram and lifecycle of the Challenger's role in the
`ATTESTATION`
game type can be found below:

### `GameType.VALIDITY`
**TODO**
> **Warning**
> The `VALIDITY` game type is not yet implemented. In the first iteration of Optimism's decentralization effort,
> challengers will respond to `ATTESTATION` games only. A validity proof based dispute game is a possibility,
> but fault proof based dispute games will be the primary focus of the team in the near future.
**Events and Responses**
*TODO*
specs/dispute-game.md
deleted
100644 → 0
View file @
b46e162e
# Dispute Game
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
**Table of Contents**
-
[
Attestation Dispute Game
](
#attestation-dispute-game
)
-
[
Smart Contract Implementation
](
#smart-contract-implementation
)
-
[
Attestation Structure
](
#attestation-structure
)
-
[
Why EIP-712
](
#why-eip-712
)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
## Attestation Dispute Game
The output attestation based dispute game shifts the current permissioned output proposal process
to a permissionless, social-consensus based architecture that can progressively decentralize over
time by increasing the size of the signer set. In this "game," output proposals can be submitted
permissionlessly. To prevent "invalid output proposals," a social quorum can revert an output proposal
when an invalid one is discovered. The set of signers is maintained in the
`SystemConfig`
contract,
and these signers will issue
[
EIP-712
](
https://eips.ethereum.org/EIPS/eip-712
)
signatures
over canonical output roots and the
`l2BlockNumber`
s they commit to as attestations. To learn more,
see the
[
DisputeGame Interface Spec
](
./dispute-game-interface.md
)
.
In the above language, an "invalid output proposal" is defined as an output proposal that represents
a non-canonical state of the L2 chain.
### Smart Contract Implementation
The
`AttestationDisputeGame`
should implement the
`IDisputeGame`
interface and also be able to call
out to the
`L2OutputOracle`
. It is expected that the
`L2OutputOracle`
will grant permissions to
`AttestationDisputeGame`
contracts to call its
`deleteL2Outputs`
function at the
*specific*
`l2BlockNumber`
that is embedded in the
`AttestationDisputeGame`
's
`extraData`
.
The
`AttestationDisputeGame`
should be configured with a quorum ratio at deploy time. It should also
maintain a set of attestor accounts, which is fetched by the
`SystemConfig`
contract and snapshotted
at deploy time. This snapshot is necessary to have a fixed upper bound on resolution cost, which in
turn gives a fix cost for the necessary bond attached to output proposals.
The ability to add and remove attestor accounts should be enabled by a single immutable
account that controls the
`SystemConfig`
. It should be impossible to remove accounts such that quorum
is not able to be reached. It is ok to allow accounts to be added or removed in the middle of an
open challenge, as it will not affect the
`signerSet`
that exists within open challenges.
A challenge is created when an alternative output root for a given
`l2BlockNumber`
is presented to the
`DisputeGameFactory`
contract. Multiple challenges should be able to run in parallel.
For simplicity, the
`AttestationDisputeGame`
does not need to track what output proposals are
committed to as part of the attestations. It only needs to check that the attested output root
is different than the proposed output root. If this is not checked, then it will be possible
to remove output proposals that are in agreement with the attestations and create a griefing vector.
#### Attestation Structure
The EIP-712
[
typeHash
](
https://eips.ethereum.org/EIPS/eip-712#rationale-for-typehash
)
should be
defined as the following:
```
solidity
TYPE_HASH = keccak256("Dispute(bytes32 outputRoot,uint256 l2BlockNumber)");
```
The components for the
`typeHash`
are as follows:
-
`outputRoot`
- The
**correct**
output root that commits to the given
`l2BlockNumber`
. This should be a
positive attestation where the
`rootClaim`
of the
`AttestationDisputeGame`
is the
**correct**
output root
for the given
`l2BlockNumber`
.
-
`l2BlockNumber`
- The L2 block number that the
`outputRoot`
commits to. The
`outputRoot`
should commit
to the entirety of the L2 state from genesis up to and including this
`l2BlockNumber`
.
### Why EIP-712
It is important to use EIP-712 to decouple the originator of the transaction and the attestor. This
will allow a decentralized network of attestors that serve attestations to bots that are responsible
for ensuring that all output proposals submitted to the network will not allow for malicious withdrawals
from the bridge.
It is important to have replay protection to ensure that attestations cannot be used more than once.
specs/fault-proof.md
View file @
86b91fc6
...
@@ -412,5 +412,5 @@ The allocated response time is limited by the dispute-game window,
...
@@ -412,5 +412,5 @@ The allocated response time is limited by the dispute-game window,
and any additional time necessary based on L1 fee changes when bonds are insufficient.
and any additional time necessary based on L1 fee changes when bonds are insufficient.
> Note: the timed, bonded, bisection dispute game is in development.
> Note: the timed, bonded, bisection dispute game is in development.
> Also see [
dispute-game specs](./dispute-game.md) for general
dispute game system specifications,
> Also see [
fault dispute-game specs](./fault-dispute-game.md) for fault
dispute game system specifications,
> And [dispute-game-interface specs](./dispute-game-interface.md) for dispute game interface specifications.
> And [dispute-game-interface specs](./dispute-game-interface.md) for dispute game interface specifications.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment