Skip to content

Commit

Permalink
Merge pull request #9765 from vegaprotocol/9760-check-market-against-…
Browse files Browse the repository at this point in the history
…asset

fix: check that an inscope market is for the asset before accepting a…
  • Loading branch information
jeremyletang authored Oct 13, 2023
2 parents 8e31309 + f42ec2c commit 529d5df
Show file tree
Hide file tree
Showing 8 changed files with 90 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@
- [8364](https://github.com/vegaprotocol/vega/issues/8364) - Initialising from network history not working after database wipe
- [8827](https://github.com/vegaprotocol/vega/issues/8827) - Add block height validation to validator initiated transactions and pruning to the `pow` engine cache
- [8836](https://github.com/vegaprotocol/vega/issues/8836) - Fix enactment of market update state
- [9760](https://github.com/vegaprotocol/vega/issues/9760) - Validate that a recurring transfer with markets matches the asset in the transfer.
- [8848](https://github.com/vegaprotocol/vega/issues/8848) - Handle the case where the market is terminated and the epoch ends at the same block.
- [8853](https://github.com/vegaprotocol/vega/issues/8853) - Liquidity provision amendment bug fixes
- [8862](https://github.com/vegaprotocol/vega/issues/8862) - Fix settlement via governance
Expand Down
1 change: 1 addition & 0 deletions core/banking/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ type MarketActivityTracker interface {
CalculateMetricForTeams(ds *vega.DispatchStrategy) ([]*types.PartyContributionScore, map[string][]*types.PartyContributionScore)
GetMarketsWithEligibleProposer(asset string, markets []string, payoutAsset string, funder string) []*types.MarketContributionScore
MarkPaidProposer(asset, market, payoutAsset string, marketsInScope []string, funder string)
MarketTrackedForAsset(market, asset string) bool
}

type EthereumEventSource interface {
Expand Down
14 changes: 14 additions & 0 deletions core/banking/mocks/mocks.go

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

14 changes: 14 additions & 0 deletions core/banking/recurring_transfers.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,20 @@ func (e *Engine) recurringTransfer(
return fmt.Errorf("could not transfer funds, invalid asset for metric: %w", err)
}
}

if hasAsset && len(transfer.DispatchStrategy.Markets) > 0 {
asset := transfer.DispatchStrategy.AssetForMetric
for _, mid := range transfer.DispatchStrategy.Markets {
if !e.marketActivityTracker.MarketTrackedForAsset(mid, asset) {
transfer.Status = types.TransferStatusRejected
e.log.Debug("cannot transfer funds, invalid market for dispatch asset",
logging.String("mid", mid),
logging.String("asset", asset),
)
return errors.New("could not transfer funds, invalid market for dispatch asset")
}
}
}
}

if err := transfer.IsValid(); err != nil {
Expand Down
46 changes: 46 additions & 0 deletions core/banking/recurring_transfers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -715,3 +715,49 @@ func testRecurringTransferInvalidTransfers(t *testing.T) {
)
})
}

func TestMarketAssetMismatchRejectsTransfer(t *testing.T) {
eng := getTestEngine(t)

fromAcc := types.Account{
Balance: num.NewUint(1000),
}

eng.assets.EXPECT().Get(gomock.Any()).AnyTimes().Return(assets.NewAsset(&mockAsset{num.DecimalFromFloat(100)}), nil)
eng.tsvc.EXPECT().GetTimeNow().Times(1)
eng.col.EXPECT().GetPartyGeneralAccount(gomock.Any(), gomock.Any()).AnyTimes().Return(&fromAcc, nil)
eng.broker.EXPECT().Send(gomock.Any()).AnyTimes()
eng.col.EXPECT().TransferFunds(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()

recurring := &types.TransferFunds{
Kind: types.TransferCommandKindRecurring,
Recurring: &types.RecurringTransfer{
TransferBase: &types.TransferBase{
From: "03ae90688632c649c4beab6040ff5bd04dbde8efbf737d8673bbda792a110301",
FromAccountType: types.AccountTypeGeneral,
To: "2e05fd230f3c9f4eaf0bdc5bfb7ca0c9d00278afc44637aab60da76653d7ccf0",
ToAccountType: types.AccountTypeGeneral,
Asset: "eth",
Amount: num.NewUint(10),
Reference: "someref",
},
StartEpoch: 10,
EndEpoch: nil, // forever
Factor: num.MustDecimalFromString("0.9"),
DispatchStrategy: &vega.DispatchStrategy{
AssetForMetric: "zohar",
Metric: vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION,
Markets: []string{"mmm"},
EntityScope: vega.EntityScope_ENTITY_SCOPE_INDIVIDUALS,
IndividualScope: vega.IndividualScope_INDIVIDUAL_SCOPE_IN_TEAM,
WindowLength: 1,
LockPeriod: 1,
DistributionStrategy: vega.DistributionStrategy_DISTRIBUTION_STRATEGY_RANK,
},
},
}

// if in-scope market has a different asset it is rejected
eng.marketActivityTracker.EXPECT().MarketTrackedForAsset(gomock.Any(), gomock.Any()).Times(1).Return(false)
require.Error(t, eng.TransferFunds(context.Background(), recurring))
}
1 change: 1 addition & 0 deletions core/banking/snapshot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,7 @@ func TestRecurringTransfersSnapshotRoundTrip(t *testing.T) {
p, _ := proto.Marshal(recurring.Recurring.DispatchStrategy)
dsHash := hex.EncodeToString(crypto.Hash(p))

eng.marketActivityTracker.EXPECT().MarketTrackedForAsset("mmm", "zohar").Times(1).Return(true)
require.NoError(t, eng.TransferFunds(ctx, recurring))

// test the new transfer prompts a change
Expand Down
10 changes: 10 additions & 0 deletions core/execution/common/market_activity_tracker.go
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,16 @@ func (mat *MarketActivityTracker) GetAllMarketIDs() []string {
return mIDs
}

// MarketTrackedForAsset returns whether the given market is seen to have the given asset by the tracker.
func (mat *MarketActivityTracker) MarketTrackedForAsset(market, asset string) bool {
if markets, ok := mat.assetToMarketTrackers[asset]; ok {
if _, ok = markets[market]; ok {
return true
}
}
return false
}

// removeMarket is called when the market is removed from the network. It is not immediately removed to give a chance for rewards to be paid at the end of the epoch for activity during the epoch.
// Instead it is marked for removal and will be removed at the beginning of the next epoch.
func (mat *MarketActivityTracker) RemoveMarket(asset, marketID string) {
Expand Down
3 changes: 3 additions & 0 deletions core/execution/common/market_activity_tracker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ func TestMarketTracker(t *testing.T) {
tracker.MarketProposed("asset1", "market1", "me")
tracker.MarketProposed("asset1", "market2", "me2")

assert.True(t, tracker.MarketTrackedForAsset("market1", "asset1"))
assert.False(t, tracker.MarketTrackedForAsset("market1", "asset2"))

require.Equal(t, false, tracker.IsMarketEligibleForBonus("asset1", "market1", "VEGA", []string{}, "zohar"))
require.Equal(t, false, tracker.IsMarketEligibleForBonus("asset1", "market2", "VEGA", []string{}, "zohar"))

Expand Down

0 comments on commit 529d5df

Please sign in to comment.