Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: usdt protection in antehandler (backport #2315) #2317

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading