Commit 39252fc2 authored by Adrian Sutton's avatar Adrian Sutton Committed by GitHub

op-challenger: Skip attempting to resolve claims when the chess clock hasn't expired (#9946)

* op-challenger: Skip attempting to resolve claims when the chess clock hasn't expired.

* Use <= when comparing chess clock
Co-authored-by: default avatarInphi <mlaw2501@gmail.com>

---------
Co-authored-by: default avatarInphi <mlaw2501@gmail.com>
parent 16bf8454
...@@ -6,6 +6,7 @@ import ( ...@@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"slices" "slices"
"sync" "sync"
"time"
"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"
...@@ -32,22 +33,26 @@ type ClaimLoader interface { ...@@ -32,22 +33,26 @@ type ClaimLoader interface {
} }
type Agent struct { type Agent struct {
metrics metrics.Metricer metrics metrics.Metricer
cl clock.Clock systemClock clock.Clock
solver *solver.GameSolver l1Clock types.ClockReader
loader ClaimLoader solver *solver.GameSolver
responder Responder loader ClaimLoader
selective bool responder Responder
claimants []common.Address selective bool
maxDepth types.Depth claimants []common.Address
log log.Logger maxDepth types.Depth
gameDuration time.Duration
log log.Logger
} }
func NewAgent( func NewAgent(
m metrics.Metricer, m metrics.Metricer,
cl clock.Clock, systemClock clock.Clock,
l1Clock types.ClockReader,
loader ClaimLoader, loader ClaimLoader,
maxDepth types.Depth, maxDepth types.Depth,
gameDuration time.Duration,
trace types.TraceAccessor, trace types.TraceAccessor,
responder Responder, responder Responder,
log log.Logger, log log.Logger,
...@@ -55,15 +60,17 @@ func NewAgent( ...@@ -55,15 +60,17 @@ func NewAgent(
claimants []common.Address, claimants []common.Address,
) *Agent { ) *Agent {
return &Agent{ return &Agent{
metrics: m, metrics: m,
cl: cl, systemClock: systemClock,
solver: solver.NewGameSolver(maxDepth, trace), l1Clock: l1Clock,
loader: loader, solver: solver.NewGameSolver(maxDepth, trace),
responder: responder, loader: loader,
selective: selective, responder: responder,
claimants: claimants, selective: selective,
maxDepth: maxDepth, claimants: claimants,
log: log, maxDepth: maxDepth,
gameDuration: gameDuration,
log: log,
} }
} }
...@@ -73,9 +80,9 @@ func (a *Agent) Act(ctx context.Context) error { ...@@ -73,9 +80,9 @@ func (a *Agent) Act(ctx context.Context) error {
return nil return nil
} }
start := a.cl.Now() start := a.systemClock.Now()
defer func() { defer func() {
a.metrics.RecordGameActTime(a.cl.Since(start).Seconds()) a.metrics.RecordGameActTime(a.systemClock.Since(start).Seconds())
}() }()
game, err := a.newGameFromContracts(ctx) game, err := a.newGameFromContracts(ctx)
if err != nil { if err != nil {
...@@ -156,9 +163,13 @@ func (a *Agent) tryResolveClaims(ctx context.Context) error { ...@@ -156,9 +163,13 @@ func (a *Agent) tryResolveClaims(ctx context.Context) error {
if len(claims) == 0 { if len(claims) == 0 {
return errNoResolvableClaims return errNoResolvableClaims
} }
maxChessTime := a.gameDuration / 2
var resolvableClaims []uint64 var resolvableClaims []uint64
for _, claim := range claims { for _, claim := range claims {
if claim.ChessTime(a.l1Clock.Now()) <= maxChessTime {
continue
}
if a.selective { if a.selective {
a.log.Trace("Selective claim resolution, checking if claim is incentivized", "claimIdx", claim.ContractIndex) a.log.Trace("Selective claim resolution, checking if claim is incentivized", "claimIdx", claim.ContractIndex)
isUncounteredClaim := slices.Contains(a.claimants, claim.Claimant) && claim.CounteredBy == common.Address{} isUncounteredClaim := slices.Contains(a.claimants, claim.Claimant) && claim.CounteredBy == common.Address{}
...@@ -186,9 +197,9 @@ func (a *Agent) tryResolveClaims(ctx context.Context) error { ...@@ -186,9 +197,9 @@ func (a *Agent) tryResolveClaims(ctx context.Context) error {
} }
func (a *Agent) resolveClaims(ctx context.Context) error { func (a *Agent) resolveClaims(ctx context.Context) error {
start := a.cl.Now() start := a.systemClock.Now()
defer func() { defer func() {
a.metrics.RecordClaimResolutionTime(a.cl.Since(start).Seconds()) a.metrics.RecordClaimResolutionTime(a.systemClock.Since(start).Seconds())
}() }()
for { for {
err := a.tryResolveClaims(ctx) err := a.tryResolveClaims(ctx)
......
...@@ -131,6 +131,22 @@ func TestAgent_SelectiveClaimResolution(t *testing.T) { ...@@ -131,6 +131,22 @@ func TestAgent_SelectiveClaimResolution(t *testing.T) {
} }
} }
func TestSkipAttemptingToResolveClaimsWhenClockNotExpired(t *testing.T) {
agent, claimLoader, responder := setupTestAgent(t)
responder.callResolveErr = errors.New("game is not resolvable")
responder.callResolveClaimErr = errors.New("claim is not resolvable")
depth := types.Depth(4)
claimBuilder := test.NewClaimBuilder(t, depth, alphabet.NewTraceProvider(big.NewInt(0), depth))
claimLoader.claims = []types.Claim{
claimBuilder.CreateRootClaim(test.WithExpiredClock(agent.gameDuration)),
}
require.NoError(t, agent.Act(context.Background()))
require.Zero(t, responder.callResolveClaimCount)
}
func TestLoadClaimsWhenGameNotResolvable(t *testing.T) { 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) agent, claimLoader, responder := setupTestAgent(t)
...@@ -154,10 +170,12 @@ func setupTestAgent(t *testing.T) (*Agent, *stubClaimLoader, *stubResponder) { ...@@ -154,10 +170,12 @@ func setupTestAgent(t *testing.T) (*Agent, *stubClaimLoader, *stubResponder) {
logger := testlog.Logger(t, log.LevelInfo) logger := testlog.Logger(t, log.LevelInfo)
claimLoader := &stubClaimLoader{} claimLoader := &stubClaimLoader{}
depth := types.Depth(4) depth := types.Depth(4)
gameDuration := 6 * time.Minute
provider := alphabet.NewTraceProvider(big.NewInt(0), depth) provider := alphabet.NewTraceProvider(big.NewInt(0), depth)
responder := &stubResponder{} responder := &stubResponder{}
cl := clock.NewDeterministicClock(time.UnixMilli(0)) systemClock := clock.NewDeterministicClock(time.UnixMilli(120200))
agent := NewAgent(metrics.NoopMetrics, cl, claimLoader, depth, trace.NewSimpleTraceAccessor(provider), responder, logger, false, []common.Address{}) l1Clock := clock.NewDeterministicClock(time.UnixMilli(100))
agent := NewAgent(metrics.NoopMetrics, systemClock, l1Clock, claimLoader, depth, gameDuration, trace.NewSimpleTraceAccessor(provider), responder, logger, false, []common.Address{})
return agent, claimLoader, responder return agent, claimLoader, responder
} }
......
...@@ -6,6 +6,7 @@ import ( ...@@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"math" "math"
"math/big" "math/big"
"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/contracts/metrics" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts/metrics"
...@@ -239,13 +240,13 @@ func (f *FaultDisputeGameContract) GetOracle(ctx context.Context) (*PreimageOrac ...@@ -239,13 +240,13 @@ func (f *FaultDisputeGameContract) GetOracle(ctx context.Context) (*PreimageOrac
return vm.Oracle(ctx) return vm.Oracle(ctx)
} }
func (f *FaultDisputeGameContract) GetGameDuration(ctx context.Context) (uint64, error) { func (f *FaultDisputeGameContract) GetGameDuration(ctx context.Context) (time.Duration, error) {
defer f.metrics.StartContractRequest("GetGameDuration")() defer f.metrics.StartContractRequest("GetGameDuration")()
result, err := f.multiCaller.SingleCall(ctx, rpcblock.Latest, f.contract.Call(methodGameDuration)) result, err := f.multiCaller.SingleCall(ctx, rpcblock.Latest, f.contract.Call(methodGameDuration))
if err != nil { if err != nil {
return 0, fmt.Errorf("failed to fetch game duration: %w", err) return 0, fmt.Errorf("failed to fetch game duration: %w", err)
} }
return result.GetUint64(0), nil return time.Duration(result.GetUint64(0)) * time.Second, nil
} }
func (f *FaultDisputeGameContract) GetMaxGameDepth(ctx context.Context) (types.Depth, error) { func (f *FaultDisputeGameContract) GetMaxGameDepth(ctx context.Context) (types.Depth, error) {
...@@ -381,18 +382,18 @@ func (f *FaultDisputeGameContract) resolveCall() *batching.ContractCall { ...@@ -381,18 +382,18 @@ func (f *FaultDisputeGameContract) resolveCall() *batching.ContractCall {
} }
// decodeClock decodes a uint128 into a Clock duration and timestamp. // decodeClock decodes a uint128 into a Clock duration and timestamp.
func decodeClock(clock *big.Int) *types.Clock { func decodeClock(clock *big.Int) types.Clock {
maxUint64 := new(big.Int).Add(new(big.Int).SetUint64(math.MaxUint64), big.NewInt(1)) maxUint64 := new(big.Int).Add(new(big.Int).SetUint64(math.MaxUint64), big.NewInt(1))
remainder := new(big.Int) remainder := new(big.Int)
quotient, _ := new(big.Int).QuoRem(clock, maxUint64, remainder) quotient, _ := new(big.Int).QuoRem(clock, maxUint64, remainder)
return types.NewClock(quotient.Uint64(), remainder.Uint64()) return types.NewClock(time.Duration(quotient.Int64())*time.Second, time.Unix(remainder.Int64(), 0))
} }
// packClock packs the Clock duration and timestamp into a uint128. // packClock packs the Clock duration and timestamp into a uint128.
func packClock(c *types.Clock) *big.Int { func packClock(c types.Clock) *big.Int {
duration := new(big.Int).SetUint64(c.Duration) duration := big.NewInt(int64(c.Duration.Seconds()))
encoded := new(big.Int).Lsh(duration, 64) encoded := new(big.Int).Lsh(duration, 64)
return new(big.Int).Or(encoded, new(big.Int).SetUint64(c.Timestamp)) return new(big.Int).Or(encoded, big.NewInt(c.Timestamp.Unix()))
} }
func (f *FaultDisputeGameContract) decodeClaim(result *batching.CallResult, contractIndex int) types.Claim { func (f *FaultDisputeGameContract) decodeClaim(result *batching.CallResult, contractIndex int) types.Claim {
......
...@@ -6,6 +6,7 @@ import ( ...@@ -6,6 +6,7 @@ import (
"math" "math"
"math/big" "math/big"
"testing" "testing"
"time"
"github.com/ethereum-optimism/optimism/op-bindings/bindings" "github.com/ethereum-optimism/optimism/op-bindings/bindings"
contractMetrics "github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts/metrics" contractMetrics "github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts/metrics"
...@@ -46,6 +47,7 @@ func TestSimpleGetters(t *testing.T) { ...@@ -46,6 +47,7 @@ func TestSimpleGetters(t *testing.T) {
methodAlias: "gameDuration", methodAlias: "gameDuration",
method: methodGameDuration, method: methodGameDuration,
result: uint64(5566), result: uint64(5566),
expected: 5566 * time.Second,
call: func(game *FaultDisputeGameContract) (any, error) { call: func(game *FaultDisputeGameContract) (any, error) {
return game.GetGameDuration(context.Background()) return game.GetGameDuration(context.Background())
}, },
...@@ -114,8 +116,8 @@ func TestClock_EncodingDecoding(t *testing.T) { ...@@ -114,8 +116,8 @@ func TestClock_EncodingDecoding(t *testing.T) {
by := common.Hex2Bytes("00000000000000050000000000000002") by := common.Hex2Bytes("00000000000000050000000000000002")
encoded := new(big.Int).SetBytes(by) encoded := new(big.Int).SetBytes(by)
clock := decodeClock(encoded) clock := decodeClock(encoded)
require.Equal(t, uint64(5), clock.Duration) require.Equal(t, 5*time.Second, clock.Duration)
require.Equal(t, uint64(2), clock.Timestamp) require.Equal(t, time.Unix(2, 0), clock.Timestamp)
require.Equal(t, encoded, packClock(clock)) require.Equal(t, encoded, packClock(clock))
}) })
...@@ -123,8 +125,8 @@ func TestClock_EncodingDecoding(t *testing.T) { ...@@ -123,8 +125,8 @@ func TestClock_EncodingDecoding(t *testing.T) {
by := common.Hex2Bytes("00000000000000000000000000000002") by := common.Hex2Bytes("00000000000000000000000000000002")
encoded := new(big.Int).SetBytes(by) encoded := new(big.Int).SetBytes(by)
clock := decodeClock(encoded) clock := decodeClock(encoded)
require.Equal(t, uint64(0), clock.Duration) require.Equal(t, 0*time.Second, clock.Duration)
require.Equal(t, uint64(2), clock.Timestamp) require.Equal(t, time.Unix(2, 0), clock.Timestamp)
require.Equal(t, encoded, packClock(clock)) require.Equal(t, encoded, packClock(clock))
}) })
...@@ -132,8 +134,8 @@ func TestClock_EncodingDecoding(t *testing.T) { ...@@ -132,8 +134,8 @@ func TestClock_EncodingDecoding(t *testing.T) {
by := common.Hex2Bytes("00000000000000050000000000000000") by := common.Hex2Bytes("00000000000000050000000000000000")
encoded := new(big.Int).SetBytes(by) encoded := new(big.Int).SetBytes(by)
clock := decodeClock(encoded) clock := decodeClock(encoded)
require.Equal(t, uint64(5), clock.Duration) require.Equal(t, 5*time.Second, clock.Duration)
require.Equal(t, uint64(0), clock.Timestamp) require.Equal(t, time.Unix(0, 0), clock.Timestamp)
require.Equal(t, encoded, packClock(clock)) require.Equal(t, encoded, packClock(clock))
}) })
...@@ -141,8 +143,8 @@ func TestClock_EncodingDecoding(t *testing.T) { ...@@ -141,8 +143,8 @@ func TestClock_EncodingDecoding(t *testing.T) {
by := common.Hex2Bytes("00000000000000000000000000000000") by := common.Hex2Bytes("00000000000000000000000000000000")
encoded := new(big.Int).SetBytes(by) encoded := new(big.Int).SetBytes(by)
clock := decodeClock(encoded) clock := decodeClock(encoded)
require.Equal(t, uint64(0), clock.Duration) require.Equal(t, 0*time.Second, clock.Duration)
require.Equal(t, uint64(0), clock.Timestamp) require.Equal(t, time.Unix(0, 0), clock.Timestamp)
require.Equal(t, encoded.Uint64(), packClock(clock).Uint64()) require.Equal(t, encoded.Uint64(), packClock(clock).Uint64())
}) })
} }
......
...@@ -4,6 +4,7 @@ import ( ...@@ -4,6 +4,7 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"time"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/claims" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/claims"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts"
...@@ -58,6 +59,7 @@ type GameContract interface { ...@@ -58,6 +59,7 @@ type GameContract interface {
ClaimLoader ClaimLoader
GetStatus(ctx context.Context) (gameTypes.GameStatus, error) GetStatus(ctx context.Context) (gameTypes.GameStatus, error)
GetMaxGameDepth(ctx context.Context) (types.Depth, error) GetMaxGameDepth(ctx context.Context) (types.Depth, error)
GetGameDuration(ctx context.Context) (time.Duration, error)
GetOracle(ctx context.Context) (*contracts.PreimageOracleContract, error) GetOracle(ctx context.Context) (*contracts.PreimageOracleContract, error)
GetL1Head(ctx context.Context) (common.Hash, error) GetL1Head(ctx context.Context) (common.Hash, error)
} }
...@@ -102,6 +104,11 @@ func NewGamePlayer( ...@@ -102,6 +104,11 @@ func NewGamePlayer(
}, nil }, nil
} }
gameDuration, err := loader.GetGameDuration(ctx)
if err != nil {
return nil, fmt.Errorf("failed to fetch the game duration: %w", err)
}
gameDepth, err := loader.GetMaxGameDepth(ctx) gameDepth, err := loader.GetMaxGameDepth(ctx)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to fetch the game depth: %w", err) return nil, fmt.Errorf("failed to fetch the game depth: %w", err)
...@@ -139,7 +146,7 @@ func NewGamePlayer( ...@@ -139,7 +146,7 @@ func NewGamePlayer(
return nil, fmt.Errorf("failed to create the responder: %w", err) return nil, fmt.Errorf("failed to create the responder: %w", err)
} }
agent := NewAgent(m, systemClock, loader, gameDepth, accessor, responder, logger, selective, claimants) agent := NewAgent(m, systemClock, l1Clock, loader, gameDepth, gameDuration, accessor, responder, logger, selective, claimants)
return &GamePlayer{ return &GamePlayer{
act: agent.Act, act: agent.Act,
loader: loader, loader: loader,
......
...@@ -320,7 +320,6 @@ func applyActions(game types.Game, claimant common.Address, actions []types.Acti ...@@ -320,7 +320,6 @@ func applyActions(game types.Game, claimant common.Address, actions []types.Acti
Position: newPosition, Position: newPosition,
}, },
Claimant: claimant, Claimant: claimant,
Clock: nil,
ContractIndex: len(claims), ContractIndex: len(claims),
ParentContractIndex: action.ParentIdx, ParentContractIndex: action.ParentIdx,
} }
......
...@@ -2,8 +2,10 @@ package test ...@@ -2,8 +2,10 @@ package test
import ( import (
"context" "context"
"math"
"math/big" "math/big"
"testing" "testing"
"time"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
...@@ -13,10 +15,11 @@ import ( ...@@ -13,10 +15,11 @@ import (
var DefaultClaimant = common.Address{0xba, 0xdb, 0xad, 0xba, 0xdb, 0xad} var DefaultClaimant = common.Address{0xba, 0xdb, 0xad, 0xba, 0xdb, 0xad}
type claimCfg struct { type claimCfg struct {
value common.Hash value common.Hash
invalidValue bool invalidValue bool
claimant common.Address claimant common.Address
parentIdx int parentIdx int
clockDuration time.Duration
} }
func newClaimCfg(opts ...ClaimOpt) *claimCfg { func newClaimCfg(opts ...ClaimOpt) *claimCfg {
...@@ -60,6 +63,11 @@ func WithParent(claim types.Claim) ClaimOpt { ...@@ -60,6 +63,11 @@ func WithParent(claim types.Claim) ClaimOpt {
cfg.parentIdx = claim.ContractIndex cfg.parentIdx = claim.ContractIndex
}) })
} }
func WithExpiredClock(gameDuration time.Duration) ClaimOpt {
return claimOptFn(func(cfg *claimCfg) {
cfg.clockDuration = gameDuration / 2
})
}
// ClaimBuilder is a test utility to enable creating claims in a wide range of situations // ClaimBuilder is a test utility to enable creating claims in a wide range of situations
type ClaimBuilder struct { type ClaimBuilder struct {
...@@ -123,6 +131,10 @@ func (c *ClaimBuilder) claim(pos types.Position, opts ...ClaimOpt) types.Claim { ...@@ -123,6 +131,10 @@ func (c *ClaimBuilder) claim(pos types.Position, opts ...ClaimOpt) types.Claim {
Position: pos, Position: pos,
}, },
Claimant: DefaultClaimant, Claimant: DefaultClaimant,
Clock: types.Clock{
Duration: cfg.clockDuration,
Timestamp: time.Unix(math.MaxInt64-1, 0),
},
} }
if cfg.claimant != (common.Address{}) { if cfg.claimant != (common.Address{}) {
claim.Claimant = cfg.claimant claim.Claimant = cfg.claimant
......
...@@ -153,7 +153,7 @@ type Claim struct { ...@@ -153,7 +153,7 @@ type Claim struct {
// to be changed/removed to avoid invalid/stale contract state. // to be changed/removed to avoid invalid/stale contract state.
CounteredBy common.Address CounteredBy common.Address
Claimant common.Address Claimant common.Address
Clock *Clock Clock Clock
// Location of the claim & it's parent inside the contract. Does not exist // Location of the claim & it's parent inside the contract. Does not exist
// for claims that have not made it to the contract. // for claims that have not made it to the contract.
ContractIndex int ContractIndex int
...@@ -169,37 +169,33 @@ func (c Claim) ID() ClaimID { ...@@ -169,37 +169,33 @@ func (c Claim) ID() ClaimID {
} }
// IsRoot returns true if this claim is the root claim. // IsRoot returns true if this claim is the root claim.
func (c *Claim) IsRoot() bool { func (c Claim) IsRoot() bool {
return c.Position.IsRootPosition() return c.Position.IsRootPosition()
} }
// ChessTime returns the amount of time accumulated in the chess clock. // ChessTime returns the amount of time accumulated in the chess clock.
// Does not assume the claim is countered and uses the specified time // Does not assume the claim is countered and uses the specified time
// to calculate the time since the claim was posted. // to calculate the time since the claim was posted.
func (c *Claim) ChessTime(now time.Time) time.Duration { func (c Claim) ChessTime(now time.Time) time.Duration {
timeSince := int64(0) timeSince := time.Duration(0)
if now.Unix() > int64(c.Clock.Timestamp) { if now.Compare(c.Clock.Timestamp) > 0 {
timeSince = now.Unix() - int64(c.Clock.Timestamp) timeSince = now.Sub(c.Clock.Timestamp)
} }
return time.Duration(c.Clock.Duration) + time.Duration(timeSince) return c.Clock.Duration + timeSince
} }
// Clock is a packed uint128 with the upper 64 bits being the // Clock tracks the chess clock for a claim.
// duration and the lower 64 bits being the timestamp.
// ┌────────────┬────────────────┐
// │ Bits │ Value │
// ├────────────┼────────────────┤
// │ [0, 64) │ Duration │
// │ [64, 128) │ Timestamp │
// └────────────┴────────────────┘
type Clock struct { type Clock struct {
Duration uint64 // Duration is the time elapsed on the chess clock at the last update.
Timestamp uint64 Duration time.Duration
// Timestamp is the time that the clock was last updated.
Timestamp time.Time
} }
// NewClock creates a new Clock instance. // NewClock creates a new Clock instance.
func NewClock(duration uint64, timestamp uint64) *Clock { func NewClock(duration time.Duration, timestamp time.Time) Clock {
return &Clock{ return Clock{
Duration: duration, Duration: duration,
Timestamp: timestamp, Timestamp: timestamp,
} }
......
...@@ -11,8 +11,8 @@ import ( ...@@ -11,8 +11,8 @@ import (
func TestClaim_RemainingDuration(t *testing.T) { func TestClaim_RemainingDuration(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
duration uint64 duration time.Duration
timestamp uint64 timestamp int64
now int64 now int64
expected uint64 expected uint64
}{ }{
...@@ -25,28 +25,28 @@ func TestClaim_RemainingDuration(t *testing.T) { ...@@ -25,28 +25,28 @@ func TestClaim_RemainingDuration(t *testing.T) {
}, },
{ {
name: "ZeroTimestamp", name: "ZeroTimestamp",
duration: 5, duration: 5 * time.Second,
timestamp: 0, timestamp: 0,
now: 0, now: 0,
expected: 5, expected: 5,
}, },
{ {
name: "ZeroTimestampWithNow", name: "ZeroTimestampWithNow",
duration: 5, duration: 5 * time.Second,
timestamp: 0, timestamp: 0,
now: 10, now: 10,
expected: 15, expected: 15,
}, },
{ {
name: "ZeroNow", name: "ZeroNow",
duration: 5, duration: 5 * time.Second,
timestamp: 10, timestamp: 10,
now: 0, now: 0,
expected: 5, expected: 5,
}, },
{ {
name: "ValidTimeSinze", name: "ValidTimeSinze",
duration: 20, duration: 20 * time.Second,
timestamp: 10, timestamp: 10,
now: 15, now: 15,
expected: 25, expected: 25,
...@@ -57,9 +57,9 @@ func TestClaim_RemainingDuration(t *testing.T) { ...@@ -57,9 +57,9 @@ func TestClaim_RemainingDuration(t *testing.T) {
test := test test := test
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
claim := &Claim{ claim := &Claim{
Clock: NewClock(test.duration, test.timestamp), Clock: NewClock(test.duration, time.Unix(test.timestamp, 0)),
} }
require.Equal(t, time.Duration(test.expected), claim.ChessTime(time.Unix(test.now, 0))) require.Equal(t, time.Duration(test.expected)*time.Second, claim.ChessTime(time.Unix(test.now, 0)))
}) })
} }
} }
......
package resolution package resolution
import ( import (
"time"
"github.com/ethereum-optimism/optimism/op-dispute-mon/mon/types" "github.com/ethereum-optimism/optimism/op-dispute-mon/mon/types"
"github.com/ethereum-optimism/optimism/op-service/clock" "github.com/ethereum-optimism/optimism/op-service/clock"
) )
...@@ -41,10 +43,10 @@ func (d *DelayCalculator) getOverflowTime(maxGameDuration uint64, claim *types.E ...@@ -41,10 +43,10 @@ func (d *DelayCalculator) getOverflowTime(maxGameDuration uint64, claim *types.E
if claim.Resolved { if claim.Resolved {
return 0 return 0
} }
maxChessTime := maxGameDuration / 2 maxChessTime := time.Duration(maxGameDuration/2) * time.Second
accumulatedTime := uint64(claim.ChessTime(d.clock.Now())) accumulatedTime := claim.ChessTime(d.clock.Now())
if accumulatedTime < maxChessTime { if accumulatedTime < maxChessTime {
return 0 return 0
} }
return accumulatedTime - maxChessTime return uint64((accumulatedTime - maxChessTime).Seconds())
} }
...@@ -29,8 +29,8 @@ func TestDelayCalculator_getOverflowTime(t *testing.T) { ...@@ -29,8 +29,8 @@ func TestDelayCalculator_getOverflowTime(t *testing.T) {
t.Run("RemainingTime", func(t *testing.T) { t.Run("RemainingTime", func(t *testing.T) {
d, metrics, cl := setupDelayCalculatorTest(t) d, metrics, cl := setupDelayCalculatorTest(t)
duration := uint64(3 * 60) duration := 3 * time.Minute
timestamp := uint64(cl.Now().Add(-time.Minute).Unix()) timestamp := cl.Now().Add(-time.Minute)
claim := &monTypes.EnrichedClaim{ claim := &monTypes.EnrichedClaim{
Claim: types.Claim{ Claim: types.Claim{
ClaimData: types.ClaimData{ ClaimData: types.ClaimData{
...@@ -46,8 +46,8 @@ func TestDelayCalculator_getOverflowTime(t *testing.T) { ...@@ -46,8 +46,8 @@ func TestDelayCalculator_getOverflowTime(t *testing.T) {
t.Run("OverflowTime", func(t *testing.T) { t.Run("OverflowTime", func(t *testing.T) {
d, metrics, cl := setupDelayCalculatorTest(t) d, metrics, cl := setupDelayCalculatorTest(t)
duration := maxGameDuration / 2 duration := time.Duration(maxGameDuration/2) * time.Second
timestamp := uint64(cl.Now().Add(4 * -time.Minute).Unix()) timestamp := cl.Now().Add(4 * -time.Minute)
claim := &monTypes.EnrichedClaim{ claim := &monTypes.EnrichedClaim{
Claim: types.Claim{ Claim: types.Claim{
ClaimData: types.ClaimData{ ClaimData: types.ClaimData{
...@@ -136,10 +136,10 @@ func createGameWithClaimsList() []*monTypes.EnrichedGameData { ...@@ -136,10 +136,10 @@ func createGameWithClaimsList() []*monTypes.EnrichedGameData {
} }
func createClaimList() []monTypes.EnrichedClaim { func createClaimList() []monTypes.EnrichedClaim {
newClock := func(multiplier int) *types.Clock { newClock := func(multiplier int) types.Clock {
duration := maxGameDuration / 2 duration := maxGameDuration / 2
timestamp := uint64(frozen.Add(-time.Minute * time.Duration(multiplier)).Unix()) timestamp := frozen.Add(-time.Minute * time.Duration(multiplier))
return types.NewClock(duration, timestamp) return types.NewClock(time.Duration(duration)*time.Second, timestamp)
} }
return []monTypes.EnrichedClaim{ return []monTypes.EnrichedClaim{
{ {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment