Commit f9c61344 authored by Adrian Sutton's avatar Adrian Sutton Committed by GitHub

dispute-mon: Fix expected credits metrics (#10242)

* dispute-mon: Load resolved state of claims from resolvedSubgames map

* dispute-mon: Fix expected credit metrics

Sum allocated bonds across all claims in a game to get the expected credit for a recipient.
Sum metrics across all games instead of replacing.
Check credits for all recipients involved in the game (not just those assigned bonds).

---------
Co-authored-by: default avatarrefcell <abigger87@gmail.com>
parent 500d4807
...@@ -39,19 +39,20 @@ func (b *Bonds) CheckBonds(games []*types.EnrichedGameData) { ...@@ -39,19 +39,20 @@ func (b *Bonds) CheckBonds(games []*types.EnrichedGameData) {
b.metrics.RecordBondCollateral(addr, collateral.Required, collateral.Actual) b.metrics.RecordBondCollateral(addr, collateral.Required, collateral.Actual)
} }
for _, game := range games { b.checkCredits(games)
b.checkCredits(game)
}
} }
func (b *Bonds) checkCredits(game *types.EnrichedGameData) { func (b *Bonds) checkCredits(games []*types.EnrichedGameData) {
creditMetrics := make(map[metrics.CreditExpectation]int)
for _, game := range games {
// Check if the max duration has been reached for this game // Check if the max duration has been reached for this game
duration := uint64(b.clock.Now().Unix()) - game.Timestamp duration := uint64(b.clock.Now().Unix()) - game.Timestamp
maxDurationReached := duration >= game.MaxClockDuration*2 maxDurationReached := duration >= game.MaxClockDuration*2
// Iterate over claims and filter out resolved ones // Iterate over claims, filter out resolved ones and sum up expected credits per recipient
recipients := make(map[int]common.Address) expectedCredits := make(map[common.Address]*big.Int)
for i, claim := range game.Claims { for _, claim := range game.Claims {
// Skip unresolved claims since these bonds will not appear in the credits. // Skip unresolved claims since these bonds will not appear in the credits.
if !claim.Resolved { if !claim.Resolved {
continue continue
...@@ -61,30 +62,50 @@ func (b *Bonds) checkCredits(game *types.EnrichedGameData) { ...@@ -61,30 +62,50 @@ func (b *Bonds) checkCredits(game *types.EnrichedGameData) {
if claim.CounteredBy != (common.Address{}) { if claim.CounteredBy != (common.Address{}) {
recipient = claim.CounteredBy recipient = claim.CounteredBy
} }
recipients[i] = recipient current := expectedCredits[recipient]
if current == nil {
current = big.NewInt(0)
}
expectedCredits[recipient] = new(big.Int).Add(current, claim.Bond)
} }
creditMetrics := make(map[metrics.CreditExpectation]int) allRecipients := make(map[common.Address]bool)
for i, recipient := range recipients { for address := range expectedCredits {
expected := game.Credits[recipient] allRecipients[address] = true
comparison := expected.Cmp(game.RequiredBonds[i]) }
for address := range game.Credits {
allRecipients[address] = true
}
for recipient := range allRecipients {
actual := game.Credits[recipient]
if actual == nil {
actual = big.NewInt(0)
}
expected := expectedCredits[recipient]
if expected == nil {
expected = big.NewInt(0)
}
comparison := actual.Cmp(expected)
if maxDurationReached { if maxDurationReached {
if comparison > 0 { if comparison > 0 {
creditMetrics[metrics.CreditBelowMaxDuration] += 1 creditMetrics[metrics.CreditAboveMaxDuration] += 1
b.logger.Warn("Credit above expected amount", "recipient", recipient, "expected", expected, "actual", actual, "gameAddr", game.Proxy, "duration", "reached")
} else if comparison == 0 { } else if comparison == 0 {
creditMetrics[metrics.CreditEqualMaxDuration] += 1 creditMetrics[metrics.CreditEqualMaxDuration] += 1
} else { } else {
creditMetrics[metrics.CreditAboveMaxDuration] += 1 creditMetrics[metrics.CreditBelowMaxDuration] += 1
b.logger.Warn("credit above expected amount", "recipient", recipient, "expected", expected, "gameAddr", game.Proxy, "duration", "max_duration")
} }
} else { } else {
if comparison > 0 { if comparison > 0 {
creditMetrics[metrics.CreditBelowNonMaxDuration] += 1 creditMetrics[metrics.CreditAboveNonMaxDuration] += 1
b.logger.Warn("Credit above expected amount", "recipient", recipient, "expected", expected, "actual", actual, "gameAddr", game.Proxy, "duration", "unreached")
} else if comparison == 0 { } else if comparison == 0 {
creditMetrics[metrics.CreditEqualNonMaxDuration] += 1 creditMetrics[metrics.CreditEqualNonMaxDuration] += 1
} else { } else {
creditMetrics[metrics.CreditAboveNonMaxDuration] += 1 creditMetrics[metrics.CreditBelowNonMaxDuration] += 1
b.logger.Warn("credit above expected amount", "recipient", recipient, "expected", expected, "gameAddr", game.Proxy, "duration", "non_max_duration") b.logger.Warn("Credit withdrawn early", "recipient", recipient, "expected", expected, "actual", actual, "gameAddr", game.Proxy, "duration", "unreached")
}
} }
} }
} }
......
This diff is collapsed.
...@@ -18,7 +18,6 @@ var ErrIncorrectCreditCount = errors.New("incorrect credit count") ...@@ -18,7 +18,6 @@ var ErrIncorrectCreditCount = errors.New("incorrect credit count")
type BondCaller interface { type BondCaller interface {
GetCredits(context.Context, rpcblock.Block, ...common.Address) ([]*big.Int, error) GetCredits(context.Context, rpcblock.Block, ...common.Address) ([]*big.Int, error)
GetRequiredBonds(context.Context, rpcblock.Block, ...*big.Int) ([]*big.Int, error)
} }
type BondEnricher struct{} type BondEnricher struct{}
...@@ -28,16 +27,6 @@ func NewBondEnricher() *BondEnricher { ...@@ -28,16 +27,6 @@ func NewBondEnricher() *BondEnricher {
} }
func (b *BondEnricher) Enrich(ctx context.Context, block rpcblock.Block, caller GameCaller, game *monTypes.EnrichedGameData) error { func (b *BondEnricher) Enrich(ctx context.Context, block rpcblock.Block, caller GameCaller, game *monTypes.EnrichedGameData) error {
if err := b.enrichCredits(ctx, block, caller, game); err != nil {
return err
}
if err := b.enrichRequiredBonds(ctx, block, caller, game); err != nil {
return err
}
return nil
}
func (b *BondEnricher) enrichCredits(ctx context.Context, block rpcblock.Block, caller GameCaller, game *monTypes.EnrichedGameData) error {
recipients := make(map[common.Address]bool) recipients := make(map[common.Address]bool)
for _, claim := range game.Claims { for _, claim := range game.Claims {
if claim.CounteredBy != (common.Address{}) { if claim.CounteredBy != (common.Address{}) {
...@@ -60,33 +49,3 @@ func (b *BondEnricher) enrichCredits(ctx context.Context, block rpcblock.Block, ...@@ -60,33 +49,3 @@ func (b *BondEnricher) enrichCredits(ctx context.Context, block rpcblock.Block,
} }
return nil return nil
} }
func (b *BondEnricher) enrichRequiredBonds(ctx context.Context, block rpcblock.Block, caller GameCaller, game *monTypes.EnrichedGameData) error {
positions := make([]*big.Int, len(game.Claims))
for _, claim := range game.Claims {
// If the claim is not resolved, we don't need to get the bond
// for it since the Bond field in the claim will be accurate.
if !claim.Resolved {
continue
}
positions = append(positions, claim.Position.ToGIndex())
}
bonds, err := caller.GetRequiredBonds(ctx, block, positions...)
if err != nil {
return err
}
if len(bonds) != len(positions) {
return fmt.Errorf("%w, requested %v values but got %v", ErrIncorrectCreditCount, len(positions), len(bonds))
}
game.RequiredBonds = make(map[int]*big.Int)
bondIndex := 0
for i, claim := range game.Claims {
if !claim.Resolved {
game.RequiredBonds[i] = claim.Bond
continue
}
game.RequiredBonds[i] = bonds[bondIndex]
bondIndex++
}
return nil
}
...@@ -24,6 +24,9 @@ func makeTestGame() (*monTypes.EnrichedGameData, []common.Address) { ...@@ -24,6 +24,9 @@ func makeTestGame() (*monTypes.EnrichedGameData, []common.Address) {
Claims: []monTypes.EnrichedClaim{ Claims: []monTypes.EnrichedClaim{
{ {
Claim: faultTypes.Claim{ Claim: faultTypes.Claim{
ClaimData: faultTypes.ClaimData{
Position: faultTypes.NewPositionFromGIndex(big.NewInt(1)),
},
Claimant: common.Address{0x01}, Claimant: common.Address{0x01},
CounteredBy: common.Address{0x02}, CounteredBy: common.Address{0x02},
}, },
...@@ -33,6 +36,7 @@ func makeTestGame() (*monTypes.EnrichedGameData, []common.Address) { ...@@ -33,6 +36,7 @@ func makeTestGame() (*monTypes.EnrichedGameData, []common.Address) {
Claim: faultTypes.Claim{ Claim: faultTypes.Claim{
ClaimData: faultTypes.ClaimData{ ClaimData: faultTypes.ClaimData{
Bond: big.NewInt(5), Bond: big.NewInt(5),
Position: faultTypes.NewPositionFromGIndex(big.NewInt(2)),
}, },
Claimant: common.Address{0x03}, Claimant: common.Address{0x03},
CounteredBy: common.Address{}, CounteredBy: common.Address{},
...@@ -42,6 +46,7 @@ func makeTestGame() (*monTypes.EnrichedGameData, []common.Address) { ...@@ -42,6 +46,7 @@ func makeTestGame() (*monTypes.EnrichedGameData, []common.Address) {
Claim: faultTypes.Claim{ Claim: faultTypes.Claim{
ClaimData: faultTypes.ClaimData{ ClaimData: faultTypes.ClaimData{
Bond: big.NewInt(7), Bond: big.NewInt(7),
Position: faultTypes.NewPositionFromGIndex(big.NewInt(3)),
}, },
Claimant: common.Address{0x03}, Claimant: common.Address{0x03},
CounteredBy: common.Address{0x04}, CounteredBy: common.Address{0x04},
...@@ -82,13 +87,7 @@ func TestBondEnricher(t *testing.T) { ...@@ -82,13 +87,7 @@ func TestBondEnricher(t *testing.T) {
recipients[1]: big.NewInt(30), recipients[1]: big.NewInt(30),
recipients[2]: big.NewInt(40), recipients[2]: big.NewInt(40),
} }
requiredBonds := []*big.Int{ caller := &mockGameCaller{credits: expectedCredits}
big.NewInt(10),
big.NewInt(20),
big.NewInt(30),
big.NewInt(40),
}
caller := &mockGameCaller{credits: expectedCredits, requiredBonds: requiredBonds}
err := enricher.Enrich(context.Background(), rpcblock.Latest, caller, game) err := enricher.Enrich(context.Background(), rpcblock.Latest, caller, game)
require.NoError(t, err) require.NoError(t, err)
......
...@@ -190,9 +190,6 @@ type mockGameCaller struct { ...@@ -190,9 +190,6 @@ type mockGameCaller struct {
balanceErr error balanceErr error
balance *big.Int balance *big.Int
balanceAddr common.Address balanceAddr common.Address
requiredBondCalls int
requiredBondErr error
requiredBonds []*big.Int
withdrawalsCalls int withdrawalsCalls int
withdrawalsErr error withdrawalsErr error
withdrawals []*contracts.WithdrawalRequest withdrawals []*contracts.WithdrawalRequest
...@@ -200,14 +197,6 @@ type mockGameCaller struct { ...@@ -200,14 +197,6 @@ type mockGameCaller struct {
resolved map[int]bool resolved map[int]bool
} }
func (m *mockGameCaller) GetRequiredBonds(ctx context.Context, block rpcblock.Block, positions ...*big.Int) ([]*big.Int, error) {
m.requiredBondCalls++
if m.requiredBondErr != nil {
return nil, m.requiredBondErr
}
return m.requiredBonds, nil
}
func (m *mockGameCaller) GetWithdrawals(_ context.Context, _ rpcblock.Block, _ common.Address, _ ...common.Address) ([]*contracts.WithdrawalRequest, error) { func (m *mockGameCaller) GetWithdrawals(_ context.Context, _ rpcblock.Block, _ common.Address, _ ...common.Address) ([]*contracts.WithdrawalRequest, error) {
m.withdrawalsCalls++ m.withdrawalsCalls++
if m.withdrawalsErr != nil { if m.withdrawalsErr != nil {
......
...@@ -31,11 +31,6 @@ type EnrichedGameData struct { ...@@ -31,11 +31,6 @@ type EnrichedGameData struct {
// Credits records the paid out bonds for the game, keyed by recipient. // Credits records the paid out bonds for the game, keyed by recipient.
Credits map[common.Address]*big.Int Credits map[common.Address]*big.Int
// RequiredBonds maps *resolved* claim indices to their required bond amounts.
// Required bonds are not needed for unresolved claims since
// the `Bond` field in the claim is the required bond amount.
RequiredBonds map[int]*big.Int
// WithdrawalRequests maps recipients with withdrawal requests in DelayedWETH for this game. // WithdrawalRequests maps recipients with withdrawal requests in DelayedWETH for this game.
WithdrawalRequests map[common.Address]*contracts.WithdrawalRequest WithdrawalRequests map[common.Address]*contracts.WithdrawalRequest
......
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