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
abb34d44
Unverified
Commit
abb34d44
authored
Feb 22, 2024
by
Adrian Sutton
Committed by
GitHub
Feb 21, 2024
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
op-challenger: Require local node to be sufficiently up to date before playing games (#9614)
parent
815684c0
Changes
6
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
204 additions
and
17 deletions
+204
-17
player.go
op-challenger/game/fault/player.go
+32
-1
player_test.go
op-challenger/game/fault/player_test.go
+35
-9
register.go
op-challenger/game/fault/register.go
+17
-6
sync.go
op-challenger/game/fault/sync.go
+36
-0
sync_test.go
op-challenger/game/fault/sync_test.go
+83
-0
service.go
op-challenger/game/service.go
+1
-1
No files found.
op-challenger/game/fault/player.go
View file @
abb34d44
...
...
@@ -10,7 +10,9 @@ import (
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
gameTypes
"github.com/ethereum-optimism/optimism/op-challenger/game/types"
"github.com/ethereum-optimism/optimism/op-challenger/metrics"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum/go-ethereum/common"
gethTypes
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
)
...
...
@@ -21,12 +23,22 @@ type GameInfo interface {
GetClaimCount
(
context
.
Context
)
(
uint64
,
error
)
}
type
SyncValidator
interface
{
ValidateNodeSynced
(
ctx
context
.
Context
,
gameL1Head
eth
.
BlockID
)
error
}
type
L1HeaderSource
interface
{
HeaderByHash
(
context
.
Context
,
common
.
Hash
)
(
*
gethTypes
.
Header
,
error
)
}
type
GamePlayer
struct
{
act
actor
loader
GameInfo
logger
log
.
Logger
syncValidator
SyncValidator
prestateValidators
[]
Validator
status
gameTypes
.
GameStatus
gameL1Head
eth
.
BlockID
}
type
GameContract
interface
{
...
...
@@ -37,6 +49,7 @@ type GameContract interface {
GetStatus
(
ctx
context
.
Context
)
(
gameTypes
.
GameStatus
,
error
)
GetMaxGameDepth
(
ctx
context
.
Context
)
(
types
.
Depth
,
error
)
GetOracle
(
ctx
context
.
Context
)
(
*
contracts
.
PreimageOracleContract
,
error
)
GetL1Head
(
ctx
context
.
Context
)
(
common
.
Hash
,
error
)
}
type
resourceCreator
func
(
ctx
context
.
Context
,
logger
log
.
Logger
,
gameDepth
types
.
Depth
,
dir
string
)
(
types
.
TraceAccessor
,
error
)
...
...
@@ -50,8 +63,10 @@ func NewGamePlayer(
addr
common
.
Address
,
txSender
gameTypes
.
TxSender
,
loader
GameContract
,
syncValidator
SyncValidator
,
validators
[]
Validator
,
creator
resourceCreator
,
l1HeaderSource
L1HeaderSource
,
)
(
*
GamePlayer
,
error
)
{
logger
=
logger
.
New
(
"game"
,
addr
)
...
...
@@ -89,6 +104,16 @@ func NewGamePlayer(
return
nil
,
fmt
.
Errorf
(
"failed to load oracle: %w"
,
err
)
}
l1HeadHash
,
err
:=
loader
.
GetL1Head
(
ctx
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"failed to load game L1 head: %w"
,
err
)
}
l1Header
,
err
:=
l1HeaderSource
.
HeaderByHash
(
ctx
,
l1HeadHash
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"failed to load L1 header %v: %w"
,
l1HeadHash
,
err
)
}
l1Head
:=
eth
.
HeaderBlockID
(
l1Header
)
minLargePreimageSize
,
err
:=
oracle
.
MinLargePreimageSize
(
ctx
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"failed to load min large preimage size: %w"
,
err
)
...
...
@@ -107,6 +132,8 @@ func NewGamePlayer(
loader
:
loader
,
logger
:
logger
,
status
:
status
,
gameL1Head
:
l1Head
,
syncValidator
:
syncValidator
,
prestateValidators
:
validators
,
},
nil
}
...
...
@@ -130,13 +157,17 @@ func (g *GamePlayer) ProgressGame(ctx context.Context) gameTypes.GameStatus {
g
.
logger
.
Trace
(
"Skipping completed game"
)
return
g
.
status
}
if
err
:=
g
.
syncValidator
.
ValidateNodeSynced
(
ctx
,
g
.
gameL1Head
);
err
!=
nil
{
g
.
logger
.
Error
(
"Local node not sufficiently up to date"
,
"err"
,
err
)
return
g
.
status
}
g
.
logger
.
Trace
(
"Checking if actions are required"
)
if
err
:=
g
.
act
(
ctx
);
err
!=
nil
{
g
.
logger
.
Error
(
"Error when acting on game"
,
"err"
,
err
)
}
status
,
err
:=
g
.
loader
.
GetStatus
(
ctx
)
if
err
!=
nil
{
g
.
logger
.
Warn
(
"Unable to retrieve game status"
,
"err"
,
err
)
g
.
logger
.
Error
(
"Unable to retrieve game status"
,
"err"
,
err
)
return
gameTypes
.
GameStatusInProgress
}
g
.
logGameStatus
(
ctx
,
status
)
...
...
op-challenger/game/fault/player_test.go
View file @
abb34d44
...
...
@@ -7,6 +7,7 @@ import (
"testing"
"github.com/ethereum-optimism/optimism/op-challenger/game/types"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-service/testlog"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
...
...
@@ -16,7 +17,7 @@ import (
var
mockValidatorError
=
fmt
.
Errorf
(
"mock validator error"
)
func
TestProgressGame_LogErrorFromAct
(
t
*
testing
.
T
)
{
handler
,
game
,
actor
:=
setupProgressGameTest
(
t
)
handler
,
game
,
actor
,
_
:=
setupProgressGameTest
(
t
)
actor
.
actErr
=
errors
.
New
(
"boom"
)
status
:=
game
.
ProgressGame
(
context
.
Background
())
require
.
Equal
(
t
,
types
.
GameStatusInProgress
,
status
)
...
...
@@ -60,7 +61,7 @@ func TestProgressGame_LogGameStatus(t *testing.T) {
for
_
,
test
:=
range
tests
{
test
:=
test
t
.
Run
(
test
.
name
,
func
(
t
*
testing
.
T
)
{
handler
,
game
,
gameState
:=
setupProgressGameTest
(
t
)
handler
,
game
,
gameState
,
_
:=
setupProgressGameTest
(
t
)
gameState
.
status
=
test
.
status
status
:=
game
.
ProgressGame
(
context
.
Background
())
...
...
@@ -78,7 +79,7 @@ func TestProgressGame_LogGameStatus(t *testing.T) {
func
TestDoNotActOnCompleteGame
(
t
*
testing
.
T
)
{
for
_
,
status
:=
range
[]
types
.
GameStatus
{
types
.
GameStatusChallengerWon
,
types
.
GameStatusDefenderWon
}
{
t
.
Run
(
status
.
String
(),
func
(
t
*
testing
.
T
)
{
_
,
game
,
gameState
:=
setupProgressGameTest
(
t
)
_
,
game
,
gameState
,
_
:=
setupProgressGameTest
(
t
)
gameState
.
status
=
status
fetched
:=
game
.
ProgressGame
(
context
.
Background
())
...
...
@@ -93,6 +94,17 @@ func TestDoNotActOnCompleteGame(t *testing.T) {
}
}
func
TestValidateLocalNodeSync
(
t
*
testing
.
T
)
{
_
,
game
,
gameState
,
syncValidator
:=
setupProgressGameTest
(
t
)
game
.
ProgressGame
(
context
.
Background
())
require
.
Equal
(
t
,
1
,
gameState
.
callCount
,
"acts when in sync"
)
syncValidator
.
result
=
errors
.
New
(
"boom"
)
game
.
ProgressGame
(
context
.
Background
())
require
.
Equal
(
t
,
1
,
gameState
.
callCount
,
"does not act when not in sync"
)
}
func
TestValidatePrestate
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
name
string
...
...
@@ -142,22 +154,36 @@ type mockValidator struct {
err
bool
}
func
(
m
*
mockValidator
)
Validate
(
ctx
context
.
Context
)
error
{
func
(
m
*
mockValidator
)
Validate
(
_
context
.
Context
)
error
{
if
m
.
err
{
return
mockValidatorError
}
return
nil
}
func
setupProgressGameTest
(
t
*
testing
.
T
)
(
*
testlog
.
CapturingHandler
,
*
GamePlayer
,
*
stubGameState
)
{
func
setupProgressGameTest
(
t
*
testing
.
T
)
(
*
testlog
.
CapturingHandler
,
*
GamePlayer
,
*
stubGameState
,
*
stubSyncValidator
)
{
logger
,
logs
:=
testlog
.
CaptureLogger
(
t
,
log
.
LevelDebug
)
gameState
:=
&
stubGameState
{
claimCount
:
1
}
syncValidator
:=
&
stubSyncValidator
{}
game
:=
&
GamePlayer
{
act
:
gameState
.
Act
,
loader
:
gameState
,
logger
:
logger
,
syncValidator
:
syncValidator
,
gameL1Head
:
eth
.
BlockID
{
Hash
:
common
.
Hash
{
0x1a
},
Number
:
32
,
},
}
return
logs
,
game
,
gameState
return
logs
,
game
,
gameState
,
syncValidator
}
type
stubSyncValidator
struct
{
result
error
}
func
(
s
*
stubSyncValidator
)
ValidateNodeSynced
(
_
context
.
Context
,
_
eth
.
BlockID
)
error
{
return
s
.
result
}
type
stubGameState
struct
{
...
...
op-challenger/game/fault/register.go
View file @
abb34d44
...
...
@@ -28,6 +28,11 @@ type Registry interface {
RegisterBondContract
(
gameType
uint32
,
creator
claims
.
BondContractCreator
)
}
type
RollupClient
interface
{
source
.
OutputRollupClient
SyncStatusProvider
}
func
RegisterGameTypes
(
registry
Registry
,
ctx
context
.
Context
,
...
...
@@ -35,10 +40,11 @@ func RegisterGameTypes(
logger
log
.
Logger
,
m
metrics
.
Metricer
,
cfg
*
config
.
Config
,
rollupClient
source
.
Output
RollupClient
,
rollupClient
RollupClient
,
txSender
types
.
TxSender
,
gameFactory
*
contracts
.
DisputeGameFactoryContract
,
caller
*
batching
.
MultiCaller
,
l1HeaderSource
L1HeaderSource
,
)
(
CloseFunc
,
error
)
{
var
closer
CloseFunc
var
l2Client
*
ethclient
.
Client
...
...
@@ -51,19 +57,20 @@ func RegisterGameTypes(
closer
=
l2Client
.
Close
}
outputSourceCreator
:=
source
.
NewOutputSourceCreator
(
logger
,
rollupClient
)
syncValidator
:=
newSyncStatusValidator
(
rollupClient
)
if
cfg
.
TraceTypeEnabled
(
config
.
TraceTypeCannon
)
{
if
err
:=
registerCannon
(
faultTypes
.
CannonGameType
,
registry
,
ctx
,
cl
,
logger
,
m
,
cfg
,
outputSourceCreator
,
txSender
,
gameFactory
,
caller
,
l2Client
);
err
!=
nil
{
if
err
:=
registerCannon
(
faultTypes
.
CannonGameType
,
registry
,
ctx
,
cl
,
logger
,
m
,
cfg
,
syncValidator
,
outputSourceCreator
,
txSender
,
gameFactory
,
caller
,
l2Client
,
l1HeaderSource
);
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"failed to register cannon game type: %w"
,
err
)
}
}
if
cfg
.
TraceTypeEnabled
(
config
.
TraceTypePermissioned
)
{
if
err
:=
registerCannon
(
faultTypes
.
PermissionedGameType
,
registry
,
ctx
,
cl
,
logger
,
m
,
cfg
,
outputSourceCreator
,
txSender
,
gameFactory
,
caller
,
l2Client
);
err
!=
nil
{
if
err
:=
registerCannon
(
faultTypes
.
PermissionedGameType
,
registry
,
ctx
,
cl
,
logger
,
m
,
cfg
,
syncValidator
,
outputSourceCreator
,
txSender
,
gameFactory
,
caller
,
l2Client
,
l1HeaderSource
);
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"failed to register permissioned cannon game type: %w"
,
err
)
}
}
if
cfg
.
TraceTypeEnabled
(
config
.
TraceTypeAlphabet
)
{
if
err
:=
registerAlphabet
(
registry
,
ctx
,
cl
,
logger
,
m
,
rollupClient
,
txSender
,
gameFactory
,
caller
);
err
!=
nil
{
if
err
:=
registerAlphabet
(
registry
,
ctx
,
cl
,
logger
,
m
,
syncValidator
,
rollupClient
,
txSender
,
gameFactory
,
caller
,
l1HeaderSource
);
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"failed to register alphabet game type: %w"
,
err
)
}
}
...
...
@@ -76,10 +83,12 @@ func registerAlphabet(
cl
faultTypes
.
ClockReader
,
logger
log
.
Logger
,
m
metrics
.
Metricer
,
syncValidator
SyncValidator
,
rollupClient
source
.
OutputRollupClient
,
txSender
types
.
TxSender
,
gameFactory
*
contracts
.
DisputeGameFactoryContract
,
caller
*
batching
.
MultiCaller
,
l1HeaderSource
L1HeaderSource
,
)
error
{
playerCreator
:=
func
(
game
types
.
GameMetadata
,
dir
string
)
(
scheduler
.
GamePlayer
,
error
)
{
contract
,
err
:=
contracts
.
NewFaultDisputeGameContract
(
game
.
Proxy
,
caller
)
...
...
@@ -105,7 +114,7 @@ func registerAlphabet(
}
prestateValidator
:=
NewPrestateValidator
(
"alphabet"
,
contract
.
GetAbsolutePrestateHash
,
alphabet
.
PrestateProvider
)
genesisValidator
:=
NewPrestateValidator
(
"output root"
,
contract
.
GetGenesisOutputRoot
,
prestateProvider
)
return
NewGamePlayer
(
ctx
,
cl
,
logger
,
m
,
dir
,
game
.
Proxy
,
txSender
,
contract
,
[]
Validator
{
prestateValidator
,
genesisValidator
},
creator
)
return
NewGamePlayer
(
ctx
,
cl
,
logger
,
m
,
dir
,
game
.
Proxy
,
txSender
,
contract
,
syncValidator
,
[]
Validator
{
prestateValidator
,
genesisValidator
},
creator
,
l1HeaderSource
)
}
oracle
,
err
:=
createOracle
(
ctx
,
gameFactory
,
caller
,
faultTypes
.
AlphabetGameType
)
if
err
!=
nil
{
...
...
@@ -144,11 +153,13 @@ func registerCannon(
logger
log
.
Logger
,
m
metrics
.
Metricer
,
cfg
*
config
.
Config
,
syncValidator
SyncValidator
,
outputSourceCreator
*
source
.
OutputSourceCreator
,
txSender
types
.
TxSender
,
gameFactory
*
contracts
.
DisputeGameFactoryContract
,
caller
*
batching
.
MultiCaller
,
l2Client
cannon
.
L2HeaderSource
,
l1HeaderSource
L1HeaderSource
,
)
error
{
playerCreator
:=
func
(
game
types
.
GameMetadata
,
dir
string
)
(
scheduler
.
GamePlayer
,
error
)
{
contract
,
err
:=
contracts
.
NewFaultDisputeGameContract
(
game
.
Proxy
,
caller
)
...
...
@@ -181,7 +192,7 @@ func registerCannon(
}
prestateValidator
:=
NewPrestateValidator
(
"cannon"
,
contract
.
GetAbsolutePrestateHash
,
cannon
.
NewPrestateProvider
(
cfg
.
CannonAbsolutePreState
))
genesisValidator
:=
NewPrestateValidator
(
"output root"
,
contract
.
GetGenesisOutputRoot
,
prestateProvider
)
return
NewGamePlayer
(
ctx
,
cl
,
logger
,
m
,
dir
,
game
.
Proxy
,
txSender
,
contract
,
[]
Validator
{
prestateValidator
,
genesisValidator
},
creator
)
return
NewGamePlayer
(
ctx
,
cl
,
logger
,
m
,
dir
,
game
.
Proxy
,
txSender
,
contract
,
syncValidator
,
[]
Validator
{
prestateValidator
,
genesisValidator
},
creator
,
l1HeaderSource
)
}
oracle
,
err
:=
createOracle
(
ctx
,
gameFactory
,
caller
,
gameType
)
if
err
!=
nil
{
...
...
op-challenger/game/fault/sync.go
0 → 100644
View file @
abb34d44
package
fault
import
(
"context"
"errors"
"fmt"
"github.com/ethereum-optimism/optimism/op-service/eth"
)
var
ErrNotInSync
=
errors
.
New
(
"local node too far behind"
)
type
SyncStatusProvider
interface
{
SyncStatus
(
context
.
Context
)
(
*
eth
.
SyncStatus
,
error
)
}
type
syncStatusValidator
struct
{
statusProvider
SyncStatusProvider
}
func
newSyncStatusValidator
(
statusProvider
SyncStatusProvider
)
*
syncStatusValidator
{
return
&
syncStatusValidator
{
statusProvider
:
statusProvider
,
}
}
func
(
s
*
syncStatusValidator
)
ValidateNodeSynced
(
ctx
context
.
Context
,
gameL1Head
eth
.
BlockID
)
error
{
syncStatus
,
err
:=
s
.
statusProvider
.
SyncStatus
(
ctx
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"failed to retrieve local node sync status: %w"
,
err
)
}
if
syncStatus
.
CurrentL1
.
Number
<=
gameL1Head
.
Number
{
return
fmt
.
Errorf
(
"%w require L1 block above %v but at %v"
,
ErrNotInSync
,
gameL1Head
.
Number
,
syncStatus
.
CurrentL1
.
Number
)
}
return
nil
}
op-challenger/game/fault/sync_test.go
0 → 100644
View file @
abb34d44
package
fault
import
(
"context"
"errors"
"testing"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/stretchr/testify/require"
)
func
TestSyncStatusProvider
(
t
*
testing
.
T
)
{
requestErr
:=
errors
.
New
(
"boom"
)
tests
:=
[]
struct
{
name
string
gameL1Head
eth
.
BlockID
syncStatus
*
eth
.
SyncStatus
statusReqErr
error
expected
error
}{
{
name
:
"ErrorFetchingStatus"
,
gameL1Head
:
eth
.
BlockID
{
Number
:
100
},
syncStatus
:
nil
,
statusReqErr
:
requestErr
,
expected
:
requestErr
,
},
{
name
:
"CurrentL1BelowGameL1Head"
,
gameL1Head
:
eth
.
BlockID
{
Number
:
100
},
syncStatus
:
&
eth
.
SyncStatus
{
CurrentL1
:
eth
.
L1BlockRef
{
Number
:
99
,
},
},
statusReqErr
:
nil
,
expected
:
ErrNotInSync
,
},
{
name
:
"CurrentL1EqualToGameL1Head"
,
gameL1Head
:
eth
.
BlockID
{
Number
:
100
},
syncStatus
:
&
eth
.
SyncStatus
{
CurrentL1
:
eth
.
L1BlockRef
{
Number
:
100
,
},
},
statusReqErr
:
nil
,
expected
:
ErrNotInSync
,
},
{
name
:
"CurrentL1AboveGameL1Head"
,
gameL1Head
:
eth
.
BlockID
{
Number
:
100
},
syncStatus
:
&
eth
.
SyncStatus
{
CurrentL1
:
eth
.
L1BlockRef
{
Number
:
101
,
},
},
statusReqErr
:
nil
,
expected
:
nil
,
},
}
for
_
,
test
:=
range
tests
{
test
:=
test
t
.
Run
(
test
.
name
,
func
(
t
*
testing
.
T
)
{
provider
:=
&
stubSyncStatusProvider
{
status
:
test
.
syncStatus
,
err
:
test
.
statusReqErr
,
}
validator
:=
newSyncStatusValidator
(
provider
)
err
:=
validator
.
ValidateNodeSynced
(
context
.
Background
(),
test
.
gameL1Head
)
require
.
ErrorIs
(
t
,
err
,
test
.
expected
)
})
}
}
type
stubSyncStatusProvider
struct
{
status
*
eth
.
SyncStatus
err
error
}
func
(
s
*
stubSyncStatusProvider
)
SyncStatus
(
_
context
.
Context
)
(
*
eth
.
SyncStatus
,
error
)
{
return
s
.
status
,
s
.
err
}
op-challenger/game/service.go
View file @
abb34d44
...
...
@@ -217,7 +217,7 @@ func (s *Service) initRollupClient(ctx context.Context, cfg *config.Config) erro
func
(
s
*
Service
)
registerGameTypes
(
ctx
context
.
Context
,
cfg
*
config
.
Config
)
error
{
gameTypeRegistry
:=
registry
.
NewGameTypeRegistry
()
caller
:=
batching
.
NewMultiCaller
(
s
.
l1Client
.
Client
(),
batching
.
DefaultBatchSize
)
closer
,
err
:=
fault
.
RegisterGameTypes
(
gameTypeRegistry
,
ctx
,
s
.
cl
,
s
.
logger
,
s
.
metrics
,
cfg
,
s
.
rollupClient
,
s
.
txSender
,
s
.
factoryContract
,
caller
)
closer
,
err
:=
fault
.
RegisterGameTypes
(
gameTypeRegistry
,
ctx
,
s
.
cl
,
s
.
logger
,
s
.
metrics
,
cfg
,
s
.
rollupClient
,
s
.
txSender
,
s
.
factoryContract
,
caller
,
s
.
l1Client
)
if
err
!=
nil
{
return
err
}
...
...
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