Skip to content

Commit

Permalink
feat: usdt protection in antehandler (#2315)
Browse files Browse the repository at this point in the history
  • Loading branch information
aljo242 authored Sep 23, 2024
1 parent 53cca79 commit 9144e78
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 21 deletions.
4 changes: 4 additions & 0 deletions protocol/app/ante.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ func NewAnteHandler(options HandlerOptions) (sdk.AnteHandler, error) {
return nil, errorsmod.Wrapf(sdkerrors.ErrLogic, "prices keeper is required for ante builder")
}

if options.MarketMapKeeper == nil {
return nil, errorsmod.Wrapf(sdkerrors.ErrLogic, "market map keeper is required for ante builder")
}

h := &lockingAnteHandler{
authStoreKey: options.AuthStoreKey,
setupContextDecorator: ante.NewSetUpContextDecorator(),
Expand Down
40 changes: 29 additions & 11 deletions protocol/app/ante/market_update.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package ante
import (
"errors"
"fmt"
slinkytypes "github.com/skip-mev/slinky/pkg/types"

errorsmod "cosmossdk.io/errors"
sdk "github.com/cosmos/cosmos-sdk/types"
Expand All @@ -14,13 +15,20 @@ import (
pricestypes "github.com/dydxprotocol/v4-chain/protocol/x/prices/types"
)

var ErrNoCrossMarketUpdates = errors.New("cannot call MsgUpdateMarkets or MsgUpsertMarkets " +
"on a market listed as cross margin")
var ErrRestrictedMarketUpdates = errors.New("cannot call MsgUpdateMarkets or MsgUpsertMarkets " +
"on a restricted market")

type MarketMapKeeper interface {
GetAllMarkets(ctx sdk.Context) (map[string]mmtypes.Market, error)
}

var (
cpUSDTUSD = slinkytypes.CurrencyPair{
Base: "USDT",
Quote: "USD",
}
)

type ValidateMarketUpdateDecorator struct {
perpKeeper perpetualstypes.PerpetualsKeeper
priceKeeper pricestypes.PricesKeeper
Expand Down Expand Up @@ -80,8 +88,8 @@ func (d ValidateMarketUpdateDecorator) AnteHandle(
return ctx, fmt.Errorf("unrecognized message type: %T", msg)
}

if contains := d.doMarketsContainCrossMarket(ctx, markets); contains {
return ctx, ErrNoCrossMarketUpdates
if contains, ticker := d.doMarketsContainRestrictedMarket(ctx, markets); contains {
return ctx, fmt.Errorf("%w: %s", ErrRestrictedMarketUpdates, ticker)
}

// check if the market updates are safe
Expand All @@ -92,10 +100,16 @@ func (d ValidateMarketUpdateDecorator) AnteHandle(
return next(ctx, tx, simulate)
}

func (d ValidateMarketUpdateDecorator) doMarketsContainCrossMarket(ctx sdk.Context, markets []mmtypes.Market) bool {
// doMarketsContainRestrictedMarket checks if any of the given markets are restricted:
// 1. markets listed as CROSS perpetuals are restricted
// 2. the USDT/USD market is always restricted
func (d ValidateMarketUpdateDecorator) doMarketsContainRestrictedMarket(
ctx sdk.Context,
markets []mmtypes.Market,
) (bool, string) {
// Grab all the perpetuals markets
perps := d.perpKeeper.GetAllPerpetuals(ctx)
perpsMap := make(map[string]perpetualstypes.PerpetualMarketType)
restrictedMap := make(map[string]bool, len(perps))

// Attempt to fetch the corresponding Prices market and map it to a currency pair
for _, perp := range perps {
Expand All @@ -107,20 +121,24 @@ func (d ValidateMarketUpdateDecorator) doMarketsContainCrossMarket(ctx sdk.Conte
if err != nil {
continue
}
perpsMap[cp.String()] = perp.Params.MarketType
restrictedMap[cp.String()] = perp.Params.MarketType == perpetualstypes.
PerpetualMarketType_PERPETUAL_MARKET_TYPE_CROSS
}

// add usdt/usd market to be restricted
restrictedMap[cpUSDTUSD.String()] = true

// Look in the mapped currency pairs to see if we have invalid updates
for _, market := range markets {
ticker := market.Ticker.CurrencyPair.String()

marketType, found := perpsMap[ticker]
if found && marketType == perpetualstypes.PerpetualMarketType_PERPETUAL_MARKET_TYPE_CROSS {
return true
restricted, found := restrictedMap[ticker]
if found && restricted {
return true, ticker
}
}

return false
return false, ""
}

// doMarketsUpdateEnabledValues checks if the given markets updates are safe, specifically:
Expand Down
98 changes: 88 additions & 10 deletions protocol/app/ante/market_update_test.go
Original file line number Diff line number Diff line change
@@ -1,32 +1,31 @@
package ante_test

import (
storetypes "cosmossdk.io/store/types"
"math/rand"
"testing"

sdkmath "cosmossdk.io/math"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
"github.com/dydxprotocol/v4-chain/protocol/dtypes"
"github.com/dydxprotocol/v4-chain/protocol/testutil/constants"
assets "github.com/dydxprotocol/v4-chain/protocol/x/assets/types"
perpetualtypes "github.com/dydxprotocol/v4-chain/protocol/x/perpetuals/types"
prices_types "github.com/dydxprotocol/v4-chain/protocol/x/prices/types"
"github.com/skip-mev/slinky/pkg/types"
mmtypes "github.com/skip-mev/slinky/x/marketmap/types"

storetypes "cosmossdk.io/store/types"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/tx"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
xauthsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
"github.com/skip-mev/slinky/pkg/types"
mmtypes "github.com/skip-mev/slinky/x/marketmap/types"
"github.com/stretchr/testify/require"

"github.com/dydxprotocol/v4-chain/protocol/app/ante"
"github.com/dydxprotocol/v4-chain/protocol/dtypes"
slinkylib "github.com/dydxprotocol/v4-chain/protocol/lib/slinky"
testante "github.com/dydxprotocol/v4-chain/protocol/testutil/ante"
testapp "github.com/dydxprotocol/v4-chain/protocol/testutil/app"
"github.com/dydxprotocol/v4-chain/protocol/testutil/constants"
assets "github.com/dydxprotocol/v4-chain/protocol/x/assets/types"
perpetualtypes "github.com/dydxprotocol/v4-chain/protocol/x/perpetuals/types"
prices_types "github.com/dydxprotocol/v4-chain/protocol/x/prices/types"
)

func TestIsMarketUpdateTx(t *testing.T) {
Expand Down Expand Up @@ -212,6 +211,25 @@ var (
},
}

testUSDTUSDMarket = mmtypes.Market{
Ticker: mmtypes.Ticker{
CurrencyPair: types.CurrencyPair{
Base: "USDT",
Quote: "USD",
},
Decimals: 1,
MinProviderCount: 1,
Enabled: true,
Metadata_JSON: "",
},
ProviderConfigs: []mmtypes.ProviderConfig{
{
Name: "test_provider",
OffChainTicker: "USDT/USD",
},
},
}

enabledTestMarketWithProviderConfig = mmtypes.Market{
Ticker: mmtypes.Ticker{
CurrencyPair: types.CurrencyPair{
Expand Down Expand Up @@ -726,6 +744,66 @@ func TestValidateMarketUpdateDecorator_AnteHandle(t *testing.T) {
},
wantErr: false,
},
{
name: "always reject USDT/USD - simulate - upsert",
args: args{
msgs: []sdk.Msg{
&mmtypes.MsgUpsertMarkets{
Authority: constants.BobAccAddress.String(),
Markets: []mmtypes.Market{
testUSDTUSDMarket,
},
},
},
simulate: true,
},
wantErr: true,
},
{
name: "always reject USDT/USD - upsert",
args: args{
msgs: []sdk.Msg{
&mmtypes.MsgUpsertMarkets{
Authority: constants.BobAccAddress.String(),
Markets: []mmtypes.Market{
testUSDTUSDMarket,
},
},
},
simulate: false,
},
wantErr: true,
},
{
name: "always reject USDT/USD - simulate - update",
args: args{
msgs: []sdk.Msg{
&mmtypes.MsgUpdateMarkets{
Authority: constants.BobAccAddress.String(),
UpdateMarkets: []mmtypes.Market{
testUSDTUSDMarket,
},
},
},
simulate: true,
},
wantErr: true,
},
{
name: "always reject USDT/USD - update",
args: args{
msgs: []sdk.Msg{
&mmtypes.MsgUpdateMarkets{
Authority: constants.BobAccAddress.String(),
UpdateMarkets: []mmtypes.Market{
testUSDTUSDMarket,
},
},
},
simulate: false,
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down
5 changes: 5 additions & 0 deletions protocol/app/ante_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ func newHandlerOptions() app.HandlerOptions {
AuthStoreKey: dydxApp.CommitMultiStore().(*rootmulti.Store).StoreKeysByName()[authtypes.StoreKey],
PerpetualsKeeper: dydxApp.PerpetualsKeeper,
PricesKeeper: dydxApp.PricesKeeper,
MarketMapKeeper: &dydxApp.MarketMapKeeper,
}
}

Expand Down Expand Up @@ -60,6 +61,10 @@ func TestNewAnteHandler_Error(t *testing.T) {
handlerMutation: func(options *app.HandlerOptions) { options.PricesKeeper = nil },
errorMsg: "prices keeper is required for ante builder",
},
"nil MarketMapKeeper": {
handlerMutation: func(options *app.HandlerOptions) { options.MarketMapKeeper = nil },
errorMsg: "market map keeper is required for ante builder",
},
"nil handlerOptions.SignModeHandler": {
handlerMutation: func(options *app.HandlerOptions) { options.SignModeHandler = nil },
errorMsg: "sign mode handler is required for ante builder",
Expand Down

0 comments on commit 9144e78

Please sign in to comment.