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
05d2352d
Unverified
Commit
05d2352d
authored
Sep 13, 2023
by
mergify[bot]
Committed by
GitHub
Sep 13, 2023
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'develop' into aj/cannon-atomic-write
parents
956cf18b
174bfbd4
Changes
14
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
414 additions
and
184 deletions
+414
-184
tag-service.yml
.github/workflows/tag-service.yml
+1
-3
agent.go
op-challenger/game/fault/agent.go
+3
-3
agent_test.go
op-challenger/game/fault/agent_test.go
+1
-2
responder.go
op-challenger/game/fault/responder/responder.go
+4
-4
responder_test.go
op-challenger/game/fault/responder/responder_test.go
+11
-11
game_solver.go
op-challenger/game/fault/solver/game_solver.go
+11
-39
game_solver_test.go
op-challenger/game/fault/solver/game_solver_test.go
+21
-92
rules.go
op-challenger/game/fault/solver/rules.go
+95
-0
game_builder.go
op-challenger/game/fault/test/game_builder.go
+77
-20
actions.go
op-challenger/game/fault/types/actions.go
+28
-0
game.go
op-challenger/game/fault/types/game.go
+10
-4
game_test.go
op-challenger/game/fault/types/game_test.go
+4
-4
fault-dispute-game.md
specs/fault-dispute-game.md
+27
-2
honest-challenger-fdg.md
specs/honest-challenger-fdg.md
+121
-0
No files found.
.github/workflows/tag-service.yml
View file @
05d2352d
...
@@ -19,16 +19,14 @@ on:
...
@@ -19,16 +19,14 @@ on:
type
:
choice
type
:
choice
options
:
options
:
-
ci-builder
-
ci-builder
-
fault-detector
-
indexer
-
indexer
-
chain-mon
-
op-node
-
op-node
-
op-batcher
-
op-batcher
-
op-proposer
-
op-proposer
-
op-challenger
-
op-challenger
-
op-ufm
-
op-ufm
-
proxyd
-
proxyd
-
indexer
-
ci-builder
prerelease
:
prerelease
:
description
:
Increment major/minor/patch as prerelease?
description
:
Increment major/minor/patch as prerelease?
required
:
false
required
:
false
...
...
op-challenger/game/fault/agent.go
View file @
05d2352d
...
@@ -17,7 +17,7 @@ import (
...
@@ -17,7 +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
PerformAction
(
ctx
context
.
Context
,
action
solver
.
Action
)
error
PerformAction
(
ctx
context
.
Context
,
action
types
.
Action
)
error
}
}
type
ClaimLoader
interface
{
type
ClaimLoader
interface
{
...
@@ -76,9 +76,9 @@ func (a *Agent) Act(ctx context.Context) error {
...
@@ -76,9 +76,9 @@ func (a *Agent) Act(ctx context.Context) error {
}
}
switch
action
.
Type
{
switch
action
.
Type
{
case
solver
.
ActionTypeMove
:
case
types
.
ActionTypeMove
:
a
.
metrics
.
RecordGameMove
()
a
.
metrics
.
RecordGameMove
()
case
solver
.
ActionTypeStep
:
case
types
.
ActionTypeStep
:
a
.
metrics
.
RecordGameStep
()
a
.
metrics
.
RecordGameStep
()
}
}
log
.
Info
(
"Performing action"
)
log
.
Info
(
"Performing action"
)
...
...
op-challenger/game/fault/agent_test.go
View file @
05d2352d
...
@@ -5,7 +5,6 @@ import (
...
@@ -5,7 +5,6 @@ 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"
...
@@ -145,7 +144,7 @@ func (s *stubResponder) Resolve(ctx context.Context) error {
...
@@ -145,7 +144,7 @@ func (s *stubResponder) Resolve(ctx context.Context) error {
return
s
.
resolveErr
return
s
.
resolveErr
}
}
func
(
s
*
stubResponder
)
PerformAction
(
ctx
context
.
Context
,
response
solver
.
Action
)
error
{
func
(
s
*
stubResponder
)
PerformAction
(
ctx
context
.
Context
,
response
types
.
Action
)
error
{
panic
(
"Not implemented"
)
panic
(
"Not implemented"
)
}
}
...
...
op-challenger/game/fault/responder/responder.go
View file @
05d2352d
...
@@ -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/
solver
"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/
types
"
gameTypes
"github.com/ethereum-optimism/optimism/op-challenger/game/types"
gameTypes
"github.com/ethereum-optimism/optimism/op-challenger/game/types"
"github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum-optimism/optimism/op-service/txmgr"
...
@@ -94,17 +94,17 @@ func (r *FaultResponder) Resolve(ctx context.Context) error {
...
@@ -94,17 +94,17 @@ func (r *FaultResponder) Resolve(ctx context.Context) error {
return
r
.
sendTxAndWait
(
ctx
,
txData
)
return
r
.
sendTxAndWait
(
ctx
,
txData
)
}
}
func
(
r
*
FaultResponder
)
PerformAction
(
ctx
context
.
Context
,
action
solver
.
Action
)
error
{
func
(
r
*
FaultResponder
)
PerformAction
(
ctx
context
.
Context
,
action
types
.
Action
)
error
{
var
txData
[]
byte
var
txData
[]
byte
var
err
error
var
err
error
switch
action
.
Type
{
switch
action
.
Type
{
case
solver
.
ActionTypeMove
:
case
types
.
ActionTypeMove
:
if
action
.
IsAttack
{
if
action
.
IsAttack
{
txData
,
err
=
r
.
buildFaultAttackData
(
action
.
ParentIdx
,
action
.
Value
)
txData
,
err
=
r
.
buildFaultAttackData
(
action
.
ParentIdx
,
action
.
Value
)
}
else
{
}
else
{
txData
,
err
=
r
.
buildFaultDefendData
(
action
.
ParentIdx
,
action
.
Value
)
txData
,
err
=
r
.
buildFaultDefendData
(
action
.
ParentIdx
,
action
.
Value
)
}
}
case
solver
.
ActionTypeStep
:
case
types
.
ActionTypeStep
:
txData
,
err
=
r
.
buildStepTxData
(
uint64
(
action
.
ParentIdx
),
action
.
IsAttack
,
action
.
PreState
,
action
.
ProofData
)
txData
,
err
=
r
.
buildStepTxData
(
uint64
(
action
.
ParentIdx
),
action
.
IsAttack
,
action
.
PreState
,
action
.
ProofData
)
}
}
if
err
!=
nil
{
if
err
!=
nil
{
...
...
op-challenger/game/fault/responder/responder_test.go
View file @
05d2352d
...
@@ -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/
solver
"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/
types
"
gameTypes
"github.com/ethereum-optimism/optimism/op-challenger/game/types"
gameTypes
"github.com/ethereum-optimism/optimism/op-challenger/game/types"
"github.com/ethereum-optimism/optimism/op-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"
...
@@ -78,8 +78,8 @@ func TestPerformAction(t *testing.T) {
...
@@ -78,8 +78,8 @@ func TestPerformAction(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
.
PerformAction
(
context
.
Background
(),
solver
.
Action
{
err
:=
responder
.
PerformAction
(
context
.
Background
(),
types
.
Action
{
Type
:
solver
.
ActionTypeMove
,
Type
:
types
.
ActionTypeMove
,
ParentIdx
:
123
,
ParentIdx
:
123
,
IsAttack
:
true
,
IsAttack
:
true
,
Value
:
common
.
Hash
{
0xaa
},
Value
:
common
.
Hash
{
0xaa
},
...
@@ -90,8 +90,8 @@ func TestPerformAction(t *testing.T) {
...
@@ -90,8 +90,8 @@ func TestPerformAction(t *testing.T) {
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
.
PerformAction
(
context
.
Background
(),
solver
.
Action
{
err
:=
responder
.
PerformAction
(
context
.
Background
(),
types
.
Action
{
Type
:
solver
.
ActionTypeMove
,
Type
:
types
.
ActionTypeMove
,
ParentIdx
:
123
,
ParentIdx
:
123
,
IsAttack
:
true
,
IsAttack
:
true
,
Value
:
common
.
Hash
{
0xaa
},
Value
:
common
.
Hash
{
0xaa
},
...
@@ -102,8 +102,8 @@ func TestPerformAction(t *testing.T) {
...
@@ -102,8 +102,8 @@ func TestPerformAction(t *testing.T) {
t
.
Run
(
"attack"
,
func
(
t
*
testing
.
T
)
{
t
.
Run
(
"attack"
,
func
(
t
*
testing
.
T
)
{
responder
,
mockTxMgr
:=
newTestFaultResponder
(
t
)
responder
,
mockTxMgr
:=
newTestFaultResponder
(
t
)
action
:=
solver
.
Action
{
action
:=
types
.
Action
{
Type
:
solver
.
ActionTypeMove
,
Type
:
types
.
ActionTypeMove
,
ParentIdx
:
123
,
ParentIdx
:
123
,
IsAttack
:
true
,
IsAttack
:
true
,
Value
:
common
.
Hash
{
0xaa
},
Value
:
common
.
Hash
{
0xaa
},
...
@@ -123,8 +123,8 @@ func TestPerformAction(t *testing.T) {
...
@@ -123,8 +123,8 @@ func TestPerformAction(t *testing.T) {
t
.
Run
(
"defend"
,
func
(
t
*
testing
.
T
)
{
t
.
Run
(
"defend"
,
func
(
t
*
testing
.
T
)
{
responder
,
mockTxMgr
:=
newTestFaultResponder
(
t
)
responder
,
mockTxMgr
:=
newTestFaultResponder
(
t
)
action
:=
solver
.
Action
{
action
:=
types
.
Action
{
Type
:
solver
.
ActionTypeMove
,
Type
:
types
.
ActionTypeMove
,
ParentIdx
:
123
,
ParentIdx
:
123
,
IsAttack
:
false
,
IsAttack
:
false
,
Value
:
common
.
Hash
{
0xaa
},
Value
:
common
.
Hash
{
0xaa
},
...
@@ -144,8 +144,8 @@ func TestPerformAction(t *testing.T) {
...
@@ -144,8 +144,8 @@ func TestPerformAction(t *testing.T) {
t
.
Run
(
"step"
,
func
(
t
*
testing
.
T
)
{
t
.
Run
(
"step"
,
func
(
t
*
testing
.
T
)
{
responder
,
mockTxMgr
:=
newTestFaultResponder
(
t
)
responder
,
mockTxMgr
:=
newTestFaultResponder
(
t
)
action
:=
solver
.
Action
{
action
:=
types
.
Action
{
Type
:
solver
.
ActionTypeStep
,
Type
:
types
.
ActionTypeStep
,
ParentIdx
:
123
,
ParentIdx
:
123
,
IsAttack
:
true
,
IsAttack
:
true
,
PreState
:
[]
byte
{
1
,
2
,
3
},
PreState
:
[]
byte
{
1
,
2
,
3
},
...
...
op-challenger/game/fault/solver/game_solver.go
View file @
05d2352d
...
@@ -6,53 +6,25 @@ import (
...
@@ -6,53 +6,25 @@ import (
"fmt"
"fmt"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum/go-ethereum/common"
)
)
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
{
type
GameSolver
struct
{
claimSolver
*
claimSolver
claimSolver
*
claimSolver
gameDepth
int
}
}
func
NewGameSolver
(
gameDepth
int
,
trace
types
.
TraceProvider
)
*
GameSolver
{
func
NewGameSolver
(
gameDepth
int
,
trace
types
.
TraceProvider
)
*
GameSolver
{
return
&
GameSolver
{
return
&
GameSolver
{
claimSolver
:
newClaimSolver
(
gameDepth
,
trace
),
claimSolver
:
newClaimSolver
(
gameDepth
,
trace
),
gameDepth
:
gameDepth
,
}
}
}
}
func
(
s
*
GameSolver
)
CalculateNextActions
(
ctx
context
.
Context
,
game
types
.
Game
)
([]
Action
,
error
)
{
func
(
s
*
GameSolver
)
CalculateNextActions
(
ctx
context
.
Context
,
game
types
.
Game
)
([]
types
.
Action
,
error
)
{
var
errs
[]
error
var
errs
[]
error
var
actions
[]
Action
var
actions
[]
types
.
Action
for
_
,
claim
:=
range
game
.
Claims
()
{
for
_
,
claim
:=
range
game
.
Claims
()
{
var
action
*
Action
var
action
*
types
.
Action
var
err
error
var
err
error
if
claim
.
Depth
()
==
s
.
gameDepth
{
if
uint64
(
claim
.
Depth
())
==
game
.
MaxDepth
()
{
action
,
err
=
s
.
calculateStep
(
ctx
,
game
,
claim
)
action
,
err
=
s
.
calculateStep
(
ctx
,
game
,
claim
)
}
else
{
}
else
{
action
,
err
=
s
.
calculateMove
(
ctx
,
game
,
claim
)
action
,
err
=
s
.
calculateMove
(
ctx
,
game
,
claim
)
...
@@ -69,7 +41,7 @@ func (s *GameSolver) CalculateNextActions(ctx context.Context, game types.Game)
...
@@ -69,7 +41,7 @@ func (s *GameSolver) CalculateNextActions(ctx context.Context, game types.Game)
return
actions
,
errors
.
Join
(
errs
...
)
return
actions
,
errors
.
Join
(
errs
...
)
}
}
func
(
s
*
GameSolver
)
calculateStep
(
ctx
context
.
Context
,
game
types
.
Game
,
claim
types
.
Claim
)
(
*
Action
,
error
)
{
func
(
s
*
GameSolver
)
calculateStep
(
ctx
context
.
Context
,
game
types
.
Game
,
claim
types
.
Claim
)
(
*
types
.
Action
,
error
)
{
if
claim
.
Countered
{
if
claim
.
Countered
{
return
nil
,
nil
return
nil
,
nil
}
}
...
@@ -80,8 +52,8 @@ func (s *GameSolver) calculateStep(ctx context.Context, game types.Game, claim t
...
@@ -80,8 +52,8 @@ func (s *GameSolver) calculateStep(ctx context.Context, game types.Game, claim t
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
err
return
nil
,
err
}
}
return
&
Action
{
return
&
types
.
Action
{
Type
:
ActionTypeStep
,
Type
:
types
.
ActionTypeStep
,
ParentIdx
:
step
.
LeafClaim
.
ContractIndex
,
ParentIdx
:
step
.
LeafClaim
.
ContractIndex
,
IsAttack
:
step
.
IsAttack
,
IsAttack
:
step
.
IsAttack
,
PreState
:
step
.
PreState
,
PreState
:
step
.
PreState
,
...
@@ -90,16 +62,16 @@ func (s *GameSolver) calculateStep(ctx context.Context, game types.Game, claim t
...
@@ -90,16 +62,16 @@ func (s *GameSolver) calculateStep(ctx context.Context, game types.Game, claim t
},
nil
},
nil
}
}
func
(
s
*
GameSolver
)
calculateMove
(
ctx
context
.
Context
,
game
types
.
Game
,
claim
types
.
Claim
)
(
*
Action
,
error
)
{
func
(
s
*
GameSolver
)
calculateMove
(
ctx
context
.
Context
,
game
types
.
Game
,
claim
types
.
Claim
)
(
*
types
.
Action
,
error
)
{
move
,
err
:=
s
.
claimSolver
.
NextMove
(
ctx
,
claim
,
game
.
AgreeWithClaimLevel
(
claim
))
move
,
err
:=
s
.
claimSolver
.
NextMove
(
ctx
,
claim
,
game
.
AgreeWithClaimLevel
(
claim
))
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"failed to calculate next move for claim index %v: %w"
,
claim
.
ContractIndex
,
err
)
return
nil
,
fmt
.
Errorf
(
"failed to calculate next move for claim index %v: %w"
,
claim
.
ContractIndex
,
err
)
}
}
if
move
==
nil
||
game
.
IsDuplicate
(
*
move
)
{
if
move
==
nil
||
game
.
IsDuplicate
(
move
.
ClaimData
)
{
return
nil
,
nil
return
nil
,
nil
}
}
return
&
Action
{
return
&
types
.
Action
{
Type
:
ActionTypeMove
,
Type
:
types
.
ActionTypeMove
,
IsAttack
:
!
move
.
DefendsParent
(),
IsAttack
:
!
move
.
DefendsParent
(),
ParentIdx
:
move
.
ParentContractIndex
,
ParentIdx
:
move
.
ParentContractIndex
,
Value
:
move
.
Value
,
Value
:
move
.
Value
,
...
...
op-challenger/game/fault/solver/game_solver_test.go
View file @
05d2352d
...
@@ -6,88 +6,31 @@ import (
...
@@ -6,88 +6,31 @@ import (
"testing"
"testing"
faulttest
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/test"
faulttest
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/test"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/require"
)
)
type
actionMaker
func
(
game
types
.
Game
)
Action
func
TestCalculateNextActions
(
t
*
testing
.
T
)
{
func
TestCalculateNextActions
(
t
*
testing
.
T
)
{
maxDepth
:=
4
maxDepth
:=
4
claimBuilder
:=
faulttest
.
NewAlphabetClaimBuilder
(
t
,
maxDepth
)
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
{
tests
:=
[]
struct
{
name
string
name
string
agreeWithOutputRoot
bool
agreeWithOutputRoot
bool
rootClaimCorrect
bool
rootClaimCorrect
bool
setupGame
func
(
builder
*
faulttest
.
GameBuilder
)
setupGame
func
(
builder
*
faulttest
.
GameBuilder
)
expectedActions
[]
actionMaker
}{
}{
{
{
name
:
"AttackRootClaim"
,
name
:
"AttackRootClaim"
,
agreeWithOutputRoot
:
true
,
agreeWithOutputRoot
:
true
,
setupGame
:
func
(
builder
*
faulttest
.
GameBuilder
)
{},
setupGame
:
func
(
builder
*
faulttest
.
GameBuilder
)
{
expectedActions
:
[]
actionMaker
{
builder
.
Seq
()
.
ExpectAttack
()
attackClaim
(
0
),
},
},
},
},
{
{
name
:
"DoNotAttackRootClaimWhenDisagreeWithOutputRoot"
,
name
:
"DoNotAttackRootClaimWhenDisagreeWithOutputRoot"
,
agreeWithOutputRoot
:
false
,
agreeWithOutputRoot
:
false
,
setupGame
:
func
(
builder
*
faulttest
.
GameBuilder
)
{},
setupGame
:
func
(
builder
*
faulttest
.
GameBuilder
)
{},
expectedActions
:
nil
,
},
},
{
{
// Note: The fault dispute game contract should prevent a correct root claim from actually being posted
// Note: The fault dispute game contract should prevent a correct root claim from actually being posted
...
@@ -96,7 +39,6 @@ func TestCalculateNextActions(t *testing.T) {
...
@@ -96,7 +39,6 @@ func TestCalculateNextActions(t *testing.T) {
agreeWithOutputRoot
:
true
,
agreeWithOutputRoot
:
true
,
rootClaimCorrect
:
true
,
rootClaimCorrect
:
true
,
setupGame
:
func
(
builder
*
faulttest
.
GameBuilder
)
{},
setupGame
:
func
(
builder
*
faulttest
.
GameBuilder
)
{},
expectedActions
:
nil
,
},
},
{
{
// Note: The fault dispute game contract should prevent a correct root claim from actually being posted
// Note: The fault dispute game contract should prevent a correct root claim from actually being posted
...
@@ -105,7 +47,6 @@ func TestCalculateNextActions(t *testing.T) {
...
@@ -105,7 +47,6 @@ func TestCalculateNextActions(t *testing.T) {
agreeWithOutputRoot
:
false
,
agreeWithOutputRoot
:
false
,
rootClaimCorrect
:
true
,
rootClaimCorrect
:
true
,
setupGame
:
func
(
builder
*
faulttest
.
GameBuilder
)
{},
setupGame
:
func
(
builder
*
faulttest
.
GameBuilder
)
{},
expectedActions
:
nil
,
},
},
{
{
...
@@ -115,31 +56,19 @@ func TestCalculateNextActions(t *testing.T) {
...
@@ -115,31 +56,19 @@ func TestCalculateNextActions(t *testing.T) {
// Expected move has already been made.
// Expected move has already been made.
builder
.
Seq
()
.
AttackCorrect
()
builder
.
Seq
()
.
AttackCorrect
()
},
},
expectedActions
:
nil
,
},
},
{
{
name
:
"RespondToAllClaimsAtDisagreeingLevel"
,
name
:
"RespondToAllClaimsAtDisagreeingLevel"
,
agreeWithOutputRoot
:
true
,
agreeWithOutputRoot
:
true
,
setupGame
:
func
(
builder
*
faulttest
.
GameBuilder
)
{
setupGame
:
func
(
builder
*
faulttest
.
GameBuilder
)
{
honestClaim
:=
builder
.
Seq
()
.
AttackCorrect
()
// 1
honestClaim
:=
builder
.
Seq
()
.
AttackCorrect
()
honestClaim
.
AttackCorrect
()
// 2
honestClaim
.
AttackCorrect
()
.
ExpectDefend
()
honestClaim
.
DefendCorrect
()
// 3
honestClaim
.
DefendCorrect
()
.
ExpectDefend
()
honestClaim
.
Attack
(
common
.
Hash
{
0xaa
})
// 4
honestClaim
.
Attack
(
common
.
Hash
{
0xaa
})
.
ExpectAttack
()
honestClaim
.
Attack
(
common
.
Hash
{
0xbb
})
// 5
honestClaim
.
Attack
(
common
.
Hash
{
0xbb
})
.
ExpectAttack
()
honestClaim
.
Defend
(
common
.
Hash
{
0xcc
})
// 6
honestClaim
.
Defend
(
common
.
Hash
{
0xcc
})
.
ExpectAttack
()
honestClaim
.
Defend
(
common
.
Hash
{
0xdd
})
// 7
honestClaim
.
Defend
(
common
.
Hash
{
0xdd
})
.
ExpectAttack
()
},
expectedActions
:
[]
actionMaker
{
// Defend the correct claims
defendClaim
(
2
),
defendClaim
(
3
),
// Attack the incorrect claims
attackClaim
(
4
),
attackClaim
(
5
),
attackClaim
(
6
),
attackClaim
(
7
),
},
},
},
},
...
@@ -148,15 +77,11 @@ func TestCalculateNextActions(t *testing.T) {
...
@@ -148,15 +77,11 @@ func TestCalculateNextActions(t *testing.T) {
agreeWithOutputRoot
:
true
,
agreeWithOutputRoot
:
true
,
setupGame
:
func
(
builder
*
faulttest
.
GameBuilder
)
{
setupGame
:
func
(
builder
*
faulttest
.
GameBuilder
)
{
lastHonestClaim
:=
builder
.
Seq
()
.
lastHonestClaim
:=
builder
.
Seq
()
.
AttackCorrect
()
.
// 1 - Honest
AttackCorrect
()
.
AttackCorrect
()
.
// 2 - Dishonest
AttackCorrect
()
.
DefendCorrect
()
// 3 - Honest
DefendCorrect
()
lastHonestClaim
.
AttackCorrect
()
// 4 - Dishonest
lastHonestClaim
.
AttackCorrect
()
.
ExpectStepDefend
()
lastHonestClaim
.
Attack
(
common
.
Hash
{
0xdd
})
// 5 - Dishonest
lastHonestClaim
.
Attack
(
common
.
Hash
{
0xdd
})
.
ExpectStepAttack
()
},
expectedActions
:
[]
actionMaker
{
stepDefend
(
4
),
stepAttack
(
5
),
},
},
},
},
}
}
...
@@ -177,11 +102,15 @@ func TestCalculateNextActions(t *testing.T) {
...
@@ -177,11 +102,15 @@ func TestCalculateNextActions(t *testing.T) {
for
i
,
action
:=
range
actions
{
for
i
,
action
:=
range
actions
{
t
.
Logf
(
"Move %v: Type: %v, ParentIdx: %v, Attack: %v, Value: %v, PreState: %v, ProofData: %v"
,
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
))
i
,
action
.
Type
,
action
.
ParentIdx
,
action
.
IsAttack
,
action
.
Value
,
hex
.
EncodeToString
(
action
.
PreState
),
hex
.
EncodeToString
(
action
.
ProofData
))
// Check that every move the solver returns meets the generic validation rules
require
.
NoError
(
t
,
checkRules
(
game
,
action
),
"Attempting to perform invalid action"
)
}
}
require
.
Len
(
t
,
actions
,
len
(
test
.
expectedActions
))
for
i
,
action
:=
range
builder
.
ExpectedActions
{
for
i
,
action
:=
range
test
.
expectedActions
{
t
.
Logf
(
"Expect %v: Type: %v, ParentIdx: %v, Attack: %v, Value: %v, PreState: %v, ProofData: %v"
,
require
.
Containsf
(
t
,
actions
,
action
(
game
),
"Expected claim %v missing"
,
i
)
i
,
action
.
Type
,
action
.
ParentIdx
,
action
.
IsAttack
,
action
.
Value
,
hex
.
EncodeToString
(
action
.
PreState
),
hex
.
EncodeToString
(
action
.
ProofData
))
require
.
Containsf
(
t
,
actions
,
action
,
"Expected claim %v missing"
,
i
)
}
}
require
.
Len
(
t
,
actions
,
len
(
builder
.
ExpectedActions
),
"Incorrect number of actions"
)
})
})
}
}
}
}
op-challenger/game/fault/solver/rules.go
0 → 100644
View file @
05d2352d
package
solver
import
(
"errors"
"fmt"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
)
type
actionRule
func
(
game
types
.
Game
,
action
types
.
Action
)
error
var
rules
=
[]
actionRule
{
parentMustExist
,
onlyStepAtMaxDepth
,
onlyMoveBeforeMaxDepth
,
onlyCounterClaimsAtDisagreeingLevels
,
doNotDuplicateExistingMoves
,
doNotDefendRootClaim
,
}
func
checkRules
(
game
types
.
Game
,
action
types
.
Action
)
error
{
var
errs
[]
error
for
_
,
rule
:=
range
rules
{
errs
=
append
(
errs
,
rule
(
game
,
action
))
}
return
errors
.
Join
(
errs
...
)
}
func
parentMustExist
(
game
types
.
Game
,
action
types
.
Action
)
error
{
if
len
(
game
.
Claims
())
<=
action
.
ParentIdx
||
action
.
ParentIdx
<
0
{
return
fmt
.
Errorf
(
"parent claim %v does not exist in game with %v claims"
,
action
.
ParentIdx
,
len
(
game
.
Claims
()))
}
return
nil
}
func
onlyStepAtMaxDepth
(
game
types
.
Game
,
action
types
.
Action
)
error
{
if
action
.
Type
==
types
.
ActionTypeStep
{
return
nil
}
parentDepth
:=
uint64
(
game
.
Claims
()[
action
.
ParentIdx
]
.
Position
.
Depth
())
if
parentDepth
>=
game
.
MaxDepth
()
{
return
fmt
.
Errorf
(
"parent at max depth (%v) but attempting to perform %v action instead of step"
,
parentDepth
,
action
.
Type
)
}
return
nil
}
func
onlyMoveBeforeMaxDepth
(
game
types
.
Game
,
action
types
.
Action
)
error
{
if
action
.
Type
==
types
.
ActionTypeMove
{
return
nil
}
parentDepth
:=
uint64
(
game
.
Claims
()[
action
.
ParentIdx
]
.
Position
.
Depth
())
if
parentDepth
<
game
.
MaxDepth
()
{
return
fmt
.
Errorf
(
"parent (%v) not at max depth (%v) but attempting to perform %v action instead of move"
,
parentDepth
,
game
.
MaxDepth
(),
action
.
Type
)
}
return
nil
}
func
onlyCounterClaimsAtDisagreeingLevels
(
game
types
.
Game
,
action
types
.
Action
)
error
{
parentClaim
:=
game
.
Claims
()[
action
.
ParentIdx
]
if
game
.
AgreeWithClaimLevel
(
parentClaim
)
{
return
fmt
.
Errorf
(
"countering a claim at depth %v that supports our view of the root"
,
parentClaim
.
Position
.
Depth
())
}
return
nil
}
func
doNotDuplicateExistingMoves
(
game
types
.
Game
,
action
types
.
Action
)
error
{
newClaimData
:=
types
.
ClaimData
{
Value
:
action
.
Value
,
Position
:
resultingPosition
(
game
,
action
),
}
if
game
.
IsDuplicate
(
newClaimData
)
{
return
fmt
.
Errorf
(
"creating duplicate claim at %v with value %v"
,
newClaimData
.
Position
.
ToGIndex
(),
newClaimData
.
Value
)
}
return
nil
}
func
doNotDefendRootClaim
(
game
types
.
Game
,
action
types
.
Action
)
error
{
if
game
.
Claims
()[
action
.
ParentIdx
]
.
IsRootPosition
()
&&
!
action
.
IsAttack
{
return
fmt
.
Errorf
(
"defending the root claim at idx %v"
,
action
.
ParentIdx
)
}
return
nil
}
func
resultingPosition
(
game
types
.
Game
,
action
types
.
Action
)
types
.
Position
{
parentPos
:=
game
.
Claims
()[
action
.
ParentIdx
]
.
Position
if
action
.
Type
==
types
.
ActionTypeStep
{
return
parentPos
}
if
action
.
IsAttack
{
return
parentPos
.
Attack
()
}
return
parentPos
.
Defend
()
}
op-challenger/game/fault/test/game_builder.go
View file @
05d2352d
...
@@ -6,8 +6,9 @@ import (
...
@@ -6,8 +6,9 @@ import (
)
)
type
GameBuilder
struct
{
type
GameBuilder
struct
{
builder
*
ClaimBuilder
builder
*
ClaimBuilder
Game
types
.
Game
Game
types
.
Game
ExpectedActions
[]
types
.
Action
}
}
func
(
c
*
ClaimBuilder
)
GameBuilder
(
agreeWithOutputRoot
bool
,
rootCorrect
bool
)
*
GameBuilder
{
func
(
c
*
ClaimBuilder
)
GameBuilder
(
agreeWithOutputRoot
bool
,
rootCorrect
bool
)
*
GameBuilder
{
...
@@ -18,16 +19,18 @@ func (c *ClaimBuilder) GameBuilder(agreeWithOutputRoot bool, rootCorrect bool) *
...
@@ -18,16 +19,18 @@ func (c *ClaimBuilder) GameBuilder(agreeWithOutputRoot bool, rootCorrect bool) *
}
}
type
GameBuilderSeq
struct
{
type
GameBuilderSeq
struct
{
builder
*
ClaimBuilder
gameBuilder
*
GameBuilder
lastClaim
types
.
Claim
builder
*
ClaimBuilder
game
types
.
Game
lastClaim
types
.
Claim
game
types
.
Game
}
}
func
(
g
*
GameBuilder
)
Seq
()
*
GameBuilderSeq
{
func
(
g
*
GameBuilder
)
Seq
()
*
GameBuilderSeq
{
return
&
GameBuilderSeq
{
return
&
GameBuilderSeq
{
builder
:
g
.
builder
,
gameBuilder
:
g
,
game
:
g
.
Game
,
builder
:
g
.
builder
,
lastClaim
:
g
.
Game
.
Claims
()[
0
],
game
:
g
.
Game
,
lastClaim
:
g
.
Game
.
Claims
()[
0
],
}
}
}
}
...
@@ -36,9 +39,10 @@ func (s *GameBuilderSeq) AttackCorrect() *GameBuilderSeq {
...
@@ -36,9 +39,10 @@ func (s *GameBuilderSeq) AttackCorrect() *GameBuilderSeq {
claim
.
ContractIndex
=
len
(
s
.
game
.
Claims
())
claim
.
ContractIndex
=
len
(
s
.
game
.
Claims
())
s
.
builder
.
require
.
NoError
(
s
.
game
.
Put
(
claim
))
s
.
builder
.
require
.
NoError
(
s
.
game
.
Put
(
claim
))
return
&
GameBuilderSeq
{
return
&
GameBuilderSeq
{
builder
:
s
.
builder
,
gameBuilder
:
s
.
gameBuilder
,
game
:
s
.
game
,
builder
:
s
.
builder
,
lastClaim
:
claim
,
game
:
s
.
game
,
lastClaim
:
claim
,
}
}
}
}
...
@@ -47,9 +51,10 @@ func (s *GameBuilderSeq) Attack(value common.Hash) *GameBuilderSeq {
...
@@ -47,9 +51,10 @@ func (s *GameBuilderSeq) Attack(value common.Hash) *GameBuilderSeq {
claim
.
ContractIndex
=
len
(
s
.
game
.
Claims
())
claim
.
ContractIndex
=
len
(
s
.
game
.
Claims
())
s
.
builder
.
require
.
NoError
(
s
.
game
.
Put
(
claim
))
s
.
builder
.
require
.
NoError
(
s
.
game
.
Put
(
claim
))
return
&
GameBuilderSeq
{
return
&
GameBuilderSeq
{
builder
:
s
.
builder
,
gameBuilder
:
s
.
gameBuilder
,
game
:
s
.
game
,
builder
:
s
.
builder
,
lastClaim
:
claim
,
game
:
s
.
game
,
lastClaim
:
claim
,
}
}
}
}
...
@@ -58,9 +63,10 @@ func (s *GameBuilderSeq) DefendCorrect() *GameBuilderSeq {
...
@@ -58,9 +63,10 @@ func (s *GameBuilderSeq) DefendCorrect() *GameBuilderSeq {
claim
.
ContractIndex
=
len
(
s
.
game
.
Claims
())
claim
.
ContractIndex
=
len
(
s
.
game
.
Claims
())
s
.
builder
.
require
.
NoError
(
s
.
game
.
Put
(
claim
))
s
.
builder
.
require
.
NoError
(
s
.
game
.
Put
(
claim
))
return
&
GameBuilderSeq
{
return
&
GameBuilderSeq
{
builder
:
s
.
builder
,
gameBuilder
:
s
.
gameBuilder
,
game
:
s
.
game
,
builder
:
s
.
builder
,
lastClaim
:
claim
,
game
:
s
.
game
,
lastClaim
:
claim
,
}
}
}
}
...
@@ -69,8 +75,59 @@ func (s *GameBuilderSeq) Defend(value common.Hash) *GameBuilderSeq {
...
@@ -69,8 +75,59 @@ func (s *GameBuilderSeq) Defend(value common.Hash) *GameBuilderSeq {
claim
.
ContractIndex
=
len
(
s
.
game
.
Claims
())
claim
.
ContractIndex
=
len
(
s
.
game
.
Claims
())
s
.
builder
.
require
.
NoError
(
s
.
game
.
Put
(
claim
))
s
.
builder
.
require
.
NoError
(
s
.
game
.
Put
(
claim
))
return
&
GameBuilderSeq
{
return
&
GameBuilderSeq
{
builder
:
s
.
builder
,
gameBuilder
:
s
.
gameBuilder
,
game
:
s
.
game
,
builder
:
s
.
builder
,
lastClaim
:
claim
,
game
:
s
.
game
,
lastClaim
:
claim
,
}
}
}
}
func
(
s
*
GameBuilderSeq
)
ExpectAttack
()
*
GameBuilderSeq
{
newPos
:=
s
.
lastClaim
.
Position
.
Attack
()
value
:=
s
.
builder
.
CorrectClaimAtPosition
(
newPos
)
s
.
gameBuilder
.
ExpectedActions
=
append
(
s
.
gameBuilder
.
ExpectedActions
,
types
.
Action
{
Type
:
types
.
ActionTypeMove
,
ParentIdx
:
s
.
lastClaim
.
ContractIndex
,
IsAttack
:
true
,
Value
:
value
,
})
return
s
}
func
(
s
*
GameBuilderSeq
)
ExpectDefend
()
*
GameBuilderSeq
{
newPos
:=
s
.
lastClaim
.
Position
.
Defend
()
value
:=
s
.
builder
.
CorrectClaimAtPosition
(
newPos
)
s
.
gameBuilder
.
ExpectedActions
=
append
(
s
.
gameBuilder
.
ExpectedActions
,
types
.
Action
{
Type
:
types
.
ActionTypeMove
,
ParentIdx
:
s
.
lastClaim
.
ContractIndex
,
IsAttack
:
false
,
Value
:
value
,
})
return
s
}
func
(
s
*
GameBuilderSeq
)
ExpectStepAttack
()
*
GameBuilderSeq
{
traceIdx
:=
s
.
lastClaim
.
TraceIndex
(
s
.
builder
.
maxDepth
)
s
.
gameBuilder
.
ExpectedActions
=
append
(
s
.
gameBuilder
.
ExpectedActions
,
types
.
Action
{
Type
:
types
.
ActionTypeStep
,
ParentIdx
:
s
.
lastClaim
.
ContractIndex
,
IsAttack
:
true
,
PreState
:
s
.
builder
.
CorrectPreState
(
traceIdx
),
ProofData
:
s
.
builder
.
CorrectProofData
(
traceIdx
),
OracleData
:
s
.
builder
.
CorrectOracleData
(
traceIdx
),
})
return
s
}
func
(
s
*
GameBuilderSeq
)
ExpectStepDefend
()
*
GameBuilderSeq
{
traceIdx
:=
s
.
lastClaim
.
TraceIndex
(
s
.
builder
.
maxDepth
)
+
1
s
.
gameBuilder
.
ExpectedActions
=
append
(
s
.
gameBuilder
.
ExpectedActions
,
types
.
Action
{
Type
:
types
.
ActionTypeStep
,
ParentIdx
:
s
.
lastClaim
.
ContractIndex
,
IsAttack
:
false
,
PreState
:
s
.
builder
.
CorrectPreState
(
traceIdx
),
ProofData
:
s
.
builder
.
CorrectProofData
(
traceIdx
),
OracleData
:
s
.
builder
.
CorrectOracleData
(
traceIdx
),
})
return
s
}
op-challenger/game/fault/types/actions.go
0 → 100644
View file @
05d2352d
package
types
import
"github.com/ethereum/go-ethereum/common"
type
ActionType
string
func
(
a
ActionType
)
String
()
string
{
return
string
(
a
)
}
const
(
ActionTypeMove
ActionType
=
"move"
ActionTypeStep
ActionType
=
"step"
)
type
Action
struct
{
Type
ActionType
ParentIdx
int
IsAttack
bool
// Moves
Value
common
.
Hash
// Steps
PreState
[]
byte
ProofData
[]
byte
OracleData
*
PreimageOracleData
}
op-challenger/game/fault/types/game.go
View file @
05d2352d
...
@@ -24,10 +24,12 @@ type Game interface {
...
@@ -24,10 +24,12 @@ type Game interface {
Claims
()
[]
Claim
Claims
()
[]
Claim
// IsDuplicate returns true if the provided [Claim] already exists in the game state.
// IsDuplicate returns true if the provided [Claim] already exists in the game state.
IsDuplicate
(
claim
Claim
)
bool
IsDuplicate
(
claim
Claim
Data
)
bool
// AgreeWithClaimLevel returns if the game state agrees with the provided claim level.
// AgreeWithClaimLevel returns if the game state agrees with the provided claim level.
AgreeWithClaimLevel
(
claim
Claim
)
bool
AgreeWithClaimLevel
(
claim
Claim
)
bool
MaxDepth
()
uint64
}
}
type
extendedClaim
struct
{
type
extendedClaim
struct
{
...
@@ -85,7 +87,7 @@ func (g *gameState) PutAll(claims []Claim) error {
...
@@ -85,7 +87,7 @@ func (g *gameState) PutAll(claims []Claim) error {
// Put adds a claim into the game state.
// Put adds a claim into the game state.
func
(
g
*
gameState
)
Put
(
claim
Claim
)
error
{
func
(
g
*
gameState
)
Put
(
claim
Claim
)
error
{
if
claim
.
IsRoot
()
||
g
.
IsDuplicate
(
claim
)
{
if
claim
.
IsRoot
()
||
g
.
IsDuplicate
(
claim
.
ClaimData
)
{
return
ErrClaimExists
return
ErrClaimExists
}
}
parent
,
ok
:=
g
.
claims
[
claim
.
Parent
]
parent
,
ok
:=
g
.
claims
[
claim
.
Parent
]
...
@@ -101,8 +103,8 @@ func (g *gameState) Put(claim Claim) error {
...
@@ -101,8 +103,8 @@ func (g *gameState) Put(claim Claim) error {
return
nil
return
nil
}
}
func
(
g
*
gameState
)
IsDuplicate
(
claim
Claim
)
bool
{
func
(
g
*
gameState
)
IsDuplicate
(
claim
Claim
Data
)
bool
{
_
,
ok
:=
g
.
claims
[
claim
.
ClaimData
]
_
,
ok
:=
g
.
claims
[
claim
]
return
ok
return
ok
}
}
...
@@ -118,6 +120,10 @@ func (g *gameState) Claims() []Claim {
...
@@ -118,6 +120,10 @@ func (g *gameState) Claims() []Claim {
return
out
return
out
}
}
func
(
g
*
gameState
)
MaxDepth
()
uint64
{
return
g
.
depth
}
func
(
g
*
gameState
)
getChildren
(
c
ClaimData
)
[]
ClaimData
{
func
(
g
*
gameState
)
getChildren
(
c
ClaimData
)
[]
ClaimData
{
return
g
.
claims
[
c
]
.
children
return
g
.
claims
[
c
]
.
children
}
}
...
...
op-challenger/game/fault/types/game_test.go
View file @
05d2352d
...
@@ -52,12 +52,12 @@ func TestIsDuplicate(t *testing.T) {
...
@@ -52,12 +52,12 @@ func TestIsDuplicate(t *testing.T) {
require
.
NoError
(
t
,
g
.
Put
(
top
))
require
.
NoError
(
t
,
g
.
Put
(
top
))
// Root + Top should be duplicates
// Root + Top should be duplicates
require
.
True
(
t
,
g
.
IsDuplicate
(
root
))
require
.
True
(
t
,
g
.
IsDuplicate
(
root
.
ClaimData
))
require
.
True
(
t
,
g
.
IsDuplicate
(
top
))
require
.
True
(
t
,
g
.
IsDuplicate
(
top
.
ClaimData
))
// Middle + Bottom should not be a duplicate
// Middle + Bottom should not be a duplicate
require
.
False
(
t
,
g
.
IsDuplicate
(
middle
))
require
.
False
(
t
,
g
.
IsDuplicate
(
middle
.
ClaimData
))
require
.
False
(
t
,
g
.
IsDuplicate
(
bottom
))
require
.
False
(
t
,
g
.
IsDuplicate
(
bottom
.
ClaimData
))
}
}
// TestGame_Put_RootAlreadyExists tests the [Game.Put] method using a [gameState]
// TestGame_Put_RootAlreadyExists tests the [Game.Put] method using a [gameState]
...
...
specs/fault-dispute-game.md
View file @
05d2352d
...
@@ -7,6 +7,7 @@
...
@@ -7,6 +7,7 @@
-
[
Overview
](
#overview
)
-
[
Overview
](
#overview
)
-
[
Definitions
](
#definitions
)
-
[
Definitions
](
#definitions
)
-
[
Virtual Machine (VM)
](
#virtual-machine-vm
)
-
[
Virtual Machine (VM)
](
#virtual-machine-vm
)
-
[
PreimageOracle
](
#preimageoracle
)
-
[
Execution Trace
](
#execution-trace
)
-
[
Execution Trace
](
#execution-trace
)
-
[
Claims
](
#claims
)
-
[
Claims
](
#claims
)
-
[
DAG
](
#dag
)
-
[
DAG
](
#dag
)
...
@@ -20,6 +21,7 @@
...
@@ -20,6 +21,7 @@
-
[
Defend
](
#defend
)
-
[
Defend
](
#defend
)
-
[
Step
](
#step
)
-
[
Step
](
#step
)
-
[
Step Types
](
#step-types
)
-
[
Step Types
](
#step-types
)
-
[
PreimageOracle Interaction
](
#preimageoracle-interaction
)
-
[
Team Dynamics
](
#team-dynamics
)
-
[
Team Dynamics
](
#team-dynamics
)
-
[
Game Clock
](
#game-clock
)
-
[
Game Clock
](
#game-clock
)
-
[
Resolution
](
#resolution
)
-
[
Resolution
](
#resolution
)
...
@@ -44,7 +46,7 @@ claims made that are disputed and which aren't, to determine the winners of the
...
@@ -44,7 +46,7 @@ claims made that are disputed and which aren't, to determine the winners of the
### Virtual Machine (VM)
### Virtual Machine (VM)
This is a state transition function (STF) that takes a _pre-state_ and computes the post-state.
This is a state transition function (STF) that takes a _pre-state_ and computes the post-state.
The VM may
reference external data
during the STF and as such, it also accepts a _proof_ of this data.
The VM may
access data referenced
during the STF and as such, it also accepts a _proof_ of this data.
Typically, the pre-state contains a commitment to the _proof_ to verify the integrity of the data referenced.
Typically, the pre-state contains a commitment to the _proof_ to verify the integrity of the data referenced.
Mathemtically, we define the STF as $VM(S_i,P_i)$ where
Mathemtically, we define the STF as $VM(S_i,P_i)$ where
...
@@ -52,6 +54,12 @@ Mathemtically, we define the STF as $VM(S_i,P_i)$ where
...
@@ -52,6 +54,12 @@ Mathemtically, we define the STF as $VM(S_i,P_i)$ where
-
$S_i$ is the pre-state
-
$S_i$ is the pre-state
-
$P_i$ is an optional proof needed for the transition from $S_i$ to $S_{i+1}$.
-
$P_i$ is an optional proof needed for the transition from $S_i$ to $S_{i+1}$.
### PreimageOracle
This is a pre-image data store. It is often used by VMs to read external data during its STF.
Before successfully executing a VM STF, it may be necessary to preload the PreimageOracle with pertinent data.
The method for key-based retrieval of these pre-images varies according to the specific VM.
### Execution Trace
### Execution Trace
An execution trace $T$ is a sequence $(S_0,S_1,S_2,...,S_n)$ where each $S_i$ is a VM state and
An execution trace $T$ is a sequence $(S_0,S_1,S_2,...,S_n)$ where each $S_i$ is a VM state and
...
@@ -62,7 +70,7 @@ We refer to this state as the **ABSOLUTE\_PRESTATE**.
...
@@ -62,7 +70,7 @@ We refer to this state as the **ABSOLUTE\_PRESTATE**.
### Claims
### Claims
Claims assert an execution trace. This is represented as
`ClaimHash`
, a
`bytes32`
commitment to
Claims assert an execution trace. This is represented as
`ClaimHash`
, a
`bytes32`
commitment to
the last VM state in
the
trace. A FDG is initialized with a root claim, which commits to the entire
the last VM state in
a
trace. A FDG is initialized with a root claim, which commits to the entire
execution trace. As we'll see later, there can be multiple claims, committing to different states in the FDG.
execution trace. As we'll see later, there can be multiple claims, committing to different states in the FDG.
### DAG
### DAG
...
@@ -97,6 +105,7 @@ Positions higher up the game tree also cover the deepest, right-most positions r
...
@@ -97,6 +105,7 @@ Positions higher up the game tree also cover the deepest, right-most positions r
We refer to this coverage as the
**trace index**
of a Position.
We refer to this coverage as the
**trace index**
of a Position.
> This means claims commit to an execution trace that terminates at the same index as their Position's trace index.
> This means claims commit to an execution trace that terminates at the same index as their Position's trace index.
> That is, for a given trace index $n$, its ClaimHash corresponds to the $S_n$ th state in the trace.
Note that there can be multiple positions covering the same _trace index_.
Note that there can be multiple positions covering the same _trace index_.
...
@@ -198,6 +207,22 @@ Players interface with `step` by providing an indicator of attack and state data
...
@@ -198,6 +207,22 @@ Players interface with `step` by providing an indicator of attack and state data
that corresponds to the expected pre/post state (depending on whether it's an attack or defend).
that corresponds to the expected pre/post state (depending on whether it's an attack or defend).
The FDG will assert that an existing claim commits to the state data provided by players.
The FDG will assert that an existing claim commits to the state data provided by players.
### PreimageOracle Interaction
Certain steps (VM state transitions) require external data to be available by the `
PreimageOracle
`.
To ensure a successful state transition, players should provide this data in advance.
The FDG provides the following interface to manage data loaded to the `
PreimageOracle
`:
```solidity
/// @notice Posts the requested local data to the VM's `PreimageOralce`.
/// @param _ident The local identifier of the data to post.
/// @param _partOffset The offset of the data to post.
function addLocalData(uint256 _ident, uint256 _partOffset) external;
```
The `
addLocalData
` function loads parts of a pre-image to VM's `
PreimageOracle
`.
Players use this to ensure pre-image parts are available to the VM during a step.
### Team Dynamics
### Team Dynamics
Challengers seek to dispute the root claim, while Defenders aim to support it.
Challengers seek to dispute the root claim, while Defenders aim to support it.
...
...
specs/honest-challenger-fdg.md
0 → 100644
View file @
05d2352d
# Honest Challenger (Fault 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**
-
[
Overview
](
#overview
)
-
[
FDG Responses
](
#fdg-responses
)
-
[
Root Claims
](
#root-claims
)
-
[
Counter Claims
](
#counter-claims
)
-
[
Steps
](
#steps
)
-
[
Resolution
](
#resolution
)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
The honest challenger is an agent interacting in the
[
Fault Dispute Game
](
./fault-dispute-game.md
)
(FDG) that supports honest claims and disputes false claims.
An honest challenger strives to ensure a correct, truthful, game resolution.
The honest challenger is also _rational_ as any deviation from its behavior will result in
negative outcomes.
This document specifies the expected behavior of an honest challenger.
## Overview
The Honest Challenger has two primary duties:
1.
Support valid root claims in Fault Dispute Games.
2.
Dispute invalid root claims in Fault Dispute Games.
The honest challenger polls the
`DisputeGameFactory`
contract for new and on-going Fault
Dispute Games.
For verifying the legitimacy of claims, it relies on a synced, trusted rollup node
as well as a trace provider (ex:
[
Cannon
](
./cannon-fault-proof-vm.md
)
).
The trace provider must be configured with the
[
ABSOLUTE_PRESTATE
](
./fault-dispute-game.md#execution-trace
)
of the FDG being interacted with to generate the traces needed to make truthful claims.
## FDG Responses
### Root Claims
When a
`FaultDisputeGame`
is created, the honest challenger has two possible correct responses
to its root claim:
1.
[
**Attack**
](
./fault-dispute-game.md#attack
)
if they disagree with the root claim.
The root claim commits to the entire execution trace, so the first move here is to
attack with the
[
ClaimHash
](
./fault-dispute-game.md#claims
)
at the midpoint
instruction within their execution trace.
2.
**Do Nothing**
if they agree with the root claim. They do nothing because if the root
claim is left un-countered, the game resolves to their agreement.
NOTE: The honest challenger will still track this game in order to defend any subsequent
claims made against the root claim - in effect, "playing the game".
### Counter Claims
For every claim made in a dispute game with a
[
game tree
](
./fault-dispute-game.md#game-tree
)
depth in the range of
`[1, MAX_DEPTH]`
, the honest challenger processes them and performs
a response.
To determine the appropriate response, the challenger first needs to know which
[
_team_
](
./fault-dispute-game.md#team-dynamics
)
it belongs to.
This determines the set of claims it should respond to in the FDG.
If the agent determines itself to be a Defender, which aims to support the root claim,
then it must dispute claims positioned at odd depths in the game tree.
Otherwise, it disputes claims positioned at even depths in the game tree.
This means an honest challenger only responds to claims made by the opposing team.
The next step is to determine whether the claim has a valid commitment (i.e.
`ClaimHash`
).
If the
`ClaimHash`
matches the honest challenger's at the same trace index, then we
disagree with the claim's stance by moving to
[
defend
](
./fault-dispute-game.md#defend
)
.
Otherwise, the claim is
[
attacked
](
./fault-dispute-game.md#attack
)
.
The following pseudocode illustrates the response logic.
```
python
class
Team
(
Enum
):
DEFENDER
=
0
CHALLENGER
=
1
class
Claim
:
position
:
uint64
claim_hash
:
ClaimHash
MAX_TRACE
=
2
**
MAX_GAME_DEPTH
def
respond
(
claim
:
Claim
,
chal
:
Team
,
chal_trace
:
List
[
ClaimHash
,
MAX_TRACE
]):
if
depth
(
claim
.
position
)
%
2
!=
chal
.
value
:
if
chal_trace
[
trace_index
(
claim
.
position
)]
==
claim
.
claim_hash
:
defend
()
else
:
attack
()
else
:
pass
# no response
```
In attack or defense, the honest challenger submit a
`ClaimHash`
corresponding to the
state identified by the trace index of their response position.
The honest challenger responds to claims as soon as possible to avoid the clock of its
counter-claim from expiring.
### Steps
At the max depth of the game, claims represent commitments to the state of the fault proof VM
at a single instruction step interval.
Because the game can no longer bisect further, when the honest challenger has a valid move
against these claims (valid defined by the response in
[
Counter Claims
](
#counter-claims
)
),
the only option for an honest challenger is to execute a VM step on-chain to disprove the claim at
`MAX_GAME_DEPTH`
.
Similar to the above section, the honest challenger will issue an
[
attack step
](
./fault-dispute-game.md#step-types
)
when in response to such claims with
invalid
`ClaimHash`
commitments. Otherwise, it issues a _defense step_.
## Resolution
When the
[
chess clock
](
./fault-dispute-game.md#game-clock
)
of a
`FaultDisputeGame`
team
runs out, the game can be resolved.
The honest challenger does this by calling the
`resolve`
function on the
`FaultDisputeGame`
contract.
The
`FaultDisputeGame`
does not put a time cap on resolution - because of the liveness
assumption on honest challengers and the bonds attached to the claims they’ve countered,
challengers are economically incentivized to resolve the game promptly to capture the bonds.
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