From 6fde06119dc813ef433a10e5507a838332545c15 Mon Sep 17 00:00:00 2001 From: Valentin Trinque Date: Fri, 10 May 2024 15:23:02 +0200 Subject: [PATCH] fix: Ensure the vesting stats events are generated after the computation --- CHANGELOG.md | 2 +- core/collateral/engine_test.go | 2 +- core/vesting/engine.go | 11 ++- core/vesting/engine_test.go | 152 +++++++++++++-------------------- core/vesting/snapshot.go | 3 +- 5 files changed, 67 insertions(+), 103 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a98b02a00a3..f9ca1de97c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,7 @@ ### 🐛 Fixes -- [](https://github.com/vegaprotocol/vega/issues/xxx) +- [11066](https://github.com/vegaprotocol/vega/issues/11066) - Ensure vesting statistics match vesting accounts numbers. ## 0.76.1 diff --git a/core/collateral/engine_test.go b/core/collateral/engine_test.go index aaea6f9e658..d418e8a015f 100644 --- a/core/collateral/engine_test.go +++ b/core/collateral/engine_test.go @@ -190,7 +190,7 @@ func TestGetAllVestingQuantumBalance(t *testing.T) { ) balance = eng.GetAllVestingQuantumBalance(party) - assert.Equal(t, balance.String(), "103.17") + assert.Equal(t, balance.String(), "103.1666666666666667") } func testClearFeeAccounts(t *testing.T) { diff --git a/core/vesting/engine.go b/core/vesting/engine.go index de05bfdd3bf..81e7852976e 100644 --- a/core/vesting/engine.go +++ b/core/vesting/engine.go @@ -133,11 +133,11 @@ func (e *Engine) OnRewardVestingMinimumTransferUpdate(_ context.Context, minimum func (e *Engine) OnEpochEvent(ctx context.Context, epoch types.Epoch) { if epoch.Action == proto.EpochAction_EPOCH_ACTION_END { - e.broadcastRewardBonusMultipliers(ctx, epoch.Seq) e.moveLocked() e.distributeVested(ctx) - e.clearState() + e.broadcastVestingStatsUpdate(ctx, epoch.Seq) e.broadcastSummary(ctx, epoch.Seq) + e.clearState() } } @@ -328,7 +328,6 @@ func (e *Engine) makeTransfer( return transfer } -// just remove party entries once they are not needed anymore. func (e *Engine) clearState() { for party, v := range e.state { if len(v.Locked) == 0 && len(v.Vesting) == 0 { @@ -344,6 +343,10 @@ func (e *Engine) broadcastSummary(ctx context.Context, seq uint64) { } for p, pRewards := range e.state { + if len(pRewards.Vesting) == 0 && len(pRewards.Locked) == 0 { + continue + } + pSummary := &eventspb.PartyVestingSummary{ Party: p, PartyLockedBalances: []*eventspb.PartyLockedBalance{}, @@ -395,7 +398,7 @@ func (e *Engine) broadcastSummary(ctx context.Context, seq uint64) { e.broker.Send(events.NewVestingBalancesSummaryEvent(ctx, evt)) } -func (e *Engine) broadcastRewardBonusMultipliers(ctx context.Context, seq uint64) { +func (e *Engine) broadcastVestingStatsUpdate(ctx context.Context, seq uint64) { evt := &eventspb.VestingStatsUpdated{ AtEpoch: seq, Stats: make([]*eventspb.PartyVestingStats, 0, len(e.state)), diff --git a/core/vesting/engine_test.go b/core/vesting/engine_test.go index 58ba3ed61e4..fcaeb17e106 100644 --- a/core/vesting/engine_test.go +++ b/core/vesting/engine_test.go @@ -148,6 +148,9 @@ func TestDistributeAfterDelay(t *testing.T) { t.Run("First reward payment", func(t *testing.T) { epochSeq += 1 + + expectLedgerMovements(t, v) + v.broker.EXPECT().Send(gomock.Any()).Do(func(evt events.Event) { e, ok := evt.(*events.VestingStatsUpdated) require.True(t, ok, "Event should be a VestingStatsUpdated, but is %T", evt) @@ -156,21 +159,13 @@ func TestDistributeAfterDelay(t *testing.T) { Stats: []*eventspb.PartyVestingStats{ { PartyId: party, - RewardBonusMultiplier: "1", - QuantumBalance: "300", + RewardBonusMultiplier: "2", + QuantumBalance: "390", }, }, }, e.Proto()) }).Times(1) - v.broker.EXPECT().Send(gomock.Any()).Do(func(evt events.Event) { - e, ok := evt.(*events.LedgerMovements) - require.True(t, ok, "Event should be a LedgerMovements, but is %T", evt) - // LedgerMovements is the result of a mock, so it doesn't really make sense to verify data - // consistency. - assert.Equal(t, eventspb.LedgerMovements{LedgerMovements: []*vegapb.LedgerMovement{}}, e.Proto()) - }).Times(1) - v.broker.EXPECT().Send(gomock.Any()).Do(func(evt events.Event) { e, ok := evt.(*events.VestingBalancesSummary) require.True(t, ok, "Event should be a VestingBalancesSummary, but is %T", evt) @@ -199,6 +194,9 @@ func TestDistributeAfterDelay(t *testing.T) { t.Run("Second reward payment", func(t *testing.T) { epochSeq += 1 + + expectLedgerMovements(t, v) + v.broker.EXPECT().Send(gomock.Any()).Do(func(evt events.Event) { e, ok := evt.(*events.VestingStatsUpdated) require.True(t, ok, "Event should be a VestingStatsUpdated, but is %T", evt) @@ -208,20 +206,12 @@ func TestDistributeAfterDelay(t *testing.T) { { PartyId: party, RewardBonusMultiplier: "2", - QuantumBalance: "390", + QuantumBalance: "400", }, }, }, e.Proto()) }).Times(1) - v.broker.EXPECT().Send(gomock.Any()).Do(func(evt events.Event) { - e, ok := evt.(*events.LedgerMovements) - require.True(t, ok, "Event should be a LedgerMovements, but is %T", evt) - // LedgerMovements is the result of a mock, so it doesn't really make sense to verify data - // consistency. - assert.Equal(t, eventspb.LedgerMovements{LedgerMovements: []*vegapb.LedgerMovement{}}, e.Proto()) - }).Times(1) - v.broker.EXPECT().Send(gomock.Any()).Do(func(evt events.Event) { e, ok := evt.(*events.VestingBalancesSummary) require.True(t, ok, "Event should be a VestingBalancesSummary, but is %T", evt) @@ -337,6 +327,9 @@ func TestDistributeWithNoDelay(t *testing.T) { t.Run("First reward payment", func(t *testing.T) { epochSeq += 1 + + expectLedgerMovements(t, v) + v.broker.EXPECT().Send(gomock.Any()).Do(func(evt events.Event) { e, ok := evt.(*events.VestingStatsUpdated) require.True(t, ok, "Event should be a VestingStatsUpdated, but is %T", evt) @@ -345,21 +338,13 @@ func TestDistributeWithNoDelay(t *testing.T) { Stats: []*eventspb.PartyVestingStats{ { PartyId: party, - RewardBonusMultiplier: "1", - QuantumBalance: "300", + RewardBonusMultiplier: "2", + QuantumBalance: "390", }, }, }, e.Proto()) }).Times(1) - v.broker.EXPECT().Send(gomock.Any()).Do(func(evt events.Event) { - e, ok := evt.(*events.LedgerMovements) - require.True(t, ok, "Event should be a LedgerMovements, but is %T", evt) - // LedgerMovements is the result of a mock, so it doesn't really make sense to verify data - // consistency. - assert.Equal(t, eventspb.LedgerMovements{LedgerMovements: []*vegapb.LedgerMovement{}}, e.Proto()) - }).Times(1) - v.broker.EXPECT().Send(gomock.Any()).Do(func(evt events.Event) { e, ok := evt.(*events.VestingBalancesSummary) require.True(t, ok, "Event should be a VestingBalancesSummary, but is %T", evt) @@ -388,6 +373,9 @@ func TestDistributeWithNoDelay(t *testing.T) { t.Run("Second reward payment", func(t *testing.T) { epochSeq += 1 + + expectLedgerMovements(t, v) + v.broker.EXPECT().Send(gomock.Any()).Do(func(evt events.Event) { e, ok := evt.(*events.VestingStatsUpdated) require.True(t, ok, "Event should be a VestingStatsUpdated, but is %T", evt) @@ -397,20 +385,12 @@ func TestDistributeWithNoDelay(t *testing.T) { { PartyId: party, RewardBonusMultiplier: "2", - QuantumBalance: "390", + QuantumBalance: "400", }, }, }, e.Proto()) }).Times(1) - v.broker.EXPECT().Send(gomock.Any()).Do(func(evt events.Event) { - e, ok := evt.(*events.LedgerMovements) - require.True(t, ok, "Event should be a LedgerMovements, but is %T", evt) - // LedgerMovements is the result of a mock, so it doesn't really make sense to verify data - // consistency. - assert.Equal(t, eventspb.LedgerMovements{LedgerMovements: []*vegapb.LedgerMovement{}}, e.Proto()) - }).Times(1) - v.broker.EXPECT().Send(gomock.Any()).Do(func(evt events.Event) { e, ok := evt.(*events.VestingBalancesSummary) require.True(t, ok, "Event should be a VestingBalancesSummary, but is %T", evt) @@ -523,6 +503,9 @@ func TestDistributeWithStreakRate(t *testing.T) { t.Run("First reward payment", func(t *testing.T) { epochSeq += 1 + + expectLedgerMovements(t, v) + v.broker.EXPECT().Send(gomock.Any()).Do(func(evt events.Event) { e, ok := evt.(*events.VestingStatsUpdated) require.True(t, ok, "Event should be a VestingStatsUpdated, but is %T", evt) @@ -531,21 +514,13 @@ func TestDistributeWithStreakRate(t *testing.T) { Stats: []*eventspb.PartyVestingStats{ { PartyId: party, - RewardBonusMultiplier: "1", - QuantumBalance: "300", + RewardBonusMultiplier: "2", + QuantumBalance: "399", }, }, }, e.Proto()) }).Times(1) - v.broker.EXPECT().Send(gomock.Any()).Do(func(evt events.Event) { - e, ok := evt.(*events.LedgerMovements) - require.True(t, ok, "Event should be a LedgerMovements, but is %T", evt) - // LedgerMovements is the result of a mock, so it doesn't really make sense to verify data - // consistency. - assert.Equal(t, eventspb.LedgerMovements{LedgerMovements: []*vegapb.LedgerMovement{}}, e.Proto()) - }).Times(1) - v.broker.EXPECT().Send(gomock.Any()).Do(func(evt events.Event) { e, ok := evt.(*events.VestingBalancesSummary) require.True(t, ok, "Event should be a VestingBalancesSummary, but is %T", evt) @@ -574,6 +549,9 @@ func TestDistributeWithStreakRate(t *testing.T) { t.Run("Second reward payment", func(t *testing.T) { epochSeq += 1 + + expectLedgerMovements(t, v) + v.broker.EXPECT().Send(gomock.Any()).Do(func(evt events.Event) { e, ok := evt.(*events.VestingStatsUpdated) require.True(t, ok, "Event should be a VestingStatsUpdated, but is %T", evt) @@ -583,20 +561,12 @@ func TestDistributeWithStreakRate(t *testing.T) { { PartyId: party, RewardBonusMultiplier: "2", - QuantumBalance: "399", + QuantumBalance: "400", }, }, }, e.Proto()) }).Times(1) - v.broker.EXPECT().Send(gomock.Any()).Do(func(evt events.Event) { - e, ok := evt.(*events.LedgerMovements) - require.True(t, ok, "Event should be a LedgerMovements, but is %T", evt) - // LedgerMovements is the result of a mock, so it doesn't really make sense to verify data - // consistency. - assert.Equal(t, eventspb.LedgerMovements{LedgerMovements: []*vegapb.LedgerMovement{}}, e.Proto()) - }).Times(1) - v.broker.EXPECT().Send(gomock.Any()).Do(func(evt events.Event) { e, ok := evt.(*events.VestingBalancesSummary) require.True(t, ok, "Event should be a VestingBalancesSummary, but is %T", evt) @@ -763,6 +733,9 @@ func TestDistributeMultipleAfterDelay(t *testing.T) { t.Run("First reward payment", func(t *testing.T) { epochSeq += 1 + + expectLedgerMovements(t, v) + v.broker.EXPECT().Send(gomock.Any()).Do(func(evt events.Event) { e, ok := evt.(*events.VestingStatsUpdated) require.True(t, ok, "Event should be a VestingStatsUpdated, but is %T", evt) @@ -771,21 +744,13 @@ func TestDistributeMultipleAfterDelay(t *testing.T) { Stats: []*eventspb.PartyVestingStats{ { PartyId: party, - RewardBonusMultiplier: "1", - QuantumBalance: "300", + RewardBonusMultiplier: "2", + QuantumBalance: "390", }, }, }, e.Proto()) }).Times(1) - v.broker.EXPECT().Send(gomock.Any()).Do(func(evt events.Event) { - e, ok := evt.(*events.LedgerMovements) - require.True(t, ok, "Event should be a LedgerMovements, but is %T", evt) - // LedgerMovements is the result of a mock, so it doesn't really make sense to verify data - // consistency. - assert.Equal(t, eventspb.LedgerMovements{LedgerMovements: []*vegapb.LedgerMovement{}}, e.Proto()) - }).Times(1) - v.broker.EXPECT().Send(gomock.Any()).Do(func(evt events.Event) { e, ok := evt.(*events.VestingBalancesSummary) require.True(t, ok, "Event should be a VestingBalancesSummary, but is %T", evt) @@ -820,6 +785,9 @@ func TestDistributeMultipleAfterDelay(t *testing.T) { t.Run("Second reward payment", func(t *testing.T) { epochSeq += 1 + + expectLedgerMovements(t, v) + v.broker.EXPECT().Send(gomock.Any()).Do(func(evt events.Event) { e, ok := evt.(*events.VestingStatsUpdated) require.True(t, ok, "Event should be a VestingStatsUpdated, but is %T", evt) @@ -829,20 +797,12 @@ func TestDistributeMultipleAfterDelay(t *testing.T) { { PartyId: party, RewardBonusMultiplier: "2", - QuantumBalance: "390", + QuantumBalance: "489", }, }, }, e.Proto()) }).Times(1) - v.broker.EXPECT().Send(gomock.Any()).Do(func(evt events.Event) { - e, ok := evt.(*events.LedgerMovements) - require.True(t, ok, "Event should be a LedgerMovements, but is %T", evt) - // LedgerMovements is the result of a mock, so it doesn't really make sense to verify data - // consistency. - assert.Equal(t, eventspb.LedgerMovements{LedgerMovements: []*vegapb.LedgerMovement{}}, e.Proto()) - }).Times(1) - v.broker.EXPECT().Send(gomock.Any()).Do(func(evt events.Event) { e, ok := evt.(*events.VestingBalancesSummary) require.True(t, ok, "Event should be a VestingBalancesSummary, but is %T", evt) @@ -871,6 +831,9 @@ func TestDistributeMultipleAfterDelay(t *testing.T) { t.Run("Third reward payment", func(t *testing.T) { epochSeq += 1 + + expectLedgerMovements(t, v) + v.broker.EXPECT().Send(gomock.Any()).Do(func(evt events.Event) { e, ok := evt.(*events.VestingStatsUpdated) require.True(t, ok, "Event should be a VestingStatsUpdated, but is %T", evt) @@ -880,20 +843,12 @@ func TestDistributeMultipleAfterDelay(t *testing.T) { { PartyId: party, RewardBonusMultiplier: "2", - QuantumBalance: "489", + QuantumBalance: "499", }, }, }, e.Proto()) }).Times(1) - v.broker.EXPECT().Send(gomock.Any()).Do(func(evt events.Event) { - e, ok := evt.(*events.LedgerMovements) - require.True(t, ok, "Event should be a LedgerMovements, but is %T", evt) - // LedgerMovements is the result of a mock, so it doesn't really make sense to verify data - // consistency. - assert.Equal(t, eventspb.LedgerMovements{LedgerMovements: []*vegapb.LedgerMovement{}}, e.Proto()) - }).Times(1) - v.broker.EXPECT().Send(gomock.Any()).Do(func(evt events.Event) { e, ok := evt.(*events.VestingBalancesSummary) require.True(t, ok, "Event should be a VestingBalancesSummary, but is %T", evt) @@ -922,6 +877,9 @@ func TestDistributeMultipleAfterDelay(t *testing.T) { t.Run("Fourth reward payment", func(t *testing.T) { epochSeq += 1 + + expectLedgerMovements(t, v) + v.broker.EXPECT().Send(gomock.Any()).Do(func(evt events.Event) { e, ok := evt.(*events.VestingStatsUpdated) require.True(t, ok, "Event should be a VestingStatsUpdated, but is %T", evt) @@ -930,21 +888,13 @@ func TestDistributeMultipleAfterDelay(t *testing.T) { Stats: []*eventspb.PartyVestingStats{ { PartyId: party, - RewardBonusMultiplier: "2", - QuantumBalance: "499", + RewardBonusMultiplier: "3", + QuantumBalance: "500", }, }, }, e.Proto()) }).Times(1) - v.broker.EXPECT().Send(gomock.Any()).Do(func(evt events.Event) { - e, ok := evt.(*events.LedgerMovements) - require.True(t, ok, "Event should be a LedgerMovements, but is %T", evt) - // LedgerMovements is the result of a mock, so it doesn't really make sense to verify data - // consistency. - assert.Equal(t, eventspb.LedgerMovements{LedgerMovements: []*vegapb.LedgerMovement{}}, e.Proto()) - }).Times(1) - v.broker.EXPECT().Send(gomock.Any()).Do(func(evt events.Event) { e, ok := evt.(*events.VestingBalancesSummary) require.True(t, ok, "Event should be a VestingBalancesSummary, but is %T", evt) @@ -986,3 +936,15 @@ func TestDistributeMultipleAfterDelay(t *testing.T) { }) }) } + +// LedgerMovements is the result of a mock, so it doesn't really make sense to +// verify data consistency. +func expectLedgerMovements(t *testing.T, v *testEngine) { + t.Helper() + + v.broker.EXPECT().Send(gomock.Any()).Do(func(evt events.Event) { + e, ok := evt.(*events.LedgerMovements) + require.True(t, ok, "Event should be a LedgerMovements, but is %T", evt) + assert.Equal(t, eventspb.LedgerMovements{LedgerMovements: []*vegapb.LedgerMovement{}}, e.Proto()) + }).Times(1) +} diff --git a/core/vesting/snapshot.go b/core/vesting/snapshot.go index 0265014c90c..ea7b9c3261f 100644 --- a/core/vesting/snapshot.go +++ b/core/vesting/snapshot.go @@ -96,8 +96,7 @@ func (e *SnapshotEngine) loadStateFromSnapshot(_ context.Context, state *snapsho e.log.Panic("uint256 in snapshot underflow", logging.String("value", epochBalance.Balance)) } - e.increaseLockedForAsset( - entry.Party, locked.Asset, balance, epochBalance.Epoch) + e.increaseLockedForAsset(entry.Party, locked.Asset, balance, epochBalance.Epoch) } } }