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
b7c02b49
Unverified
Commit
b7c02b49
authored
Jun 29, 2023
by
OptimismBot
Committed by
GitHub
Jun 29, 2023
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #6186 from ethereum-optimism/refcell/claimloader
feat(op-challenger): Claim Loader
parents
7af00426
4e058691
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
346 additions
and
0 deletions
+346
-0
game.go
op-challenger/fault/game.go
+15
-0
game_test.go
op-challenger/fault/game_test.go
+50
-0
loader.go
op-challenger/fault/loader.go
+101
-0
loader_test.go
op-challenger/fault/loader_test.go
+180
-0
No files found.
op-challenger/fault/game.go
View file @
b7c02b49
...
...
@@ -17,9 +17,13 @@ type Game interface {
// Put adds a claim into the game state.
Put
(
claim
Claim
)
error
// PutAll adds a list of claims into the game state.
PutAll
(
claims
[]
Claim
)
error
// Claims returns all of the claims in the game.
Claims
()
[]
Claim
// IsDuplicate returns true if the provided [Claim] already exists in the game state.
IsDuplicate
(
claim
Claim
)
bool
}
...
...
@@ -88,6 +92,17 @@ func (g *gameState) recurseTree(treeNode *Node, claim ClaimData) (*Node, error)
return
nil
,
ErrClaimNotFound
}
// PutAll adds a list of claims into the [Game] state.
// If any of the claims already exist in the game state, an error is returned.
func
(
g
*
gameState
)
PutAll
(
claims
[]
Claim
)
error
{
for
_
,
claim
:=
range
claims
{
if
err
:=
g
.
Put
(
claim
);
err
!=
nil
{
return
err
}
}
return
nil
}
// Put adds a claim into the game state.
func
(
g
*
gameState
)
Put
(
claim
Claim
)
error
{
// The game is always initialized with a root claim. Cannot add a second.
...
...
op-challenger/fault/game_test.go
View file @
b7c02b49
...
...
@@ -50,6 +50,56 @@ func TestIsDuplicate(t *testing.T) {
require
.
False
(
t
,
g
.
IsDuplicate
(
bottom
))
}
// TestGame_PutAll_RootAlreadyExists tests the [Game.PutAll] method using a [gameState]
// instance errors when the root claim already exists in state.
func
TestGame_PutAll_RootAlreadyExists
(
t
*
testing
.
T
)
{
// Setup the game state.
top
,
_
,
_
:=
createTestClaims
()
g
:=
NewGameState
(
top
)
// Try to put the root claim into the game state again.
err
:=
g
.
PutAll
([]
Claim
{
top
})
require
.
ErrorIs
(
t
,
err
,
ErrClaimExists
)
}
// TestGame_PutAll_AlreadyExists tests the [Game.PutAll] method using a [gameState]
// instance errors when the given claim already exists in state.
func
TestGame_PutAll_AlreadyExists
(
t
*
testing
.
T
)
{
// Setup the game state.
top
,
middle
,
_
:=
createTestClaims
()
g
:=
NewGameState
(
top
)
// Put the next claim into state.
err
:=
g
.
PutAll
([]
Claim
{
middle
})
require
.
NoError
(
t
,
err
)
// Try to put the root claim into the game state again.
err
=
g
.
PutAll
([]
Claim
{
middle
})
require
.
ErrorIs
(
t
,
err
,
ErrClaimExists
)
}
// TestGame_PutAll_ParentsAndChildren tests the [Game.PutAll] method using a [gameState] instance.
func
TestGame_PutAll_ParentsAndChildren
(
t
*
testing
.
T
)
{
// Setup the game state.
top
,
middle
,
bottom
:=
createTestClaims
()
g
:=
NewGameState
(
top
)
// We should not be able to get the parent of the root claim.
parent
,
err
:=
g
.
getParent
(
top
)
require
.
ErrorIs
(
t
,
err
,
ErrClaimNotFound
)
require
.
Equal
(
t
,
parent
,
Claim
{})
// Put the rest of the claims in the state.
err
=
g
.
PutAll
([]
Claim
{
middle
,
bottom
})
require
.
NoError
(
t
,
err
)
parent
,
err
=
g
.
getParent
(
middle
)
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
parent
,
top
)
parent
,
err
=
g
.
getParent
(
bottom
)
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
parent
,
middle
)
}
// TestGame_Put_RootAlreadyExists tests the [Game.Put] method using a [gameState]
// instance errors when the root claim already exists in state.
func
TestGame_Put_RootAlreadyExists
(
t
*
testing
.
T
)
{
...
...
op-challenger/fault/loader.go
0 → 100644
View file @
b7c02b49
package
fault
import
(
"context"
"math/big"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/log"
)
// ClaimFetcher is a minimal interface around [bindings.FaultDisputeGameCaller].
// This needs to be updated if the [bindings.FaultDisputeGameCaller] interface changes.
type
ClaimFetcher
interface
{
ClaimData
(
opts
*
bind
.
CallOpts
,
arg0
*
big
.
Int
)
(
struct
{
ParentIndex
uint32
Countered
bool
Claim
[
32
]
byte
Position
*
big
.
Int
Clock
*
big
.
Int
},
error
)
ClaimDataLen
(
opts
*
bind
.
CallOpts
)
(
*
big
.
Int
,
error
)
}
// Loader is a minimal interface for loading onchain [Claim] data.
type
Loader
interface
{
FetchClaims
(
ctx
context
.
Context
)
([]
Claim
,
error
)
}
// loader pulls in fault dispute game claim data periodically and over subscriptions.
type
loader
struct
{
log
log
.
Logger
state
Game
claimFetcher
ClaimFetcher
}
// NewLoader creates a new [loader].
func
NewLoader
(
log
log
.
Logger
,
state
Game
,
claimFetcher
ClaimFetcher
)
*
loader
{
return
&
loader
{
log
:
log
,
state
:
state
,
claimFetcher
:
claimFetcher
,
}
}
// fetchClaim fetches a single [Claim] with a hydrated parent.
func
(
l
*
loader
)
fetchClaim
(
ctx
context
.
Context
,
arrIndex
uint64
)
(
Claim
,
error
)
{
callOpts
:=
bind
.
CallOpts
{
Context
:
ctx
,
}
fetchedClaim
,
err
:=
l
.
claimFetcher
.
ClaimData
(
&
callOpts
,
new
(
big
.
Int
)
.
SetUint64
(
arrIndex
))
if
err
!=
nil
{
return
Claim
{},
err
}
claim
:=
Claim
{
ClaimData
:
ClaimData
{
Value
:
fetchedClaim
.
Claim
,
Position
:
NewPositionFromGIndex
(
fetchedClaim
.
Position
.
Uint64
()),
},
}
if
!
claim
.
IsRootPosition
()
{
parentIndex
:=
uint64
(
fetchedClaim
.
ParentIndex
)
parentClaim
,
err
:=
l
.
claimFetcher
.
ClaimData
(
&
callOpts
,
new
(
big
.
Int
)
.
SetUint64
(
parentIndex
))
if
err
!=
nil
{
return
Claim
{},
err
}
claim
.
Parent
=
ClaimData
{
Value
:
parentClaim
.
Claim
,
Position
:
NewPositionFromGIndex
(
parentClaim
.
Position
.
Uint64
()),
}
}
return
claim
,
nil
}
// FetchClaims fetches all claims from the fault dispute game.
func
(
l
*
loader
)
FetchClaims
(
ctx
context
.
Context
)
([]
Claim
,
error
)
{
// Get the current claim count.
claimCount
,
err
:=
l
.
claimFetcher
.
ClaimDataLen
(
&
bind
.
CallOpts
{
Context
:
ctx
,
})
if
err
!=
nil
{
return
nil
,
err
}
// Fetch each claim and build a list.
claimList
:=
make
([]
Claim
,
claimCount
.
Uint64
())
for
i
:=
uint64
(
0
);
i
<
claimCount
.
Uint64
();
i
++
{
claim
,
err
:=
l
.
fetchClaim
(
ctx
,
i
)
if
err
!=
nil
{
return
nil
,
err
}
claimList
[
i
]
=
claim
}
return
claimList
,
nil
}
op-challenger/fault/loader_test.go
0 → 100644
View file @
b7c02b49
package
fault
import
(
"context"
"fmt"
"math/big"
"testing"
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require"
)
var
(
mockClaimDataError
=
fmt
.
Errorf
(
"claim data errored"
)
mockClaimLenError
=
fmt
.
Errorf
(
"claim len errored"
)
mockPutError
=
fmt
.
Errorf
(
"put errored"
)
)
type
mockGameState
struct
{
putCalled
int
putErrors
bool
}
func
(
m
*
mockGameState
)
Put
(
claim
Claim
)
error
{
m
.
putCalled
++
if
m
.
putErrors
{
return
mockPutError
}
return
nil
}
func
(
m
*
mockGameState
)
PutAll
(
claims
[]
Claim
)
error
{
m
.
putCalled
+=
len
(
claims
)
if
m
.
putErrors
{
return
mockPutError
}
return
nil
}
func
(
m
*
mockGameState
)
Claims
()
[]
Claim
{
return
[]
Claim
{}
}
func
(
m
*
mockGameState
)
IsDuplicate
(
claim
Claim
)
bool
{
return
false
}
type
mockClaimFetcher
struct
{
claimDataError
bool
claimLenError
bool
currentIndex
uint64
returnClaims
[]
struct
{
ParentIndex
uint32
Countered
bool
Claim
[
32
]
byte
Position
*
big
.
Int
Clock
*
big
.
Int
}
}
func
newMockClaimFetcher
()
*
mockClaimFetcher
{
return
&
mockClaimFetcher
{
returnClaims
:
[]
struct
{
ParentIndex
uint32
Countered
bool
Claim
[
32
]
byte
Position
*
big
.
Int
Clock
*
big
.
Int
}{
{
Claim
:
[
32
]
byte
{
0x00
},
Position
:
big
.
NewInt
(
0
),
},
{
Claim
:
[
32
]
byte
{
0x01
},
Position
:
big
.
NewInt
(
0
),
},
{
Claim
:
[
32
]
byte
{
0x02
},
Position
:
big
.
NewInt
(
0
),
},
},
}
}
func
(
m
*
mockClaimFetcher
)
ClaimData
(
opts
*
bind
.
CallOpts
,
arg0
*
big
.
Int
)
(
struct
{
ParentIndex
uint32
Countered
bool
Claim
[
32
]
byte
Position
*
big
.
Int
Clock
*
big
.
Int
},
error
)
{
if
m
.
claimDataError
{
return
struct
{
ParentIndex
uint32
Countered
bool
Claim
[
32
]
byte
Position
*
big
.
Int
Clock
*
big
.
Int
}{},
mockClaimDataError
}
returnClaim
:=
m
.
returnClaims
[
m
.
currentIndex
]
m
.
currentIndex
++
return
returnClaim
,
nil
}
func
(
m
*
mockClaimFetcher
)
ClaimDataLen
(
opts
*
bind
.
CallOpts
)
(
*
big
.
Int
,
error
)
{
if
m
.
claimLenError
{
return
big
.
NewInt
(
0
),
mockClaimLenError
}
return
big
.
NewInt
(
int64
(
len
(
m
.
returnClaims
))),
nil
}
// TestLoader_FetchClaims_Succeeds tests [loader.FetchClaims].
func
TestLoader_FetchClaims_Succeeds
(
t
*
testing
.
T
)
{
log
:=
testlog
.
Logger
(
t
,
log
.
LvlError
)
mockClaimFetcher
:=
newMockClaimFetcher
()
expectedClaims
:=
mockClaimFetcher
.
returnClaims
loader
:=
NewLoader
(
log
,
&
mockGameState
{},
mockClaimFetcher
)
claims
,
err
:=
loader
.
FetchClaims
(
context
.
Background
())
require
.
NoError
(
t
,
err
)
require
.
ElementsMatch
(
t
,
[]
Claim
{
{
ClaimData
:
ClaimData
{
Value
:
expectedClaims
[
0
]
.
Claim
,
Position
:
NewPositionFromGIndex
(
expectedClaims
[
0
]
.
Position
.
Uint64
()),
},
Parent
:
ClaimData
{
Value
:
expectedClaims
[
0
]
.
Claim
,
Position
:
NewPositionFromGIndex
(
expectedClaims
[
0
]
.
Position
.
Uint64
()),
},
},
{
ClaimData
:
ClaimData
{
Value
:
expectedClaims
[
1
]
.
Claim
,
Position
:
NewPositionFromGIndex
(
expectedClaims
[
1
]
.
Position
.
Uint64
()),
},
Parent
:
ClaimData
{
Value
:
expectedClaims
[
0
]
.
Claim
,
Position
:
NewPositionFromGIndex
(
expectedClaims
[
1
]
.
Position
.
Uint64
()),
},
},
{
ClaimData
:
ClaimData
{
Value
:
expectedClaims
[
2
]
.
Claim
,
Position
:
NewPositionFromGIndex
(
expectedClaims
[
2
]
.
Position
.
Uint64
()),
},
Parent
:
ClaimData
{
Value
:
expectedClaims
[
0
]
.
Claim
,
Position
:
NewPositionFromGIndex
(
expectedClaims
[
2
]
.
Position
.
Uint64
()),
},
},
},
claims
)
}
// TestLoader_FetchClaims_ClaimDataErrors tests [loader.FetchClaims]
// when the claim fetcher [ClaimData] function call errors.
func
TestLoader_FetchClaims_ClaimDataErrors
(
t
*
testing
.
T
)
{
log
:=
testlog
.
Logger
(
t
,
log
.
LvlError
)
mockClaimFetcher
:=
newMockClaimFetcher
()
mockClaimFetcher
.
claimDataError
=
true
loader
:=
NewLoader
(
log
,
&
mockGameState
{},
mockClaimFetcher
)
claims
,
err
:=
loader
.
FetchClaims
(
context
.
Background
())
require
.
ErrorIs
(
t
,
err
,
mockClaimDataError
)
require
.
Empty
(
t
,
claims
)
}
// TestLoader_FetchClaims_ClaimLenErrors tests [loader.FetchClaims]
// when the claim fetcher [ClaimDataLen] function call errors.
func
TestLoader_FetchClaims_ClaimLenErrors
(
t
*
testing
.
T
)
{
log
:=
testlog
.
Logger
(
t
,
log
.
LvlError
)
mockClaimFetcher
:=
newMockClaimFetcher
()
mockClaimFetcher
.
claimLenError
=
true
loader
:=
NewLoader
(
log
,
&
mockGameState
{},
mockClaimFetcher
)
claims
,
err
:=
loader
.
FetchClaims
(
context
.
Background
())
require
.
ErrorIs
(
t
,
err
,
mockClaimLenError
)
require
.
Empty
(
t
,
claims
)
}
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