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
6a3a96c3
Unverified
Commit
6a3a96c3
authored
Sep 20, 2023
by
mergify[bot]
Committed by
GitHub
Sep 20, 2023
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'develop' into refcell/cleanup-bootnode
parents
7e385008
c93a5531
Changes
27
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
27 changed files
with
1119 additions
and
426 deletions
+1119
-426
faultdisputegame.go
op-bindings/bindings/faultdisputegame.go
+23
-2
faultdisputegame_more.go
op-bindings/bindings/faultdisputegame_more.go
+2
-2
agent.go
op-challenger/game/fault/agent.go
+61
-0
agent_test.go
op-challenger/game/fault/agent_test.go
+20
-3
responder.go
op-challenger/game/fault/responder/responder.go
+28
-0
responder_test.go
op-challenger/game/fault/responder/responder_test.go
+34
-0
game_solver.go
op-challenger/game/fault/solver/game_solver.go
+9
-3
game_solver_test.go
op-challenger/game/fault/solver/game_solver_test.go
+9
-11
rules.go
op-challenger/game/fault/solver/rules.go
+1
-10
solver.go
op-challenger/game/fault/solver/solver.go
+60
-9
solver_test.go
op-challenger/game/fault/solver/solver_test.go
+118
-153
claim_builder.go
op-challenger/game/fault/test/claim_builder.go
+2
-1
game.go
op-challenger/game/fault/types/game.go
+53
-26
game_test.go
op-challenger/game/fault/types/game_test.go
+21
-39
helper.go
op-e2e/e2eutils/challenger/helper.go
+7
-1
alphabet_helper.go
op-e2e/e2eutils/disputegame/alphabet_helper.go
+10
-0
game_helper.go
op-e2e/e2eutils/disputegame/game_helper.go
+200
-2
faultproof_test.go
op-e2e/faultproof_test.go
+109
-25
.gas-snapshot
packages/contracts-bedrock/.gas-snapshot
+39
-33
semver-lock.json
packages/contracts-bedrock/semver-lock.json
+1
-1
FaultDisputeGame.sol
packages/contracts-bedrock/src/dispute/FaultDisputeGame.sol
+60
-53
IFaultDisputeGame.sol
...acts-bedrock/src/dispute/interfaces/IFaultDisputeGame.sol
+8
-0
LibHashing.sol
packages/contracts-bedrock/src/dispute/lib/LibHashing.sol
+12
-3
DisputeErrors.sol
packages/contracts-bedrock/src/libraries/DisputeErrors.sol
+6
-0
DelayedVetoable.t.sol
packages/contracts-bedrock/test/DelayedVetoable.t.sol
+37
-17
FaultDisputeGame.t.sol
packages/contracts-bedrock/test/FaultDisputeGame.t.sol
+128
-13
pnpm-lock.yaml
pnpm-lock.yaml
+61
-19
No files found.
op-bindings/bindings/faultdisputegame.go
View file @
6a3a96c3
This diff is collapsed.
Click to expand it.
op-bindings/bindings/faultdisputegame_more.go
View file @
6a3a96c3
This diff is collapsed.
Click to expand it.
op-challenger/game/fault/agent.go
View file @
6a3a96c3
...
@@ -4,6 +4,7 @@ import (
...
@@ -4,6 +4,7 @@ import (
"context"
"context"
"errors"
"errors"
"fmt"
"fmt"
"sync"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/solver"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/solver"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
...
@@ -18,6 +19,8 @@ import (
...
@@ -18,6 +19,8 @@ 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
CallResolveClaim
(
ctx
context
.
Context
,
claimIdx
uint64
)
error
ResolveClaim
(
ctx
context
.
Context
,
claimIdx
uint64
)
error
PerformAction
(
ctx
context
.
Context
,
action
types
.
Action
)
error
PerformAction
(
ctx
context
.
Context
,
action
types
.
Action
)
error
}
}
...
@@ -112,6 +115,10 @@ func (a *Agent) shouldResolve(status gameTypes.GameStatus) bool {
...
@@ -112,6 +115,10 @@ func (a *Agent) shouldResolve(status gameTypes.GameStatus) bool {
// tryResolve resolves the game if it is in a winning state
// tryResolve resolves the game if it is in a winning state
// Returns true if the game is resolvable (regardless of whether it was actually resolved)
// Returns true if the game is resolvable (regardless of whether it was actually resolved)
func
(
a
*
Agent
)
tryResolve
(
ctx
context
.
Context
)
bool
{
func
(
a
*
Agent
)
tryResolve
(
ctx
context
.
Context
)
bool
{
if
err
:=
a
.
resolveClaims
(
ctx
);
err
!=
nil
{
a
.
log
.
Error
(
"Failed to resolve claims"
,
"err"
,
err
)
return
false
}
status
,
err
:=
a
.
responder
.
CallResolve
(
ctx
)
status
,
err
:=
a
.
responder
.
CallResolve
(
ctx
)
if
err
!=
nil
||
status
==
gameTypes
.
GameStatusInProgress
{
if
err
!=
nil
||
status
==
gameTypes
.
GameStatusInProgress
{
return
false
return
false
...
@@ -126,6 +133,60 @@ func (a *Agent) tryResolve(ctx context.Context) bool {
...
@@ -126,6 +133,60 @@ func (a *Agent) tryResolve(ctx context.Context) bool {
return
true
return
true
}
}
var
errNoResolvableClaims
=
errors
.
New
(
"no resolvable claims"
)
func
(
a
*
Agent
)
tryResolveClaims
(
ctx
context
.
Context
)
error
{
claims
,
err
:=
a
.
loader
.
FetchClaims
(
ctx
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"failed to fetch claims: %w"
,
err
)
}
if
len
(
claims
)
==
0
{
return
errNoResolvableClaims
}
var
resolvableClaims
[]
int64
for
_
,
claim
:=
range
claims
{
a
.
log
.
Debug
(
"checking if claim is resolvable"
,
"claimIdx"
,
claim
.
ContractIndex
)
if
err
:=
a
.
responder
.
CallResolveClaim
(
ctx
,
uint64
(
claim
.
ContractIndex
));
err
==
nil
{
a
.
log
.
Info
(
"Resolving claim"
,
"claimIdx"
,
claim
.
ContractIndex
)
resolvableClaims
=
append
(
resolvableClaims
,
int64
(
claim
.
ContractIndex
))
}
}
a
.
log
.
Info
(
"Resolving claims"
,
"numClaims"
,
len
(
resolvableClaims
))
if
len
(
resolvableClaims
)
==
0
{
return
errNoResolvableClaims
}
var
wg
sync
.
WaitGroup
wg
.
Add
(
len
(
resolvableClaims
))
for
_
,
claimIdx
:=
range
resolvableClaims
{
claimIdx
:=
claimIdx
go
func
()
{
defer
wg
.
Done
()
err
:=
a
.
responder
.
ResolveClaim
(
ctx
,
uint64
(
claimIdx
))
if
err
!=
nil
{
a
.
log
.
Error
(
"Failed to resolve claim"
,
"err"
,
err
)
}
}()
}
wg
.
Wait
()
return
nil
}
func
(
a
*
Agent
)
resolveClaims
(
ctx
context
.
Context
)
error
{
for
{
err
:=
a
.
tryResolveClaims
(
ctx
)
switch
err
{
case
errNoResolvableClaims
:
return
nil
case
nil
:
continue
default
:
return
err
}
}
}
// newGameFromContracts initializes a new game state from the state in the contract
// newGameFromContracts initializes a new game state from the state in the contract
func
(
a
*
Agent
)
newGameFromContracts
(
ctx
context
.
Context
)
(
types
.
Game
,
error
)
{
func
(
a
*
Agent
)
newGameFromContracts
(
ctx
context
.
Context
)
(
types
.
Game
,
error
)
{
claims
,
err
:=
a
.
loader
.
FetchClaims
(
ctx
)
claims
,
err
:=
a
.
loader
.
FetchClaims
(
ctx
)
...
...
op-challenger/game/fault/agent_test.go
View file @
6a3a96c3
...
@@ -77,7 +77,7 @@ func TestDoNotMakeMovesWhenGameIsResolvable(t *testing.T) {
...
@@ -77,7 +77,7 @@ func TestDoNotMakeMovesWhenGameIsResolvable(t *testing.T) {
require
.
NoError
(
t
,
agent
.
Act
(
ctx
))
require
.
NoError
(
t
,
agent
.
Act
(
ctx
))
require
.
Equal
(
t
,
1
,
responder
.
callResolveCount
,
"should check if game is resolvable"
)
require
.
Equal
(
t
,
1
,
responder
.
callResolveCount
,
"should check if game is resolvable"
)
require
.
Zero
(
t
,
claimLoader
.
callCount
,
"should not fetch claims for resolvable game
"
)
require
.
Equal
(
t
,
1
,
claimLoader
.
callCount
,
"should fetch claims once for resolveClaim
"
)
if
test
.
shouldResolve
{
if
test
.
shouldResolve
{
require
.
EqualValues
(
t
,
1
,
responder
.
resolveCount
,
"should resolve winning game"
)
require
.
EqualValues
(
t
,
1
,
responder
.
resolveCount
,
"should resolve winning game"
)
...
@@ -92,6 +92,7 @@ func TestLoadClaimsWhenGameNotResolvable(t *testing.T) {
...
@@ -92,6 +92,7 @@ func TestLoadClaimsWhenGameNotResolvable(t *testing.T) {
// Checks that if the game isn't resolvable, that the agent continues on to start checking claims
// Checks that if the game isn't resolvable, that the agent continues on to start checking claims
agent
,
claimLoader
,
responder
:=
setupTestAgent
(
t
,
false
)
agent
,
claimLoader
,
responder
:=
setupTestAgent
(
t
,
false
)
responder
.
callResolveErr
=
errors
.
New
(
"game is not resolvable"
)
responder
.
callResolveErr
=
errors
.
New
(
"game is not resolvable"
)
responder
.
callResolveClaimErr
=
errors
.
New
(
"claim is not resolvable"
)
depth
:=
4
depth
:=
4
claimBuilder
:=
test
.
NewClaimBuilder
(
t
,
depth
,
alphabet
.
NewTraceProvider
(
"abcdefg"
,
uint64
(
depth
)))
claimBuilder
:=
test
.
NewClaimBuilder
(
t
,
depth
,
alphabet
.
NewTraceProvider
(
"abcdefg"
,
uint64
(
depth
)))
...
@@ -101,7 +102,9 @@ func TestLoadClaimsWhenGameNotResolvable(t *testing.T) {
...
@@ -101,7 +102,9 @@ func TestLoadClaimsWhenGameNotResolvable(t *testing.T) {
require
.
NoError
(
t
,
agent
.
Act
(
context
.
Background
()))
require
.
NoError
(
t
,
agent
.
Act
(
context
.
Background
()))
require
.
EqualValues
(
t
,
1
,
claimLoader
.
callCount
,
"should load claims for unresolvable game"
)
require
.
EqualValues
(
t
,
2
,
claimLoader
.
callCount
,
"should load claims for unresolvable game"
)
require
.
EqualValues
(
t
,
responder
.
callResolveClaimCount
,
1
,
"should check if claim is resolvable"
)
require
.
Zero
(
t
,
responder
.
resolveClaimCount
,
"should not send resolveClaim"
)
}
}
func
setupTestAgent
(
t
*
testing
.
T
,
agreeWithProposedOutput
bool
)
(
*
Agent
,
*
stubClaimLoader
,
*
stubResponder
)
{
func
setupTestAgent
(
t
*
testing
.
T
,
agreeWithProposedOutput
bool
)
(
*
Agent
,
*
stubClaimLoader
,
*
stubResponder
)
{
...
@@ -132,6 +135,10 @@ type stubResponder struct {
...
@@ -132,6 +135,10 @@ type stubResponder struct {
resolveCount
int
resolveCount
int
resolveErr
error
resolveErr
error
callResolveClaimCount
int
callResolveClaimErr
error
resolveClaimCount
int
}
}
func
(
s
*
stubResponder
)
CallResolve
(
ctx
context
.
Context
)
(
gameTypes
.
GameStatus
,
error
)
{
func
(
s
*
stubResponder
)
CallResolve
(
ctx
context
.
Context
)
(
gameTypes
.
GameStatus
,
error
)
{
...
@@ -144,8 +151,18 @@ func (s *stubResponder) Resolve(ctx context.Context) error {
...
@@ -144,8 +151,18 @@ func (s *stubResponder) Resolve(ctx context.Context) error {
return
s
.
resolveErr
return
s
.
resolveErr
}
}
func
(
s
*
stubResponder
)
CallResolveClaim
(
ctx
context
.
Context
,
clainIdx
uint64
)
error
{
s
.
callResolveClaimCount
++
return
s
.
callResolveClaimErr
}
func
(
s
*
stubResponder
)
ResolveClaim
(
ctx
context
.
Context
,
clainIdx
uint64
)
error
{
s
.
resolveClaimCount
++
return
nil
}
func
(
s
*
stubResponder
)
PerformAction
(
ctx
context
.
Context
,
response
types
.
Action
)
error
{
func
(
s
*
stubResponder
)
PerformAction
(
ctx
context
.
Context
,
response
types
.
Action
)
error
{
panic
(
"Not implemented"
)
return
nil
}
}
type
stubUpdater
struct
{
type
stubUpdater
struct
{
...
...
op-challenger/game/fault/responder/responder.go
View file @
6a3a96c3
...
@@ -94,6 +94,34 @@ func (r *FaultResponder) Resolve(ctx context.Context) error {
...
@@ -94,6 +94,34 @@ func (r *FaultResponder) Resolve(ctx context.Context) error {
return
r
.
sendTxAndWait
(
ctx
,
txData
)
return
r
.
sendTxAndWait
(
ctx
,
txData
)
}
}
// buildResolveClaimData creates the transaction data for the ResolveClaim function.
func
(
r
*
FaultResponder
)
buildResolveClaimData
(
ctx
context
.
Context
,
claimIdx
uint64
)
([]
byte
,
error
)
{
return
r
.
fdgAbi
.
Pack
(
"resolveClaim"
,
big
.
NewInt
(
int64
(
claimIdx
)))
}
// CallResolveClaim determines if the resolveClaim function on the fault dispute game contract
// would succeed.
func
(
r
*
FaultResponder
)
CallResolveClaim
(
ctx
context
.
Context
,
claimIdx
uint64
)
error
{
txData
,
err
:=
r
.
buildResolveClaimData
(
ctx
,
claimIdx
)
if
err
!=
nil
{
return
err
}
_
,
err
=
r
.
txMgr
.
Call
(
ctx
,
ethereum
.
CallMsg
{
To
:
&
r
.
fdgAddr
,
Data
:
txData
,
},
nil
)
return
err
}
// ResolveClaim executes a resolveClaim transaction to resolve a fault dispute game.
func
(
r
*
FaultResponder
)
ResolveClaim
(
ctx
context
.
Context
,
claimIdx
uint64
)
error
{
txData
,
err
:=
r
.
buildResolveClaimData
(
ctx
,
claimIdx
)
if
err
!=
nil
{
return
err
}
return
r
.
sendTxAndWait
(
ctx
,
txData
)
}
func
(
r
*
FaultResponder
)
PerformAction
(
ctx
context
.
Context
,
action
types
.
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
...
...
op-challenger/game/fault/responder/responder_test.go
View file @
6a3a96c3
...
@@ -73,6 +73,40 @@ func TestResolve(t *testing.T) {
...
@@ -73,6 +73,40 @@ func TestResolve(t *testing.T) {
})
})
}
}
func
TestCallResolveClaim
(
t
*
testing
.
T
)
{
t
.
Run
(
"SendFails"
,
func
(
t
*
testing
.
T
)
{
responder
,
mockTxMgr
:=
newTestFaultResponder
(
t
)
mockTxMgr
.
callFails
=
true
err
:=
responder
.
CallResolveClaim
(
context
.
Background
(),
0
)
require
.
ErrorIs
(
t
,
err
,
mockCallError
)
require
.
Equal
(
t
,
0
,
mockTxMgr
.
calls
)
})
t
.
Run
(
"Success"
,
func
(
t
*
testing
.
T
)
{
responder
,
mockTxMgr
:=
newTestFaultResponder
(
t
)
err
:=
responder
.
CallResolveClaim
(
context
.
Background
(),
0
)
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
1
,
mockTxMgr
.
calls
)
})
}
func
TestResolveClaim
(
t
*
testing
.
T
)
{
t
.
Run
(
"SendFails"
,
func
(
t
*
testing
.
T
)
{
responder
,
mockTxMgr
:=
newTestFaultResponder
(
t
)
mockTxMgr
.
sendFails
=
true
err
:=
responder
.
ResolveClaim
(
context
.
Background
(),
0
)
require
.
ErrorIs
(
t
,
err
,
mockSendError
)
require
.
Equal
(
t
,
0
,
mockTxMgr
.
sends
)
})
t
.
Run
(
"Success"
,
func
(
t
*
testing
.
T
)
{
responder
,
mockTxMgr
:=
newTestFaultResponder
(
t
)
err
:=
responder
.
ResolveClaim
(
context
.
Background
(),
0
)
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
1
,
mockTxMgr
.
sends
)
})
}
// TestRespond tests the [Responder.Respond] method.
// TestRespond tests the [Responder.Respond] method.
func
TestPerformAction
(
t
*
testing
.
T
)
{
func
TestPerformAction
(
t
*
testing
.
T
)
{
t
.
Run
(
"send fails"
,
func
(
t
*
testing
.
T
)
{
t
.
Run
(
"send fails"
,
func
(
t
*
testing
.
T
)
{
...
...
op-challenger/game/fault/solver/game_solver.go
View file @
6a3a96c3
...
@@ -48,7 +48,10 @@ func (s *GameSolver) calculateStep(ctx context.Context, game types.Game, claim t
...
@@ -48,7 +48,10 @@ func (s *GameSolver) calculateStep(ctx context.Context, game types.Game, claim t
if
game
.
AgreeWithClaimLevel
(
claim
)
{
if
game
.
AgreeWithClaimLevel
(
claim
)
{
return
nil
,
nil
return
nil
,
nil
}
}
step
,
err
:=
s
.
claimSolver
.
AttemptStep
(
ctx
,
claim
,
game
.
AgreeWithClaimLevel
(
claim
))
step
,
err
:=
s
.
claimSolver
.
AttemptStep
(
ctx
,
game
,
claim
)
if
err
==
ErrStepIgnoreInvalidPath
{
return
nil
,
nil
}
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
err
return
nil
,
err
}
}
...
@@ -63,11 +66,14 @@ func (s *GameSolver) calculateStep(ctx context.Context, game types.Game, claim t
...
@@ -63,11 +66,14 @@ func (s *GameSolver) calculateStep(ctx context.Context, game types.Game, claim t
}
}
func
(
s
*
GameSolver
)
calculateMove
(
ctx
context
.
Context
,
game
types
.
Game
,
claim
types
.
Claim
)
(
*
types
.
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
))
if
game
.
AgreeWithClaimLevel
(
claim
)
{
return
nil
,
nil
}
move
,
err
:=
s
.
claimSolver
.
NextMove
(
ctx
,
claim
,
game
)
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
.
ClaimData
)
{
if
move
==
nil
||
game
.
IsDuplicate
(
*
move
)
{
return
nil
,
nil
return
nil
,
nil
}
}
return
&
types
.
Action
{
return
&
types
.
Action
{
...
...
op-challenger/game/fault/solver/game_solver_test.go
View file @
6a3a96c3
...
@@ -48,7 +48,6 @@ func TestCalculateNextActions(t *testing.T) {
...
@@ -48,7 +48,6 @@ func TestCalculateNextActions(t *testing.T) {
rootClaimCorrect
:
true
,
rootClaimCorrect
:
true
,
setupGame
:
func
(
builder
*
faulttest
.
GameBuilder
)
{},
setupGame
:
func
(
builder
*
faulttest
.
GameBuilder
)
{},
},
},
{
{
name
:
"DoNotPerformDuplicateMoves"
,
name
:
"DoNotPerformDuplicateMoves"
,
agreeWithOutputRoot
:
true
,
agreeWithOutputRoot
:
true
,
...
@@ -93,16 +92,15 @@ func TestCalculateNextActions(t *testing.T) {
...
@@ -93,16 +92,15 @@ func TestCalculateNextActions(t *testing.T) {
maliciousStateHash
:=
common
.
Hash
{
0x01
,
0xaa
}
maliciousStateHash
:=
common
.
Hash
{
0x01
,
0xaa
}
// Dishonest actor counters their own claims to set up a situation with an invalid prestate
// Dishonest actor counters their own claims to set up a situation with an invalid prestate
// The honest actor should attack all claims that support the root claim (disagree with the output root)
// The honest actor should ignore path created by the dishonest actor, only supporting its own attack on the root claim
builder
.
Seq
()
.
ExpectAttack
()
.
// This expected action is the winning move.
honestMove
:=
builder
.
Seq
()
.
AttackCorrect
()
// This expected action is the winning move.
Attack
(
maliciousStateHash
)
.
dishonestMove
:=
honestMove
.
Attack
(
maliciousStateHash
)
Defend
(
maliciousStateHash
)
.
ExpectAttack
()
.
// The expected action by the honest actor
Attack
(
maliciousStateHash
)
.
dishonestMove
.
ExpectAttack
()
Attack
(
maliciousStateHash
)
.
ExpectStepAttack
()
// The honest actor will ignore this poisoned path
dishonestMove
.
// The attempt to step against our malicious leaf node will fail because the pre-state won't match our
Defend
(
maliciousStateHash
)
.
// malicious state hash. However, it is the very first expected action, attacking the root claim with
Attack
(
maliciousStateHash
)
// the correct hash that wins the game since it will be the left-most uncountered claim.
},
},
},
},
}
}
...
...
op-challenger/game/fault/solver/rules.go
View file @
6a3a96c3
...
@@ -13,7 +13,6 @@ var rules = []actionRule{
...
@@ -13,7 +13,6 @@ var rules = []actionRule{
parentMustExist
,
parentMustExist
,
onlyStepAtMaxDepth
,
onlyStepAtMaxDepth
,
onlyMoveBeforeMaxDepth
,
onlyMoveBeforeMaxDepth
,
onlyCounterClaimsAtDisagreeingLevels
,
doNotDuplicateExistingMoves
,
doNotDuplicateExistingMoves
,
doNotDefendRootClaim
,
doNotDefendRootClaim
,
}
}
...
@@ -57,20 +56,12 @@ func onlyMoveBeforeMaxDepth(game types.Game, action types.Action) error {
...
@@ -57,20 +56,12 @@ func onlyMoveBeforeMaxDepth(game types.Game, action types.Action) error {
return
nil
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
{
func
doNotDuplicateExistingMoves
(
game
types
.
Game
,
action
types
.
Action
)
error
{
newClaimData
:=
types
.
ClaimData
{
newClaimData
:=
types
.
ClaimData
{
Value
:
action
.
Value
,
Value
:
action
.
Value
,
Position
:
resultingPosition
(
game
,
action
),
Position
:
resultingPosition
(
game
,
action
),
}
}
if
game
.
IsDuplicate
(
newClaimData
)
{
if
game
.
IsDuplicate
(
types
.
Claim
{
ClaimData
:
newClaimData
,
ParentContractIndex
:
action
.
ParentIdx
}
)
{
return
fmt
.
Errorf
(
"creating duplicate claim at %v with value %v"
,
newClaimData
.
Position
.
ToGIndex
(),
newClaimData
.
Value
)
return
fmt
.
Errorf
(
"creating duplicate claim at %v with value %v"
,
newClaimData
.
Position
.
ToGIndex
(),
newClaimData
.
Value
)
}
}
return
nil
return
nil
...
...
op-challenger/game/fault/solver/solver.go
View file @
6a3a96c3
...
@@ -11,8 +11,9 @@ import (
...
@@ -11,8 +11,9 @@ import (
)
)
var
(
var
(
ErrStepNonLeafNode
=
errors
.
New
(
"cannot step on non-leaf claims"
)
ErrStepNonLeafNode
=
errors
.
New
(
"cannot step on non-leaf claims"
)
ErrStepAgreedClaim
=
errors
.
New
(
"cannot step on claims we agree with"
)
ErrStepAgreedClaim
=
errors
.
New
(
"cannot step on claims we agree with"
)
ErrStepIgnoreInvalidPath
=
errors
.
New
(
"cannot step on claims that dispute invalid paths"
)
)
)
// claimSolver uses a [TraceProvider] to determine the moves to make in a dispute game.
// claimSolver uses a [TraceProvider] to determine the moves to make in a dispute game.
...
@@ -30,10 +31,7 @@ func newClaimSolver(gameDepth int, traceProvider types.TraceProvider) *claimSolv
...
@@ -30,10 +31,7 @@ func newClaimSolver(gameDepth int, traceProvider types.TraceProvider) *claimSolv
}
}
// 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
*
claimSolver
)
NextMove
(
ctx
context
.
Context
,
claim
types
.
Claim
,
agreeWithClaimLevel
bool
)
(
*
types
.
Claim
,
error
)
{
func
(
s
*
claimSolver
)
NextMove
(
ctx
context
.
Context
,
claim
types
.
Claim
,
game
types
.
Game
)
(
*
types
.
Claim
,
error
)
{
if
agreeWithClaimLevel
{
return
nil
,
nil
}
if
claim
.
Depth
()
==
s
.
gameDepth
{
if
claim
.
Depth
()
==
s
.
gameDepth
{
return
nil
,
types
.
ErrGameDepthReached
return
nil
,
types
.
ErrGameDepthReached
}
}
...
@@ -41,6 +39,24 @@ func (s *claimSolver) NextMove(ctx context.Context, claim types.Claim, agreeWith
...
@@ -41,6 +39,24 @@ func (s *claimSolver) NextMove(ctx context.Context, claim types.Claim, agreeWith
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
err
return
nil
,
err
}
}
// Before challenging this claim, first check that the move wasn't warranted.
// If the parent claim is on a dishonest path, then we would have moved against it anyways. So we don't move.
// Avoiding dishonest paths ensures that there's always a valid claim available to support ours during step.
if
!
claim
.
IsRoot
()
{
parent
,
err
:=
game
.
GetParent
(
claim
)
if
err
!=
nil
{
return
nil
,
err
}
agreeWithParent
,
err
:=
s
.
agreeWithClaimPath
(
ctx
,
game
,
parent
)
if
err
!=
nil
{
return
nil
,
err
}
if
!
agreeWithParent
{
return
nil
,
nil
}
}
if
agree
{
if
agree
{
return
s
.
defend
(
ctx
,
claim
)
return
s
.
defend
(
ctx
,
claim
)
}
else
{
}
else
{
...
@@ -58,13 +74,25 @@ type StepData struct {
...
@@ -58,13 +74,25 @@ 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
*
claimSolver
)
AttemptStep
(
ctx
context
.
Context
,
claim
types
.
Claim
,
agreeWithClaimLevel
bool
)
(
StepData
,
error
)
{
// Returns ErrStepIgnoreInvalidPath if the claim disputes an invalid path
func
(
s
*
claimSolver
)
AttemptStep
(
ctx
context
.
Context
,
game
types
.
Game
,
claim
types
.
Claim
)
(
StepData
,
error
)
{
if
claim
.
Depth
()
!=
s
.
gameDepth
{
if
claim
.
Depth
()
!=
s
.
gameDepth
{
return
StepData
{},
ErrStepNonLeafNode
return
StepData
{},
ErrStepNonLeafNode
}
}
if
agreeWithClaimLevel
{
return
StepData
{},
ErrStepAgreedClaim
// Step only on claims that dispute a valid path
parent
,
err
:=
game
.
GetParent
(
claim
)
if
err
!=
nil
{
return
StepData
{},
err
}
}
parentValid
,
err
:=
s
.
agreeWithClaimPath
(
ctx
,
game
,
parent
)
if
err
!=
nil
{
return
StepData
{},
err
}
if
!
parentValid
{
return
StepData
{},
ErrStepIgnoreInvalidPath
}
claimCorrect
,
err
:=
s
.
agreeWithClaim
(
ctx
,
claim
.
ClaimData
)
claimCorrect
,
err
:=
s
.
agreeWithClaim
(
ctx
,
claim
.
ClaimData
)
if
err
!=
nil
{
if
err
!=
nil
{
return
StepData
{},
err
return
StepData
{},
err
...
@@ -142,3 +170,26 @@ func (s *claimSolver) traceAtPosition(ctx context.Context, p types.Position) (co
...
@@ -142,3 +170,26 @@ func (s *claimSolver) traceAtPosition(ctx context.Context, p types.Position) (co
hash
,
err
:=
s
.
trace
.
Get
(
ctx
,
index
)
hash
,
err
:=
s
.
trace
.
Get
(
ctx
,
index
)
return
hash
,
err
return
hash
,
err
}
}
// agreeWithClaimPath returns true if the every other claim in the path to root is correct according to the internal [TraceProvider].
func
(
s
*
claimSolver
)
agreeWithClaimPath
(
ctx
context
.
Context
,
game
types
.
Game
,
claim
types
.
Claim
)
(
bool
,
error
)
{
agree
,
err
:=
s
.
agreeWithClaim
(
ctx
,
claim
.
ClaimData
)
if
err
!=
nil
{
return
false
,
err
}
if
!
agree
{
return
false
,
nil
}
if
claim
.
IsRoot
()
||
claim
.
Parent
.
IsRootPosition
()
{
return
true
,
nil
}
parent
,
err
:=
game
.
GetParent
(
claim
)
if
err
!=
nil
{
return
false
,
err
}
grandParent
,
err
:=
game
.
GetParent
(
parent
)
if
err
!=
nil
{
return
false
,
err
}
return
s
.
agreeWithClaimPath
(
ctx
,
game
,
grandParent
)
}
op-challenger/game/fault/solver/solver_test.go
View file @
6a3a96c3
This diff is collapsed.
Click to expand it.
op-challenger/game/fault/test/claim_builder.go
View file @
6a3a96c3
...
@@ -79,12 +79,13 @@ func (c *ClaimBuilder) claim(idx uint64, correct bool) common.Hash {
...
@@ -79,12 +79,13 @@ func (c *ClaimBuilder) claim(idx uint64, correct bool) common.Hash {
func
(
c
*
ClaimBuilder
)
CreateRootClaim
(
correct
bool
)
types
.
Claim
{
func
(
c
*
ClaimBuilder
)
CreateRootClaim
(
correct
bool
)
types
.
Claim
{
value
:=
c
.
claim
((
1
<<
c
.
maxDepth
)
-
1
,
correct
)
value
:=
c
.
claim
((
1
<<
c
.
maxDepth
)
-
1
,
correct
)
return
types
.
Claim
{
claim
:=
types
.
Claim
{
ClaimData
:
types
.
ClaimData
{
ClaimData
:
types
.
ClaimData
{
Value
:
value
,
Value
:
value
,
Position
:
types
.
NewPosition
(
0
,
0
),
Position
:
types
.
NewPosition
(
0
,
0
),
},
},
}
}
return
claim
}
}
func
(
c
*
ClaimBuilder
)
CreateLeafClaim
(
traceIndex
uint64
,
correct
bool
)
types
.
Claim
{
func
(
c
*
ClaimBuilder
)
CreateLeafClaim
(
traceIndex
uint64
,
correct
bool
)
types
.
Claim
{
...
...
op-challenger/game/fault/types/game.go
View file @
6a3a96c3
...
@@ -23,8 +23,12 @@ type Game interface {
...
@@ -23,8 +23,12 @@ type Game interface {
// Claims returns all of the claims in the game.
// Claims returns all of the claims in the game.
Claims
()
[]
Claim
Claims
()
[]
Claim
// IsDuplicate returns true if the provided [Claim] already exists in the game state.
// GetParent returns the parent of the provided claim.
IsDuplicate
(
claim
ClaimData
)
bool
GetParent
(
claim
Claim
)
(
Claim
,
error
)
// IsDuplicate returns true if the provided [Claim] already exists in the game state
// referencing the same parent claim
IsDuplicate
(
claim
Claim
)
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
...
@@ -32,31 +36,37 @@ type Game interface {
...
@@ -32,31 +36,37 @@ type Game interface {
MaxDepth
()
uint64
MaxDepth
()
uint64
}
}
type
claimEntry
struct
{
ClaimData
ParentContractIndex
int
}
type
extendedClaim
struct
{
type
extendedClaim
struct
{
self
Claim
self
Claim
children
[]
ClaimData
children
[]
claimEntry
}
}
// gameState is a struct that represents the state of a dispute game.
// gameState is a struct that represents the state of a dispute game.
// The game state implements the [Game] interface.
// The game state implements the [Game] interface.
type
gameState
struct
{
type
gameState
struct
{
agreeWithProposedOutput
bool
agreeWithProposedOutput
bool
root
ClaimData
root
claimEntry
claims
map
[
ClaimData
]
*
extendedClaim
claims
map
[
claimEntry
]
*
extendedClaim
depth
uint64
depth
uint64
}
}
// NewGameState returns a new game state.
// NewGameState returns a new game state.
// The provided [Claim] is used as the root node.
// The provided [Claim] is used as the root node.
func
NewGameState
(
agreeWithProposedOutput
bool
,
root
Claim
,
depth
uint64
)
*
gameState
{
func
NewGameState
(
agreeWithProposedOutput
bool
,
root
Claim
,
depth
uint64
)
*
gameState
{
claims
:=
make
(
map
[
ClaimData
]
*
extendedClaim
)
claims
:=
make
(
map
[
claimEntry
]
*
extendedClaim
)
claims
[
root
.
ClaimData
]
=
&
extendedClaim
{
rootClaimEntry
:=
makeClaimEntry
(
root
)
claims
[
rootClaimEntry
]
=
&
extendedClaim
{
self
:
root
,
self
:
root
,
children
:
make
([]
ClaimData
,
0
),
children
:
make
([]
claimEntry
,
0
),
}
}
return
&
gameState
{
return
&
gameState
{
agreeWithProposedOutput
:
agreeWithProposedOutput
,
agreeWithProposedOutput
:
agreeWithProposedOutput
,
root
:
root
.
ClaimData
,
root
:
root
ClaimEntry
,
claims
:
claims
,
claims
:
claims
,
depth
:
depth
,
depth
:
depth
,
}
}
...
@@ -87,29 +97,29 @@ func (g *gameState) PutAll(claims []Claim) error {
...
@@ -87,29 +97,29 @@ 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
.
ClaimData
)
{
if
claim
.
IsRoot
()
||
g
.
IsDuplicate
(
claim
)
{
return
ErrClaimExists
return
ErrClaimExists
}
}
parent
,
ok
:=
g
.
claims
[
claim
.
Parent
]
if
!
ok
{
parent
:=
g
.
getParent
(
claim
)
if
parent
==
nil
{
return
errors
.
New
(
"no parent claim"
)
return
errors
.
New
(
"no parent claim"
)
}
else
{
parent
.
children
=
append
(
parent
.
children
,
claim
.
ClaimData
)
}
}
g
.
claims
[
claim
.
ClaimData
]
=
&
extendedClaim
{
parent
.
children
=
append
(
parent
.
children
,
makeClaimEntry
(
claim
))
g
.
claims
[
makeClaimEntry
(
claim
)]
=
&
extendedClaim
{
self
:
claim
,
self
:
claim
,
children
:
make
([]
ClaimData
,
0
),
children
:
make
([]
claimEntry
,
0
),
}
}
return
nil
return
nil
}
}
func
(
g
*
gameState
)
IsDuplicate
(
claim
Claim
Data
)
bool
{
func
(
g
*
gameState
)
IsDuplicate
(
claim
Claim
)
bool
{
_
,
ok
:=
g
.
claims
[
claim
]
_
,
ok
:=
g
.
claims
[
makeClaimEntry
(
claim
)
]
return
ok
return
ok
}
}
func
(
g
*
gameState
)
Claims
()
[]
Claim
{
func
(
g
*
gameState
)
Claims
()
[]
Claim
{
queue
:=
[]
ClaimData
{
g
.
root
}
queue
:=
[]
claimEntry
{
g
.
root
}
var
out
[]
Claim
var
out
[]
Claim
for
len
(
queue
)
>
0
{
for
len
(
queue
)
>
0
{
item
:=
queue
[
0
]
item
:=
queue
[
0
]
...
@@ -124,17 +134,34 @@ func (g *gameState) MaxDepth() uint64 {
...
@@ -124,17 +134,34 @@ func (g *gameState) MaxDepth() uint64 {
return
g
.
depth
return
g
.
depth
}
}
func
(
g
*
gameState
)
getChildren
(
c
ClaimData
)
[]
ClaimData
{
func
(
g
*
gameState
)
getChildren
(
c
claimEntry
)
[]
claimEntry
{
return
g
.
claims
[
c
]
.
children
return
g
.
claims
[
c
]
.
children
}
}
func
(
g
*
gameState
)
getParent
(
claim
Claim
)
(
Claim
,
error
)
{
func
(
g
*
gameState
)
GetParent
(
claim
Claim
)
(
Claim
,
error
)
{
if
claim
.
IsRoot
()
{
parent
:=
g
.
getParent
(
claim
)
if
parent
==
nil
{
return
Claim
{},
ErrClaimNotFound
return
Claim
{},
ErrClaimNotFound
}
}
if
parent
,
ok
:=
g
.
claims
[
claim
.
Parent
];
!
ok
{
return
parent
.
self
,
nil
return
Claim
{},
ErrClaimNotFound
}
}
else
{
return
parent
.
self
,
nil
func
(
g
*
gameState
)
getParent
(
claim
Claim
)
*
extendedClaim
{
if
claim
.
IsRoot
()
{
return
nil
}
// TODO(inphi): refactor gameState for faster parent lookups
for
_
,
c
:=
range
g
.
claims
{
if
c
.
self
.
ContractIndex
==
claim
.
ParentContractIndex
{
return
c
}
}
return
nil
}
func
makeClaimEntry
(
claim
Claim
)
claimEntry
{
return
claimEntry
{
ClaimData
:
claim
.
ClaimData
,
ParentContractIndex
:
claim
.
ParentContractIndex
,
}
}
}
}
op-challenger/game/fault/types/game_test.go
View file @
6a3a96c3
...
@@ -24,14 +24,18 @@ func createTestClaims() (Claim, Claim, Claim, Claim) {
...
@@ -24,14 +24,18 @@ func createTestClaims() (Claim, Claim, Claim, Claim) {
Value
:
common
.
HexToHash
(
"0x0000000000000000000000000000000000000000000000000000000000000364"
),
Value
:
common
.
HexToHash
(
"0x0000000000000000000000000000000000000000000000000000000000000364"
),
Position
:
NewPosition
(
1
,
0
),
Position
:
NewPosition
(
1
,
0
),
},
},
Parent
:
root
.
ClaimData
,
Parent
:
root
.
ClaimData
,
ContractIndex
:
1
,
ParentContractIndex
:
0
,
}
}
middle
:=
Claim
{
middle
:=
Claim
{
ClaimData
:
ClaimData
{
ClaimData
:
ClaimData
{
Value
:
common
.
HexToHash
(
"0x0000000000000000000000000000000000000000000000000000000000000578"
),
Value
:
common
.
HexToHash
(
"0x0000000000000000000000000000000000000000000000000000000000000578"
),
Position
:
NewPosition
(
2
,
2
),
Position
:
NewPosition
(
2
,
2
),
},
},
Parent
:
top
.
ClaimData
,
Parent
:
top
.
ClaimData
,
ContractIndex
:
2
,
ParentContractIndex
:
1
,
}
}
bottom
:=
Claim
{
bottom
:=
Claim
{
...
@@ -39,7 +43,9 @@ func createTestClaims() (Claim, Claim, Claim, Claim) {
...
@@ -39,7 +43,9 @@ func createTestClaims() (Claim, Claim, Claim, Claim) {
Value
:
common
.
HexToHash
(
"0x0000000000000000000000000000000000000000000000000000000000000465"
),
Value
:
common
.
HexToHash
(
"0x0000000000000000000000000000000000000000000000000000000000000465"
),
Position
:
NewPosition
(
3
,
4
),
Position
:
NewPosition
(
3
,
4
),
},
},
Parent
:
middle
.
ClaimData
,
Parent
:
middle
.
ClaimData
,
ContractIndex
:
3
,
ParentContractIndex
:
2
,
}
}
return
root
,
top
,
middle
,
bottom
return
root
,
top
,
middle
,
bottom
...
@@ -52,12 +58,12 @@ func TestIsDuplicate(t *testing.T) {
...
@@ -52,12 +58,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
.
ClaimData
))
require
.
True
(
t
,
g
.
IsDuplicate
(
root
))
require
.
True
(
t
,
g
.
IsDuplicate
(
top
.
ClaimData
))
require
.
True
(
t
,
g
.
IsDuplicate
(
top
))
// Middle + Bottom should not be a duplicate
// Middle + Bottom should not be a duplicate
require
.
False
(
t
,
g
.
IsDuplicate
(
middle
.
ClaimData
))
require
.
False
(
t
,
g
.
IsDuplicate
(
middle
))
require
.
False
(
t
,
g
.
IsDuplicate
(
bottom
.
ClaimData
))
require
.
False
(
t
,
g
.
IsDuplicate
(
bottom
))
}
}
// TestGame_Put_RootAlreadyExists tests the [Game.Put] method using a [gameState]
// TestGame_Put_RootAlreadyExists tests the [Game.Put] method using a [gameState]
...
@@ -104,20 +110,20 @@ func TestGame_PutAll_ParentsAndChildren(t *testing.T) {
...
@@ -104,20 +110,20 @@ func TestGame_PutAll_ParentsAndChildren(t *testing.T) {
g
:=
NewGameState
(
false
,
root
,
testMaxDepth
)
g
:=
NewGameState
(
false
,
root
,
testMaxDepth
)
// We should not be able to get the parent of the root claim.
// We should not be able to get the parent of the root claim.
parent
,
err
:=
g
.
g
etParent
(
root
)
parent
,
err
:=
g
.
G
etParent
(
root
)
require
.
ErrorIs
(
t
,
err
,
ErrClaimNotFound
)
require
.
ErrorIs
(
t
,
err
,
ErrClaimNotFound
)
require
.
Equal
(
t
,
parent
,
Claim
{})
require
.
Equal
(
t
,
parent
,
Claim
{})
// Put the rest of the claims in the state.
// Put the rest of the claims in the state.
err
=
g
.
PutAll
([]
Claim
{
top
,
middle
,
bottom
})
err
=
g
.
PutAll
([]
Claim
{
top
,
middle
,
bottom
})
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
parent
,
err
=
g
.
g
etParent
(
top
)
parent
,
err
=
g
.
G
etParent
(
top
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
parent
,
root
)
require
.
Equal
(
t
,
parent
,
root
)
parent
,
err
=
g
.
g
etParent
(
middle
)
parent
,
err
=
g
.
G
etParent
(
middle
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
parent
,
top
)
require
.
Equal
(
t
,
parent
,
top
)
parent
,
err
=
g
.
g
etParent
(
bottom
)
parent
,
err
=
g
.
G
etParent
(
bottom
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
parent
,
middle
)
require
.
Equal
(
t
,
parent
,
middle
)
}
}
...
@@ -145,28 +151,28 @@ func TestGame_Put_ParentsAndChildren(t *testing.T) {
...
@@ -145,28 +151,28 @@ func TestGame_Put_ParentsAndChildren(t *testing.T) {
g
:=
NewGameState
(
false
,
root
,
testMaxDepth
)
g
:=
NewGameState
(
false
,
root
,
testMaxDepth
)
// We should not be able to get the parent of the root claim.
// We should not be able to get the parent of the root claim.
parent
,
err
:=
g
.
g
etParent
(
root
)
parent
,
err
:=
g
.
G
etParent
(
root
)
require
.
ErrorIs
(
t
,
err
,
ErrClaimNotFound
)
require
.
ErrorIs
(
t
,
err
,
ErrClaimNotFound
)
require
.
Equal
(
t
,
parent
,
Claim
{})
require
.
Equal
(
t
,
parent
,
Claim
{})
// Put + Check Top
// Put + Check Top
err
=
g
.
Put
(
top
)
err
=
g
.
Put
(
top
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
parent
,
err
=
g
.
g
etParent
(
top
)
parent
,
err
=
g
.
G
etParent
(
top
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
parent
,
root
)
require
.
Equal
(
t
,
parent
,
root
)
// Put + Check Top Middle
// Put + Check Top Middle
err
=
g
.
Put
(
middle
)
err
=
g
.
Put
(
middle
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
parent
,
err
=
g
.
g
etParent
(
middle
)
parent
,
err
=
g
.
G
etParent
(
middle
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
parent
,
top
)
require
.
Equal
(
t
,
parent
,
top
)
// Put + Check Top Bottom
// Put + Check Top Bottom
err
=
g
.
Put
(
bottom
)
err
=
g
.
Put
(
bottom
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
parent
,
err
=
g
.
g
etParent
(
bottom
)
parent
,
err
=
g
.
G
etParent
(
bottom
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
parent
,
middle
)
require
.
Equal
(
t
,
parent
,
middle
)
}
}
...
@@ -194,27 +200,3 @@ func TestGame_ClaimPairs(t *testing.T) {
...
@@ -194,27 +200,3 @@ func TestGame_ClaimPairs(t *testing.T) {
claims
:=
g
.
Claims
()
claims
:=
g
.
Claims
()
require
.
ElementsMatch
(
t
,
expected
,
claims
)
require
.
ElementsMatch
(
t
,
expected
,
claims
)
}
}
func
TestAgreeWithClaimLevelDisagreeWithOutput
(
t
*
testing
.
T
)
{
// Setup the game state.
root
,
top
,
middle
,
bottom
:=
createTestClaims
()
g
:=
NewGameState
(
false
,
root
,
testMaxDepth
)
require
.
NoError
(
t
,
g
.
PutAll
([]
Claim
{
top
,
middle
,
bottom
}))
require
.
True
(
t
,
g
.
AgreeWithClaimLevel
(
root
))
require
.
False
(
t
,
g
.
AgreeWithClaimLevel
(
top
))
require
.
True
(
t
,
g
.
AgreeWithClaimLevel
(
middle
))
require
.
False
(
t
,
g
.
AgreeWithClaimLevel
(
bottom
))
}
func
TestAgreeWithClaimLevelAgreeWithOutput
(
t
*
testing
.
T
)
{
// Setup the game state.
root
,
top
,
middle
,
bottom
:=
createTestClaims
()
g
:=
NewGameState
(
true
,
root
,
testMaxDepth
)
require
.
NoError
(
t
,
g
.
PutAll
([]
Claim
{
top
,
middle
,
bottom
}))
require
.
False
(
t
,
g
.
AgreeWithClaimLevel
(
root
))
require
.
True
(
t
,
g
.
AgreeWithClaimLevel
(
top
))
require
.
False
(
t
,
g
.
AgreeWithClaimLevel
(
middle
))
require
.
True
(
t
,
g
.
AgreeWithClaimLevel
(
bottom
))
}
op-e2e/e2eutils/challenger/helper.go
View file @
6a3a96c3
...
@@ -68,6 +68,12 @@ func WithAlphabet(alphabet string) Option {
...
@@ -68,6 +68,12 @@ func WithAlphabet(alphabet string) Option {
}
}
}
}
func
WithPollInterval
(
pollInterval
time
.
Duration
)
Option
{
return
func
(
c
*
config
.
Config
)
{
c
.
PollInterval
=
pollInterval
}
}
func
WithCannon
(
func
WithCannon
(
t
*
testing
.
T
,
t
*
testing
.
T
,
rollupCfg
*
rollup
.
Config
,
rollupCfg
*
rollup
.
Config
,
...
@@ -98,7 +104,7 @@ func WithCannon(
...
@@ -98,7 +104,7 @@ func WithCannon(
}
}
func
NewChallenger
(
t
*
testing
.
T
,
ctx
context
.
Context
,
l1Endpoint
string
,
name
string
,
options
...
Option
)
*
Helper
{
func
NewChallenger
(
t
*
testing
.
T
,
ctx
context
.
Context
,
l1Endpoint
string
,
name
string
,
options
...
Option
)
*
Helper
{
log
:=
testlog
.
Logger
(
t
,
log
.
Lvl
Info
)
.
New
(
"role"
,
name
)
log
:=
testlog
.
Logger
(
t
,
log
.
Lvl
Debug
)
.
New
(
"role"
,
name
)
log
.
Info
(
"Creating challenger"
,
"l1"
,
l1Endpoint
)
log
.
Info
(
"Creating challenger"
,
"l1"
,
l1Endpoint
)
cfg
:=
NewChallengerConfig
(
t
,
l1Endpoint
,
options
...
)
cfg
:=
NewChallengerConfig
(
t
,
l1Endpoint
,
options
...
)
...
...
op-e2e/e2eutils/disputegame/alphabet_helper.go
View file @
6a3a96c3
...
@@ -4,6 +4,7 @@ import (
...
@@ -4,6 +4,7 @@ import (
"context"
"context"
"github.com/ethereum-optimism/optimism/op-challenger/config"
"github.com/ethereum-optimism/optimism/op-challenger/config"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/alphabet"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/challenger"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/challenger"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common"
...
@@ -33,3 +34,12 @@ func (g *AlphabetGameHelper) StartChallenger(ctx context.Context, l1Endpoint str
...
@@ -33,3 +34,12 @@ func (g *AlphabetGameHelper) StartChallenger(ctx context.Context, l1Endpoint str
})
})
return
c
return
c
}
}
func
(
g
*
AlphabetGameHelper
)
CreateHonestActor
(
ctx
context
.
Context
,
alphabetTrace
string
,
depth
uint64
)
*
HonestHelper
{
return
&
HonestHelper
{
t
:
g
.
t
,
require
:
g
.
require
,
game
:
&
g
.
FaultGameHelper
,
correctTrace
:
alphabet
.
NewTraceProvider
(
alphabetTrace
,
depth
),
}
}
op-e2e/e2eutils/disputegame/game_helper.go
View file @
6a3a96c3
...
@@ -2,16 +2,19 @@ package disputegame
...
@@ -2,16 +2,19 @@ package disputegame
import
(
import
(
"context"
"context"
"errors"
"fmt"
"fmt"
"math/big"
"math/big"
"testing"
"testing"
"time"
"time"
"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"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common"
gethtypes
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/require"
)
)
...
@@ -130,6 +133,10 @@ func (g *FaultGameHelper) getClaim(ctx context.Context, claimIdx int64) Contract
...
@@ -130,6 +133,10 @@ func (g *FaultGameHelper) getClaim(ctx context.Context, claimIdx int64) Contract
return
claimData
return
claimData
}
}
func
(
g
*
FaultGameHelper
)
GetClaimUnsafe
(
ctx
context
.
Context
,
claimIdx
int64
)
ContractClaim
{
return
g
.
getClaim
(
ctx
,
claimIdx
)
}
func
(
g
*
FaultGameHelper
)
WaitForClaimAtDepth
(
ctx
context
.
Context
,
depth
int
)
{
func
(
g
*
FaultGameHelper
)
WaitForClaimAtDepth
(
ctx
context
.
Context
,
depth
int
)
{
g
.
waitForClaim
(
g
.
waitForClaim
(
ctx
,
ctx
,
...
@@ -169,6 +176,12 @@ func (g *FaultGameHelper) Resolve(ctx context.Context) {
...
@@ -169,6 +176,12 @@ func (g *FaultGameHelper) Resolve(ctx context.Context) {
g
.
require
.
NoError
(
err
)
g
.
require
.
NoError
(
err
)
}
}
func
(
g
*
FaultGameHelper
)
Status
(
ctx
context
.
Context
)
Status
{
status
,
err
:=
g
.
game
.
Status
(
&
bind
.
CallOpts
{
Context
:
ctx
})
g
.
require
.
NoError
(
err
)
return
Status
(
status
)
}
func
(
g
*
FaultGameHelper
)
WaitForGameStatus
(
ctx
context
.
Context
,
expected
Status
)
{
func
(
g
*
FaultGameHelper
)
WaitForGameStatus
(
ctx
context
.
Context
,
expected
Status
)
{
g
.
t
.
Logf
(
"Waiting for game %v to have status %v"
,
g
.
addr
,
expected
)
g
.
t
.
Logf
(
"Waiting for game %v to have status %v"
,
g
.
addr
,
expected
)
timedCtx
,
cancel
:=
context
.
WithTimeout
(
ctx
,
time
.
Minute
)
timedCtx
,
cancel
:=
context
.
WithTimeout
(
ctx
,
time
.
Minute
)
...
@@ -186,6 +199,46 @@ func (g *FaultGameHelper) WaitForGameStatus(ctx context.Context, expected Status
...
@@ -186,6 +199,46 @@ func (g *FaultGameHelper) WaitForGameStatus(ctx context.Context, expected Status
g
.
require
.
NoErrorf
(
err
,
"wait for game status. Game state:
\n
%v"
,
g
.
gameData
(
ctx
))
g
.
require
.
NoErrorf
(
err
,
"wait for game status. Game state:
\n
%v"
,
g
.
gameData
(
ctx
))
}
}
func
(
g
*
FaultGameHelper
)
WaitForInactivity
(
ctx
context
.
Context
,
numInactiveBlocks
int
,
untilGameEnds
bool
)
{
g
.
t
.
Logf
(
"Waiting for game %v to have no activity for %v blocks"
,
g
.
addr
,
numInactiveBlocks
)
headCh
:=
make
(
chan
*
gethtypes
.
Header
,
100
)
headSub
,
err
:=
g
.
client
.
SubscribeNewHead
(
ctx
,
headCh
)
g
.
require
.
NoError
(
err
)
defer
headSub
.
Unsubscribe
()
var
lastActiveBlock
uint64
for
{
if
untilGameEnds
&&
g
.
Status
(
ctx
)
!=
StatusInProgress
{
break
}
select
{
case
head
:=
<-
headCh
:
if
lastActiveBlock
==
0
{
lastActiveBlock
=
head
.
Number
.
Uint64
()
continue
}
else
if
lastActiveBlock
+
uint64
(
numInactiveBlocks
)
<
head
.
Number
.
Uint64
()
{
return
}
block
,
err
:=
g
.
client
.
BlockByNumber
(
ctx
,
head
.
Number
)
g
.
require
.
NoError
(
err
)
numActions
:=
0
for
_
,
tx
:=
range
block
.
Transactions
()
{
if
tx
.
To
()
.
Hex
()
==
g
.
addr
.
Hex
()
{
numActions
++
}
}
if
numActions
!=
0
{
g
.
t
.
Logf
(
"Game %v has %v actions in block %d. Resetting inactivity timeout"
,
g
.
addr
,
numActions
,
block
.
NumberU64
())
lastActiveBlock
=
head
.
Number
.
Uint64
()
}
case
err
:=
<-
headSub
.
Err
()
:
g
.
require
.
NoError
(
err
)
case
<-
ctx
.
Done
()
:
g
.
require
.
Fail
(
"Context canceled"
,
ctx
.
Err
())
}
}
}
// Mover is a function that either attacks or defends the claim at parentClaimIdx
// Mover is a function that either attacks or defends the claim at parentClaimIdx
type
Mover
func
(
parentClaimIdx
int64
)
type
Mover
func
(
parentClaimIdx
int64
)
...
@@ -239,6 +292,21 @@ func (g *FaultGameHelper) ChallengeRootClaim(ctx context.Context, performMove Mo
...
@@ -239,6 +292,21 @@ func (g *FaultGameHelper) ChallengeRootClaim(ctx context.Context, performMove Mo
attemptStep
(
maxDepth
)
attemptStep
(
maxDepth
)
}
}
func
(
g
*
FaultGameHelper
)
WaitForNewClaim
(
ctx
context
.
Context
,
checkPoint
int64
)
(
int64
,
error
)
{
timedCtx
,
cancel
:=
context
.
WithTimeout
(
ctx
,
2
*
time
.
Minute
)
defer
cancel
()
var
newClaimLen
int64
err
:=
wait
.
For
(
timedCtx
,
time
.
Second
,
func
()
(
bool
,
error
)
{
actual
,
err
:=
g
.
game
.
ClaimDataLen
(
&
bind
.
CallOpts
{
Context
:
ctx
})
if
err
!=
nil
{
return
false
,
err
}
newClaimLen
=
actual
.
Int64
()
return
actual
.
Cmp
(
big
.
NewInt
(
checkPoint
))
>
0
,
nil
})
return
newClaimLen
,
err
}
func
(
g
*
FaultGameHelper
)
Attack
(
ctx
context
.
Context
,
claimIdx
int64
,
claim
common
.
Hash
)
{
func
(
g
*
FaultGameHelper
)
Attack
(
ctx
context
.
Context
,
claimIdx
int64
,
claim
common
.
Hash
)
{
tx
,
err
:=
g
.
game
.
Attack
(
g
.
opts
,
big
.
NewInt
(
claimIdx
),
claim
)
tx
,
err
:=
g
.
game
.
Attack
(
g
.
opts
,
big
.
NewInt
(
claimIdx
),
claim
)
g
.
require
.
NoError
(
err
,
"Attack transaction did not send"
)
g
.
require
.
NoError
(
err
,
"Attack transaction did not send"
)
...
@@ -266,6 +334,33 @@ func (g *FaultGameHelper) StepFails(claimIdx int64, isAttack bool, stateData []b
...
@@ -266,6 +334,33 @@ func (g *FaultGameHelper) StepFails(claimIdx int64, isAttack bool, stateData []b
g
.
require
.
Equal
(
"0xfb4e40dd"
,
errData
.
ErrorData
(),
"Revert reason should be abi encoded ValidStep()"
)
g
.
require
.
Equal
(
"0xfb4e40dd"
,
errData
.
ErrorData
(),
"Revert reason should be abi encoded ValidStep()"
)
}
}
// ResolveClaim resolves a single subgame
func
(
g
*
FaultGameHelper
)
ResolveClaim
(
ctx
context
.
Context
,
claimIdx
int64
)
{
tx
,
err
:=
g
.
game
.
ResolveClaim
(
g
.
opts
,
big
.
NewInt
(
claimIdx
))
g
.
require
.
NoError
(
err
,
"ResolveClaim transaction did not send"
)
_
,
err
=
wait
.
ForReceiptOK
(
ctx
,
g
.
client
,
tx
.
Hash
())
g
.
require
.
NoError
(
err
,
"ResolveClaim transaction was not OK"
)
}
// ResolveAllClaims resolves all subgames
// This function does not resolve the game. That's the responsibility of challengers
func
(
g
*
FaultGameHelper
)
ResolveAllClaims
(
ctx
context
.
Context
)
{
loader
:=
fault
.
NewLoader
(
g
.
game
)
claims
,
err
:=
loader
.
FetchClaims
(
ctx
)
g
.
require
.
NoError
(
err
,
"Failed to fetch claims"
)
subgames
:=
make
(
map
[
int
]
bool
)
for
i
:=
len
(
claims
)
-
1
;
i
>
0
;
i
--
{
subgames
[
claims
[
i
]
.
ParentContractIndex
]
=
true
// Subgames containing only one node are implicitly resolved
// i.e. uncountered and claims at MAX_DEPTH
if
!
subgames
[
i
]
{
continue
}
g
.
ResolveClaim
(
ctx
,
int64
(
i
))
}
g
.
ResolveClaim
(
ctx
,
0
)
}
func
(
g
*
FaultGameHelper
)
gameData
(
ctx
context
.
Context
)
string
{
func
(
g
*
FaultGameHelper
)
gameData
(
ctx
context
.
Context
)
string
{
opts
:=
&
bind
.
CallOpts
{
Context
:
ctx
}
opts
:=
&
bind
.
CallOpts
{
Context
:
ctx
}
maxDepth
:=
int
(
g
.
MaxDepth
(
ctx
))
maxDepth
:=
int
(
g
.
MaxDepth
(
ctx
))
...
@@ -277,8 +372,8 @@ func (g *FaultGameHelper) gameData(ctx context.Context) string {
...
@@ -277,8 +372,8 @@ func (g *FaultGameHelper) gameData(ctx context.Context) string {
g
.
require
.
NoErrorf
(
err
,
"Fetch claim %v"
,
i
)
g
.
require
.
NoErrorf
(
err
,
"Fetch claim %v"
,
i
)
pos
:=
types
.
NewPositionFromGIndex
(
claim
.
Position
.
Uint64
())
pos
:=
types
.
NewPositionFromGIndex
(
claim
.
Position
.
Uint64
())
info
=
info
+
fmt
.
Sprintf
(
"%v - Position: %v, Depth: %v, IndexAtDepth: %v Trace Index: %v, Value: %v, Countered: %v
\n
"
,
info
=
info
+
fmt
.
Sprintf
(
"%v - Position: %v, Depth: %v, IndexAtDepth: %v Trace Index: %v, Value: %v, Countered: %v
, ParentIndex: %v
\n
"
,
i
,
claim
.
Position
.
Int64
(),
pos
.
Depth
(),
pos
.
IndexAtDepth
(),
pos
.
TraceIndex
(
maxDepth
),
common
.
Hash
(
claim
.
Claim
)
.
Hex
(),
claim
.
Countered
)
i
,
claim
.
Position
.
Int64
(),
pos
.
Depth
(),
pos
.
IndexAtDepth
(),
pos
.
TraceIndex
(
maxDepth
),
common
.
Hash
(
claim
.
Claim
)
.
Hex
(),
claim
.
Countered
,
claim
.
ParentIndex
)
}
}
status
,
err
:=
g
.
game
.
Status
(
opts
)
status
,
err
:=
g
.
game
.
Status
(
opts
)
g
.
require
.
NoError
(
err
,
"Load game status"
)
g
.
require
.
NoError
(
err
,
"Load game status"
)
...
@@ -288,3 +383,106 @@ func (g *FaultGameHelper) gameData(ctx context.Context) string {
...
@@ -288,3 +383,106 @@ func (g *FaultGameHelper) gameData(ctx context.Context) string {
func
(
g
*
FaultGameHelper
)
LogGameData
(
ctx
context
.
Context
)
{
func
(
g
*
FaultGameHelper
)
LogGameData
(
ctx
context
.
Context
)
{
g
.
t
.
Log
(
g
.
gameData
(
ctx
))
g
.
t
.
Log
(
g
.
gameData
(
ctx
))
}
}
type
dishonestClaim
struct
{
ParentIndex
int64
IsAttack
bool
Valid
bool
}
type
DishonestHelper
struct
{
*
FaultGameHelper
*
HonestHelper
claims
map
[
dishonestClaim
]
bool
defender
bool
}
func
NewDishonestHelper
(
g
*
FaultGameHelper
,
correctTrace
*
HonestHelper
,
defender
bool
)
*
DishonestHelper
{
return
&
DishonestHelper
{
g
,
correctTrace
,
make
(
map
[
dishonestClaim
]
bool
),
defender
}
}
func
(
t
*
DishonestHelper
)
Attack
(
ctx
context
.
Context
,
claimIndex
int64
)
{
c
:=
dishonestClaim
{
claimIndex
,
true
,
false
}
if
t
.
claims
[
c
]
{
return
}
t
.
claims
[
c
]
=
true
t
.
FaultGameHelper
.
Attack
(
ctx
,
claimIndex
,
common
.
Hash
{
byte
(
claimIndex
)})
}
func
(
t
*
DishonestHelper
)
Defend
(
ctx
context
.
Context
,
claimIndex
int64
)
{
c
:=
dishonestClaim
{
claimIndex
,
false
,
false
}
if
t
.
claims
[
c
]
{
return
}
t
.
claims
[
c
]
=
true
t
.
FaultGameHelper
.
Defend
(
ctx
,
claimIndex
,
common
.
Hash
{
byte
(
claimIndex
)})
}
func
(
t
*
DishonestHelper
)
AttackCorrect
(
ctx
context
.
Context
,
claimIndex
int64
)
{
c
:=
dishonestClaim
{
claimIndex
,
true
,
true
}
if
t
.
claims
[
c
]
{
return
}
t
.
claims
[
c
]
=
true
t
.
HonestHelper
.
Attack
(
ctx
,
claimIndex
)
}
func
(
t
*
DishonestHelper
)
DefendCorrect
(
ctx
context
.
Context
,
claimIndex
int64
)
{
c
:=
dishonestClaim
{
claimIndex
,
false
,
true
}
if
t
.
claims
[
c
]
{
return
}
t
.
claims
[
c
]
=
true
t
.
HonestHelper
.
Defend
(
ctx
,
claimIndex
)
}
// ExhaustDishonestClaims makes all possible significant moves (mod honest challenger's) in a game.
// It is very inefficient and should NOT be used on games with large depths
func
(
d
*
DishonestHelper
)
ExhaustDishonestClaims
(
ctx
context
.
Context
)
{
depth
:=
d
.
MaxDepth
(
ctx
)
move
:=
func
(
claimIndex
int64
,
claimData
ContractClaim
)
{
// dishonest level, valid attack
// dishonest level, invalid attack
// dishonest level, valid defense
// dishonest level, invalid defense
// honest level, invalid attack
// honest level, invalid defense
pos
:=
types
.
NewPositionFromGIndex
(
claimData
.
Position
.
Uint64
())
if
int64
(
pos
.
Depth
())
==
depth
{
return
}
d
.
LogGameData
(
ctx
)
d
.
FaultGameHelper
.
t
.
Logf
(
"Dishonest moves against claimIndex %d"
,
claimIndex
)
agreeWithLevel
:=
d
.
defender
==
(
pos
.
Depth
()
%
2
==
0
)
if
!
agreeWithLevel
{
d
.
AttackCorrect
(
ctx
,
claimIndex
)
if
claimIndex
!=
0
{
d
.
DefendCorrect
(
ctx
,
claimIndex
)
}
}
d
.
Attack
(
ctx
,
claimIndex
)
if
claimIndex
!=
0
{
d
.
Defend
(
ctx
,
claimIndex
)
}
}
var
numClaimsSeen
int64
for
{
newCount
,
err
:=
d
.
WaitForNewClaim
(
ctx
,
numClaimsSeen
)
if
errors
.
Is
(
err
,
context
.
DeadlineExceeded
)
{
// we assume that the honest challenger has stopped responding
// There's nothing to respond to.
break
}
d
.
FaultGameHelper
.
require
.
NoError
(
err
)
for
i
:=
numClaimsSeen
;
i
<
newCount
;
i
++
{
claimData
:=
d
.
getClaim
(
ctx
,
numClaimsSeen
)
move
(
numClaimsSeen
,
claimData
)
numClaimsSeen
++
}
}
}
op-e2e/faultproof_test.go
View file @
6a3a96c3
...
@@ -3,7 +3,9 @@ package op_e2e
...
@@ -3,7 +3,9 @@ package op_e2e
import
(
import
(
"context"
"context"
"testing"
"testing"
"time"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/challenger"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/challenger"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/disputegame"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/disputegame"
l2oo2
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/l2oo"
l2oo2
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/l2oo"
...
@@ -62,8 +64,12 @@ func TestMultipleCannonGames(t *testing.T) {
...
@@ -62,8 +64,12 @@ func TestMultipleCannonGames(t *testing.T) {
sys
.
TimeTravelClock
.
AdvanceTime
(
gameDuration
)
sys
.
TimeTravelClock
.
AdvanceTime
(
gameDuration
)
require
.
NoError
(
t
,
wait
.
ForNextBlock
(
ctx
,
l1Client
))
require
.
NoError
(
t
,
wait
.
ForNextBlock
(
ctx
,
l1Client
))
game1
.
WaitForGameStatus
(
ctx
,
disputegame
.
StatusChallengerWins
)
game1
.
WaitForInactivity
(
ctx
,
10
,
true
)
game2
.
WaitForGameStatus
(
ctx
,
disputegame
.
StatusChallengerWins
)
game2
.
WaitForInactivity
(
ctx
,
10
,
true
)
game1
.
LogGameData
(
ctx
)
game2
.
LogGameData
(
ctx
)
require
.
EqualValues
(
t
,
disputegame
.
StatusChallengerWins
,
game1
.
Status
(
ctx
))
require
.
EqualValues
(
t
,
disputegame
.
StatusChallengerWins
,
game2
.
Status
(
ctx
))
// Check that the game directories are removed
// Check that the game directories are removed
challenger
.
WaitForGameDataDeletion
(
ctx
,
game1
,
game2
)
challenger
.
WaitForGameDataDeletion
(
ctx
,
game1
,
game2
)
...
@@ -168,11 +174,72 @@ func TestChallengerCompleteDisputeGame(t *testing.T) {
...
@@ -168,11 +174,72 @@ func TestChallengerCompleteDisputeGame(t *testing.T) {
sys
.
TimeTravelClock
.
AdvanceTime
(
gameDuration
)
sys
.
TimeTravelClock
.
AdvanceTime
(
gameDuration
)
require
.
NoError
(
t
,
wait
.
ForNextBlock
(
ctx
,
l1Client
))
require
.
NoError
(
t
,
wait
.
ForNextBlock
(
ctx
,
l1Client
))
game
.
WaitForGameStatus
(
ctx
,
test
.
expectedResult
)
game
.
WaitForInactivity
(
ctx
,
10
,
true
)
game
.
LogGameData
(
ctx
)
require
.
EqualValues
(
t
,
test
.
expectedResult
,
game
.
Status
(
ctx
))
})
})
}
}
}
}
func
TestChallengerCompleteExhaustiveDisputeGame
(
t
*
testing
.
T
)
{
InitParallel
(
t
)
testCase
:=
func
(
t
*
testing
.
T
,
isRootCorrect
bool
)
{
ctx
:=
context
.
Background
()
sys
,
l1Client
:=
startFaultDisputeSystem
(
t
)
t
.
Cleanup
(
sys
.
Close
)
disputeGameFactory
:=
disputegame
.
NewFactoryHelper
(
t
,
ctx
,
sys
.
cfg
.
L1Deployments
,
l1Client
)
rootClaimedAlphabet
:=
disputegame
.
CorrectAlphabet
if
!
isRootCorrect
{
rootClaimedAlphabet
=
"abcdexyz"
}
game
:=
disputeGameFactory
.
StartAlphabetGame
(
ctx
,
rootClaimedAlphabet
)
require
.
NotNil
(
t
,
game
)
gameDuration
:=
game
.
GameDuration
(
ctx
)
// Start honest challenger
game
.
StartChallenger
(
ctx
,
sys
.
NodeEndpoint
(
"l1"
),
"Challenger"
,
challenger
.
WithAgreeProposedOutput
(
!
isRootCorrect
),
challenger
.
WithAlphabet
(
disputegame
.
CorrectAlphabet
),
challenger
.
WithPrivKey
(
sys
.
cfg
.
Secrets
.
Alice
),
// Ensures the challenger responds to all claims before test timeout
challenger
.
WithPollInterval
(
time
.
Millisecond
*
400
),
)
// Start dishonest challenger
correctTrace
:=
game
.
CreateHonestActor
(
ctx
,
disputegame
.
CorrectAlphabet
,
4
)
dishonestHelper
:=
disputegame
.
NewDishonestHelper
(
&
game
.
FaultGameHelper
,
correctTrace
,
!
isRootCorrect
)
dishonestHelper
.
ExhaustDishonestClaims
(
ctx
)
// Wait until we've reached max depth before checking for inactivity
game
.
WaitForClaimAtDepth
(
ctx
,
int
(
game
.
MaxDepth
(
ctx
)))
// Wait for 4 blocks of no challenger responses. The challenger may still be stepping on invalid claims at max depth
game
.
WaitForInactivity
(
ctx
,
4
,
false
)
sys
.
TimeTravelClock
.
AdvanceTime
(
gameDuration
)
require
.
NoError
(
t
,
wait
.
ForNextBlock
(
ctx
,
l1Client
))
expectedStatus
:=
disputegame
.
StatusChallengerWins
if
isRootCorrect
{
expectedStatus
=
disputegame
.
StatusDefenderWins
}
game
.
WaitForInactivity
(
ctx
,
10
,
true
)
game
.
LogGameData
(
ctx
)
require
.
EqualValues
(
t
,
expectedStatus
,
game
.
Status
(
ctx
))
}
t
.
Run
(
"RootCorrect"
,
func
(
t
*
testing
.
T
)
{
InitParallel
(
t
)
testCase
(
t
,
true
)
})
t
.
Run
(
"RootIncorrect"
,
func
(
t
*
testing
.
T
)
{
InitParallel
(
t
)
testCase
(
t
,
false
)
})
}
func
TestCannonDisputeGame
(
t
*
testing
.
T
)
{
func
TestCannonDisputeGame
(
t
*
testing
.
T
)
{
InitParallel
(
t
)
InitParallel
(
t
)
...
@@ -217,8 +284,9 @@ func TestCannonDisputeGame(t *testing.T) {
...
@@ -217,8 +284,9 @@ func TestCannonDisputeGame(t *testing.T) {
sys
.
TimeTravelClock
.
AdvanceTime
(
game
.
GameDuration
(
ctx
))
sys
.
TimeTravelClock
.
AdvanceTime
(
game
.
GameDuration
(
ctx
))
require
.
NoError
(
t
,
wait
.
ForNextBlock
(
ctx
,
l1Client
))
require
.
NoError
(
t
,
wait
.
ForNextBlock
(
ctx
,
l1Client
))
game
.
WaitFor
GameStatus
(
ctx
,
disputegame
.
StatusChallengerWins
)
game
.
WaitFor
Inactivity
(
ctx
,
10
,
true
)
game
.
LogGameData
(
ctx
)
game
.
LogGameData
(
ctx
)
require
.
EqualValues
(
t
,
disputegame
.
StatusChallengerWins
,
game
.
Status
(
ctx
))
})
})
}
}
}
}
...
@@ -260,8 +328,9 @@ func TestCannonDefendStep(t *testing.T) {
...
@@ -260,8 +328,9 @@ func TestCannonDefendStep(t *testing.T) {
sys
.
TimeTravelClock
.
AdvanceTime
(
game
.
GameDuration
(
ctx
))
sys
.
TimeTravelClock
.
AdvanceTime
(
game
.
GameDuration
(
ctx
))
require
.
NoError
(
t
,
wait
.
ForNextBlock
(
ctx
,
l1Client
))
require
.
NoError
(
t
,
wait
.
ForNextBlock
(
ctx
,
l1Client
))
game
.
WaitFor
GameStatus
(
ctx
,
disputegame
.
StatusChallengerWins
)
game
.
WaitFor
Inactivity
(
ctx
,
10
,
true
)
game
.
LogGameData
(
ctx
)
game
.
LogGameData
(
ctx
)
require
.
EqualValues
(
t
,
disputegame
.
StatusChallengerWins
,
game
.
Status
(
ctx
))
}
}
func
TestCannonProposedOutputRootInvalid
(
t
*
testing
.
T
)
{
func
TestCannonProposedOutputRootInvalid
(
t
*
testing
.
T
)
{
...
@@ -335,14 +404,14 @@ func TestCannonProposedOutputRootInvalid(t *testing.T) {
...
@@ -335,14 +404,14 @@ func TestCannonProposedOutputRootInvalid(t *testing.T) {
sys
.
TimeTravelClock
.
AdvanceTime
(
game
.
GameDuration
(
ctx
))
sys
.
TimeTravelClock
.
AdvanceTime
(
game
.
GameDuration
(
ctx
))
require
.
NoError
(
t
,
wait
.
ForNextBlock
(
ctx
,
l1Client
))
require
.
NoError
(
t
,
wait
.
ForNextBlock
(
ctx
,
l1Client
))
game
.
WaitFor
GameStatus
(
ctx
,
disputegame
.
StatusDefenderWins
)
game
.
WaitFor
Inactivity
(
ctx
,
10
,
true
)
game
.
LogGameData
(
ctx
)
game
.
LogGameData
(
ctx
)
require
.
EqualValues
(
t
,
disputegame
.
StatusDefenderWins
,
game
.
Status
(
ctx
))
})
})
}
}
}
}
func
TestCannonPoisonedPostState
(
t
*
testing
.
T
)
{
func
TestCannonPoisonedPostState
(
t
*
testing
.
T
)
{
t
.
Skip
(
"Known failure case"
)
InitParallel
(
t
)
InitParallel
(
t
)
ctx
:=
context
.
Background
()
ctx
:=
context
.
Background
()
...
@@ -365,9 +434,12 @@ func TestCannonPoisonedPostState(t *testing.T) {
...
@@ -365,9 +434,12 @@ func TestCannonPoisonedPostState(t *testing.T) {
// Honest defense at "dishonest" level
// Honest defense at "dishonest" level
correctTrace
.
Defend
(
ctx
,
1
)
correctTrace
.
Defend
(
ctx
,
1
)
// Dishonest attack at "honest" level - honest move would be to
defend
// Dishonest attack at "honest" level - honest move would be to
ignore
game
.
Attack
(
ctx
,
2
,
common
.
Hash
{
0x03
,
0xaa
})
game
.
Attack
(
ctx
,
2
,
common
.
Hash
{
0x03
,
0xaa
})
// Honest attack at "dishonest" level - honest move would be to ignore
correctTrace
.
Attack
(
ctx
,
3
)
// Start the honest challenger
// Start the honest challenger
game
.
StartChallenger
(
ctx
,
sys
.
RollupConfig
,
sys
.
L2GenesisCfg
,
l1Endpoint
,
l2Endpoint
,
"Honest"
,
game
.
StartChallenger
(
ctx
,
sys
.
RollupConfig
,
sys
.
L2GenesisCfg
,
l1Endpoint
,
l2Endpoint
,
"Honest"
,
// Agree with the proposed output, so disagree with the root claim
// Agree with the proposed output, so disagree with the root claim
...
@@ -376,29 +448,40 @@ func TestCannonPoisonedPostState(t *testing.T) {
...
@@ -376,29 +448,40 @@ func TestCannonPoisonedPostState(t *testing.T) {
)
)
// Start dishonest challenger that posts correct claims
// Start dishonest challenger that posts correct claims
game
.
StartChallenger
(
ctx
,
sys
.
RollupConfig
,
sys
.
L2GenesisCfg
,
l1Endpoint
,
l2Endpoint
,
"DishonestCorrect"
,
// It participates in the subgame root the honest claim index 4
// Disagree with the proposed output, so agree with the root claim
func
()
{
challenger
.
WithAgreeProposedOutput
(
false
),
claimCount
:=
int64
(
5
)
challenger
.
WithPrivKey
(
sys
.
cfg
.
Secrets
.
Mallory
),
depth
:=
game
.
MaxDepth
(
ctx
)
)
for
{
game
.
LogGameData
(
ctx
)
// Give the challengers time to progress down the full game depth
claimCount
++
depth
:=
game
.
MaxDepth
(
ctx
)
// Wait for the challenger to counter
for
i
:=
3
;
i
<=
int
(
depth
);
i
++
{
game
.
WaitForClaimCount
(
ctx
,
claimCount
)
game
.
WaitForClaimAtDepth
(
ctx
,
i
)
game
.
LogGameData
(
ctx
)
// Respond with our own move
}
correctTrace
.
Defend
(
ctx
,
claimCount
-
1
)
claimCount
++
game
.
WaitForClaimCount
(
ctx
,
claimCount
)
// Defender moves last. If we're at max depth, then we're done
dishonestClaim
:=
game
.
GetClaimUnsafe
(
ctx
,
claimCount
-
1
)
pos
:=
types
.
NewPositionFromGIndex
(
dishonestClaim
.
Position
.
Uint64
())
if
int64
(
pos
.
Depth
())
==
depth
{
break
}
}
}()
// Wait for all the leaf nodes to be countered
// Wait for the challenger to drive the subgame at 4 to the leaf node, which should be countered
// Wait for the challengers to drive the game down to the leaf node which should be countered
game
.
WaitForClaimAtMaxDepth
(
ctx
,
true
)
game
.
WaitForAllClaimsCountered
(
ctx
)
// Time travel past when the game will be resolvable.
// Time travel past when the game will be resolvable.
sys
.
TimeTravelClock
.
AdvanceTime
(
game
.
GameDuration
(
ctx
))
sys
.
TimeTravelClock
.
AdvanceTime
(
game
.
GameDuration
(
ctx
))
require
.
NoError
(
t
,
wait
.
ForNextBlock
(
ctx
,
l1Client
))
require
.
NoError
(
t
,
wait
.
ForNextBlock
(
ctx
,
l1Client
))
game
.
WaitFor
GameStatus
(
ctx
,
disputegame
.
StatusChallengerWins
)
game
.
WaitFor
Inactivity
(
ctx
,
10
,
true
)
game
.
LogGameData
(
ctx
)
game
.
LogGameData
(
ctx
)
require
.
EqualValues
(
t
,
disputegame
.
StatusChallengerWins
,
game
.
Status
(
ctx
))
}
}
// setupDisputeGameForInvalidOutputRoot sets up an L2 chain with at least one valid output root followed by an invalid output root.
// setupDisputeGameForInvalidOutputRoot sets up an L2 chain with at least one valid output root followed by an invalid output root.
...
@@ -470,8 +553,9 @@ func TestCannonChallengeWithCorrectRoot(t *testing.T) {
...
@@ -470,8 +553,9 @@ func TestCannonChallengeWithCorrectRoot(t *testing.T) {
sys
.
TimeTravelClock
.
AdvanceTime
(
game
.
GameDuration
(
ctx
))
sys
.
TimeTravelClock
.
AdvanceTime
(
game
.
GameDuration
(
ctx
))
require
.
NoError
(
t
,
wait
.
ForNextBlock
(
ctx
,
l1Client
))
require
.
NoError
(
t
,
wait
.
ForNextBlock
(
ctx
,
l1Client
))
game
.
WaitFor
GameStatus
(
ctx
,
disputegame
.
StatusChallengerWins
)
game
.
WaitFor
Inactivity
(
ctx
,
10
,
true
)
game
.
LogGameData
(
ctx
)
game
.
LogGameData
(
ctx
)
require
.
EqualValues
(
t
,
disputegame
.
StatusChallengerWins
,
game
.
Status
(
ctx
))
}
}
func
startFaultDisputeSystem
(
t
*
testing
.
T
)
(
*
System
,
*
ethclient
.
Client
)
{
func
startFaultDisputeSystem
(
t
*
testing
.
T
)
(
*
System
,
*
ethclient
.
Client
)
{
...
...
packages/contracts-bedrock/.gas-snapshot
View file @
6a3a96c3
...
@@ -46,8 +46,8 @@ CrossDomainOwnable3_Test:test_transferOwnership_zeroAddress_reverts() (gas: 1208
...
@@ -46,8 +46,8 @@ CrossDomainOwnable3_Test:test_transferOwnership_zeroAddress_reverts() (gas: 1208
CrossDomainOwnableThroughPortal_Test:test_depositTransaction_crossDomainOwner_succeeds() (gas: 81417)
CrossDomainOwnableThroughPortal_Test:test_depositTransaction_crossDomainOwner_succeeds() (gas: 81417)
CrossDomainOwnable_Test:test_onlyOwner_notOwner_reverts() (gas: 10597)
CrossDomainOwnable_Test:test_onlyOwner_notOwner_reverts() (gas: 10597)
CrossDomainOwnable_Test:test_onlyOwner_succeeds() (gas: 34883)
CrossDomainOwnable_Test:test_onlyOwner_succeeds() (gas: 34883)
DelayedVetoable_Getters_Test:test_getters() (gas: 2
1342
)
DelayedVetoable_Getters_Test:test_getters() (gas: 2
4466
)
DelayedVetoable_Getters_TestFail:test_getters_notZeroAddress_reverts() (gas:
26649
)
DelayedVetoable_Getters_TestFail:test_getters_notZeroAddress_reverts() (gas:
31166
)
DelayedVetoable_HandleCall_TestFail:test_handleCall_unauthorizedInitiation_reverts() (gas: 20234)
DelayedVetoable_HandleCall_TestFail:test_handleCall_unauthorizedInitiation_reverts() (gas: 20234)
DeleteOutput:test_script_succeeds() (gas: 3100)
DeleteOutput:test_script_succeeds() (gas: 3100)
DeployerWhitelist_Test:test_owner_succeeds() (gas: 7582)
DeployerWhitelist_Test:test_owner_succeeds() (gas: 7582)
...
@@ -91,39 +91,45 @@ FaucetTest:test_nonAdmin_drip_fails() (gas: 262520)
...
@@ -91,39 +91,45 @@ FaucetTest:test_nonAdmin_drip_fails() (gas: 262520)
FaucetTest:test_receive_succeeds() (gas: 17401)
FaucetTest:test_receive_succeeds() (gas: 17401)
FaucetTest:test_withdraw_nonAdmin_reverts() (gas: 13145)
FaucetTest:test_withdraw_nonAdmin_reverts() (gas: 13145)
FaucetTest:test_withdraw_succeeds() (gas: 78359)
FaucetTest:test_withdraw_succeeds() (gas: 78359)
FaultDisputeGame_ResolvesCorrectly_CorrectRoot1:test_resolvesCorrectly_succeeds() (gas:
499197
)
FaultDisputeGame_ResolvesCorrectly_CorrectRoot1:test_resolvesCorrectly_succeeds() (gas:
660411
)
FaultDisputeGame_ResolvesCorrectly_CorrectRoot2:test_resolvesCorrectly_succeeds() (gas:
506057
)
FaultDisputeGame_ResolvesCorrectly_CorrectRoot2:test_resolvesCorrectly_succeeds() (gas:
667293
)
FaultDisputeGame_ResolvesCorrectly_CorrectRoot3:test_resolvesCorrectly_succeeds() (gas:
502738
)
FaultDisputeGame_ResolvesCorrectly_CorrectRoot3:test_resolvesCorrectly_succeeds() (gas:
663974
)
FaultDisputeGame_ResolvesCorrectly_CorrectRoot4:test_resolvesCorrectly_succeeds() (gas:
505955
)
FaultDisputeGame_ResolvesCorrectly_CorrectRoot4:test_resolvesCorrectly_succeeds() (gas:
667169
)
FaultDisputeGame_ResolvesCorrectly_CorrectRoot5:test_resolvesCorrectly_succeeds() (gas:
505224
)
FaultDisputeGame_ResolvesCorrectly_CorrectRoot5:test_resolvesCorrectly_succeeds() (gas:
666460
)
FaultDisputeGame_ResolvesCorrectly_IncorrectRoot1:test_resolvesCorrectly_succeeds() (gas:
49796
2)
FaultDisputeGame_ResolvesCorrectly_IncorrectRoot1:test_resolvesCorrectly_succeeds() (gas:
65309
2)
FaultDisputeGame_ResolvesCorrectly_IncorrectRoot2:test_resolvesCorrectly_succeeds() (gas:
504822
)
FaultDisputeGame_ResolvesCorrectly_IncorrectRoot2:test_resolvesCorrectly_succeeds() (gas:
658598
)
FaultDisputeGame_ResolvesCorrectly_IncorrectRoot3:test_resolvesCorrectly_succeeds() (gas:
50150
3)
FaultDisputeGame_ResolvesCorrectly_IncorrectRoot3:test_resolvesCorrectly_succeeds() (gas:
65594
3)
FaultDisputeGame_ResolvesCorrectly_IncorrectRoot4:test_resolvesCorrectly_succeeds() (gas:
502720
)
FaultDisputeGame_ResolvesCorrectly_IncorrectRoot4:test_resolvesCorrectly_succeeds() (gas:
656899
)
FaultDisputeGame_ResolvesCorrectly_IncorrectRoot5:test_resolvesCorrectly_succeeds() (gas:
501989
)
FaultDisputeGame_ResolvesCorrectly_IncorrectRoot5:test_resolvesCorrectly_succeeds() (gas:
656332
)
FaultDisputeGame_Test:test_addLocalData_static_succeeds() (gas: 6405
04
)
FaultDisputeGame_Test:test_addLocalData_static_succeeds() (gas: 6405
67
)
FaultDisputeGame_Test:test_createdAt_succeeds() (gas: 10342)
FaultDisputeGame_Test:test_createdAt_succeeds() (gas: 10342)
FaultDisputeGame_Test:test_extraData_succeeds() (gas: 32377)
FaultDisputeGame_Test:test_extraData_succeeds() (gas: 32355)
FaultDisputeGame_Test:test_gameData_succeeds() (gas: 32804)
FaultDisputeGame_Test:test_gameData_succeeds() (gas: 32782)
FaultDisputeGame_Test:test_gameType_succeeds() (gas: 8309)
FaultDisputeGame_Test:test_gameType_succeeds() (gas: 8265)
FaultDisputeGame_Test:test_initialize_correctData_succeeds() (gas: 57628)
FaultDisputeGame_Test:test_initialize_correctData_succeeds() (gas: 57739)
FaultDisputeGame_Test:test_initialize_firstOutput_reverts() (gas: 210629)
FaultDisputeGame_Test:test_initialize_firstOutput_reverts() (gas: 210563)
FaultDisputeGame_Test:test_initialize_l1HeadTooOld_reverts() (gas: 228390)
FaultDisputeGame_Test:test_initialize_l1HeadTooOld_reverts() (gas: 228368)
FaultDisputeGame_Test:test_move_clockCorrectness_succeeds() (gas: 415971)
FaultDisputeGame_Test:test_move_clockCorrectness_succeeds() (gas: 594268)
FaultDisputeGame_Test:test_move_clockTimeExceeded_reverts() (gas: 23197)
FaultDisputeGame_Test:test_move_clockTimeExceeded_reverts() (gas: 23175)
FaultDisputeGame_Test:test_move_defendRoot_reverts() (gas: 13344)
FaultDisputeGame_Test:test_move_defendRoot_reverts() (gas: 13366)
FaultDisputeGame_Test:test_move_duplicateClaim_reverts() (gas: 102898)
FaultDisputeGame_Test:test_move_duplicateClaim_reverts() (gas: 147389)
FaultDisputeGame_Test:test_move_gameDepthExceeded_reverts() (gas: 407913)
FaultDisputeGame_Test:test_move_duplicateClaimsDifferentSubgames_succeeds() (gas: 556885)
FaultDisputeGame_Test:test_move_gameDepthExceeded_reverts() (gas: 585897)
FaultDisputeGame_Test:test_move_gameNotInProgress_reverts() (gas: 11002)
FaultDisputeGame_Test:test_move_gameNotInProgress_reverts() (gas: 11002)
FaultDisputeGame_Test:test_move_nonExistentParent_reverts() (gas: 24710)
FaultDisputeGame_Test:test_move_nonExistentParent_reverts() (gas: 24666)
FaultDisputeGame_Test:test_move_simpleAttack_succeeds() (gas: 107384)
FaultDisputeGame_Test:test_move_simpleAttack_succeeds() (gas: 151959)
FaultDisputeGame_Test:test_resolve_challengeContested_succeeds() (gas: 224949)
FaultDisputeGame_Test:test_resolve_challengeContested_succeeds() (gas: 269413)
FaultDisputeGame_Test:test_resolve_notInProgress_reverts() (gas: 9686)
FaultDisputeGame_Test:test_resolve_claimAlreadyResolved_reverts() (gas: 272356)
FaultDisputeGame_Test:test_resolve_rootContested_succeeds() (gas: 109879)
FaultDisputeGame_Test:test_resolve_claimAtMaxDepthAlreadyResolved_reverts() (gas: 586672)
FaultDisputeGame_Test:test_resolve_rootUncontestedClockNotExpired_succeeds() (gas: 21421)
FaultDisputeGame_Test:test_resolve_notInProgress_reverts() (gas: 9732)
FaultDisputeGame_Test:test_resolve_rootUncontested_succeeds() (gas: 27279)
FaultDisputeGame_Test:test_resolve_outOfOrderResolution_reverts() (gas: 309037)
FaultDisputeGame_Test:test_resolve_teamDeathmatch_succeeds() (gas: 395658)
FaultDisputeGame_Test:test_resolve_rootContested_succeeds() (gas: 139044)
FaultDisputeGame_Test:test_rootClaim_succeeds() (gas: 8276)
FaultDisputeGame_Test:test_resolve_rootUncontestedButUnresolved_reverts() (gas: 15883)
FaultDisputeGame_Test:test_resolve_rootUncontestedClockNotExpired_succeeds() (gas: 18406)
FaultDisputeGame_Test:test_resolve_rootUncontested_succeeds() (gas: 51409)
FaultDisputeGame_Test:test_resolve_stepReached_succeeds() (gas: 498476)
FaultDisputeGame_Test:test_resolve_teamDeathmatch_succeeds() (gas: 443373)
FaultDisputeGame_Test:test_rootClaim_succeeds() (gas: 8232)
FeeVault_Test:test_constructor_succeeds() (gas: 18185)
FeeVault_Test:test_constructor_succeeds() (gas: 18185)
GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_0() (gas: 354421)
GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_0() (gas: 354421)
GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_1() (gas: 2952628)
GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_1() (gas: 2952628)
...
...
packages/contracts-bedrock/semver-lock.json
View file @
6a3a96c3
...
@@ -18,7 +18,7 @@
...
@@ -18,7 +18,7 @@
"src/L2/L2StandardBridge.sol"
:
"0xfe01bcb1ddc947b9b8a7093d0971854b9fa8d49da5bd933a3dd106167907f882"
,
"src/L2/L2StandardBridge.sol"
:
"0xfe01bcb1ddc947b9b8a7093d0971854b9fa8d49da5bd933a3dd106167907f882"
,
"src/L2/L2ToL1MessagePasser.sol"
:
"0xafc710b4d320ef450586d96a61cbd58cac814cb3b0c4fdc280eace3efdcdf321"
,
"src/L2/L2ToL1MessagePasser.sol"
:
"0xafc710b4d320ef450586d96a61cbd58cac814cb3b0c4fdc280eace3efdcdf321"
,
"src/L2/SequencerFeeVault.sol"
:
"0xc2f733c1128d06ad60bf1e1d98c8f684a4825b11875ccdf2376ede33f5aad4e6"
,
"src/L2/SequencerFeeVault.sol"
:
"0xc2f733c1128d06ad60bf1e1d98c8f684a4825b11875ccdf2376ede33f5aad4e6"
,
"src/dispute/FaultDisputeGame.sol"
:
"0x7
b8462c29d003e96a73491c644001e1a9034bcc45c5be2a7bac3caf80d521635
"
,
"src/dispute/FaultDisputeGame.sol"
:
"0x7
6e7c16431faa32e2074e6abdfe3e86f5ec90b4ac8a6b662edba8c3ce791ad80
"
,
"src/legacy/DeployerWhitelist.sol"
:
"0xf2129ec3da75307ba8e21bc943c332bb04704642e6e263149b5c8ee92dbcb7a8"
,
"src/legacy/DeployerWhitelist.sol"
:
"0xf2129ec3da75307ba8e21bc943c332bb04704642e6e263149b5c8ee92dbcb7a8"
,
"src/legacy/L1BlockNumber.sol"
:
"0x30aae1fc85103476af0226b6e98c71c01feebbdc35d93401390b1ad438a37be6"
,
"src/legacy/L1BlockNumber.sol"
:
"0x30aae1fc85103476af0226b6e98c71c01feebbdc35d93401390b1ad438a37be6"
,
"src/legacy/LegacyMessagePasser.sol"
:
"0x5c08b0a663cc49d30e4e38540f6aefab19ef287c3ecd31c8d8c3decd5f5bd497"
,
"src/legacy/LegacyMessagePasser.sol"
:
"0x5c08b0a663cc49d30e4e38540f6aefab19ef287c3ecd31c8d8c3decd5f5bd497"
,
...
...
packages/contracts-bedrock/src/dispute/FaultDisputeGame.sol
View file @
6a3a96c3
...
@@ -75,6 +75,12 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, Semver {
...
@@ -75,6 +75,12 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, Semver {
/// @notice An internal mapping to allow for constant-time lookups of existing claims.
/// @notice An internal mapping to allow for constant-time lookups of existing claims.
mapping(ClaimHash => bool) internal claims;
mapping(ClaimHash => bool) internal claims;
/// @notice An internal mapping of subgames rooted at a claim index to other claim indices in the subgame.
mapping(uint256 => uint256[]) internal subgames;
/// @notice Indicates whether the subgame rooted at the root claim has been resolved.
bool internal subgameAtRootResolved;
/// @param _gameType The type ID of the game.
/// @param _gameType The type ID of the game.
/// @param _absolutePrestate The absolute prestate of the instruction trace.
/// @param _absolutePrestate The absolute prestate of the instruction trace.
/// @param _maxGameDepth The maximum depth of bisection.
/// @param _maxGameDepth The maximum depth of bisection.
...
@@ -232,9 +238,10 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, Semver {
...
@@ -232,9 +238,10 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, Semver {
// Construct the next clock with the new duration and the current block timestamp.
// Construct the next clock with the new duration and the current block timestamp.
Clock nextClock = LibClock.wrap(nextDuration, Timestamp.wrap(uint64(block.timestamp)));
Clock nextClock = LibClock.wrap(nextDuration, Timestamp.wrap(uint64(block.timestamp)));
// INVARIANT: A claim may only exist at a given position once. Multiple claims may exist
// INVARIANT: There cannot be multiple identical claims with identical moves on the same challengeIndex. Multiple
// at the same position, however they must have different values.
// claims
ClaimHash claimHash = _claim.hashClaimPos(nextPosition);
// at the same position may dispute the same challengeIndex. However, the must have different values.
ClaimHash claimHash = _claim.hashClaimPos(nextPosition, _challengeIndex);
if (claims[claimHash]) revert ClaimAlreadyExists();
if (claims[claimHash]) revert ClaimAlreadyExists();
claims[claimHash] = true;
claims[claimHash] = true;
...
@@ -252,6 +259,9 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, Semver {
...
@@ -252,6 +259,9 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, Semver {
// Set the parent claim as countered.
// Set the parent claim as countered.
claimData[_challengeIndex].countered = true;
claimData[_challengeIndex].countered = true;
// Update the subgame rooted at the parent claim.
subgames[_challengeIndex].push(claimData.length - 1);
// Emit the appropriate event for the attack or defense.
// Emit the appropriate event for the attack or defense.
emit Move(_challengeIndex, _claim, msg.sender);
emit Move(_challengeIndex, _claim, msg.sender);
}
}
...
@@ -348,67 +358,64 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, Semver {
...
@@ -348,67 +358,64 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, Semver {
// INVARIANT: Resolution cannot occur unless the game is currently in progress.
// INVARIANT: Resolution cannot occur unless the game is currently in progress.
if (status != GameStatus.IN_PROGRESS) revert GameNotInProgress();
if (status != GameStatus.IN_PROGRESS) revert GameNotInProgress();
// Search for the left-most dangling non-bottom node
// INVARIANT: Resolution cannot occur unless the absolute root subgame has been resolved.
// The most recent claim is always a dangling, non-bottom node so we start with that
if (!subgameAtRootResolved) revert OutOfOrderResolution();
uint256 leftMostIndex = claimData.length - 1;
uint256 leftMostTraceIndex = type(uint128).max;
for (uint256 i = leftMostIndex; i < type(uint64).max;) {
// Fetch the claim at the current index.
ClaimData storage claim = claimData[i];
// Decrement the loop counter; If it underflows, we've reached the root
// claim and can stop searching.
unchecked {
--i;
}
// INVARIANT: A claim can never be considered as the leftMostIndex or leftMostTraceIndex
status_ = claimData[0].countered ? GameStatus.CHALLENGER_WINS : GameStatus.DEFENDER_WINS;
// if it has been countered.
emit Resolved(status = status_);
if (claim.countered) continue;
}
// If the claim is a dangling node, we can check if it is the left-most
// dangling node we've come across so far. If it is, we can update the
// left-most trace index.
uint256 traceIndex = claim.position.traceIndex(MAX_GAME_DEPTH);
if (traceIndex < leftMostTraceIndex) {
leftMostTraceIndex = traceIndex;
unchecked {
leftMostIndex = i + 1;
}
}
}
// Create a reference to the left most uncontested claim and its parent.
/// @inheritdoc IFaultDisputeGame
ClaimData storage leftMostUncontested = claimData[leftMostIndex];
function resolveClaim(uint256 _claimIndex) external payable {
// INVARIANT: Resolution cannot occur unless the game is currently in progress.
if (status != GameStatus.IN_PROGRESS) revert GameNotInProgress();
// INVARIANT: The game may never be resolved unless the clock of the left-most uncontested
ClaimData storage parent = claimData[_claimIndex];
// claim's parent has expired. If the left-most uncontested claim is the root
// claim, it is uncountered, and we check if 3.5 days has passed since its
// INVARIANT: Cannot resolve a subgame unless the clock of its root has expired
// creation.
uint256 parentIndex = leftMostUncontested.parentIndex;
Clock opposingClock = parentIndex == type(uint32).max ? leftMostUncontested.clock : claimData[parentIndex].clock;
if (
if (
Duration.unwrap(
opposingClock.duration()) + (block.timestamp - Timestamp.unwrap(opposingC
lock.timestamp()))
Duration.unwrap(
parent.clock.duration()) + (block.timestamp - Timestamp.unwrap(parent.c
lock.timestamp()))
<= Duration.unwrap(GAME_DURATION) >> 1
<= Duration.unwrap(GAME_DURATION) >> 1
) {
) {
revert ClockNotExpired();
revert ClockNotExpired();
}
}
// If the left-most dangling node is at an even depth, the defender wins.
uint256[] storage challengeIndices = subgames[_claimIndex];
// Otherwise, the challenger wins and the root claim is deemed invalid.
if (
// INVARIANT: Cannot resolve subgames twice
leftMostUncontested
// Uncontested claims are resolved implicitly unless they are the root claim
.position
if (_claimIndex == 0 && subgameAtRootResolved) revert ClaimAlreadyResolved();
// slither-disable-next-line weak-prng
if (challengeIndices.length == 0 && _claimIndex != 0) revert ClaimAlreadyResolved();
.depth() % 2 == 0 && leftMostTraceIndex != type(uint128).max
) {
// Assume parent is honest until proven otherwise
status_ = GameStatus.DEFENDER_WINS;
bool countered = false;
} else {
status_ = GameStatus.CHALLENGER_WINS;
for (uint256 i = 0; i < challengeIndices.length; ++i) {
uint256 challengeIndex = challengeIndices[i];
// INVARIANT: Cannot resolve a subgame containing an unresolved claim
if (subgames[challengeIndex].length != 0) revert OutOfOrderResolution();
ClaimData storage claim = claimData[challengeIndex];
// Ignore false claims
if (!claim.countered) {
countered = true;
break;
}
}
}
// Update the game status
// Once a subgame is resolved, we percolate the result up the DAG so subsequent calls to
emit Resolved(status = status_);
// resolveClaim will not need to traverse this subgame.
parent.countered = countered;
// Resolved subgames have no entries
delete subgames[_claimIndex];
// Indicate the game is ready to be resolved
if (_claimIndex == 0) {
subgameAtRootResolved = true;
}
}
}
/// @inheritdoc IDisputeGame
/// @inheritdoc IDisputeGame
...
...
packages/contracts-bedrock/src/dispute/interfaces/IFaultDisputeGame.sol
View file @
6a3a96c3
...
@@ -73,6 +73,14 @@ interface IFaultDisputeGame is IDisputeGame {
...
@@ -73,6 +73,14 @@ interface IFaultDisputeGame is IDisputeGame {
/// @param _partOffset The offset of the data to post.
/// @param _partOffset The offset of the data to post.
function addLocalData(uint256 _ident, uint256 _partOffset) external;
function addLocalData(uint256 _ident, uint256 _partOffset) external;
/// @notice Resolves the subgame rooted at the given claim index.
/// @dev This function must be called bottom-up in the DAG
/// A subgame is a tree of claims that has a maximum depth of 1.
/// A subgame root claims is valid if, and only if, all of its child claims are invalid.
/// At the deepest level in the DAG, a claim is invalid if there's a successful step against it.
/// @param _claimIndex The index of the subgame root claim to resolve.
function resolveClaim(uint256 _claimIndex) external payable;
/// @notice An L1 block hash that contains the disputed output root, fetched from the
/// @notice An L1 block hash that contains the disputed output root, fetched from the
/// `BlockOracle` and verified by referencing the timestamp associated with the
/// `BlockOracle` and verified by referencing the timestamp associated with the
/// first L2 Output Proposal in the `L2OutputOracle` that contains the disputed
/// first L2 Output Proposal in the `L2OutputOracle` that contains the disputed
...
...
packages/contracts-bedrock/src/dispute/lib/LibHashing.sol
View file @
6a3a96c3
...
@@ -9,11 +9,20 @@ library LibHashing {
...
@@ -9,11 +9,20 @@ library LibHashing {
/// @notice Hashes a claim and a position together.
/// @notice Hashes a claim and a position together.
/// @param _claim A Claim type.
/// @param _claim A Claim type.
/// @param _position The position of `claim`.
/// @param _position The position of `claim`.
/// @return claimHash_ A hash of abi.encodePacked(claim, position);
/// @param _challengeIndex The index of the claim being moved against.
function hashClaimPos(Claim _claim, Position _position) internal pure returns (ClaimHash claimHash_) {
/// @return claimHash_ A hash of abi.encodePacked(claim, position|challengeIndex);
function hashClaimPos(
Claim _claim,
Position _position,
uint256 _challengeIndex
)
internal
pure
returns (ClaimHash claimHash_)
{
assembly {
assembly {
mstore(0x00, _claim)
mstore(0x00, _claim)
mstore(0x20,
_position
)
mstore(0x20,
or(shl(128, _position), and(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, _challengeIndex))
)
claimHash_ := keccak256(0x00, 0x40)
claimHash_ := keccak256(0x00, 0x40)
}
}
}
}
...
...
packages/contracts-bedrock/src/libraries/DisputeErrors.sol
View file @
6a3a96c3
...
@@ -66,6 +66,12 @@ error L1HeadTooOld();
...
@@ -66,6 +66,12 @@ error L1HeadTooOld();
/// @notice Thrown when an invalid local identifier is passed to the `addLocalData` function.
/// @notice Thrown when an invalid local identifier is passed to the `addLocalData` function.
error InvalidLocalIdent();
error InvalidLocalIdent();
/// @notice Thrown when resolving claims out of order.
error OutOfOrderResolution();
/// @notice Thrown when resolving a claim that has already been resolved.
error ClaimAlreadyResolved();
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
// `AttestationDisputeGame` Errors //
// `AttestationDisputeGame` Errors //
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
...
...
packages/contracts-bedrock/test/DelayedVetoable.t.sol
View file @
6a3a96c3
...
@@ -34,11 +34,20 @@ contract DelayedVetoable_Init is CommonTest {
...
@@ -34,11 +34,20 @@ contract DelayedVetoable_Init is CommonTest {
(bool success,) = address(delayedVetoable).call(hex"");
(bool success,) = address(delayedVetoable).call(hex"");
}
}
/// @dev This function is used to prevent initiating the delay unintentionally..
/// @dev This function is used to prevent initiating the delay unintentionally.
/// It should only be used on tests prior to the delay being activated.
/// @param data The data to be used in the call.
/// @param data The data to be used in the call.
function assumeNonzeroData(bytes memory data) internal pure {
function assumeNonzeroData(bytes memory data) internal pure {
vm.assume(data.length > 0);
vm.assume(data.length > 0);
}
}
/// @dev This function is used to ensure that the data does not clash with the queuedAt function selector.
/// @param data The data to be used in the call.
function assumeNoClash(bytes calldata data) internal pure {
if (data.length >= 4) {
vm.assume(bytes4(data[0:4]) != bytes4(keccak256("queuedAt(bytes32)")));
}
}
}
}
contract DelayedVetoable_Getters_Test is DelayedVetoable_Init {
contract DelayedVetoable_Getters_Test is DelayedVetoable_Init {
...
@@ -49,6 +58,7 @@ contract DelayedVetoable_Getters_Test is DelayedVetoable_Init {
...
@@ -49,6 +58,7 @@ contract DelayedVetoable_Getters_Test is DelayedVetoable_Init {
assertEq(delayedVetoable.vetoer(), vetoer);
assertEq(delayedVetoable.vetoer(), vetoer);
assertEq(delayedVetoable.target(), target);
assertEq(delayedVetoable.target(), target);
assertEq(delayedVetoable.delay(), operatingDelay);
assertEq(delayedVetoable.delay(), operatingDelay);
assertEq(delayedVetoable.queuedAt(keccak256(abi.encode(0))), 0);
}
}
}
}
...
@@ -63,14 +73,15 @@ contract DelayedVetoable_Getters_TestFail is DelayedVetoable_Init {
...
@@ -63,14 +73,15 @@ contract DelayedVetoable_Getters_TestFail is DelayedVetoable_Init {
delayedVetoable.target();
delayedVetoable.target();
vm.expectRevert(abi.encodeWithSelector(Unauthorized.selector, initiator, address(this)));
vm.expectRevert(abi.encodeWithSelector(Unauthorized.selector, initiator, address(this)));
delayedVetoable.delay();
delayedVetoable.delay();
vm.expectRevert(abi.encodeWithSelector(Unauthorized.selector, initiator, address(this)));
delayedVetoable.queuedAt(keccak256(abi.encode(0)));
}
}
}
}
contract DelayedVetoable_HandleCall_Test is DelayedVetoable_Init {
contract DelayedVetoable_HandleCall_Test is DelayedVetoable_Init {
/// @dev A call can be initiated by the initiator.
/// @dev A call can be initiated by the initiator.
function testFuzz_handleCall_initiation_succeeds(bytes memory data) external {
function testFuzz_handleCall_initiation_succeeds(bytes calldata data) external {
assumeNonzeroData(data);
assumeNoClash(data);
vm.expectEmit(true, false, false, true, address(delayedVetoable));
vm.expectEmit(true, false, false, true, address(delayedVetoable));
emit Initiated(keccak256(data), data);
emit Initiated(keccak256(data), data);
...
@@ -87,9 +98,7 @@ contract DelayedVetoable_HandleCall_Test is DelayedVetoable_Init {
...
@@ -87,9 +98,7 @@ contract DelayedVetoable_HandleCall_Test is DelayedVetoable_Init {
external
external
{
{
assumeNonzeroData(inData);
assumeNonzeroData(inData);
if (inData.length >= 4) {
assumeNoClash(inData);
vm.assume(bytes4(inData[0:4]) != bytes4(keccak256("queuedAt(bytes32)")));
}
// Reset the delay to zero
// Reset the delay to zero
vm.store(address(delayedVetoable), bytes32(uint256(0)), bytes32(uint256(0)));
vm.store(address(delayedVetoable), bytes32(uint256(0)), bytes32(uint256(0)));
...
@@ -102,12 +111,17 @@ contract DelayedVetoable_HandleCall_Test is DelayedVetoable_Init {
...
@@ -102,12 +111,17 @@ contract DelayedVetoable_HandleCall_Test is DelayedVetoable_Init {
(bool success, bytes memory returnData) = address(delayedVetoable).call(inData);
(bool success, bytes memory returnData) = address(delayedVetoable).call(inData);
assertTrue(success);
assertTrue(success);
assertEq(returnData, outData);
assertEq(returnData, outData);
// Check that the callHash is not stored for future forwarding
bytes32 callHash = keccak256(inData);
vm.prank(address(0));
assertEq(delayedVetoable.queuedAt(callHash), 0);
}
}
/// @dev The delay can be activated by the vetoer or initiator, and are not forwarded until the delay has passed
/// @dev Calls are not forwarded until the delay has passed.
/// once activated.
function testFuzz_handleCall_forwardingWithDelay_succeeds(bytes calldata data) external {
function testFuzz_handleCall_forwardingWithDelay_succeeds(bytes memory data) external {
assumeNonzeroData(data);
assumeNonzeroData(data);
assumeNoClash(data);
vm.prank(initiator);
vm.prank(initiator);
(bool success,) = address(delayedVetoable).call(data);
(bool success,) = address(delayedVetoable).call(data);
...
@@ -128,14 +142,15 @@ contract DelayedVetoable_HandleCall_Test is DelayedVetoable_Init {
...
@@ -128,14 +142,15 @@ contract DelayedVetoable_HandleCall_Test is DelayedVetoable_Init {
}
}
contract DelayedVetoable_HandleCall_TestFail is DelayedVetoable_Init {
contract DelayedVetoable_HandleCall_TestFail is DelayedVetoable_Init {
/// @dev
The delay is inititially set to zero and the call is immediately forwarded
.
/// @dev
Only the initiator can initiate a call
.
function test_handleCall_unauthorizedInitiation_reverts() external {
function test_handleCall_unauthorizedInitiation_reverts() external {
vm.expectRevert(abi.encodeWithSelector(Unauthorized.selector, initiator, address(this)));
vm.expectRevert(abi.encodeWithSelector(Unauthorized.selector, initiator, address(this)));
(bool success,) = address(delayedVetoable).call(NON_ZERO_DATA);
(bool success,) = address(delayedVetoable).call(NON_ZERO_DATA);
}
}
/// @dev The call cannot be forewarded until the delay has passed.
/// @dev The call cannot be forwarded until the delay has passed.
function testFuzz_handleCall_forwardingTooSoon_reverts(bytes memory data) external {
function testFuzz_handleCall_forwardingTooSoon_reverts(bytes calldata data) external {
assumeNoClash(data);
vm.prank(initiator);
vm.prank(initiator);
(bool success,) = address(delayedVetoable).call(data);
(bool success,) = address(delayedVetoable).call(data);
...
@@ -144,8 +159,8 @@ contract DelayedVetoable_HandleCall_TestFail is DelayedVetoable_Init {
...
@@ -144,8 +159,8 @@ contract DelayedVetoable_HandleCall_TestFail is DelayedVetoable_Init {
}
}
/// @dev The call cannot be forwarded a second time.
/// @dev The call cannot be forwarded a second time.
function testFuzz_handleCall_forwardingTwice_reverts(bytes
memory
data) external {
function testFuzz_handleCall_forwardingTwice_reverts(bytes
calldata
data) external {
assumeNo
nzeroData
(data);
assumeNo
Clash
(data);
// Initiate the call
// Initiate the call
vm.prank(initiator);
vm.prank(initiator);
...
@@ -167,8 +182,13 @@ contract DelayedVetoable_HandleCall_TestFail is DelayedVetoable_Init {
...
@@ -167,8 +182,13 @@ contract DelayedVetoable_HandleCall_TestFail is DelayedVetoable_Init {
}
}
/// @dev If the target reverts, it is bubbled up.
/// @dev If the target reverts, it is bubbled up.
function testFuzz_handleCall_forwardingTargetReverts_reverts(bytes memory inData, bytes memory outData) external {
function testFuzz_handleCall_forwardingTargetReverts_reverts(
assumeNonzeroData(inData);
bytes calldata inData,
bytes calldata outData
)
external
{
assumeNoClash(inData);
// Initiate the call
// Initiate the call
vm.prank(initiator);
vm.prank(initiator);
...
...
packages/contracts-bedrock/test/FaultDisputeGame.t.sol
View file @
6a3a96c3
This diff is collapsed.
Click to expand it.
pnpm-lock.yaml
View file @
6a3a96c3
...
@@ -38,10 +38,10 @@ importers:
...
@@ -38,10 +38,10 @@ importers:
version
:
20.6.2
version
:
20.6.2
'
@typescript-eslint/eslint-plugin'
:
'
@typescript-eslint/eslint-plugin'
:
specifier
:
^6.7.0
specifier
:
^6.7.0
version
:
6.7.0(@typescript-eslint/parser@6.7.
0
)(eslint@8.49.0)(typescript@5.1.6)
version
:
6.7.0(@typescript-eslint/parser@6.7.
2
)(eslint@8.49.0)(typescript@5.1.6)
'
@typescript-eslint/parser'
:
'
@typescript-eslint/parser'
:
specifier
:
^6.7.0
specifier
:
^6.7.0
version
:
6.7.
0
(eslint@8.49.0)(typescript@5.1.6)
version
:
6.7.
2
(eslint@8.49.0)(typescript@5.1.6)
chai
:
chai
:
specifier
:
^4.2.0
specifier
:
^4.2.0
version
:
4.3.7
version
:
4.3.7
...
@@ -62,7 +62,7 @@ importers:
...
@@ -62,7 +62,7 @@ importers:
version
:
16.0.3(eslint-plugin-import@2.28.1)(eslint-plugin-node@11.1.0)(eslint-plugin-promise@5.2.0)(eslint@8.49.0)
version
:
16.0.3(eslint-plugin-import@2.28.1)(eslint-plugin-node@11.1.0)(eslint-plugin-promise@5.2.0)(eslint@8.49.0)
eslint-plugin-import
:
eslint-plugin-import
:
specifier
:
^2.26.0
specifier
:
^2.26.0
version
:
2.28.1(@typescript-eslint/parser@6.7.
0
)(eslint@8.49.0)
version
:
2.28.1(@typescript-eslint/parser@6.7.
2
)(eslint@8.49.0)
eslint-plugin-jsdoc
:
eslint-plugin-jsdoc
:
specifier
:
^35.1.2
specifier
:
^35.1.2
version
:
35.5.1(eslint@8.49.0)
version
:
35.5.1(eslint@8.49.0)
...
@@ -3060,7 +3060,7 @@ packages:
...
@@ -3060,7 +3060,7 @@ packages:
resolution
:
{
integrity
:
sha512-bcKpo1oj54hGholplGLpqPHRbIsnbixFtc06nwuNM5/dwSXOq/AAYoIBRsBmnZJSdfeNW5rnff7NTAz3ZCqR9Q==
}
resolution
:
{
integrity
:
sha512-bcKpo1oj54hGholplGLpqPHRbIsnbixFtc06nwuNM5/dwSXOq/AAYoIBRsBmnZJSdfeNW5rnff7NTAz3ZCqR9Q==
}
dependencies
:
dependencies
:
'
@noble/curves'
:
1.0.0
'
@noble/curves'
:
1.0.0
'
@noble/hashes'
:
1.3.
1
'
@noble/hashes'
:
1.3.
2
'
@scure/base'
:
1.1.1
'
@scure/base'
:
1.1.1
/@scure/bip32@1.3.1
:
/@scure/bip32@1.3.1
:
...
@@ -3080,7 +3080,7 @@ packages:
...
@@ -3080,7 +3080,7 @@ packages:
/@scure/bip39@1.2.0
:
/@scure/bip39@1.2.0
:
resolution
:
{
integrity
:
sha512-SX/uKq52cuxm4YFXWFaVByaSHJh2w3BnokVSeUJVCv6K7WulT9u2BuNRBhuFl8vAuYnzx9bEu9WgpcNYTrYieg==
}
resolution
:
{
integrity
:
sha512-SX/uKq52cuxm4YFXWFaVByaSHJh2w3BnokVSeUJVCv6K7WulT9u2BuNRBhuFl8vAuYnzx9bEu9WgpcNYTrYieg==
}
dependencies
:
dependencies
:
'
@noble/hashes'
:
1.3.
1
'
@noble/hashes'
:
1.3.
2
'
@scure/base'
:
1.1.1
'
@scure/base'
:
1.1.1
/@scure/bip39@1.2.1
:
/@scure/bip39@1.2.1
:
...
@@ -4076,7 +4076,7 @@ packages:
...
@@ -4076,7 +4076,7 @@ packages:
-
supports-color
-
supports-color
dev
:
true
dev
:
true
/@typescript-eslint/eslint-plugin@6.7.0(@typescript-eslint/parser@6.7.
0
)(eslint@8.49.0)(typescript@5.1.6)
:
/@typescript-eslint/eslint-plugin@6.7.0(@typescript-eslint/parser@6.7.
2
)(eslint@8.49.0)(typescript@5.1.6)
:
resolution
:
{
integrity
:
sha512-gUqtknHm0TDs1LhY12K2NA3Rmlmp88jK9Tx8vGZMfHeNMLE3GH2e9TRub+y+SOjuYgtOmok+wt1AyDPZqxbNag==
}
resolution
:
{
integrity
:
sha512-gUqtknHm0TDs1LhY12K2NA3Rmlmp88jK9Tx8vGZMfHeNMLE3GH2e9TRub+y+SOjuYgtOmok+wt1AyDPZqxbNag==
}
engines
:
{
node
:
^16.0.0 || >=18.0.0
}
engines
:
{
node
:
^16.0.0 || >=18.0.0
}
peerDependencies
:
peerDependencies
:
...
@@ -4088,7 +4088,7 @@ packages:
...
@@ -4088,7 +4088,7 @@ packages:
optional
:
true
optional
:
true
dependencies
:
dependencies
:
'
@eslint-community/regexpp'
:
4.6.2
'
@eslint-community/regexpp'
:
4.6.2
'
@typescript-eslint/parser'
:
6.7.
0
(eslint@8.49.0)(typescript@5.1.6)
'
@typescript-eslint/parser'
:
6.7.
2
(eslint@8.49.0)(typescript@5.1.6)
'
@typescript-eslint/scope-manager'
:
6.7.0
'
@typescript-eslint/scope-manager'
:
6.7.0
'
@typescript-eslint/type-utils'
:
6.7.0(eslint@8.49.0)(typescript@5.1.6)
'
@typescript-eslint/type-utils'
:
6.7.0(eslint@8.49.0)(typescript@5.1.6)
'
@typescript-eslint/utils'
:
6.7.0(eslint@8.49.0)(typescript@5.1.6)
'
@typescript-eslint/utils'
:
6.7.0(eslint@8.49.0)(typescript@5.1.6)
...
@@ -4126,8 +4126,8 @@ packages:
...
@@ -4126,8 +4126,8 @@ packages:
-
supports-color
-
supports-color
dev
:
true
dev
:
true
/@typescript-eslint/parser@6.7.
0
(eslint@8.49.0)(typescript@5.1.6)
:
/@typescript-eslint/parser@6.7.
2
(eslint@8.49.0)(typescript@5.1.6)
:
resolution
:
{
integrity
:
sha512-
jZKYwqNpNm5kzPVP5z1JXAuxjtl2uG+5NpaMocFPTNC2EdYIgbXIPImObOkhbONxtFTTdoZstLZefbaK+wXZng
==
}
resolution
:
{
integrity
:
sha512-
KA3E4ox0ws+SPyxQf9iSI25R6b4Ne78ORhNHeVKrPQnoYsb9UhieoiRoJgrzgEeKGOXhcY1i8YtOeCHHTDa6Fw
==
}
engines
:
{
node
:
^16.0.0 || >=18.0.0
}
engines
:
{
node
:
^16.0.0 || >=18.0.0
}
peerDependencies
:
peerDependencies
:
eslint
:
^7.0.0 || ^8.0.0
eslint
:
^7.0.0 || ^8.0.0
...
@@ -4136,10 +4136,10 @@ packages:
...
@@ -4136,10 +4136,10 @@ packages:
typescript
:
typescript
:
optional
:
true
optional
:
true
dependencies
:
dependencies
:
'
@typescript-eslint/scope-manager'
:
6.7.
0
'
@typescript-eslint/scope-manager'
:
6.7.
2
'
@typescript-eslint/types'
:
6.7.
0
'
@typescript-eslint/types'
:
6.7.
2
'
@typescript-eslint/typescript-estree'
:
6.7.
0
(typescript@5.1.6)
'
@typescript-eslint/typescript-estree'
:
6.7.
2
(typescript@5.1.6)
'
@typescript-eslint/visitor-keys'
:
6.7.
0
'
@typescript-eslint/visitor-keys'
:
6.7.
2
debug
:
4.3.4(supports-color@8.1.1)
debug
:
4.3.4(supports-color@8.1.1)
eslint
:
8.49.0
eslint
:
8.49.0
typescript
:
5.1.6
typescript
:
5.1.6
...
@@ -4163,6 +4163,14 @@ packages:
...
@@ -4163,6 +4163,14 @@ packages:
'
@typescript-eslint/visitor-keys'
:
6.7.0
'
@typescript-eslint/visitor-keys'
:
6.7.0
dev
:
true
dev
:
true
/@typescript-eslint/scope-manager@6.7.2
:
resolution
:
{
integrity
:
sha512-bgi6plgyZjEqapr7u2mhxGR6E8WCzKNUFWNh6fkpVe9+yzRZeYtDTbsIBzKbcxI+r1qVWt6VIoMSNZ4r2A+6Yw==
}
engines
:
{
node
:
^16.0.0 || >=18.0.0
}
dependencies
:
'
@typescript-eslint/types'
:
6.7.2
'
@typescript-eslint/visitor-keys'
:
6.7.2
dev
:
true
/@typescript-eslint/type-utils@6.7.0(eslint@8.49.0)(typescript@5.1.6)
:
/@typescript-eslint/type-utils@6.7.0(eslint@8.49.0)(typescript@5.1.6)
:
resolution
:
{
integrity
:
sha512-f/QabJgDAlpSz3qduCyQT0Fw7hHpmhOzY/Rv6zO3yO+HVIdPfIWhrQoAyG+uZVtWAIS85zAyzgAFfyEr+MgBpg==
}
resolution
:
{
integrity
:
sha512-f/QabJgDAlpSz3qduCyQT0Fw7hHpmhOzY/Rv6zO3yO+HVIdPfIWhrQoAyG+uZVtWAIS85zAyzgAFfyEr+MgBpg==
}
engines
:
{
node
:
^16.0.0 || >=18.0.0
}
engines
:
{
node
:
^16.0.0 || >=18.0.0
}
...
@@ -4193,6 +4201,11 @@ packages:
...
@@ -4193,6 +4201,11 @@ packages:
engines
:
{
node
:
^16.0.0 || >=18.0.0
}
engines
:
{
node
:
^16.0.0 || >=18.0.0
}
dev
:
true
dev
:
true
/@typescript-eslint/types@6.7.2
:
resolution
:
{
integrity
:
sha512-flJYwMYgnUNDAN9/GAI3l8+wTmvTYdv64fcH8aoJK76Y+1FCZ08RtI5zDerM/FYT5DMkAc+19E4aLmd5KqdFyg==
}
engines
:
{
node
:
^16.0.0 || >=18.0.0
}
dev
:
true
/@typescript-eslint/typescript-estree@6.4.0(typescript@5.1.6)
:
/@typescript-eslint/typescript-estree@6.4.0(typescript@5.1.6)
:
resolution
:
{
integrity
:
sha512-iDPJArf/K2sxvjOR6skeUCNgHR/tCQXBsa+ee1/clRKr3olZjZ/dSkXPZjG6YkPtnW6p5D1egeEPMCW6Gn4yLA==
}
resolution
:
{
integrity
:
sha512-iDPJArf/K2sxvjOR6skeUCNgHR/tCQXBsa+ee1/clRKr3olZjZ/dSkXPZjG6YkPtnW6p5D1egeEPMCW6Gn4yLA==
}
engines
:
{
node
:
^16.0.0 || >=18.0.0
}
engines
:
{
node
:
^16.0.0 || >=18.0.0
}
...
@@ -4235,6 +4248,27 @@ packages:
...
@@ -4235,6 +4248,27 @@ packages:
-
supports-color
-
supports-color
dev
:
true
dev
:
true
/@typescript-eslint/typescript-estree@6.7.2(typescript@5.1.6)
:
resolution
:
{
integrity
:
sha512-kiJKVMLkoSciGyFU0TOY0fRxnp9qq1AzVOHNeN1+B9erKFCJ4Z8WdjAkKQPP+b1pWStGFqezMLltxO+308dJTQ==
}
engines
:
{
node
:
^16.0.0 || >=18.0.0
}
peerDependencies
:
typescript
:
'
*'
peerDependenciesMeta
:
typescript
:
optional
:
true
dependencies
:
'
@typescript-eslint/types'
:
6.7.2
'
@typescript-eslint/visitor-keys'
:
6.7.2
debug
:
4.3.4(supports-color@8.1.1)
globby
:
11.1.0
is-glob
:
4.0.3
semver
:
7.5.4
ts-api-utils
:
1.0.1(typescript@5.1.6)
typescript
:
5.1.6
transitivePeerDependencies
:
-
supports-color
dev
:
true
/@typescript-eslint/utils@6.7.0(eslint@8.49.0)(typescript@5.1.6)
:
/@typescript-eslint/utils@6.7.0(eslint@8.49.0)(typescript@5.1.6)
:
resolution
:
{
integrity
:
sha512-MfCq3cM0vh2slSikQYqK2Gq52gvOhe57vD2RM3V4gQRZYX4rDPnKLu5p6cm89+LJiGlwEXU8hkYxhqqEC/V3qA==
}
resolution
:
{
integrity
:
sha512-MfCq3cM0vh2slSikQYqK2Gq52gvOhe57vD2RM3V4gQRZYX4rDPnKLu5p6cm89+LJiGlwEXU8hkYxhqqEC/V3qA==
}
engines
:
{
node
:
^16.0.0 || >=18.0.0
}
engines
:
{
node
:
^16.0.0 || >=18.0.0
}
...
@@ -4270,6 +4304,14 @@ packages:
...
@@ -4270,6 +4304,14 @@ packages:
eslint-visitor-keys
:
3.4.3
eslint-visitor-keys
:
3.4.3
dev
:
true
dev
:
true
/@typescript-eslint/visitor-keys@6.7.2
:
resolution
:
{
integrity
:
sha512-uVw9VIMFBUTz8rIeaUT3fFe8xIUx8r4ywAdlQv1ifH+6acn/XF8Y6rwJ7XNmkNMDrTW+7+vxFFPIF40nJCVsMQ==
}
engines
:
{
node
:
^16.0.0 || >=18.0.0
}
dependencies
:
'
@typescript-eslint/types'
:
6.7.2
eslint-visitor-keys
:
3.4.3
dev
:
true
/@vitest/coverage-istanbul@0.34.1(vitest@0.34.1)
:
/@vitest/coverage-istanbul@0.34.1(vitest@0.34.1)
:
resolution
:
{
integrity
:
sha512-5GprlyY2t1g6+RrssWcN/w5RnZV3qIOM0eoaSDJw3jXbHpBpMvAfTg791zXo7PIqNYs5ORUqBWXIIU0gyAfZxA==
}
resolution
:
{
integrity
:
sha512-5GprlyY2t1g6+RrssWcN/w5RnZV3qIOM0eoaSDJw3jXbHpBpMvAfTg791zXo7PIqNYs5ORUqBWXIIU0gyAfZxA==
}
peerDependencies
:
peerDependencies
:
...
@@ -7575,7 +7617,7 @@ packages:
...
@@ -7575,7 +7617,7 @@ packages:
eslint-plugin-promise
:
^4.2.1 || ^5.0.0
eslint-plugin-promise
:
^4.2.1 || ^5.0.0
dependencies
:
dependencies
:
eslint
:
8.49.0
eslint
:
8.49.0
eslint-plugin-import
:
2.28.1(@typescript-eslint/parser@6.7.
0
)(eslint@8.49.0)
eslint-plugin-import
:
2.28.1(@typescript-eslint/parser@6.7.
2
)(eslint@8.49.0)
eslint-plugin-node
:
11.1.0(eslint@8.49.0)
eslint-plugin-node
:
11.1.0(eslint@8.49.0)
eslint-plugin-promise
:
5.2.0(eslint@8.49.0)
eslint-plugin-promise
:
5.2.0(eslint@8.49.0)
dev
:
true
dev
:
true
...
@@ -7590,7 +7632,7 @@ packages:
...
@@ -7590,7 +7632,7 @@ packages:
-
supports-color
-
supports-color
dev
:
true
dev
:
true
/eslint-module-utils@2.8.0(@typescript-eslint/parser@6.7.
0
)(eslint-import-resolver-node@0.3.9)(eslint@8.49.0)
:
/eslint-module-utils@2.8.0(@typescript-eslint/parser@6.7.
2
)(eslint-import-resolver-node@0.3.9)(eslint@8.49.0)
:
resolution
:
{
integrity
:
sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==
}
resolution
:
{
integrity
:
sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==
}
engines
:
{
node
:
'
>=4'
}
engines
:
{
node
:
'
>=4'
}
peerDependencies
:
peerDependencies
:
...
@@ -7611,7 +7653,7 @@ packages:
...
@@ -7611,7 +7653,7 @@ packages:
eslint-import-resolver-webpack
:
eslint-import-resolver-webpack
:
optional
:
true
optional
:
true
dependencies
:
dependencies
:
'
@typescript-eslint/parser'
:
6.7.
0
(eslint@8.49.0)(typescript@5.1.6)
'
@typescript-eslint/parser'
:
6.7.
2
(eslint@8.49.0)(typescript@5.1.6)
debug
:
3.2.7
debug
:
3.2.7
eslint
:
8.49.0
eslint
:
8.49.0
eslint-import-resolver-node
:
0.3.9
eslint-import-resolver-node
:
0.3.9
...
@@ -7630,7 +7672,7 @@ packages:
...
@@ -7630,7 +7672,7 @@ packages:
regexpp
:
3.2.0
regexpp
:
3.2.0
dev
:
true
dev
:
true
/eslint-plugin-import@2.28.1(@typescript-eslint/parser@6.7.
0
)(eslint@8.49.0)
:
/eslint-plugin-import@2.28.1(@typescript-eslint/parser@6.7.
2
)(eslint@8.49.0)
:
resolution
:
{
integrity
:
sha512-9I9hFlITvOV55alzoKBI+K9q74kv0iKMeY6av5+umsNwayt59fz692daGyjR+oStBQgx6nwR9rXldDev3Clw+A==
}
resolution
:
{
integrity
:
sha512-9I9hFlITvOV55alzoKBI+K9q74kv0iKMeY6av5+umsNwayt59fz692daGyjR+oStBQgx6nwR9rXldDev3Clw+A==
}
engines
:
{
node
:
'
>=4'
}
engines
:
{
node
:
'
>=4'
}
peerDependencies
:
peerDependencies
:
...
@@ -7640,7 +7682,7 @@ packages:
...
@@ -7640,7 +7682,7 @@ packages:
'
@typescript-eslint/parser'
:
'
@typescript-eslint/parser'
:
optional
:
true
optional
:
true
dependencies
:
dependencies
:
'
@typescript-eslint/parser'
:
6.7.
0
(eslint@8.49.0)(typescript@5.1.6)
'
@typescript-eslint/parser'
:
6.7.
2
(eslint@8.49.0)(typescript@5.1.6)
array-includes
:
3.1.6
array-includes
:
3.1.6
array.prototype.findlastindex
:
1.2.2
array.prototype.findlastindex
:
1.2.2
array.prototype.flat
:
1.3.1
array.prototype.flat
:
1.3.1
...
@@ -7649,7 +7691,7 @@ packages:
...
@@ -7649,7 +7691,7 @@ packages:
doctrine
:
2.1.0
doctrine
:
2.1.0
eslint
:
8.49.0
eslint
:
8.49.0
eslint-import-resolver-node
:
0.3.9
eslint-import-resolver-node
:
0.3.9
eslint-module-utils
:
2.8.0(@typescript-eslint/parser@6.7.
0
)(eslint-import-resolver-node@0.3.9)(eslint@8.49.0)
eslint-module-utils
:
2.8.0(@typescript-eslint/parser@6.7.
2
)(eslint-import-resolver-node@0.3.9)(eslint@8.49.0)
has
:
1.0.3
has
:
1.0.3
is-core-module
:
2.13.0
is-core-module
:
2.13.0
is-glob
:
4.0.3
is-glob
:
4.0.3
...
...
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