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
23d0635b
Unverified
Commit
23d0635b
authored
Sep 05, 2023
by
OptimismBot
Committed by
GitHub
Sep 05, 2023
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #7080 from ethereum-optimism/refcell/game-gauge
feat(op-challenger): Game GaugeVec
parents
1d861eb5
07cf16db
Changes
24
Show whitespace changes
Inline
Side-by-side
Showing
24 changed files
with
240 additions
and
166 deletions
+240
-166
agent.go
op-challenger/game/fault/agent.go
+6
-5
agent_test.go
op-challenger/game/fault/agent_test.go
+14
-13
loader.go
op-challenger/game/fault/loader.go
+3
-2
loader_test.go
op-challenger/game/fault/loader_test.go
+5
-4
player.go
op-challenger/game/fault/player.go
+20
-20
player_test.go
op-challenger/game/fault/player_test.go
+18
-17
responder.go
op-challenger/game/fault/responder/responder.go
+6
-5
responder_test.go
op-challenger/game/fault/responder/responder_test.go
+4
-3
types.go
op-challenger/game/fault/types/types.go
+0
-31
types_test.go
op-challenger/game/fault/types/types_test.go
+0
-23
monitor.go
op-challenger/game/monitor.go
+0
-4
monitor_test.go
op-challenger/game/monitor_test.go
+1
-2
coordinator.go
op-challenger/game/scheduler/coordinator.go
+26
-5
coordinator_test.go
op-challenger/game/scheduler/coordinator_test.go
+17
-9
scheduler.go
op-challenger/game/scheduler/scheduler.go
+6
-2
scheduler_test.go
op-challenger/game/scheduler/scheduler_test.go
+3
-2
types.go
op-challenger/game/scheduler/types.go
+6
-4
worker.go
op-challenger/game/scheduler/worker.go
+1
-1
worker_test.go
op-challenger/game/scheduler/worker_test.go
+9
-7
service.go
op-challenger/game/service.go
+2
-1
types.go
op-challenger/game/types/types.go
+35
-0
types_test.go
op-challenger/game/types/types_test.go
+30
-0
metrics.go
op-challenger/metrics/metrics.go
+20
-2
noop.go
op-challenger/metrics/noop.go
+8
-4
No files found.
op-challenger/game/fault/agent.go
View file @
23d0635b
...
...
@@ -7,6 +7,7 @@ import (
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/solver"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
gameTypes
"github.com/ethereum-optimism/optimism/op-challenger/game/types"
"github.com/ethereum-optimism/optimism/op-challenger/metrics"
"github.com/ethereum/go-ethereum/log"
)
...
...
@@ -14,7 +15,7 @@ import (
// Responder takes a response action & executes.
// For full op-challenger this means executing the transaction on chain.
type
Responder
interface
{
CallResolve
(
ctx
context
.
Context
)
(
t
ypes
.
GameStatus
,
error
)
CallResolve
(
ctx
context
.
Context
)
(
gameT
ypes
.
GameStatus
,
error
)
Resolve
(
ctx
context
.
Context
)
error
Respond
(
ctx
context
.
Context
,
response
types
.
Claim
)
error
Step
(
ctx
context
.
Context
,
stepData
types
.
StepCallData
)
error
...
...
@@ -74,10 +75,10 @@ func (a *Agent) Act(ctx context.Context) error {
// shouldResolve returns true if the agent should resolve the game.
// This method will return false if the game is still in progress.
func
(
a
*
Agent
)
shouldResolve
(
status
t
ypes
.
GameStatus
)
bool
{
expected
:=
t
ypes
.
GameStatusDefenderWon
func
(
a
*
Agent
)
shouldResolve
(
status
gameT
ypes
.
GameStatus
)
bool
{
expected
:=
gameT
ypes
.
GameStatusDefenderWon
if
a
.
agreeWithProposedOutput
{
expected
=
t
ypes
.
GameStatusChallengerWon
expected
=
gameT
ypes
.
GameStatusChallengerWon
}
if
expected
!=
status
{
a
.
log
.
Warn
(
"Game will be lost"
,
"expected"
,
expected
,
"actual"
,
status
)
...
...
@@ -89,7 +90,7 @@ func (a *Agent) shouldResolve(status types.GameStatus) bool {
// Returns true if the game is resolvable (regardless of whether it was actually resolved)
func
(
a
*
Agent
)
tryResolve
(
ctx
context
.
Context
)
bool
{
status
,
err
:=
a
.
responder
.
CallResolve
(
ctx
)
if
err
!=
nil
||
status
==
t
ypes
.
GameStatusInProgress
{
if
err
!=
nil
||
status
==
gameT
ypes
.
GameStatusInProgress
{
return
false
}
if
!
a
.
shouldResolve
(
status
)
{
...
...
op-challenger/game/fault/agent_test.go
View file @
23d0635b
...
...
@@ -8,6 +8,7 @@ import (
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/test"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/alphabet"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
gameTypes
"github.com/ethereum-optimism/optimism/op-challenger/game/types"
"github.com/ethereum-optimism/optimism/op-challenger/metrics"
"github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require"
...
...
@@ -19,16 +20,16 @@ import (
func
TestShouldResolve
(
t
*
testing
.
T
)
{
t
.
Run
(
"AgreeWithProposedOutput"
,
func
(
t
*
testing
.
T
)
{
agent
,
_
,
_
:=
setupTestAgent
(
t
,
true
)
require
.
False
(
t
,
agent
.
shouldResolve
(
t
ypes
.
GameStatusDefenderWon
))
require
.
True
(
t
,
agent
.
shouldResolve
(
t
ypes
.
GameStatusChallengerWon
))
require
.
False
(
t
,
agent
.
shouldResolve
(
t
ypes
.
GameStatusInProgress
))
require
.
False
(
t
,
agent
.
shouldResolve
(
gameT
ypes
.
GameStatusDefenderWon
))
require
.
True
(
t
,
agent
.
shouldResolve
(
gameT
ypes
.
GameStatusChallengerWon
))
require
.
False
(
t
,
agent
.
shouldResolve
(
gameT
ypes
.
GameStatusInProgress
))
})
t
.
Run
(
"DisagreeWithProposedOutput"
,
func
(
t
*
testing
.
T
)
{
agent
,
_
,
_
:=
setupTestAgent
(
t
,
false
)
require
.
True
(
t
,
agent
.
shouldResolve
(
t
ypes
.
GameStatusDefenderWon
))
require
.
False
(
t
,
agent
.
shouldResolve
(
t
ypes
.
GameStatusChallengerWon
))
require
.
False
(
t
,
agent
.
shouldResolve
(
t
ypes
.
GameStatusInProgress
))
require
.
True
(
t
,
agent
.
shouldResolve
(
gameT
ypes
.
GameStatusDefenderWon
))
require
.
False
(
t
,
agent
.
shouldResolve
(
gameT
ypes
.
GameStatusChallengerWon
))
require
.
False
(
t
,
agent
.
shouldResolve
(
gameT
ypes
.
GameStatusInProgress
))
})
}
...
...
@@ -38,31 +39,31 @@ func TestDoNotMakeMovesWhenGameIsResolvable(t *testing.T) {
tests
:=
[]
struct
{
name
string
agreeWithProposedOutput
bool
callResolveStatus
t
ypes
.
GameStatus
callResolveStatus
gameT
ypes
.
GameStatus
shouldResolve
bool
}{
{
name
:
"Agree_Losing"
,
agreeWithProposedOutput
:
true
,
callResolveStatus
:
t
ypes
.
GameStatusDefenderWon
,
callResolveStatus
:
gameT
ypes
.
GameStatusDefenderWon
,
shouldResolve
:
false
,
},
{
name
:
"Agree_Winning"
,
agreeWithProposedOutput
:
true
,
callResolveStatus
:
t
ypes
.
GameStatusChallengerWon
,
callResolveStatus
:
gameT
ypes
.
GameStatusChallengerWon
,
shouldResolve
:
true
,
},
{
name
:
"Disagree_Losing"
,
agreeWithProposedOutput
:
false
,
callResolveStatus
:
t
ypes
.
GameStatusChallengerWon
,
callResolveStatus
:
gameT
ypes
.
GameStatusChallengerWon
,
shouldResolve
:
false
,
},
{
name
:
"Disagree_Winning"
,
agreeWithProposedOutput
:
false
,
callResolveStatus
:
t
ypes
.
GameStatusDefenderWon
,
callResolveStatus
:
gameT
ypes
.
GameStatusDefenderWon
,
shouldResolve
:
true
,
},
}
...
...
@@ -126,14 +127,14 @@ func (s *stubClaimLoader) FetchClaims(ctx context.Context) ([]types.Claim, error
type
stubResponder
struct
{
callResolveCount
int
callResolveStatus
t
ypes
.
GameStatus
callResolveStatus
gameT
ypes
.
GameStatus
callResolveErr
error
resolveCount
int
resolveErr
error
}
func
(
s
*
stubResponder
)
CallResolve
(
ctx
context
.
Context
)
(
t
ypes
.
GameStatus
,
error
)
{
func
(
s
*
stubResponder
)
CallResolve
(
ctx
context
.
Context
)
(
gameT
ypes
.
GameStatus
,
error
)
{
s
.
callResolveCount
++
return
s
.
callResolveStatus
,
s
.
callResolveErr
}
...
...
op-challenger/game/fault/loader.go
View file @
23d0635b
...
...
@@ -6,6 +6,7 @@ import (
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
gameTypes
"github.com/ethereum-optimism/optimism/op-challenger/game/types"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
...
...
@@ -49,9 +50,9 @@ func NewLoaderFromBindings(fdgAddr common.Address, client bind.ContractCaller) (
}
// GetGameStatus returns the current game status.
func
(
l
*
loader
)
GetGameStatus
(
ctx
context
.
Context
)
(
t
ypes
.
GameStatus
,
error
)
{
func
(
l
*
loader
)
GetGameStatus
(
ctx
context
.
Context
)
(
gameT
ypes
.
GameStatus
,
error
)
{
status
,
err
:=
l
.
caller
.
Status
(
&
bind
.
CallOpts
{
Context
:
ctx
})
return
t
ypes
.
GameStatus
(
status
),
err
return
gameT
ypes
.
GameStatus
(
status
),
err
}
// GetClaimCount returns the number of claims in the game.
...
...
op-challenger/game/fault/loader_test.go
View file @
23d0635b
...
...
@@ -7,6 +7,7 @@ import (
"testing"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
gameTypes
"github.com/ethereum-optimism/optimism/op-challenger/game/types"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
...
...
@@ -30,15 +31,15 @@ func TestLoader_GetGameStatus(t *testing.T) {
}{
{
name
:
"challenger won status"
,
status
:
uint8
(
t
ypes
.
GameStatusChallengerWon
),
status
:
uint8
(
gameT
ypes
.
GameStatusChallengerWon
),
},
{
name
:
"defender won status"
,
status
:
uint8
(
t
ypes
.
GameStatusDefenderWon
),
status
:
uint8
(
gameT
ypes
.
GameStatusDefenderWon
),
},
{
name
:
"in progress status"
,
status
:
uint8
(
t
ypes
.
GameStatusInProgress
),
status
:
uint8
(
gameT
ypes
.
GameStatusInProgress
),
},
{
name
:
"error bubbled up"
,
...
...
@@ -57,7 +58,7 @@ func TestLoader_GetGameStatus(t *testing.T) {
require
.
ErrorIs
(
t
,
err
,
mockStatusError
)
}
else
{
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
t
ypes
.
GameStatus
(
test
.
status
),
status
)
require
.
Equal
(
t
,
gameT
ypes
.
GameStatus
(
test
.
status
),
status
)
}
})
}
...
...
op-challenger/game/fault/player.go
View file @
23d0635b
...
...
@@ -11,6 +11,7 @@ import (
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/alphabet"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/cannon"
"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/txmgr"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
...
...
@@ -22,7 +23,7 @@ import (
type
actor
func
(
ctx
context
.
Context
)
error
type
GameInfo
interface
{
GetGameStatus
(
context
.
Context
)
(
t
ypes
.
GameStatus
,
error
)
GetGameStatus
(
context
.
Context
)
(
gameT
ypes
.
GameStatus
,
error
)
GetClaimCount
(
context
.
Context
)
(
uint64
,
error
)
}
...
...
@@ -31,8 +32,7 @@ type GamePlayer struct {
agreeWithProposedOutput
bool
loader
GameInfo
logger
log
.
Logger
completed
bool
status
gameTypes
.
GameStatus
}
func
NewGamePlayer
(
...
...
@@ -57,14 +57,14 @@ func NewGamePlayer(
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"failed to fetch game status: %w"
,
err
)
}
if
status
!=
t
ypes
.
GameStatusInProgress
{
if
status
!=
gameT
ypes
.
GameStatusInProgress
{
logger
.
Info
(
"Game already resolved"
,
"status"
,
status
)
// Game is already complete so skip creating the trace provider, loading game inputs etc.
return
&
GamePlayer
{
logger
:
logger
,
loader
:
loader
,
agreeWithProposedOutput
:
cfg
.
AgreeWithProposedOutput
,
completed
:
true
,
status
:
status
,
// Act function does nothing because the game is already complete
act
:
func
(
ctx
context
.
Context
)
error
{
return
nil
...
...
@@ -111,32 +111,32 @@ func NewGamePlayer(
agreeWithProposedOutput
:
cfg
.
AgreeWithProposedOutput
,
loader
:
loader
,
logger
:
logger
,
completed
:
status
!=
types
.
GameStatusInProgres
s
,
status
:
statu
s
,
},
nil
}
func
(
g
*
GamePlayer
)
ProgressGame
(
ctx
context
.
Context
)
bool
{
if
g
.
completed
{
func
(
g
*
GamePlayer
)
ProgressGame
(
ctx
context
.
Context
)
gameTypes
.
GameStatus
{
if
g
.
status
!=
gameTypes
.
GameStatusInProgress
{
// Game is already complete so don't try to perform further actions.
g
.
logger
.
Trace
(
"Skipping completed game"
)
return
true
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
)
}
if
status
,
err
:=
g
.
loader
.
GetGameStatus
(
ctx
);
err
!=
nil
{
status
,
err
:=
g
.
loader
.
GetGameStatus
(
ctx
)
if
err
!=
nil
{
g
.
logger
.
Warn
(
"Unable to retrieve game status"
,
"err"
,
err
)
}
else
{
g
.
logGameStatus
(
ctx
,
status
)
g
.
completed
=
status
!=
types
.
GameStatusInProgress
return
g
.
completed
return
gameTypes
.
GameStatusInProgress
}
return
false
g
.
logGameStatus
(
ctx
,
status
)
g
.
status
=
status
return
status
}
func
(
g
*
GamePlayer
)
logGameStatus
(
ctx
context
.
Context
,
status
t
ypes
.
GameStatus
)
{
if
status
==
t
ypes
.
GameStatusInProgress
{
func
(
g
*
GamePlayer
)
logGameStatus
(
ctx
context
.
Context
,
status
gameT
ypes
.
GameStatus
)
{
if
status
==
gameT
ypes
.
GameStatusInProgress
{
claimCount
,
err
:=
g
.
loader
.
GetClaimCount
(
ctx
)
if
err
!=
nil
{
g
.
logger
.
Error
(
"Failed to get claim count for in progress game"
,
"err"
,
err
)
...
...
@@ -145,11 +145,11 @@ func (g *GamePlayer) logGameStatus(ctx context.Context, status types.GameStatus)
g
.
logger
.
Info
(
"Game info"
,
"claims"
,
claimCount
,
"status"
,
status
)
return
}
var
expectedStatus
t
ypes
.
GameStatus
var
expectedStatus
gameT
ypes
.
GameStatus
if
g
.
agreeWithProposedOutput
{
expectedStatus
=
t
ypes
.
GameStatusChallengerWon
expectedStatus
=
gameT
ypes
.
GameStatusChallengerWon
}
else
{
expectedStatus
=
t
ypes
.
GameStatusDefenderWon
expectedStatus
=
gameT
ypes
.
GameStatusDefenderWon
}
if
expectedStatus
==
status
{
g
.
logger
.
Info
(
"Game won"
,
"status"
,
status
)
...
...
op-challenger/game/fault/player_test.go
View file @
23d0635b
...
...
@@ -7,6 +7,7 @@ import (
"testing"
"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-node/testlog"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
...
...
@@ -22,8 +23,8 @@ var (
func
TestProgressGame_LogErrorFromAct
(
t
*
testing
.
T
)
{
handler
,
game
,
actor
:=
setupProgressGameTest
(
t
,
true
)
actor
.
actErr
=
errors
.
New
(
"boom"
)
done
:=
game
.
ProgressGame
(
context
.
Background
())
require
.
False
(
t
,
done
,
"should not be done"
)
status
:=
game
.
ProgressGame
(
context
.
Background
())
require
.
Equal
(
t
,
gameTypes
.
GameStatusInProgress
,
status
)
require
.
Equal
(
t
,
1
,
actor
.
callCount
,
"should perform next actions"
)
errLog
:=
handler
.
FindLog
(
log
.
LvlError
,
"Error when acting on game"
)
require
.
NotNil
(
t
,
errLog
,
"should log error"
)
...
...
@@ -38,42 +39,42 @@ func TestProgressGame_LogErrorFromAct(t *testing.T) {
func
TestProgressGame_LogGameStatus
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
name
string
status
t
ypes
.
GameStatus
status
gameT
ypes
.
GameStatus
agreeWithOutput
bool
logLevel
log
.
Lvl
logMsg
string
}{
{
name
:
"GameLostAsDefender"
,
status
:
t
ypes
.
GameStatusChallengerWon
,
status
:
gameT
ypes
.
GameStatusChallengerWon
,
agreeWithOutput
:
false
,
logLevel
:
log
.
LvlError
,
logMsg
:
"Game lost"
,
},
{
name
:
"GameLostAsChallenger"
,
status
:
t
ypes
.
GameStatusDefenderWon
,
status
:
gameT
ypes
.
GameStatusDefenderWon
,
agreeWithOutput
:
true
,
logLevel
:
log
.
LvlError
,
logMsg
:
"Game lost"
,
},
{
name
:
"GameWonAsDefender"
,
status
:
t
ypes
.
GameStatusDefenderWon
,
status
:
gameT
ypes
.
GameStatusDefenderWon
,
agreeWithOutput
:
false
,
logLevel
:
log
.
LvlInfo
,
logMsg
:
"Game won"
,
},
{
name
:
"GameWonAsChallenger"
,
status
:
t
ypes
.
GameStatusChallengerWon
,
status
:
gameT
ypes
.
GameStatusChallengerWon
,
agreeWithOutput
:
true
,
logLevel
:
log
.
LvlInfo
,
logMsg
:
"Game won"
,
},
{
name
:
"GameInProgress"
,
status
:
t
ypes
.
GameStatusInProgress
,
status
:
gameT
ypes
.
GameStatusInProgress
,
agreeWithOutput
:
true
,
logLevel
:
log
.
LvlInfo
,
logMsg
:
"Game info"
,
...
...
@@ -85,9 +86,9 @@ func TestProgressGame_LogGameStatus(t *testing.T) {
handler
,
game
,
gameState
:=
setupProgressGameTest
(
t
,
test
.
agreeWithOutput
)
gameState
.
status
=
test
.
status
done
:=
game
.
ProgressGame
(
context
.
Background
())
status
:=
game
.
ProgressGame
(
context
.
Background
())
require
.
Equal
(
t
,
1
,
gameState
.
callCount
,
"should perform next actions"
)
require
.
Equal
(
t
,
test
.
status
!=
types
.
GameStatusInProgress
,
done
,
"should be done when not in progress"
)
require
.
Equal
(
t
,
test
.
status
,
status
)
errLog
:=
handler
.
FindLog
(
test
.
logLevel
,
test
.
logMsg
)
require
.
NotNil
(
t
,
errLog
,
"should log game result"
)
require
.
Equal
(
t
,
test
.
status
,
errLog
.
GetContextValue
(
"status"
))
...
...
@@ -96,19 +97,19 @@ func TestProgressGame_LogGameStatus(t *testing.T) {
}
func
TestDoNotActOnCompleteGame
(
t
*
testing
.
T
)
{
for
_
,
status
:=
range
[]
types
.
GameStatus
{
types
.
GameStatusChallengerWon
,
t
ypes
.
GameStatusDefenderWon
}
{
for
_
,
status
:=
range
[]
gameTypes
.
GameStatus
{
gameTypes
.
GameStatusChallengerWon
,
gameT
ypes
.
GameStatusDefenderWon
}
{
t
.
Run
(
status
.
String
(),
func
(
t
*
testing
.
T
)
{
_
,
game
,
gameState
:=
setupProgressGameTest
(
t
,
true
)
gameState
.
status
=
status
done
:=
game
.
ProgressGame
(
context
.
Background
())
fetched
:=
game
.
ProgressGame
(
context
.
Background
())
require
.
Equal
(
t
,
1
,
gameState
.
callCount
,
"acts the first time"
)
require
.
True
(
t
,
done
,
"should be done"
)
require
.
Equal
(
t
,
status
,
fetched
)
// Should not act when it knows the game is already complete
done
=
game
.
ProgressGame
(
context
.
Background
())
fetched
=
game
.
ProgressGame
(
context
.
Background
())
require
.
Equal
(
t
,
1
,
gameState
.
callCount
,
"does not act after game is complete"
)
require
.
True
(
t
,
done
,
"should still be done"
)
require
.
Equal
(
t
,
status
,
fetched
)
})
}
}
...
...
@@ -166,7 +167,7 @@ func setupProgressGameTest(t *testing.T, agreeWithProposedRoot bool) (*testlog.C
}
type
stubGameState
struct
{
status
t
ypes
.
GameStatus
status
gameT
ypes
.
GameStatus
claimCount
uint64
callCount
int
actErr
error
...
...
@@ -178,7 +179,7 @@ func (s *stubGameState) Act(ctx context.Context) error {
return
s
.
actErr
}
func
(
s
*
stubGameState
)
GetGameStatus
(
ctx
context
.
Context
)
(
t
ypes
.
GameStatus
,
error
)
{
func
(
s
*
stubGameState
)
GetGameStatus
(
ctx
context
.
Context
)
(
gameT
ypes
.
GameStatus
,
error
)
{
return
s
.
status
,
nil
}
...
...
op-challenger/game/fault/responder/responder.go
View file @
23d0635b
...
...
@@ -6,6 +6,7 @@ import (
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"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-service/txmgr"
"github.com/ethereum/go-ethereum"
...
...
@@ -81,23 +82,23 @@ func (r *faultResponder) BuildTx(ctx context.Context, response types.Claim) ([]b
// CallResolve determines if the resolve function on the fault dispute game contract
// would succeed. Returns the game status if the call would succeed, errors otherwise.
func
(
r
*
faultResponder
)
CallResolve
(
ctx
context
.
Context
)
(
t
ypes
.
GameStatus
,
error
)
{
func
(
r
*
faultResponder
)
CallResolve
(
ctx
context
.
Context
)
(
gameT
ypes
.
GameStatus
,
error
)
{
txData
,
err
:=
r
.
buildResolveData
()
if
err
!=
nil
{
return
t
ypes
.
GameStatusInProgress
,
err
return
gameT
ypes
.
GameStatusInProgress
,
err
}
res
,
err
:=
r
.
txMgr
.
Call
(
ctx
,
ethereum
.
CallMsg
{
To
:
&
r
.
fdgAddr
,
Data
:
txData
,
},
nil
)
if
err
!=
nil
{
return
t
ypes
.
GameStatusInProgress
,
err
return
gameT
ypes
.
GameStatusInProgress
,
err
}
var
status
uint8
if
err
=
r
.
fdgAbi
.
UnpackIntoInterface
(
&
status
,
"resolve"
,
res
);
err
!=
nil
{
return
t
ypes
.
GameStatusInProgress
,
err
return
gameT
ypes
.
GameStatusInProgress
,
err
}
return
t
ypes
.
GameStatusFromUint8
(
status
)
return
gameT
ypes
.
GameStatusFromUint8
(
status
)
}
// Resolve executes a resolve transaction to resolve a fault dispute game.
...
...
op-challenger/game/fault/responder/responder_test.go
View file @
23d0635b
...
...
@@ -8,6 +8,7 @@ import (
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"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-node/testlog"
"github.com/ethereum-optimism/optimism/op-service/txmgr"
...
...
@@ -32,7 +33,7 @@ func TestCallResolve(t *testing.T) {
mockTxMgr
.
callFails
=
true
status
,
err
:=
responder
.
CallResolve
(
context
.
Background
())
require
.
ErrorIs
(
t
,
err
,
mockCallError
)
require
.
Equal
(
t
,
t
ypes
.
GameStatusInProgress
,
status
)
require
.
Equal
(
t
,
gameT
ypes
.
GameStatusInProgress
,
status
)
require
.
Equal
(
t
,
0
,
mockTxMgr
.
calls
)
})
...
...
@@ -41,7 +42,7 @@ func TestCallResolve(t *testing.T) {
mockTxMgr
.
callBytes
=
[]
byte
{
0x00
,
0x01
}
status
,
err
:=
responder
.
CallResolve
(
context
.
Background
())
require
.
Error
(
t
,
err
)
require
.
Equal
(
t
,
t
ypes
.
GameStatusInProgress
,
status
)
require
.
Equal
(
t
,
gameT
ypes
.
GameStatusInProgress
,
status
)
require
.
Equal
(
t
,
1
,
mockTxMgr
.
calls
)
})
...
...
@@ -49,7 +50,7 @@ func TestCallResolve(t *testing.T) {
responder
,
mockTxMgr
:=
newTestFaultResponder
(
t
)
status
,
err
:=
responder
.
CallResolve
(
context
.
Background
())
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
t
ypes
.
GameStatusInProgress
,
status
)
require
.
Equal
(
t
,
gameT
ypes
.
GameStatusInProgress
,
status
)
require
.
Equal
(
t
,
1
,
mockTxMgr
.
calls
)
})
}
...
...
op-challenger/game/fault/types/types.go
View file @
23d0635b
...
...
@@ -3,7 +3,6 @@ package types
import
(
"context"
"errors"
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/common"
...
...
@@ -13,36 +12,6 @@ var (
ErrGameDepthReached
=
errors
.
New
(
"game depth reached"
)
)
type
GameStatus
uint8
const
(
GameStatusInProgress
GameStatus
=
iota
GameStatusChallengerWon
GameStatusDefenderWon
)
// String returns the string representation of the game status.
func
(
s
GameStatus
)
String
()
string
{
switch
s
{
case
GameStatusInProgress
:
return
"In Progress"
case
GameStatusChallengerWon
:
return
"Challenger Won"
case
GameStatusDefenderWon
:
return
"Defender Won"
default
:
return
"Unknown"
}
}
// GameStatusFromUint8 returns a game status from the uint8 representation.
func
GameStatusFromUint8
(
i
uint8
)
(
GameStatus
,
error
)
{
if
i
>
2
{
return
GameStatus
(
i
),
fmt
.
Errorf
(
"invalid game status: %d"
,
i
)
}
return
GameStatus
(
i
),
nil
}
// PreimageOracleData encapsulates the preimage oracle data
// to load into the onchain oracle.
type
PreimageOracleData
struct
{
...
...
op-challenger/game/fault/types/types_test.go
View file @
23d0635b
package
types
import
(
"fmt"
"testing"
"github.com/stretchr/testify/require"
)
var
validGameStatuses
=
[]
GameStatus
{
GameStatusInProgress
,
GameStatusChallengerWon
,
GameStatusDefenderWon
,
}
func
TestGameStatusFromUint8
(
t
*
testing
.
T
)
{
for
_
,
status
:=
range
validGameStatuses
{
t
.
Run
(
fmt
.
Sprintf
(
"Valid Game Status %v"
,
status
),
func
(
t
*
testing
.
T
)
{
parsed
,
err
:=
GameStatusFromUint8
(
uint8
(
status
))
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
status
,
parsed
)
})
}
t
.
Run
(
"Invalid"
,
func
(
t
*
testing
.
T
)
{
status
,
err
:=
GameStatusFromUint8
(
3
)
require
.
Error
(
t
,
err
)
require
.
Equal
(
t
,
GameStatus
(
3
),
status
)
})
}
func
TestNewPreimageOracleData
(
t
*
testing
.
T
)
{
t
.
Run
(
"LocalData"
,
func
(
t
*
testing
.
T
)
{
data
:=
NewPreimageOracleData
([]
byte
{
1
,
2
,
3
},
[]
byte
{
4
,
5
,
6
},
7
)
...
...
op-challenger/game/monitor.go
View file @
23d0635b
...
...
@@ -8,7 +8,6 @@ import (
"time"
"github.com/ethereum-optimism/optimism/op-challenger/game/scheduler"
"github.com/ethereum-optimism/optimism/op-challenger/metrics"
"github.com/ethereum-optimism/optimism/op-service/clock"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
...
...
@@ -27,7 +26,6 @@ type gameScheduler interface {
type
gameMonitor
struct
{
logger
log
.
Logger
metrics
metrics
.
Metricer
clock
clock
.
Clock
source
gameSource
scheduler
gameScheduler
...
...
@@ -38,7 +36,6 @@ type gameMonitor struct {
func
newGameMonitor
(
logger
log
.
Logger
,
m
metrics
.
Metricer
,
cl
clock
.
Clock
,
source
gameSource
,
scheduler
gameScheduler
,
...
...
@@ -48,7 +45,6 @@ func newGameMonitor(
)
*
gameMonitor
{
return
&
gameMonitor
{
logger
:
logger
,
metrics
:
m
,
clock
:
cl
,
scheduler
:
scheduler
,
source
:
source
,
...
...
op-challenger/game/monitor_test.go
View file @
23d0635b
...
...
@@ -6,7 +6,6 @@ import (
"testing"
"time"
"github.com/ethereum-optimism/optimism/op-challenger/metrics"
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum-optimism/optimism/op-service/clock"
"github.com/ethereum/go-ethereum/common"
...
...
@@ -101,7 +100,7 @@ func setupMonitorTest(t *testing.T, allowedGames []common.Address) (*gameMonitor
return
i
,
nil
}
sched
:=
&
stubScheduler
{}
monitor
:=
newGameMonitor
(
logger
,
metrics
.
NoopMetrics
,
clock
.
SystemClock
,
source
,
sched
,
time
.
Duration
(
0
),
fetchBlockNum
,
allowedGames
)
monitor
:=
newGameMonitor
(
logger
,
clock
.
SystemClock
,
source
,
sched
,
time
.
Duration
(
0
),
fetchBlockNum
,
allowedGames
)
return
monitor
,
source
,
sched
}
...
...
op-challenger/game/scheduler/coordinator.go
View file @
23d0635b
...
...
@@ -5,6 +5,8 @@ import (
"errors"
"fmt"
"github.com/ethereum-optimism/optimism/op-challenger/game/types"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
"golang.org/x/exp/slices"
...
...
@@ -17,7 +19,7 @@ type PlayerCreator func(address common.Address, dir string) (GamePlayer, error)
type
gameState
struct
{
player
GamePlayer
inflight
bool
resolved
bool
status
types
.
GameStatus
}
// coordinator manages the set of current games, queues games to be played (on separate worker threads) and
...
...
@@ -31,6 +33,7 @@ type coordinator struct {
resultQueue
<-
chan
job
logger
log
.
Logger
m
SchedulerMetricer
createPlayer
PlayerCreator
states
map
[
common
.
Address
]
*
gameState
disk
DiskManager
...
...
@@ -49,18 +52,35 @@ func (c *coordinator) schedule(ctx context.Context, games []common.Address) erro
}
}
var
gamesInProgress
int
var
gamesChallengerWon
int
var
gamesDefenderWon
int
var
errs
[]
error
var
jobs
[]
job
// Next collect all the jobs to schedule and ensure all games are recorded in the states map.
// Otherwise, results may start being processed before all games are recorded, resulting in existing
// data directories potentially being deleted for games that are required.
var
jobs
[]
job
for
_
,
addr
:=
range
games
{
if
j
,
err
:=
c
.
createJob
(
addr
);
err
!=
nil
{
errs
=
append
(
errs
,
err
)
}
else
if
j
!=
nil
{
jobs
=
append
(
jobs
,
*
j
)
}
state
,
ok
:=
c
.
states
[
addr
]
if
ok
{
switch
state
.
status
{
case
types
.
GameStatusInProgress
:
gamesInProgress
++
case
types
.
GameStatusDefenderWon
:
gamesDefenderWon
++
case
types
.
GameStatusChallengerWon
:
gamesChallengerWon
++
}
}
else
{
c
.
logger
.
Warn
(
"Game not found in states map"
,
"game"
,
addr
)
}
}
c
.
m
.
RecordGamesStatus
(
gamesInProgress
,
gamesChallengerWon
,
gamesDefenderWon
)
// Finally, enqueue the jobs
for
_
,
j
:=
range
jobs
{
...
...
@@ -114,7 +134,7 @@ func (c *coordinator) processResult(j job) error {
return
fmt
.
Errorf
(
"game %v received unexpected result: %w"
,
j
.
addr
,
errUnknownGame
)
}
state
.
inflight
=
false
state
.
resolved
=
j
.
resolved
state
.
status
=
j
.
status
c
.
deleteResolvedGameFiles
()
return
nil
}
...
...
@@ -122,7 +142,7 @@ func (c *coordinator) processResult(j job) error {
func
(
c
*
coordinator
)
deleteResolvedGameFiles
()
{
var
keepGames
[]
common
.
Address
for
addr
,
state
:=
range
c
.
states
{
if
!
state
.
resolved
||
state
.
inflight
{
if
state
.
status
==
types
.
GameStatusInProgress
||
state
.
inflight
{
keepGames
=
append
(
keepGames
,
addr
)
}
}
...
...
@@ -131,9 +151,10 @@ func (c *coordinator) deleteResolvedGameFiles() {
}
}
func
newCoordinator
(
logger
log
.
Logger
,
jobQueue
chan
<-
job
,
resultQueue
<-
chan
job
,
createPlayer
PlayerCreator
,
disk
DiskManager
)
*
coordinator
{
func
newCoordinator
(
logger
log
.
Logger
,
m
SchedulerMetricer
,
jobQueue
chan
<-
job
,
resultQueue
<-
chan
job
,
createPlayer
PlayerCreator
,
disk
DiskManager
)
*
coordinator
{
return
&
coordinator
{
logger
:
logger
,
m
:
m
,
jobQueue
:
jobQueue
,
resultQueue
:
resultQueue
,
createPlayer
:
createPlayer
,
...
...
op-challenger/game/scheduler/coordinator_test.go
View file @
23d0635b
...
...
@@ -5,6 +5,8 @@ import (
"fmt"
"testing"
"github.com/ethereum-optimism/optimism/op-challenger/game/types"
"github.com/ethereum-optimism/optimism/op-challenger/metrics"
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
...
...
@@ -140,7 +142,7 @@ func TestDeleteDataForResolvedGames(t *testing.T) {
require
.
NoError
(
t
,
c
.
schedule
(
ctx
,
[]
common
.
Address
{
gameAddr3
}))
require
.
Len
(
t
,
workQueue
,
1
)
j
:=
<-
workQueue
j
.
resolved
=
true
j
.
status
=
types
.
GameStatusDefenderWon
require
.
NoError
(
t
,
c
.
processResult
(
j
))
// But ensure its data directory is marked as existing
disk
.
DirForGame
(
gameAddr3
)
...
...
@@ -155,7 +157,9 @@ func TestDeleteDataForResolvedGames(t *testing.T) {
// Game 3 hasn't yet progressed (update is still in flight)
for
i
:=
0
;
i
<
len
(
gameAddrs
)
-
1
;
i
++
{
j
:=
<-
workQueue
j
.
resolved
=
j
.
addr
==
gameAddr2
if
j
.
addr
==
gameAddr2
{
j
.
status
=
types
.
GameStatusDefenderWon
}
require
.
NoError
(
t
,
c
.
processResult
(
j
))
}
...
...
@@ -229,20 +233,20 @@ func setupCoordinatorTest(t *testing.T, bufferSize int) (*coordinator, <-chan jo
created
:
make
(
map
[
common
.
Address
]
*
stubGame
),
}
disk
:=
&
stubDiskManager
{
gameDirExists
:
make
(
map
[
common
.
Address
]
bool
)}
c
:=
newCoordinator
(
logger
,
workQueue
,
resultQueue
,
games
.
CreateGame
,
disk
)
c
:=
newCoordinator
(
logger
,
metrics
.
NoopMetrics
,
workQueue
,
resultQueue
,
games
.
CreateGame
,
disk
)
return
c
,
workQueue
,
resultQueue
,
games
,
disk
}
type
stubGame
struct
{
addr
common
.
Address
progressCount
int
done
bool
status
types
.
GameStatus
dir
string
}
func
(
g
*
stubGame
)
ProgressGame
(
_
context
.
Context
)
bool
{
func
(
g
*
stubGame
)
ProgressGame
(
_
context
.
Context
)
types
.
GameStatus
{
g
.
progressCount
++
return
g
.
done
return
g
.
status
}
type
createdGames
struct
{
...
...
@@ -259,9 +263,13 @@ func (c *createdGames) CreateGame(addr common.Address, dir string) (GamePlayer,
if
_
,
exists
:=
c
.
created
[
addr
];
exists
{
c
.
t
.
Fatalf
(
"game %v already exists"
,
addr
)
}
status
:=
types
.
GameStatusInProgress
if
addr
==
c
.
createCompleted
{
status
=
types
.
GameStatusDefenderWon
}
game
:=
&
stubGame
{
addr
:
addr
,
done
:
addr
==
c
.
createCompleted
,
status
:
status
,
dir
:
dir
,
}
c
.
created
[
addr
]
=
game
...
...
op-challenger/game/scheduler/scheduler.go
View file @
23d0635b
...
...
@@ -11,6 +11,10 @@ import (
var
ErrBusy
=
errors
.
New
(
"busy scheduling previous update"
)
type
SchedulerMetricer
interface
{
RecordGamesStatus
(
inProgress
,
defenderWon
,
challengerWon
int
)
}
type
Scheduler
struct
{
logger
log
.
Logger
coordinator
*
coordinator
...
...
@@ -22,7 +26,7 @@ type Scheduler struct {
cancel
func
()
}
func
NewScheduler
(
logger
log
.
Logger
,
disk
DiskManager
,
maxConcurrency
uint
,
createPlayer
PlayerCreator
)
*
Scheduler
{
func
NewScheduler
(
logger
log
.
Logger
,
m
SchedulerMetricer
,
disk
DiskManager
,
maxConcurrency
uint
,
createPlayer
PlayerCreator
)
*
Scheduler
{
// Size job and results queues to be fairly small so backpressure is applied early
// but with enough capacity to keep the workers busy
jobQueue
:=
make
(
chan
job
,
maxConcurrency
*
2
)
...
...
@@ -34,7 +38,7 @@ func NewScheduler(logger log.Logger, disk DiskManager, maxConcurrency uint, crea
return
&
Scheduler
{
logger
:
logger
,
coordinator
:
newCoordinator
(
logger
,
jobQueue
,
resultQueue
,
createPlayer
,
disk
),
coordinator
:
newCoordinator
(
logger
,
m
,
jobQueue
,
resultQueue
,
createPlayer
,
disk
),
maxConcurrency
:
maxConcurrency
,
scheduleQueue
:
scheduleQueue
,
jobQueue
:
jobQueue
,
...
...
op-challenger/game/scheduler/scheduler_test.go
View file @
23d0635b
...
...
@@ -4,6 +4,7 @@ import (
"context"
"testing"
"github.com/ethereum-optimism/optimism/op-challenger/metrics"
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
...
...
@@ -18,7 +19,7 @@ func TestSchedulerProcessesGames(t *testing.T) {
}
removeExceptCalls
:=
make
(
chan
[]
common
.
Address
)
disk
:=
&
trackingDiskManager
{
removeExceptCalls
:
removeExceptCalls
}
s
:=
NewScheduler
(
logger
,
disk
,
2
,
createPlayer
)
s
:=
NewScheduler
(
logger
,
metrics
.
NoopMetrics
,
disk
,
2
,
createPlayer
)
s
.
Start
(
ctx
)
gameAddr1
:=
common
.
Address
{
0xaa
}
...
...
@@ -46,7 +47,7 @@ func TestReturnBusyWhenScheduleQueueFull(t *testing.T) {
}
removeExceptCalls
:=
make
(
chan
[]
common
.
Address
)
disk
:=
&
trackingDiskManager
{
removeExceptCalls
:
removeExceptCalls
}
s
:=
NewScheduler
(
logger
,
disk
,
2
,
createPlayer
)
s
:=
NewScheduler
(
logger
,
metrics
.
NoopMetrics
,
disk
,
2
,
createPlayer
)
// Scheduler not started - first call fills the queue
require
.
NoError
(
t
,
s
.
Schedule
([]
common
.
Address
{{
0xaa
}}))
...
...
op-challenger/game/scheduler/types.go
View file @
23d0635b
...
...
@@ -4,10 +4,12 @@ import (
"context"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum-optimism/optimism/op-challenger/game/types"
)
type
GamePlayer
interface
{
ProgressGame
(
ctx
context
.
Context
)
bool
ProgressGame
(
ctx
context
.
Context
)
types
.
GameStatus
}
type
DiskManager
interface
{
...
...
@@ -18,5 +20,5 @@ type DiskManager interface {
type
job
struct
{
addr
common
.
Address
player
GamePlayer
resolved
bool
status
types
.
GameStatus
}
op-challenger/game/scheduler/worker.go
View file @
23d0635b
...
...
@@ -15,7 +15,7 @@ func progressGames(ctx context.Context, in <-chan job, out chan<- job, wg *sync.
case
<-
ctx
.
Done
()
:
return
case
j
:=
<-
in
:
j
.
resolved
=
j
.
player
.
ProgressGame
(
ctx
)
j
.
status
=
j
.
player
.
ProgressGame
(
ctx
)
out
<-
j
}
}
...
...
op-challenger/game/scheduler/worker_test.go
View file @
23d0635b
...
...
@@ -6,6 +6,8 @@ import (
"testing"
"time"
"github.com/ethereum-optimism/optimism/op-challenger/game/types"
"github.com/stretchr/testify/require"
)
...
...
@@ -20,17 +22,17 @@ func TestWorkerShouldProcessJobsUntilContextDone(t *testing.T) {
go
progressGames
(
ctx
,
in
,
out
,
&
wg
)
in
<-
job
{
player
:
&
stubPlayer
{
done
:
false
},
player
:
&
stubPlayer
{
status
:
types
.
GameStatusInProgress
},
}
in
<-
job
{
player
:
&
stubPlayer
{
done
:
true
},
player
:
&
stubPlayer
{
status
:
types
.
GameStatusDefenderWon
},
}
result1
:=
readWithTimeout
(
t
,
out
)
result2
:=
readWithTimeout
(
t
,
out
)
require
.
Equal
(
t
,
result1
.
resolved
,
false
)
require
.
Equal
(
t
,
result2
.
resolved
,
true
)
require
.
Equal
(
t
,
result1
.
status
,
types
.
GameStatusInProgress
)
require
.
Equal
(
t
,
result2
.
status
,
types
.
GameStatusDefenderWon
)
// Cancel the context which should exit the worker
cancel
()
...
...
@@ -38,11 +40,11 @@ func TestWorkerShouldProcessJobsUntilContextDone(t *testing.T) {
}
type
stubPlayer
struct
{
done
bool
status
types
.
GameStatus
}
func
(
s
*
stubPlayer
)
ProgressGame
(
ctx
context
.
Context
)
bool
{
return
s
.
done
func
(
s
*
stubPlayer
)
ProgressGame
(
ctx
context
.
Context
)
types
.
GameStatus
{
return
s
.
status
}
func
readWithTimeout
[
T
any
](
t
*
testing
.
T
,
ch
<-
chan
T
)
T
{
...
...
op-challenger/game/service.go
View file @
23d0635b
...
...
@@ -69,13 +69,14 @@ func NewService(ctx context.Context, logger log.Logger, cfg *config.Config) (*Se
disk
:=
newDiskManager
(
cfg
.
Datadir
)
sched
:=
scheduler
.
NewScheduler
(
logger
,
m
,
disk
,
cfg
.
MaxConcurrency
,
func
(
addr
common
.
Address
,
dir
string
)
(
scheduler
.
GamePlayer
,
error
)
{
return
fault
.
NewGamePlayer
(
ctx
,
logger
,
m
,
cfg
,
dir
,
addr
,
txMgr
,
client
)
})
monitor
:=
newGameMonitor
(
logger
,
m
,
cl
,
loader
,
sched
,
cfg
.
GameWindow
,
client
.
BlockNumber
,
cfg
.
GameAllowlist
)
monitor
:=
newGameMonitor
(
logger
,
cl
,
loader
,
sched
,
cfg
.
GameWindow
,
client
.
BlockNumber
,
cfg
.
GameAllowlist
)
m
.
RecordInfo
(
version
.
SimpleWithMeta
)
m
.
RecordUp
()
...
...
op-challenger/game/types/types.go
0 → 100644
View file @
23d0635b
package
types
import
(
"fmt"
)
type
GameStatus
uint8
const
(
GameStatusInProgress
GameStatus
=
iota
GameStatusChallengerWon
GameStatusDefenderWon
)
// String returns the string representation of the game status.
func
(
s
GameStatus
)
String
()
string
{
switch
s
{
case
GameStatusInProgress
:
return
"In Progress"
case
GameStatusChallengerWon
:
return
"Challenger Won"
case
GameStatusDefenderWon
:
return
"Defender Won"
default
:
return
"Unknown"
}
}
// GameStatusFromUint8 returns a game status from the uint8 representation.
func
GameStatusFromUint8
(
i
uint8
)
(
GameStatus
,
error
)
{
if
i
>
2
{
return
GameStatus
(
i
),
fmt
.
Errorf
(
"invalid game status: %d"
,
i
)
}
return
GameStatus
(
i
),
nil
}
op-challenger/game/types/types_test.go
0 → 100644
View file @
23d0635b
package
types
import
(
"fmt"
"testing"
"github.com/stretchr/testify/require"
)
var
validGameStatuses
=
[]
GameStatus
{
GameStatusInProgress
,
GameStatusChallengerWon
,
GameStatusDefenderWon
,
}
func
TestGameStatusFromUint8
(
t
*
testing
.
T
)
{
for
_
,
status
:=
range
validGameStatuses
{
t
.
Run
(
fmt
.
Sprintf
(
"Valid Game Status %v"
,
status
),
func
(
t
*
testing
.
T
)
{
parsed
,
err
:=
GameStatusFromUint8
(
uint8
(
status
))
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
status
,
parsed
)
})
}
t
.
Run
(
"Invalid"
,
func
(
t
*
testing
.
T
)
{
status
,
err
:=
GameStatusFromUint8
(
3
)
require
.
Error
(
t
,
err
)
require
.
Equal
(
t
,
GameStatus
(
3
),
status
)
})
}
op-challenger/metrics/metrics.go
View file @
23d0635b
...
...
@@ -24,6 +24,8 @@ type Metricer interface {
RecordGameStep
()
RecordGameMove
()
RecordCannonExecutionTime
(
t
float64
)
RecordGamesStatus
(
inProgress
,
defenderWon
,
challengerWon
int
)
}
type
Metrics
struct
{
...
...
@@ -38,7 +40,10 @@ type Metrics struct {
moves
prometheus
.
Counter
steps
prometheus
.
Counter
cannonExecutionTime
prometheus
.
Histogram
trackedGames
prometheus
.
GaugeVec
}
var
_
Metricer
=
(
*
Metrics
)(
nil
)
...
...
@@ -82,6 +87,13 @@ func NewMetrics() *Metrics {
Help
:
"Time (in seconds) to execute cannon"
,
Buckets
:
append
([]
float64
{
1.0
,
10.0
},
prometheus
.
ExponentialBuckets
(
30.0
,
2.0
,
14
)
...
),
}),
trackedGames
:
*
factory
.
NewGaugeVec
(
prometheus
.
GaugeOpts
{
Namespace
:
Namespace
,
Name
:
"tracked_games"
,
Help
:
"Number of games being tracked by the challenger"
,
},
[]
string
{
"status"
,
}),
}
}
...
...
@@ -120,3 +132,9 @@ func (m *Metrics) RecordGameStep() {
func
(
m
*
Metrics
)
RecordCannonExecutionTime
(
t
float64
)
{
m
.
cannonExecutionTime
.
Observe
(
t
)
}
func
(
m
*
Metrics
)
RecordGamesStatus
(
inProgress
,
defenderWon
,
challengerWon
int
)
{
m
.
trackedGames
.
WithLabelValues
(
"in_progress"
)
.
Set
(
float64
(
inProgress
))
m
.
trackedGames
.
WithLabelValues
(
"defender_won"
)
.
Set
(
float64
(
defenderWon
))
m
.
trackedGames
.
WithLabelValues
(
"challenger_won"
)
.
Set
(
float64
(
challengerWon
))
}
op-challenger/metrics/noop.go
View file @
23d0635b
...
...
@@ -12,6 +12,10 @@ var NoopMetrics Metricer = new(noopMetrics)
func
(
*
noopMetrics
)
RecordInfo
(
version
string
)
{}
func
(
*
noopMetrics
)
RecordUp
()
{}
func
(
*
noopMetrics
)
RecordGameMove
()
{}
func
(
*
noopMetrics
)
RecordGameStep
()
{}
func
(
*
noopMetrics
)
RecordCannonExecutionTime
(
t
float64
)
{}
func
(
*
noopMetrics
)
RecordGamesStatus
(
inProgress
,
defenderWon
,
challengerWon
int
)
{}
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