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,52 +39,73 @@ func (b *Bonds) CheckBonds(games []*types.EnrichedGameData) {
b.metrics.RecordBondCollateral(addr, collateral.Required, collateral.Actual)
}
for _, game := range games {
b.checkCredits(game)
}
b.checkCredits(games)
}
func (b *Bonds) checkCredits(game *types.EnrichedGameData) {
// Check if the max duration has been reached for this game
duration := uint64(b.clock.Now().Unix()) - game.Timestamp
maxDurationReached := duration >= game.MaxClockDuration*2
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
duration := uint64(b.clock.Now().Unix()) - game.Timestamp
maxDurationReached := duration >= game.MaxClockDuration*2
// Iterate over claims, filter out resolved ones and sum up expected credits per recipient
expectedCredits := make(map[common.Address]*big.Int)
for _, claim := range game.Claims {
// Skip unresolved claims since these bonds will not appear in the credits.
if !claim.Resolved {
continue
}
// The recipient of a resolved claim is the claimant unless it's been countered.
recipient := claim.Claimant
if claim.CounteredBy != (common.Address{}) {
recipient = claim.CounteredBy
}
current := expectedCredits[recipient]
if current == nil {
current = big.NewInt(0)
}
expectedCredits[recipient] = new(big.Int).Add(current, claim.Bond)
}
// Iterate over claims and filter out resolved ones
recipients := make(map[int]common.Address)
for i, claim := range game.Claims {
// Skip unresolved claims since these bonds will not appear in the credits.
if !claim.Resolved {
continue
allRecipients := make(map[common.Address]bool)
for address := range expectedCredits {
allRecipients[address] = true
}
// The recipient of a resolved claim is the claimant unless it's been countered.
recipient := claim.Claimant
if claim.CounteredBy != (common.Address{}) {
recipient = claim.CounteredBy
for address := range game.Credits {
allRecipients[address] = true
}
recipients[i] = recipient
}
creditMetrics := make(map[metrics.CreditExpectation]int)
for i, recipient := range recipients {
expected := game.Credits[recipient]
comparison := expected.Cmp(game.RequiredBonds[i])
if maxDurationReached {
if comparison > 0 {
creditMetrics[metrics.CreditBelowMaxDuration] += 1
} else if comparison == 0 {
creditMetrics[metrics.CreditEqualMaxDuration] += 1
} else {
creditMetrics[metrics.CreditAboveMaxDuration] += 1
b.logger.Warn("credit above expected amount", "recipient", recipient, "expected", expected, "gameAddr", game.Proxy, "duration", "max_duration")
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)
}
} else {
if comparison > 0 {
creditMetrics[metrics.CreditBelowNonMaxDuration] += 1
} else if comparison == 0 {
creditMetrics[metrics.CreditEqualNonMaxDuration] += 1
comparison := actual.Cmp(expected)
if maxDurationReached {
if comparison > 0 {
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 {
creditMetrics[metrics.CreditEqualMaxDuration] += 1
} else {
creditMetrics[metrics.CreditBelowMaxDuration] += 1
}
} else {
creditMetrics[metrics.CreditAboveNonMaxDuration] += 1
b.logger.Warn("credit above expected amount", "recipient", recipient, "expected", expected, "gameAddr", game.Proxy, "duration", "non_max_duration")
if comparison > 0 {
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 {
creditMetrics[metrics.CreditEqualNonMaxDuration] += 1
} else {
creditMetrics[metrics.CreditBelowNonMaxDuration] += 1
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")
type BondCaller interface {
GetCredits(context.Context, rpcblock.Block, ...common.Address) ([]*big.Int, error)
GetRequiredBonds(context.Context, rpcblock.Block, ...*big.Int) ([]*big.Int, error)
}
type BondEnricher struct{}
......@@ -28,16 +27,6 @@ func NewBondEnricher() *BondEnricher {
}
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)
for _, claim := range game.Claims {
if claim.CounteredBy != (common.Address{}) {
......@@ -60,33 +49,3 @@ func (b *BondEnricher) enrichCredits(ctx context.Context, block rpcblock.Block,
}
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) {
Claims: []monTypes.EnrichedClaim{
{
Claim: faultTypes.Claim{
ClaimData: faultTypes.ClaimData{
Position: faultTypes.NewPositionFromGIndex(big.NewInt(1)),
},
Claimant: common.Address{0x01},
CounteredBy: common.Address{0x02},
},
......@@ -32,7 +35,8 @@ func makeTestGame() (*monTypes.EnrichedGameData, []common.Address) {
{
Claim: faultTypes.Claim{
ClaimData: faultTypes.ClaimData{
Bond: big.NewInt(5),
Bond: big.NewInt(5),
Position: faultTypes.NewPositionFromGIndex(big.NewInt(2)),
},
Claimant: common.Address{0x03},
CounteredBy: common.Address{},
......@@ -41,7 +45,8 @@ func makeTestGame() (*monTypes.EnrichedGameData, []common.Address) {
{
Claim: faultTypes.Claim{
ClaimData: faultTypes.ClaimData{
Bond: big.NewInt(7),
Bond: big.NewInt(7),
Position: faultTypes.NewPositionFromGIndex(big.NewInt(3)),
},
Claimant: common.Address{0x03},
CounteredBy: common.Address{0x04},
......@@ -82,13 +87,7 @@ func TestBondEnricher(t *testing.T) {
recipients[1]: big.NewInt(30),
recipients[2]: big.NewInt(40),
}
requiredBonds := []*big.Int{
big.NewInt(10),
big.NewInt(20),
big.NewInt(30),
big.NewInt(40),
}
caller := &mockGameCaller{credits: expectedCredits, requiredBonds: requiredBonds}
caller := &mockGameCaller{credits: expectedCredits}
err := enricher.Enrich(context.Background(), rpcblock.Latest, caller, game)
require.NoError(t, err)
......
......@@ -177,35 +177,24 @@ func (m *mockGameCallerCreator) CreateGameCaller(_ gameTypes.GameMetadata) (Game
}
type mockGameCaller struct {
metadataCalls int
metadataErr error
claimsCalls int
claimsErr error
rootClaim common.Hash
claims []faultTypes.Claim
requestedCredits []common.Address
creditsErr error
credits map[common.Address]*big.Int
extraCredit []*big.Int
balanceErr error
balance *big.Int
balanceAddr common.Address
requiredBondCalls int
requiredBondErr error
requiredBonds []*big.Int
withdrawalsCalls int
withdrawalsErr error
withdrawals []*contracts.WithdrawalRequest
resolvedErr error
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
metadataCalls int
metadataErr error
claimsCalls int
claimsErr error
rootClaim common.Hash
claims []faultTypes.Claim
requestedCredits []common.Address
creditsErr error
credits map[common.Address]*big.Int
extraCredit []*big.Int
balanceErr error
balance *big.Int
balanceAddr common.Address
withdrawalsCalls int
withdrawalsErr error
withdrawals []*contracts.WithdrawalRequest
resolvedErr error
resolved map[int]bool
}
func (m *mockGameCaller) GetWithdrawals(_ context.Context, _ rpcblock.Block, _ common.Address, _ ...common.Address) ([]*contracts.WithdrawalRequest, error) {
......
......@@ -31,11 +31,6 @@ type EnrichedGameData struct {
// Credits records the paid out bonds for the game, keyed by recipient.
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 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