Skip to content

Commit

Permalink
Merge pull request #10212 from vegaprotocol/reward-fix
Browse files Browse the repository at this point in the history
fix: ensure infra fees reward are not counted for vesting
  • Loading branch information
jeremyletang committed Dec 5, 2023
1 parent 434a75d commit 661c919
Show file tree
Hide file tree
Showing 8 changed files with 159 additions and 8 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@

### 🐛 Fixes

- [10211](https://github.com/vegaprotocol/vega/issues/10211) - Ensure infra fees don't get counted for vesting.

## 0.73.7

### 🐛 Fixes

- [10166](https://github.com/vegaprotocol/vega/issues/10166) - Closed markets should not be subscribed to data sources when restored from a snapshot.
- [10177](https://github.com/vegaprotocol/vega/issues/10177) - Add validation that order sizes are not strangely large.

Expand Down
13 changes: 13 additions & 0 deletions core/collateral/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -4360,3 +4360,16 @@ func (e *Engine) TransferSpot(ctx context.Context, partyID, toPartyID, asset str
}
return res, nil
}

func (e *Engine) GetVestingAccounts() []*types.Account {
accs := []*types.Account{}
for _, a := range e.accs {
if a.Type == types.AccountTypeVestingRewards {
accs = append(accs, a.Clone())
}
}
sort.Slice(accs, func(i, j int) bool {
return accs[i].ID < accs[j].ID
})
return accs
}
1 change: 1 addition & 0 deletions core/protocol/all_services.go
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,7 @@ func newServices(
)

svcs.vesting = vesting.NewSnapshotEngine(svcs.log, svcs.collateral, svcs.activityStreak, svcs.broker, svcs.assets)
svcs.timeService.NotifyOnTick(svcs.vesting.OnTick)
svcs.rewards = rewards.New(svcs.log, svcs.conf.Rewards, svcs.broker, svcs.delegation, svcs.epochService, svcs.collateral, svcs.timeService, svcs.marketActivityTracker, svcs.topology, svcs.vesting, svcs.banking, svcs.activityStreak)

// register this after the rewards engine is created to make sure the on epoch is called in the right order.
Expand Down
9 changes: 6 additions & 3 deletions core/rewards/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -447,9 +447,12 @@ func (e *Engine) distributePayout(ctx context.Context, po *payout) {
return
}

for _, party := range partyIDs {
amt := po.partyToAmount[party]
e.vesting.AddReward(party, po.asset, amt, po.lockedForEpochs)
// if the reward type is not infra fee, report it to the vesting engine
if po.rewardType != types.AccountTypeFeesInfrastructure {
for _, party := range partyIDs {
amt := po.partyToAmount[party]
e.vesting.AddReward(party, po.asset, amt, po.lockedForEpochs)
}
}
e.broker.Send(events.NewLedgerMovements(ctx, responses))
}
14 changes: 14 additions & 0 deletions core/vesting/mocks/mocks.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 23 additions & 2 deletions core/vesting/vesting.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package vesting
import (
"context"
"sort"
"time"

"code.vegaprotocol.io/vega/core/assets"
"code.vegaprotocol.io/vega/core/events"
Expand All @@ -39,6 +40,7 @@ type Collateral interface {
) ([]*types.LedgerMovement, error)
GetVestingRecovery() map[string]map[string]*num.Uint
GetAllVestingQuantumBalance(party string) *num.Uint
GetVestingAccounts() []*types.Account
}

type ActivityStreakVestingMultiplier interface {
Expand Down Expand Up @@ -76,7 +78,9 @@ type Engine struct {
baseRate num.Decimal
benefitTiers []*types.VestingBenefitTier

state map[string]*PartyRewards
state map[string]*PartyRewards
epochSeq uint64
upgradeHackActivated bool
}

func New(
Expand Down Expand Up @@ -146,7 +150,9 @@ func (e *Engine) OnEpochEvent(ctx context.Context, epoch types.Epoch) {
}
}

func (e *Engine) OnEpochRestore(ctx context.Context, epoch types.Epoch) {}
func (e *Engine) OnEpochRestore(ctx context.Context, epoch types.Epoch) {
e.epochSeq = epoch.Seq
}

func (e *Engine) AddReward(
party, asset string,
Expand Down Expand Up @@ -295,6 +301,15 @@ func (e *Engine) distributeVested(ctx context.Context) {
e.broker.Send(events.NewLedgerMovements(ctx, responses))
}

// OnTick is called on the beginning of the block. In here
// this is a post upgrade.
func (e *Engine) OnTick(ctx context.Context, _ time.Time) {
if e.upgradeHackActivated {
e.broadcastSummary(ctx, e.epochSeq)
e.upgradeHackActivated = false
}
}

func (e *Engine) makeTransfer(
party, assetID string,
balance *num.Uint,
Expand Down Expand Up @@ -343,6 +358,12 @@ func (e *Engine) broadcastSummary(ctx context.Context, seq uint64) {
PartiesVestingSummary: []*eventspb.PartyVestingSummary{},
}

parties := make([]string, 0, len(e.state))
for k := range e.state {
parties = append(parties, k)
}
sort.Strings(parties)

for p, pRewards := range e.state {
pSummary := &eventspb.PartyVestingSummary{
Party: p,
Expand Down
20 changes: 17 additions & 3 deletions core/vesting/vesting_snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"sort"

"code.vegaprotocol.io/vega/core/types"
vgcontext "code.vegaprotocol.io/vega/libs/context"
"code.vegaprotocol.io/vega/libs/num"
"code.vegaprotocol.io/vega/libs/proto"
"code.vegaprotocol.io/vega/logging"
Expand Down Expand Up @@ -64,21 +65,34 @@ func (e *SnapshotEngine) GetState(k string) ([]byte, []types.StateProvider, erro
return state, nil, err
}

func (e *SnapshotEngine) LoadState(_ context.Context, p *types.Payload) ([]types.StateProvider, error) {
func (e *SnapshotEngine) LoadState(ctx context.Context, p *types.Payload) ([]types.StateProvider, error) {
if e.Namespace() != p.Data.Namespace() {
return nil, types.ErrInvalidSnapshotNamespace
}

switch data := p.Data.(type) {
case *types.PayloadVesting:
e.loadStateFromSnapshot(data.Vesting)
e.loadStateFromSnapshot(ctx, data.Vesting)
return nil, nil
default:
return nil, types.ErrUnknownSnapshotType
}
}

func (e *SnapshotEngine) loadStateFromSnapshot(state *snapshotpb.Vesting) {
func (e *SnapshotEngine) recoverVesting736() {
e.upgradeHackActivated = true
accs := e.c.GetVestingAccounts()
for _, a := range accs {
e.increaseVestingBalance(a.Owner, a.Asset, a.Balance)
}
}

func (e *SnapshotEngine) loadStateFromSnapshot(ctx context.Context, state *snapshotpb.Vesting) {
if vgcontext.InProgressUpgradeFrom(ctx, "v0.73.6") {
e.recoverVesting736()
return
}

for _, entry := range state.PartiesReward {
for _, v := range entry.InVesting {
balance, underflow := num.UintFromString(v.Balance, 10)
Expand Down
79 changes: 79 additions & 0 deletions core/vesting/vesting_snapshot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,14 @@ package vesting_test
import (
"context"
"testing"
"time"

"code.vegaprotocol.io/vega/core/assets"
"code.vegaprotocol.io/vega/core/events"
"code.vegaprotocol.io/vega/core/types"
"code.vegaprotocol.io/vega/core/vesting"
"code.vegaprotocol.io/vega/core/vesting/mocks"
vegacontext "code.vegaprotocol.io/vega/libs/context"
"code.vegaprotocol.io/vega/libs/num"
"code.vegaprotocol.io/vega/libs/proto"
"code.vegaprotocol.io/vega/logging"
Expand All @@ -31,6 +34,7 @@ import (

"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

type testSnapshotEngine struct {
Expand Down Expand Up @@ -115,6 +119,81 @@ func TestSnapshot(t *testing.T) {
assert.Equal(t, state1, state2)
}

func TestSnapshotUpgrade73_6(t *testing.T) {
v1 := getTestSnapshotEngine(t)
setDefaults(t, v1)
v1.AddReward("party1", "eth", num.NewUint(100), 4)
v1.AddReward("party1", "btc", num.NewUint(150), 1)
v1.AddReward("party1", "eth", num.NewUint(200), 0)
v1.AddReward("party2", "btc", num.NewUint(100), 2)
v1.AddReward("party3", "btc", num.NewUint(100), 0)
v1.AddReward("party4", "eth", num.NewUint(100), 1)
v1.AddReward("party5", "doge", num.NewUint(100), 0)
v1.AddReward("party5", "btc", num.NewUint(1420), 1)
v1.AddReward("party6", "doge", num.NewUint(100), 3)
v1.AddReward("party7", "eth", num.NewUint(100), 2)
v1.AddReward("party8", "vega", num.NewUint(100), 10)

state1, _, err := v1.GetState(vesting.VestingKey)
assert.NoError(t, err)
assert.NotNil(t, state1)

ppayload := &snapshotpb.Payload{}
err = proto.Unmarshal(state1, ppayload)
assert.NoError(t, err)

v2 := getTestSnapshotEngine(t)
setDefaults(t, v2)
ctx := vegacontext.WithSnapshotInfo(context.Background(), "v0.73.6", true)
v2.col.EXPECT().GetVestingAccounts().Return([]*types.Account{
{Owner: "party1", Asset: "eth", Balance: num.NewUint(100)},
{Owner: "party1", Asset: "btc", Balance: num.NewUint(200)},
{Owner: "party2", Asset: "btc", Balance: num.NewUint(300)},
{Owner: "party3", Asset: "doge", Balance: num.NewUint(400)},
}).Times(1)

// note a few things here:
// 1. we ignore whatever comes from the snapshot and set the vesting balance to what the collateral account has
// 2. All locked will be set implicitly to 0
// 3. it is assumed and I think it's safe to do so that it is not possible to have a vesting balance here without having a collateral account,
// so it is guaranteed that we're including in the summary an updateed balance for anyone with a vesting account.
v2.broker.EXPECT().Send(gomock.Any()).Times(1).Do(func(evt events.Event) {
summary := evt.StreamMessage().GetVestingBalancesSummary()
require.Equal(t, uint64(500), summary.EpochSeq)
require.Equal(t, 3, len(summary.PartiesVestingSummary))
require.Equal(t, "party1", summary.PartiesVestingSummary[0].Party)
require.Equal(t, 0, len(summary.PartiesVestingSummary[0].PartyLockedBalances))
require.Equal(t, 2, len(summary.PartiesVestingSummary[0].PartyVestingBalances))
require.Equal(t, "btc", summary.PartiesVestingSummary[0].PartyVestingBalances[0].Asset)
require.Equal(t, "200", summary.PartiesVestingSummary[0].PartyVestingBalances[0].Balance)
require.Equal(t, "eth", summary.PartiesVestingSummary[0].PartyVestingBalances[1].Asset)
require.Equal(t, "100", summary.PartiesVestingSummary[0].PartyVestingBalances[1].Balance)
require.Equal(t, "party2", summary.PartiesVestingSummary[1].Party)
require.Equal(t, 0, len(summary.PartiesVestingSummary[1].PartyLockedBalances))
require.Equal(t, 1, len(summary.PartiesVestingSummary[1].PartyVestingBalances))
require.Equal(t, "btc", summary.PartiesVestingSummary[1].PartyVestingBalances[0].Asset)
require.Equal(t, "300", summary.PartiesVestingSummary[1].PartyVestingBalances[0].Balance)
require.Equal(t, "party3", summary.PartiesVestingSummary[2].Party)
require.Equal(t, 0, len(summary.PartiesVestingSummary[2].PartyLockedBalances))
require.Equal(t, 1, len(summary.PartiesVestingSummary[2].PartyVestingBalances))
require.Equal(t, "doge", summary.PartiesVestingSummary[2].PartyVestingBalances[0].Asset)
require.Equal(t, "400", summary.PartiesVestingSummary[2].PartyVestingBalances[0].Balance)
})
v2.OnEpochRestore(ctx, types.Epoch{Seq: 500})
_, err = v2.LoadState(ctx, types.PayloadFromProto(ppayload))
assert.NoError(t, err)

v2.OnTick(ctx, time.Now())

// now assert the v2 produce the same state
state2, _, err := v2.GetState(vesting.VestingKey)
assert.NoError(t, err)
assert.NotNil(t, state2)

// the state won't match as we reset the locked to 0 and we got the full amount into vesting
assert.NotEqual(t, state1, state2)
}

func epochsForward(t *testing.T, v *testSnapshotEngine) {
t.Helper()

Expand Down

0 comments on commit 661c919

Please sign in to comment.