From 929f09e5451bcc51d2d2fa91189bd3fffed5c787 Mon Sep 17 00:00:00 2001 From: Jonathan Fung <121899091+jonfung-dydx@users.noreply.github.com> Date: Mon, 4 Mar 2024 13:48:04 -0800 Subject: [PATCH] [CT-514] Clob `MsgBatchCancel` functionality (#1110) * wip implementation * use new cometbft * Revert "use new cometbft" This reverts commit e5b8a03dfe61a05f27a12595dad6a27d686a9515. * go mod tidy * basic e2e test * more msgBatchCancels in code * repeated fixed32 -> uint32 * remove debug prints * update cometbft replace go.mod sha * one more debug print * typo * regen indexer protos * update comment on proto * proto comment changes * extract stateful validation into own fn * pr format comments * clean up test file * new return type with success and failure Signed-off-by: Eric --- .../src/codegen/dydxprotocol/clob/tx.ts | 18 +- proto/dydxprotocol/clob/tx.proto | 4 +- protocol/app/module/interface_registry.go | 6 +- protocol/app/process/other_msgs_test.go | 10 +- protocol/app/process/utils_disallow_msgs.go | 2 + .../app/process/utils_disallow_msgs_test.go | 2 +- protocol/go.mod | 2 +- protocol/go.sum | 4 +- protocol/lib/log/constants.go | 7 +- protocol/mocks/ClobKeeper.go | 35 +++ protocol/testutil/app/app.go | 5 +- protocol/testutil/constants/messages.go | 15 + protocol/testutil/encoding/utils.go | 4 +- protocol/x/clob/ante/clob.go | 49 ++- protocol/x/clob/e2e/batch_cancel_test.go | 283 ++++++++++++++++++ protocol/x/clob/keeper/orders.go | 70 +++++ protocol/x/clob/types/clob_keeper.go | 4 + protocol/x/clob/types/errors.go | 5 + protocol/x/clob/types/tx.pb.go | 155 ++++++---- 19 files changed, 599 insertions(+), 81 deletions(-) create mode 100644 protocol/x/clob/e2e/batch_cancel_test.go diff --git a/indexer/packages/v4-protos/src/codegen/dydxprotocol/clob/tx.ts b/indexer/packages/v4-protos/src/codegen/dydxprotocol/clob/tx.ts index 7bb37ee2b6..09a1c058fc 100644 --- a/indexer/packages/v4-protos/src/codegen/dydxprotocol/clob/tx.ts +++ b/indexer/packages/v4-protos/src/codegen/dydxprotocol/clob/tx.ts @@ -167,7 +167,11 @@ export interface MsgBatchCancelSDKType { export interface OrderBatch { /** The Clob Pair ID all orders in this order batch belong to. */ clobPairId: number; - /** List of client ids in this order batch. */ + /** + * List of client ids in this order batch. + * Note that this is serialized as a uint32 instead of a fixed32 to + * avoid issues when decoding repeated packed fixed32. + */ clientIds: number[]; } @@ -180,7 +184,11 @@ export interface OrderBatch { export interface OrderBatchSDKType { /** The Clob Pair ID all orders in this order batch belong to. */ clob_pair_id: number; - /** List of client ids in this order batch. */ + /** + * List of client ids in this order batch. + * Note that this is serialized as a uint32 instead of a fixed32 to + * avoid issues when decoding repeated packed fixed32. + */ client_ids: number[]; } @@ -802,7 +810,7 @@ export const OrderBatch = { writer.uint32(18).fork(); for (const v of message.clientIds) { - writer.fixed32(v); + writer.uint32(v); } writer.ldelim(); @@ -827,10 +835,10 @@ export const OrderBatch = { const end2 = reader.uint32() + reader.pos; while (reader.pos < end2) { - message.clientIds.push(reader.fixed32()); + message.clientIds.push(reader.uint32()); } } else { - message.clientIds.push(reader.fixed32()); + message.clientIds.push(reader.uint32()); } break; diff --git a/proto/dydxprotocol/clob/tx.proto b/proto/dydxprotocol/clob/tx.proto index 6f1ac50f3a..d11ebb3516 100644 --- a/proto/dydxprotocol/clob/tx.proto +++ b/proto/dydxprotocol/clob/tx.proto @@ -123,7 +123,9 @@ message OrderBatch { // The Clob Pair ID all orders in this order batch belong to. uint32 clob_pair_id = 1; // List of client ids in this order batch. - repeated fixed32 client_ids = 2; + // Note that this is serialized as a uint32 instead of a fixed32 to + // avoid issues when decoding repeated packed fixed32. + repeated uint32 client_ids = 2; } // MsgBatchCancelResponse is a response type used for batch canceling orders. diff --git a/protocol/app/module/interface_registry.go b/protocol/app/module/interface_registry.go index 35a92e52db..88cd60655b 100644 --- a/protocol/app/module/interface_registry.go +++ b/protocol/app/module/interface_registry.go @@ -1,8 +1,9 @@ package module import ( - "cosmossdk.io/x/tx/signing" "fmt" + + "cosmossdk.io/x/tx/signing" addresscodec "github.com/cosmos/cosmos-sdk/codec/address" "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" @@ -76,6 +77,9 @@ func NewInterfaceRegistry(addrPrefix string, valAddrPrefix string) (types.Interf // https://github.com/cosmos/cosmos-sdk/issues/18722 is fixed, replace this with the cosmos.msg.v1.signing // annotation on the protos. CustomGetSigners: map[protoreflect.FullName]signing.GetSignersFunc{ + "dydxprotocol.clob.MsgBatchCancel": getLegacyMsgSignerFn( + []string{"subaccount_id", "owner"}, + ), "dydxprotocol.clob.MsgCancelOrder": getLegacyMsgSignerFn( []string{"order_id", "subaccount_id", "owner"}, ), diff --git a/protocol/app/process/other_msgs_test.go b/protocol/app/process/other_msgs_test.go index 3db7ddaf2d..b431790c81 100644 --- a/protocol/app/process/other_msgs_test.go +++ b/protocol/app/process/other_msgs_test.go @@ -1,9 +1,10 @@ package process_test import ( - errorsmod "cosmossdk.io/errors" "testing" + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/dydxprotocol/v4-chain/protocol/app/process" "github.com/dydxprotocol/v4-chain/protocol/testutil/constants" @@ -87,6 +88,13 @@ func TestDecodeOtherMsgsTx(t *testing.T) { "Msg type *types.MsgCancelOrder is not allowed in OtherTxs", ), }, + "Error: batch cancel order is not allowed": { + txBytes: constants.Msg_BatchCancel_TxBtyes, + expectedErr: errorsmod.Wrap( + process.ErrUnexpectedMsgType, + "Msg type *types.MsgBatchCancel is not allowed in OtherTxs", + ), + }, "Valid: single msg": { txBytes: constants.Msg_Send_TxBytes, expectedMsgs: []sdk.Msg{constants.Msg_Send}, diff --git a/protocol/app/process/utils_disallow_msgs.go b/protocol/app/process/utils_disallow_msgs.go index b9ea960028..79585817b4 100644 --- a/protocol/app/process/utils_disallow_msgs.go +++ b/protocol/app/process/utils_disallow_msgs.go @@ -18,6 +18,8 @@ func IsDisallowClobOrderMsgInOtherTxs(targetMsg sdk.Msg) bool { order := msg.GetOrder() orderId := order.GetOrderId() return !orderId.IsStatefulOrder() // not stateful -> returns true -> disallow + case *clobtypes.MsgBatchCancel: + return true } return false } diff --git a/protocol/app/process/utils_disallow_msgs_test.go b/protocol/app/process/utils_disallow_msgs_test.go index ddbf1c7dd1..af233fb672 100644 --- a/protocol/app/process/utils_disallow_msgs_test.go +++ b/protocol/app/process/utils_disallow_msgs_test.go @@ -25,7 +25,7 @@ func TestIsDisallowClobOrderMsgInOtherTxs(t *testing.T) { for _, msg := range allMsgSamples { result := process.IsDisallowClobOrderMsgInOtherTxs(msg) switch msg.(type) { - case *clobtypes.MsgCancelOrder, *clobtypes.MsgPlaceOrder: + case *clobtypes.MsgCancelOrder, *clobtypes.MsgPlaceOrder, *clobtypes.MsgBatchCancel: // The sample msgs are short-term orders, so we expect these to be disallowed. require.True(t, result) // true -> disallow default: diff --git a/protocol/go.mod b/protocol/go.mod index cbfa4b625d..8b8541b499 100644 --- a/protocol/go.mod +++ b/protocol/go.mod @@ -434,7 +434,7 @@ replace ( // Use dYdX fork of Cosmos SDK/store cosmossdk.io/store => github.com/dydxprotocol/cosmos-sdk/store v1.0.3-0.20240227194839-f4e166bc1057 // Use dYdX fork of CometBFT - github.com/cometbft/cometbft => github.com/dydxprotocol/cometbft v0.38.6-0.20240220185844-e704122c8540 + github.com/cometbft/cometbft => github.com/dydxprotocol/cometbft v0.38.6-0.20240229050000-3b085f30b462 // Use dYdX fork of Cosmos SDK github.com/cosmos/cosmos-sdk => github.com/dydxprotocol/cosmos-sdk v0.50.5-0.20240227194839-f4e166bc1057 ) diff --git a/protocol/go.sum b/protocol/go.sum index 4d97ec94c6..2260aa0c70 100644 --- a/protocol/go.sum +++ b/protocol/go.sum @@ -525,8 +525,8 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/dvsekhvalnov/jose2go v1.6.0 h1:Y9gnSnP4qEI0+/uQkHvFXeD2PLPJeXEL+ySMEA2EjTY= github.com/dvsekhvalnov/jose2go v1.6.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU= -github.com/dydxprotocol/cometbft v0.38.6-0.20240220185844-e704122c8540 h1:pkYQbAdOAAoZBSId9kLupCgZHj8YvA9LzM31fVYpjlw= -github.com/dydxprotocol/cometbft v0.38.6-0.20240220185844-e704122c8540/go.mod h1:REQN+ObgfYxi39TcYR/Hv95C9bPxY3sYJCvghryj7vY= +github.com/dydxprotocol/cometbft v0.38.6-0.20240229050000-3b085f30b462 h1:ufl8wDrax5K/33NMX/2P54iJAb5LlHJA8CYIcdfeR+o= +github.com/dydxprotocol/cometbft v0.38.6-0.20240229050000-3b085f30b462/go.mod h1:REQN+ObgfYxi39TcYR/Hv95C9bPxY3sYJCvghryj7vY= github.com/dydxprotocol/cosmos-sdk v0.50.5-0.20240227194839-f4e166bc1057 h1:5Aq2oldr7T5+PEo6HYWA2cTcD4tY5ZM6755pKxhOvNg= github.com/dydxprotocol/cosmos-sdk v0.50.5-0.20240227194839-f4e166bc1057/go.mod h1:I2uLntIWztPOXW0abkUb3SRphofiLL5DzrvHObyJVkM= github.com/dydxprotocol/cosmos-sdk/store v1.0.3-0.20240227194839-f4e166bc1057 h1:rY2cckHMrpk3+9ggVqp3Zsvfn7AwEjjazvyp4HK3lgw= diff --git a/protocol/lib/log/constants.go b/protocol/lib/log/constants.go index 589c558789..23a0bacf98 100644 --- a/protocol/lib/log/constants.go +++ b/protocol/lib/log/constants.go @@ -46,9 +46,10 @@ const ( // Module tag values are prefixed with `x/` Clob = "x/clob" - CheckTx = "check_tx" - RecheckTx = "recheck_tx" - DeliverTx = "deliver_tx" + CheckTx = "check_tx" + RecheckTx = "recheck_tx" + DeliverTx = "deliver_tx" + MsgBatchCancel = "msg_batch_cancel" ) // Special tag values that should be PascalCased (i.e function names) diff --git a/protocol/mocks/ClobKeeper.go b/protocol/mocks/ClobKeeper.go index 2f2704b71f..e29a53517c 100644 --- a/protocol/mocks/ClobKeeper.go +++ b/protocol/mocks/ClobKeeper.go @@ -50,6 +50,41 @@ func (_m *ClobKeeper) AddOrderToOrderbookCollatCheck(ctx types.Context, clobPair return r0, r1 } +// BatchCancelShortTermOrder provides a mock function with given fields: ctx, msg +func (_m *ClobKeeper) BatchCancelShortTermOrder(ctx types.Context, msg *clobtypes.MsgBatchCancel) ([]uint32, []uint32, error) { + ret := _m.Called(ctx, msg) + + var r0 []uint32 + var r1 []uint32 + var r2 error + if rf, ok := ret.Get(0).(func(types.Context, *clobtypes.MsgBatchCancel) ([]uint32, []uint32, error)); ok { + return rf(ctx, msg) + } + if rf, ok := ret.Get(0).(func(types.Context, *clobtypes.MsgBatchCancel) []uint32); ok { + r0 = rf(ctx, msg) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]uint32) + } + } + + if rf, ok := ret.Get(1).(func(types.Context, *clobtypes.MsgBatchCancel) []uint32); ok { + r1 = rf(ctx, msg) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).([]uint32) + } + } + + if rf, ok := ret.Get(2).(func(types.Context, *clobtypes.MsgBatchCancel) error); ok { + r2 = rf(ctx, msg) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + // CancelShortTermOrder provides a mock function with given fields: ctx, msg func (_m *ClobKeeper) CancelShortTermOrder(ctx types.Context, msg *clobtypes.MsgCancelOrder) error { ret := _m.Called(ctx, msg) diff --git a/protocol/testutil/app/app.go b/protocol/testutil/app/app.go index c8cc7eb822..da22536657 100644 --- a/protocol/testutil/app/app.go +++ b/protocol/testutil/app/app.go @@ -1320,7 +1320,7 @@ func launchValidatorInDir( // MustMakeCheckTxsWithClobMsg creates one signed RequestCheckTx for each msg passed in. // The messsage must use one of the hard-coded well known subaccount owners otherwise this will panic. -func MustMakeCheckTxsWithClobMsg[T clobtypes.MsgPlaceOrder | clobtypes.MsgCancelOrder]( +func MustMakeCheckTxsWithClobMsg[T clobtypes.MsgPlaceOrder | clobtypes.MsgCancelOrder | clobtypes.MsgBatchCancel]( ctx sdk.Context, app *app.App, messages ...T, @@ -1336,6 +1336,9 @@ func MustMakeCheckTxsWithClobMsg[T clobtypes.MsgPlaceOrder | clobtypes.MsgCancel case clobtypes.MsgCancelOrder: signerAddress = v.OrderId.SubaccountId.Owner m = &v + case clobtypes.MsgBatchCancel: + signerAddress = v.SubaccountId.Owner + m = &v default: panic(fmt.Errorf("MustMakeCheckTxsWithClobMsg: Unknown message type %T", msg)) } diff --git a/protocol/testutil/constants/messages.go b/protocol/testutil/constants/messages.go index fb10883d59..aac9b31167 100644 --- a/protocol/testutil/constants/messages.go +++ b/protocol/testutil/constants/messages.go @@ -21,6 +21,9 @@ func init() { _ = TestTxBuilder.SetMsgs(Msg_CancelOrder) Msg_CancelOrder_TxBtyes, _ = TestEncodingCfg.TxConfig.TxEncoder()(TestTxBuilder.GetTx()) + _ = TestTxBuilder.SetMsgs(Msg_BatchCancel) + Msg_BatchCancel_TxBtyes, _ = TestEncodingCfg.TxConfig.TxEncoder()(TestTxBuilder.GetTx()) + _ = TestTxBuilder.SetMsgs(Msg_Send) Msg_Send_TxBytes, _ = TestEncodingCfg.TxConfig.TxEncoder()(TestTxBuilder.GetTx()) @@ -54,6 +57,18 @@ var ( } Msg_PlaceOrder_TxBtyes []byte + Msg_BatchCancel = &clobtypes.MsgBatchCancel{ + SubaccountId: Alice_Num0, + ShortTermCancels: []clobtypes.OrderBatch{ + { + ClobPairId: 0, + ClientIds: []uint32{0}, + }, + }, + GoodTilBlock: 5, + } + Msg_BatchCancel_TxBtyes []byte + Msg_PlaceOrder_LongTerm = &clobtypes.MsgPlaceOrder{ Order: LongTermOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTBT15, } diff --git a/protocol/testutil/encoding/utils.go b/protocol/testutil/encoding/utils.go index abd2489025..8b27df37da 100644 --- a/protocol/testutil/encoding/utils.go +++ b/protocol/testutil/encoding/utils.go @@ -1,9 +1,10 @@ package encoding import ( - "github.com/dydxprotocol/v4-chain/protocol/testutil/ante" "testing" + "github.com/dydxprotocol/v4-chain/protocol/testutil/ante" + feegrantmodule "cosmossdk.io/x/feegrant/module" "cosmossdk.io/x/upgrade" codectypes "github.com/cosmos/cosmos-sdk/codec/types" @@ -73,6 +74,7 @@ func GetTestEncodingCfg() testutil.TestEncodingConfig { &clobtypes.MsgProposedOperations{}, &clobtypes.MsgPlaceOrder{}, &clobtypes.MsgCancelOrder{}, + &clobtypes.MsgBatchCancel{}, // Perpetuals. &perpetualtypes.MsgAddPremiumVotes{}, diff --git a/protocol/x/clob/ante/clob.go b/protocol/x/clob/ante/clob.go index 4a50e7c28b..59b60220fd 100644 --- a/protocol/x/clob/ante/clob.go +++ b/protocol/x/clob/ante/clob.go @@ -130,6 +130,38 @@ func (cd ClobDecorator) AnteHandle( log.Error, err, ) } + case *types.MsgBatchCancel: + // MsgBatchCancel currently only processes short-term cancels right now. + // No need to process short term orders on `ReCheckTx`. + if ctx.IsReCheckTx() { + return next(ctx, tx, simulate) + } + + ctx = log.AddPersistentTagsToLogger( + ctx, + log.Handler, log.MsgBatchCancel, + ) + + success, failures, err := cd.clobKeeper.BatchCancelShortTermOrder( + ctx, + msg, + ) + // If there are no successful cancellations and no validation errors, + // return an error indicating no cancels have succeeded. + if len(success) == 0 && err == nil { + err = errorsmod.Wrapf( + types.ErrBatchCancelFailed, + "No successful cancellations. Failures: %+v", + failures, + ) + } + + log.DebugLog( + ctx, + "Received new batch cancellation", + log.Tx, cometbftlog.NewLazySprintf("%X", tmhash.Sum(ctx.TxBytes())), + log.Error, err, + ) } if err != nil { return ctx, err @@ -139,15 +171,15 @@ func (cd ClobDecorator) AnteHandle( } // IsSingleClobMsgTx returns `true` if the supplied `tx` consist of a single clob message -// (`MsgPlaceOrder` or `MsgCancelOrder`). If `msgs` consist of multiple clob messages, -// or a mix of on-chain and clob messages, an error is returned. +// (`MsgPlaceOrder` or `MsgCancelOrder` or `MsgBatchCancel`). If `msgs` consist of multiple +// clob messages, or a mix of on-chain and clob messages, an error is returned. func IsSingleClobMsgTx(tx sdk.Tx) (bool, error) { msgs := tx.GetMsgs() var hasMessage = false for _, msg := range msgs { switch msg.(type) { - case *types.MsgCancelOrder, *types.MsgPlaceOrder: + case *types.MsgCancelOrder, *types.MsgPlaceOrder, *types.MsgBatchCancel: hasMessage = true } @@ -164,7 +196,7 @@ func IsSingleClobMsgTx(tx sdk.Tx) (bool, error) { if numMsgs > 1 { return false, errorsmod.Wrap( sdkerrors.ErrInvalidRequest, - "a transaction containing MsgCancelOrder or MsgPlaceOrder may not contain more than one message", + "a transaction containing MsgCancelOrder or MsgPlaceOrder or MsgBatchCancel may not contain more than one message", ) } @@ -172,8 +204,8 @@ func IsSingleClobMsgTx(tx sdk.Tx) (bool, error) { } // IsShortTermClobMsgTx returns `true` if the supplied `tx` consist of a single clob message -// (`MsgPlaceOrder` or `MsgCancelOrder`) which references a Short-Term Order. If `msgs` consist of multiple -// clob messages, or a mix of on-chain and clob messages, an error is returned. +// (`MsgPlaceOrder` or `MsgCancelOrder` or `MsgBatchCancel`) which references a Short-Term Order. +// If `msgs` consist of multiple clob messages, or a mix of on-chain and clob messages, an error is returned. func IsShortTermClobMsgTx(ctx sdk.Context, tx sdk.Tx) (bool, error) { msgs := tx.GetMsgs() @@ -193,6 +225,11 @@ func IsShortTermClobMsgTx(ctx sdk.Context, tx sdk.Tx) (bool, error) { isShortTermOrder = true } } + case *types.MsgBatchCancel: + { + // MsgBatchCancel processes only short term orders for now. + isShortTermOrder = true + } } if isShortTermOrder { diff --git a/protocol/x/clob/e2e/batch_cancel_test.go b/protocol/x/clob/e2e/batch_cancel_test.go new file mode 100644 index 0000000000..b39f487afb --- /dev/null +++ b/protocol/x/clob/e2e/batch_cancel_test.go @@ -0,0 +1,283 @@ +package clob_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + testapp "github.com/dydxprotocol/v4-chain/protocol/testutil/app" + "github.com/dydxprotocol/v4-chain/protocol/testutil/constants" + clobtypes "github.com/dydxprotocol/v4-chain/protocol/x/clob/types" +) + +// These tests are the same as the e2e tests for single order cancellations. +func TestBatchCancelSingleCancelFunctionality(t *testing.T) { + tests := map[string]struct { + firstBlockOrders []clobtypes.MsgPlaceOrder + firstBlockBatchCancel []clobtypes.MsgBatchCancel + secondBlockOrders []clobtypes.MsgPlaceOrder + secondBlockBatchCancel []clobtypes.MsgBatchCancel + + expectedOrderIdsInMemclob map[clobtypes.OrderId]bool + expectedCancelExpirationsInMemclob map[clobtypes.OrderId]uint32 + expectedOrderFillAmounts map[clobtypes.OrderId]uint64 + }{ + "Cancel unfilled short term order": { + firstBlockOrders: []clobtypes.MsgPlaceOrder{ + PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB5, + }, + firstBlockBatchCancel: []clobtypes.MsgBatchCancel{ + { + SubaccountId: PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB5.Order.OrderId.SubaccountId, + ShortTermCancels: []clobtypes.OrderBatch{ + { + ClobPairId: 0, + ClientIds: []uint32{0}, + }, + }, + GoodTilBlock: 5, + }, + }, + expectedOrderIdsInMemclob: map[clobtypes.OrderId]bool{ + PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB5.Order.OrderId: false, + }, + expectedCancelExpirationsInMemclob: map[clobtypes.OrderId]uint32{ + CancelOrder_Alice_Num0_Id0_Clob0_GTB5.OrderId: 5, + }, + expectedOrderFillAmounts: map[clobtypes.OrderId]uint64{ + PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB5.Order.OrderId: 0, + }, + }, + "Batch cancel partially filled short term order in same block": { + firstBlockOrders: []clobtypes.MsgPlaceOrder{ + PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB5, + *clobtypes.NewMsgPlaceOrder(testapp.MustScaleOrder( + clobtypes.Order{ + OrderId: clobtypes.OrderId{SubaccountId: constants.Bob_Num0, ClientId: 0, ClobPairId: 0}, + Side: clobtypes.Order_SIDE_SELL, + Quantums: 4, + Subticks: 10, + GoodTilOneof: &clobtypes.Order_GoodTilBlock{GoodTilBlock: 20}, + }, + testapp.DefaultGenesis(), + )), + }, + firstBlockBatchCancel: []clobtypes.MsgBatchCancel{ + { + SubaccountId: PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB5.Order.OrderId.SubaccountId, + ShortTermCancels: []clobtypes.OrderBatch{ + { + ClobPairId: 0, + ClientIds: []uint32{0}, + }, + }, + GoodTilBlock: 5, + }, + }, + + expectedOrderIdsInMemclob: map[clobtypes.OrderId]bool{ + PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB5.Order.OrderId: false, + }, + expectedCancelExpirationsInMemclob: map[clobtypes.OrderId]uint32{ + CancelOrder_Alice_Num0_Id0_Clob0_GTB5.OrderId: 5, + }, + expectedOrderFillAmounts: map[clobtypes.OrderId]uint64{ + PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB5.Order.OrderId: 40, + }, + }, + "Cancel partially filled short term order in next block": { + firstBlockOrders: []clobtypes.MsgPlaceOrder{ + PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB5, + *clobtypes.NewMsgPlaceOrder(testapp.MustScaleOrder( + clobtypes.Order{ + OrderId: clobtypes.OrderId{SubaccountId: constants.Bob_Num0, ClientId: 0, ClobPairId: 0}, + Side: clobtypes.Order_SIDE_SELL, + Quantums: 4, + Subticks: 10, + GoodTilOneof: &clobtypes.Order_GoodTilBlock{GoodTilBlock: 20}, + }, + testapp.DefaultGenesis(), + )), + }, + secondBlockBatchCancel: []clobtypes.MsgBatchCancel{ + { + SubaccountId: PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB5.Order.OrderId.SubaccountId, + ShortTermCancels: []clobtypes.OrderBatch{ + { + ClobPairId: 0, + ClientIds: []uint32{0}, + }, + }, + GoodTilBlock: 5, + }, + }, + + expectedOrderIdsInMemclob: map[clobtypes.OrderId]bool{ + PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB5.Order.OrderId: false, + }, + expectedCancelExpirationsInMemclob: map[clobtypes.OrderId]uint32{ + CancelOrder_Alice_Num0_Id0_Clob0_GTB5.OrderId: 5, + }, + expectedOrderFillAmounts: map[clobtypes.OrderId]uint64{ + PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB5.Order.OrderId: 40, + }, + }, + "Cancel succeeds for fully-filled order": { + firstBlockOrders: []clobtypes.MsgPlaceOrder{ + PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB5, + PlaceOrder_Bob_Num0_Id0_Clob0_Sell5_Price10_GTB20, + }, + secondBlockBatchCancel: []clobtypes.MsgBatchCancel{ + { + SubaccountId: PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB5.Order.OrderId.SubaccountId, + ShortTermCancels: []clobtypes.OrderBatch{ + { + ClobPairId: 0, + ClientIds: []uint32{0}, + }, + }, + GoodTilBlock: 5, + }, + }, + + expectedOrderIdsInMemclob: map[clobtypes.OrderId]bool{ + PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB5.Order.OrderId: false, + }, + expectedCancelExpirationsInMemclob: map[clobtypes.OrderId]uint32{ + CancelOrder_Alice_Num0_Id0_Clob0_GTB5.OrderId: 5, + }, + expectedOrderFillAmounts: map[clobtypes.OrderId]uint64{ + PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB5.Order.OrderId: 50, + }, + }, + "Cancel with GTB < existing order GTB does not remove order from memclob": { + firstBlockOrders: []clobtypes.MsgPlaceOrder{ + PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB20, + }, + secondBlockBatchCancel: []clobtypes.MsgBatchCancel{ + { + SubaccountId: PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB5.Order.OrderId.SubaccountId, + ShortTermCancels: []clobtypes.OrderBatch{ + { + ClobPairId: 0, + ClientIds: []uint32{0}, + }, + }, + GoodTilBlock: 5, + }, + }, + expectedOrderIdsInMemclob: map[clobtypes.OrderId]bool{ + PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB20.Order.OrderId: true, + }, + expectedCancelExpirationsInMemclob: map[clobtypes.OrderId]uint32{ + CancelOrder_Alice_Num0_Id0_Clob0_GTB5.OrderId: 5, + }, + }, + "Cancel with GTB < existing cancel GTB is not placed on memclob": { + firstBlockBatchCancel: []clobtypes.MsgBatchCancel{ + { + SubaccountId: PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB5.Order.OrderId.SubaccountId, + ShortTermCancels: []clobtypes.OrderBatch{ + { + ClobPairId: 0, + ClientIds: []uint32{0}, + }, + }, + GoodTilBlock: 5, + }, + { + SubaccountId: PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB5.Order.OrderId.SubaccountId, + ShortTermCancels: []clobtypes.OrderBatch{ + { + ClobPairId: 0, + ClientIds: []uint32{0}, + }, + }, + GoodTilBlock: 3, + }, + }, + + expectedCancelExpirationsInMemclob: map[clobtypes.OrderId]uint32{ + CancelOrder_Alice_Num0_Id0_Clob0_GTB5.OrderId: 5, + }, + }, + "Cancel with GTB > existing cancel GTB is placed on memclob": { + firstBlockBatchCancel: []clobtypes.MsgBatchCancel{ + { + SubaccountId: PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB5.Order.OrderId.SubaccountId, + ShortTermCancels: []clobtypes.OrderBatch{ + { + ClobPairId: 0, + ClientIds: []uint32{0}, + }, + }, + GoodTilBlock: 5, + }, + { + SubaccountId: PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB5.Order.OrderId.SubaccountId, + ShortTermCancels: []clobtypes.OrderBatch{ + { + ClobPairId: 0, + ClientIds: []uint32{0}, + }, + }, + GoodTilBlock: 6, + }, + }, + + expectedCancelExpirationsInMemclob: map[clobtypes.OrderId]uint32{ + CancelOrder_Alice_Num0_Id0_Clob0_GTB5.OrderId: 6, + }, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + tApp := testapp.NewTestAppBuilder(t).Build() + ctx := tApp.InitChain() + // Place first block orders and cancels + for _, order := range tc.firstBlockOrders { + for _, checkTx := range testapp.MustMakeCheckTxsWithClobMsg(ctx, tApp.App, order) { + resp := tApp.CheckTx(checkTx) + require.Conditionf(t, resp.IsOK, "Expected CheckTx to succeed. Response: %+v", resp) + } + } + for _, batch := range tc.firstBlockBatchCancel { + for _, checkTx := range testapp.MustMakeCheckTxsWithClobMsg(ctx, tApp.App, batch) { + tApp.CheckTx(checkTx) + } + } + + // Advance block + ctx = tApp.AdvanceToBlock(2, testapp.AdvanceToBlockOptions{}) + + // Place second block orders and cancels + for _, order := range tc.secondBlockOrders { + for _, checkTx := range testapp.MustMakeCheckTxsWithClobMsg(ctx, tApp.App, order) { + resp := tApp.CheckTx(checkTx) + require.Conditionf(t, resp.IsOK, "Expected CheckTx to succeed. Response: %+v", resp) + } + } + for _, batch := range tc.secondBlockBatchCancel { + for _, checkTx := range testapp.MustMakeCheckTxsWithClobMsg(ctx, tApp.App, batch) { + tApp.CheckTx(checkTx) + } + } + + // Verify expectations + for orderId, shouldHaveOrder := range tc.expectedOrderIdsInMemclob { + _, exists := tApp.App.ClobKeeper.MemClob.GetOrder(ctx, orderId) + require.Equal(t, shouldHaveOrder, exists) + } + for orderId, expectedCancelExpirationBlock := range tc.expectedCancelExpirationsInMemclob { + cancelExpirationBlock, exists := tApp.App.ClobKeeper.MemClob.GetCancelOrder(ctx, orderId) + require.True(t, exists) + require.Equal(t, expectedCancelExpirationBlock, cancelExpirationBlock) + } + for orderId, expectedFillAmount := range tc.expectedOrderFillAmounts { + _, fillAmount, _ := tApp.App.ClobKeeper.GetOrderFillAmount(ctx, orderId) + require.Equal(t, expectedFillAmount, fillAmount.ToUint64()) + } + }) + } +} diff --git a/protocol/x/clob/keeper/orders.go b/protocol/x/clob/keeper/orders.go index 19eed98b8a..c6169c11e8 100644 --- a/protocol/x/clob/keeper/orders.go +++ b/protocol/x/clob/keeper/orders.go @@ -47,6 +47,76 @@ func (k Keeper) GetOperations(ctx sdk.Context) *types.MsgProposedOperations { return msgProposedOperations } +// BatchCancelShortTermOrder removes a specified batch of short term orders from all order-related data +// structures in the memclob. As well, BatchCancelShortTermOrder adds (or updates) cancels to the desired +// `goodTilBlock` in the memclob for all specified orders. +// This message is not atomic. It will optimistically call `CancelShortTermOrder` for every order in the batch. +// If any of the orders error, the error will be silently logged. This msg will only error if: +// - Stateful validation fails +// This function will return two client id lists, one for successes and one for failures. +// This method assumes the provided MsgBatchCancel has already passed ValidateBasic in CheckTx. +func (k Keeper) BatchCancelShortTermOrder( + ctx sdk.Context, + msg *types.MsgBatchCancel, +) (success []uint32, failure []uint32, err error) { + lib.AssertCheckTxMode(ctx) + // Note that we add `+1` here to account for the fact that `ctx.BlockHeight()` is technically the + // previously mined block, not the next block that will be proposed. This is due to the fact that + // this function is only ever called during `CheckTx`. + nextBlockHeight := lib.MustConvertIntegerToUint32(ctx.BlockHeight() + 1) + + // Statefully validate the GTB and the clob pair ids. + goodTilBlock := msg.GetGoodTilBlock() + if err := k.validateGoodTilBlock(goodTilBlock, nextBlockHeight); err != nil { + return success, failure, err + } + for _, batchOrder := range msg.GetShortTermCancels() { + clobPairId := batchOrder.GetClobPairId() + if _, found := k.GetClobPair(ctx, types.ClobPairId(clobPairId)); !found { + return success, failure, errorsmod.Wrapf( + types.ErrInvalidClobPairParameter, + "Invalid clob pair id %+v", + clobPairId, + ) + } + } + subaccountId := msg.GetSubaccountId() + for _, batchOrder := range msg.GetShortTermCancels() { + clobPairId := batchOrder.GetClobPairId() + for _, clientId := range batchOrder.GetClientIds() { + msgCancelOrder := types.MsgCancelOrder{ + OrderId: types.OrderId{ + SubaccountId: subaccountId, + OrderFlags: types.OrderIdFlags_ShortTerm, + ClobPairId: clobPairId, + ClientId: clientId, + }, + GoodTilOneof: &types.MsgCancelOrder_GoodTilBlock{ + GoodTilBlock: goodTilBlock, + }, + } + // Run the short term order. If it errors, just log silently. + err := k.CancelShortTermOrder( + ctx, + &msgCancelOrder, + ) + + if err != nil { + failure = append(failure, clientId) + log.InfoLog( + ctx, + "Failed to cancel short term order.", + log.Error, err, + ) + } else { + success = append(failure, clientId) + } + } + } + + return success, failure, nil +} + // CancelShortTermOrder removes a Short-Term order by `OrderId` (if it exists) from all order-related data structures // in the memclob. As well, CancelShortTermOrder adds (or updates) a cancel to the desired `goodTilBlock` in the // memclob. diff --git a/protocol/x/clob/types/clob_keeper.go b/protocol/x/clob/types/clob_keeper.go index ccac1d061f..02b64e7c25 100644 --- a/protocol/x/clob/types/clob_keeper.go +++ b/protocol/x/clob/types/clob_keeper.go @@ -22,6 +22,10 @@ type ClobKeeper interface { success bool, successPerUpdate map[satypes.SubaccountId]satypes.UpdateResult, ) + BatchCancelShortTermOrder( + ctx sdk.Context, + msg *MsgBatchCancel, + ) (success []uint32, failure []uint32, err error) CancelShortTermOrder(ctx sdk.Context, msg *MsgCancelOrder) error CancelStatefulOrder(ctx sdk.Context, msg *MsgCancelOrder) error CreatePerpetualClobPair( diff --git a/protocol/x/clob/types/errors.go b/protocol/x/clob/types/errors.go index 792f0a9451..882015c7a1 100644 --- a/protocol/x/clob/types/errors.go +++ b/protocol/x/clob/types/errors.go @@ -211,6 +211,11 @@ var ( 45, "Invalid batch cancel message", ) + ErrBatchCancelFailed = errorsmod.Register( + ModuleName, + 46, + "Batch cancel has failed", + ) // Liquidations errors. ErrInvalidLiquidationsConfig = errorsmod.Register( diff --git a/protocol/x/clob/types/tx.pb.go b/protocol/x/clob/types/tx.pb.go index 6e9d086e15..a30668d511 100644 --- a/protocol/x/clob/types/tx.pb.go +++ b/protocol/x/clob/types/tx.pb.go @@ -497,7 +497,9 @@ type OrderBatch struct { // The Clob Pair ID all orders in this order batch belong to. ClobPairId uint32 `protobuf:"varint,1,opt,name=clob_pair_id,json=clobPairId,proto3" json:"clob_pair_id,omitempty"` // List of client ids in this order batch. - ClientIds []uint32 `protobuf:"fixed32,2,rep,packed,name=client_ids,json=clientIds,proto3" json:"client_ids,omitempty"` + // Note that this is serialized as a uint32 instead of a fixed32 to + // avoid issues when decoding repeated packed fixed32. + ClientIds []uint32 `protobuf:"varint,2,rep,packed,name=client_ids,json=clientIds,proto3" json:"client_ids,omitempty"` } func (m *OrderBatch) Reset() { *m = OrderBatch{} } @@ -1118,7 +1120,7 @@ func init() { proto.RegisterFile("dydxprotocol/clob/tx.proto", fileDescriptor_19 var fileDescriptor_19b9e2c0de4ab64a = []byte{ // 1149 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x57, 0x4d, 0x4f, 0x24, 0x45, - 0x18, 0x9e, 0xde, 0x55, 0x81, 0x17, 0x86, 0x85, 0x5a, 0x56, 0x66, 0x1b, 0x19, 0x86, 0x11, 0xc8, + 0x18, 0x9e, 0x5e, 0xd4, 0x85, 0x17, 0x86, 0x85, 0x5a, 0x56, 0x66, 0x1b, 0x19, 0x86, 0x11, 0xc8, 0xac, 0x2e, 0x33, 0x2b, 0x6e, 0xd0, 0x68, 0xfc, 0x1a, 0xb2, 0x1b, 0x48, 0x96, 0x00, 0x0d, 0x26, 0x46, 0x4d, 0x3a, 0x3d, 0xdd, 0xc5, 0x50, 0xd9, 0xee, 0xa9, 0xa1, 0xab, 0x07, 0xe1, 0xba, 0xbf, 0xc0, 0xbb, 0x31, 0xfa, 0x13, 0x3c, 0xec, 0xc1, 0xbb, 0x97, 0x3d, 0x6e, 0x3c, 0x6d, 0xa2, 0x51, @@ -1141,54 +1143,54 @@ var fileDescriptor_19b9e2c0de4ab64a = []byte{ 0xd4, 0xe3, 0x38, 0xcd, 0x57, 0x9e, 0xfd, 0xb1, 0x50, 0x30, 0x46, 0xed, 0x68, 0xfc, 0xc1, 0xe4, 0x93, 0xbf, 0x7f, 0x7a, 0xab, 0xef, 0xaf, 0x3a, 0x07, 0xb7, 0x53, 0xc9, 0x19, 0x98, 0x75, 0x69, 0x87, 0xe1, 0x2a, 0x81, 0x5b, 0xdb, 0xac, 0xbd, 0xeb, 0xd3, 0x2e, 0x65, 0xd8, 0xd9, 0xe9, 0x62, - 0x5f, 0x68, 0x81, 0x76, 0x61, 0x8a, 0xca, 0x91, 0x79, 0xdc, 0xc3, 0x3d, 0x5c, 0xd2, 0x2a, 0xd7, - 0x6b, 0xe3, 0x6b, 0x0b, 0x19, 0xc9, 0x48, 0x43, 0xc3, 0xfa, 0x26, 0x4a, 0xe8, 0x46, 0xdf, 0x7c, - 0x2f, 0xb4, 0xae, 0x2e, 0xc0, 0x7c, 0x66, 0x28, 0x99, 0xcb, 0x03, 0x28, 0x86, 0x00, 0xd7, 0xb2, - 0xf1, 0x4e, 0x58, 0x3e, 0x74, 0x1f, 0x5e, 0xe5, 0x75, 0xe4, 0xea, 0x8d, 0xaf, 0x95, 0xb2, 0x02, - 0x87, 0xeb, 0x51, 0x44, 0x01, 0xae, 0xce, 0x0a, 0x4a, 0xd2, 0x8d, 0xf4, 0xff, 0xb3, 0x06, 0x93, - 0xa1, 0x12, 0x56, 0xc7, 0xc6, 0xae, 0x88, 0xf0, 0x21, 0x8c, 0x8a, 0x4e, 0x21, 0x4e, 0x14, 0x44, - 0xcf, 0x0b, 0xb2, 0xe5, 0x44, 0x61, 0x46, 0xa8, 0x18, 0xa2, 0x15, 0x98, 0x6c, 0x53, 0xea, 0x98, + 0x5f, 0x68, 0x81, 0x76, 0x61, 0x8a, 0xca, 0x91, 0x79, 0xdc, 0xc3, 0x3d, 0x5c, 0xd2, 0x2a, 0x23, + 0xb5, 0xf1, 0xb5, 0x85, 0x8c, 0x64, 0xa4, 0xa1, 0x61, 0x7d, 0x13, 0x25, 0x74, 0xa3, 0x6f, 0xbe, + 0x17, 0x5a, 0x57, 0x17, 0x60, 0x3e, 0x33, 0x94, 0xcc, 0xe5, 0x01, 0x14, 0x43, 0x80, 0x6b, 0xd9, + 0x78, 0x27, 0x2c, 0x1f, 0xba, 0x0f, 0xaf, 0xf2, 0x3a, 0x72, 0xf5, 0xc6, 0xd7, 0x4a, 0x59, 0x81, + 0xc3, 0xf5, 0x28, 0xa2, 0x00, 0x57, 0x67, 0x05, 0x25, 0xe9, 0x46, 0xfa, 0xff, 0x59, 0x83, 0xc9, + 0x50, 0x09, 0xab, 0x63, 0x63, 0x57, 0x44, 0xf8, 0x10, 0x46, 0x45, 0xa7, 0x10, 0x27, 0x0a, 0xa2, + 0xe7, 0x05, 0xd9, 0x72, 0xa2, 0x30, 0xd7, 0xa9, 0x18, 0xa2, 0x15, 0x98, 0x6c, 0x53, 0xea, 0x98, 0x01, 0x71, 0x4d, 0xbe, 0x6b, 0x78, 0xb5, 0x8a, 0x9b, 0x05, 0x63, 0x22, 0x9c, 0x3f, 0x20, 0x6e, - 0x33, 0x9c, 0x45, 0x0d, 0xb8, 0x99, 0xc4, 0x99, 0x01, 0xf1, 0x70, 0xe9, 0x7a, 0x45, 0xab, 0x8d, - 0x6c, 0x16, 0x8c, 0x29, 0x15, 0x7c, 0x40, 0x3c, 0xdc, 0x9c, 0x52, 0x1c, 0xd3, 0x0e, 0xa6, 0x87, + 0x33, 0x9c, 0x45, 0x0d, 0xb8, 0x99, 0xc4, 0x99, 0x01, 0xf1, 0x70, 0x69, 0xa4, 0xa2, 0xd5, 0xae, + 0x6f, 0x16, 0x8c, 0x29, 0x15, 0x7c, 0x40, 0x3c, 0xdc, 0x9c, 0x52, 0x1c, 0xd3, 0x0e, 0xa6, 0x87, 0xd5, 0x12, 0xbc, 0x9e, 0xcc, 0x5c, 0x92, 0xfa, 0x5d, 0x90, 0x6a, 0x86, 0xfb, 0x45, 0xac, 0xa3, 0x3d, 0x28, 0xf6, 0x5b, 0xb4, 0xcf, 0x6c, 0x25, 0xc9, 0x4c, 0xe9, 0xe8, 0xfa, 0xbe, 0xfc, 0x96, 0x2c, 0x27, 0x98, 0x32, 0x87, 0xf6, 0x00, 0xb1, 0x23, 0xea, 0x07, 0x66, 0x80, 0x7d, 0xcf, 0xb4, 0x79, 0x1c, 0x56, 0xba, 0xc6, 0xfb, 0x61, 0x3e, 0xb7, 0x2c, 0x61, 0x4e, 0x91, 0xbb, 0x29, 0x6e, 0x7e, 0x80, 0x7d, 0x4f, 0x24, 0xc9, 0xd0, 0x52, 0x4a, 0xbd, 0x50, 0x90, 0x62, 0x52, 0xbb, 0xea, 0x36, 0x40, 0xdf, 0x17, 0xaa, 0xc0, 0x84, 0xdc, 0x1a, 0x31, 0xb1, 0xa2, 0x01, 0x71, 0xeb, 0x6f, - 0x39, 0x68, 0x1e, 0xc0, 0x76, 0x09, 0xe6, 0xbc, 0x45, 0x82, 0x23, 0xc6, 0x98, 0x98, 0xd9, 0x72, - 0x58, 0xf5, 0xa9, 0xc6, 0x85, 0x54, 0xd4, 0x8a, 0x85, 0x44, 0x3b, 0x30, 0xa3, 0x50, 0x64, 0x3d, - 0xdb, 0xc6, 0xd8, 0xc1, 0x4e, 0xd4, 0xf4, 0x83, 0x49, 0x1a, 0x48, 0xd2, 0xdb, 0x8f, 0x0d, 0xd1, - 0x16, 0x4c, 0x2b, 0x0e, 0x0f, 0x2d, 0xe2, 0x62, 0x67, 0x28, 0xc9, 0x8c, 0x1b, 0xd2, 0xdb, 0x43, - 0x6e, 0x15, 0x1f, 0x30, 0x9f, 0x77, 0x9d, 0xff, 0xef, 0x01, 0x93, 0x4c, 0x4e, 0xf6, 0xe7, 0x0b, - 0x0d, 0x26, 0xd4, 0xd3, 0x21, 0xdc, 0xd4, 0xfc, 0x70, 0x8f, 0xba, 0xf2, 0x8d, 0x9c, 0xc8, 0xdb, - 0x21, 0x66, 0xb3, 0x60, 0x08, 0x30, 0xfa, 0x08, 0x74, 0x45, 0x4c, 0xb1, 0x67, 0xbb, 0xe1, 0x16, - 0xf7, 0x70, 0x27, 0xe0, 0x24, 0x26, 0x36, 0x0b, 0xc6, 0xac, 0x14, 0x8e, 0xab, 0xb9, 0x1b, 0x03, - 0xd0, 0x43, 0x28, 0x26, 0x6e, 0x04, 0xde, 0x6b, 0x39, 0x47, 0x99, 0xd8, 0x5e, 0x1c, 0x16, 0x6e, - 0x65, 0xaa, 0x8c, 0x9b, 0xe3, 0x30, 0x26, 0x8f, 0xb5, 0xea, 0x9f, 0x1a, 0x2c, 0x4b, 0xe2, 0x0f, - 0xf8, 0x15, 0x77, 0x40, 0xb0, 0xff, 0x28, 0xbc, 0xe0, 0x36, 0xf8, 0x55, 0xd2, 0x13, 0xc8, 0x2b, - 0x57, 0xaa, 0x03, 0xa5, 0xbc, 0xab, 0x33, 0x2a, 0x5c, 0x23, 0x83, 0xc1, 0xa0, 0x54, 0xa2, 0x62, - 0xde, 0xc2, 0x59, 0x98, 0x54, 0x65, 0x1b, 0xb0, 0x3a, 0x14, 0x41, 0x59, 0xed, 0xdf, 0x34, 0x58, - 0x92, 0x16, 0x7c, 0x07, 0x1b, 0x56, 0x80, 0xff, 0x43, 0x45, 0x1e, 0xc3, 0x6c, 0xce, 0x03, 0x25, - 0x2a, 0x69, 0x3d, 0x43, 0x90, 0x01, 0x89, 0x44, 0x7a, 0xcc, 0xb4, 0x32, 0x20, 0x29, 0x39, 0xea, - 0x70, 0x77, 0x18, 0x72, 0x52, 0x8d, 0x5f, 0x34, 0x98, 0x93, 0x06, 0x8f, 0x94, 0x97, 0x86, 0x80, - 0x5f, 0x59, 0x84, 0xaf, 0xe1, 0x66, 0xc6, 0xbb, 0x25, 0xea, 0x88, 0xe5, 0x0c, 0x01, 0xd2, 0xb1, - 0x23, 0xde, 0xc8, 0x4d, 0xad, 0xa4, 0x58, 0x2f, 0xc3, 0x9b, 0x03, 0x48, 0xc4, 0x64, 0xd7, 0x7e, - 0x18, 0x85, 0xeb, 0xdb, 0xac, 0x8d, 0xba, 0x80, 0x32, 0x9e, 0x13, 0xb5, 0x8c, 0xac, 0x32, 0x5f, - 0x03, 0xfa, 0xbd, 0x61, 0x91, 0xf2, 0xe4, 0xfe, 0x02, 0x40, 0x79, 0x34, 0x54, 0x72, 0xec, 0x25, - 0x42, 0xaf, 0x5d, 0x86, 0x90, 0x9e, 0xbf, 0x82, 0x71, 0xf5, 0xb5, 0xb0, 0x98, 0x6d, 0xa8, 0x40, - 0xf4, 0x3b, 0x97, 0x42, 0x54, 0xe7, 0xea, 0xad, 0x9d, 0xe3, 0x5c, 0x81, 0xe4, 0x39, 0xcf, 0xba, - 0xcd, 0x1c, 0x98, 0x7c, 0xe9, 0x39, 0xba, 0x94, 0x93, 0x59, 0x02, 0xa5, 0xdf, 0x1d, 0x06, 0xa5, - 0x46, 0x79, 0xe9, 0x4e, 0xca, 0x89, 0x92, 0x44, 0xe5, 0x45, 0xc9, 0xbe, 0x42, 0xd0, 0x8f, 0x1a, - 0x54, 0x87, 0x38, 0x64, 0xdf, 0x1f, 0xe4, 0x74, 0x90, 0xa5, 0xfe, 0xe9, 0x55, 0x2d, 0x65, 0x8a, - 0xdf, 0x6b, 0xb0, 0x78, 0xf9, 0xa1, 0xf7, 0xde, 0xa0, 0x38, 0x03, 0x0c, 0xf5, 0x4f, 0xae, 0x68, - 0x28, 0xf3, 0x7b, 0xa2, 0x41, 0x29, 0xf7, 0x18, 0xaa, 0x0f, 0xf2, 0x9e, 0xc6, 0xeb, 0xeb, 0xff, - 0x0e, 0x1f, 0x27, 0xd1, 0xdc, 0x7d, 0x76, 0x5e, 0xd6, 0x9e, 0x9f, 0x97, 0xb5, 0xbf, 0xce, 0xcb, - 0xda, 0xb7, 0x17, 0xe5, 0xc2, 0xf3, 0x8b, 0x72, 0xe1, 0xc5, 0x45, 0xb9, 0xf0, 0xe5, 0x7a, 0x9b, - 0x04, 0x47, 0xbd, 0x56, 0xdd, 0xa6, 0x5e, 0xf2, 0x17, 0xe9, 0xc9, 0xfd, 0x55, 0xfb, 0xc8, 0x22, - 0x9d, 0x86, 0x9c, 0x39, 0x8d, 0x7e, 0x1e, 0x9f, 0x75, 0x31, 0x6b, 0xbd, 0xc6, 0xa7, 0xdf, 0xfd, - 0x27, 0x00, 0x00, 0xff, 0xff, 0x42, 0xe5, 0xdf, 0x15, 0x40, 0x0f, 0x00, 0x00, + 0x39, 0x68, 0x1e, 0xc0, 0x76, 0x09, 0xe6, 0xbc, 0x45, 0x82, 0x45, 0x63, 0x4c, 0xcc, 0x6c, 0x39, + 0xac, 0xfa, 0x54, 0xe3, 0x42, 0x2a, 0x6a, 0xc5, 0x42, 0xa2, 0x1d, 0x98, 0x51, 0x28, 0xb2, 0x9e, + 0x6d, 0x63, 0xec, 0x60, 0x27, 0x6a, 0xfa, 0xc1, 0x24, 0x0d, 0x24, 0xe9, 0xed, 0xc7, 0x86, 0x68, + 0x0b, 0xa6, 0x15, 0x87, 0x87, 0x16, 0x71, 0xb1, 0x33, 0x94, 0x64, 0xc6, 0x0d, 0xe9, 0xed, 0x21, + 0xb7, 0x8a, 0x0f, 0x98, 0xcf, 0xbb, 0xce, 0xff, 0xf7, 0x80, 0x49, 0x26, 0x27, 0xfb, 0xf3, 0x85, + 0x06, 0x13, 0xea, 0xe9, 0x10, 0x6e, 0x6a, 0x7e, 0xb8, 0x47, 0x5d, 0xf9, 0x46, 0x4e, 0xe4, 0xed, + 0x10, 0xb3, 0x59, 0x30, 0x04, 0x18, 0x7d, 0x04, 0xba, 0x22, 0xa6, 0xd8, 0xb3, 0xdd, 0x70, 0x8b, + 0x7b, 0xb8, 0x13, 0x70, 0x12, 0x13, 0x9b, 0x05, 0x63, 0x56, 0x0a, 0xc7, 0xd5, 0xdc, 0x8d, 0x01, + 0xe8, 0x21, 0x14, 0x13, 0x37, 0x02, 0xef, 0xb5, 0x9c, 0xa3, 0x4c, 0x6c, 0x2f, 0x0e, 0x0b, 0xb7, + 0x32, 0x55, 0xc6, 0xcd, 0x71, 0x18, 0x93, 0xc7, 0x5a, 0xf5, 0x4f, 0x0d, 0x96, 0x25, 0xf1, 0x07, + 0xfc, 0x8a, 0x3b, 0x20, 0xd8, 0x7f, 0x14, 0x5e, 0x70, 0x1b, 0xfc, 0x2a, 0xe9, 0x09, 0xe4, 0x95, + 0x2b, 0xd5, 0x81, 0x52, 0xde, 0xd5, 0x19, 0x15, 0xae, 0x91, 0xc1, 0x60, 0x50, 0x2a, 0x51, 0x31, + 0x6f, 0xe1, 0x2c, 0x4c, 0xaa, 0xb2, 0x0d, 0x58, 0x1d, 0x8a, 0xa0, 0xac, 0xf6, 0x6f, 0x1a, 0x2c, + 0x49, 0x0b, 0xbe, 0x83, 0x0d, 0x2b, 0xc0, 0xff, 0xa1, 0x22, 0x8f, 0x61, 0x36, 0xe7, 0x81, 0x12, + 0x95, 0xb4, 0x9e, 0x21, 0xc8, 0x80, 0x44, 0x22, 0x3d, 0x66, 0x5a, 0x19, 0x90, 0x94, 0x1c, 0x75, + 0xb8, 0x3b, 0x0c, 0x39, 0xa9, 0xc6, 0x2f, 0x1a, 0xcc, 0x49, 0x83, 0x47, 0xca, 0x4b, 0x43, 0xc0, + 0xaf, 0x2c, 0xc2, 0xd7, 0x70, 0x33, 0xe3, 0xdd, 0x12, 0x75, 0xc4, 0x72, 0x86, 0x00, 0xe9, 0xd8, + 0x11, 0x6f, 0xe4, 0xa6, 0x56, 0x52, 0xac, 0x97, 0xe1, 0xcd, 0x01, 0x24, 0x62, 0xb2, 0x6b, 0x3f, + 0x8c, 0xc2, 0xc8, 0x36, 0x6b, 0xa3, 0x2e, 0xa0, 0x8c, 0xe7, 0x44, 0x2d, 0x23, 0xab, 0xcc, 0xd7, + 0x80, 0x7e, 0x6f, 0x58, 0xa4, 0x3c, 0xb9, 0xbf, 0x00, 0x50, 0x1e, 0x0d, 0x95, 0x1c, 0x7b, 0x89, + 0xd0, 0x6b, 0x97, 0x21, 0xa4, 0xe7, 0xaf, 0x60, 0x5c, 0x7d, 0x2d, 0x2c, 0x66, 0x1b, 0x2a, 0x10, + 0xfd, 0xce, 0xa5, 0x10, 0xd5, 0xb9, 0x7a, 0x6b, 0xe7, 0x38, 0x57, 0x20, 0x79, 0xce, 0xb3, 0x6e, + 0x33, 0x07, 0x26, 0x5f, 0x7a, 0x8e, 0x2e, 0xe5, 0x64, 0x96, 0x40, 0xe9, 0x77, 0x87, 0x41, 0xa9, + 0x51, 0x5e, 0xba, 0x93, 0x72, 0xa2, 0x24, 0x51, 0x79, 0x51, 0xb2, 0xaf, 0x10, 0xf4, 0xa3, 0x06, + 0xd5, 0x21, 0x0e, 0xd9, 0xf7, 0x07, 0x39, 0x1d, 0x64, 0xa9, 0x7f, 0x7a, 0x55, 0x4b, 0x99, 0xe2, + 0xf7, 0x1a, 0x2c, 0x5e, 0x7e, 0xe8, 0xbd, 0x37, 0x28, 0xce, 0x00, 0x43, 0xfd, 0x93, 0x2b, 0x1a, + 0xca, 0xfc, 0x9e, 0x68, 0x50, 0xca, 0x3d, 0x86, 0xea, 0x83, 0xbc, 0xa7, 0xf1, 0xfa, 0xfa, 0xbf, + 0xc3, 0xc7, 0x49, 0x34, 0x77, 0x9f, 0x9d, 0x97, 0xb5, 0xe7, 0xe7, 0x65, 0xed, 0xaf, 0xf3, 0xb2, + 0xf6, 0xed, 0x45, 0xb9, 0xf0, 0xfc, 0xa2, 0x5c, 0x78, 0x71, 0x51, 0x2e, 0x7c, 0xb9, 0xde, 0x26, + 0xc1, 0x51, 0xaf, 0x55, 0xb7, 0xa9, 0x97, 0xfc, 0x45, 0x7a, 0x72, 0x7f, 0xd5, 0x3e, 0xb2, 0x48, + 0xa7, 0x21, 0x67, 0x4e, 0xa3, 0x9f, 0xc7, 0x67, 0x5d, 0xcc, 0x5a, 0xaf, 0xf1, 0xe9, 0x77, 0xff, + 0x09, 0x00, 0x00, 0xff, 0xff, 0xf5, 0x64, 0x28, 0x2e, 0x40, 0x0f, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1931,11 +1933,20 @@ func (m *OrderBatch) MarshalToSizedBuffer(dAtA []byte) (int, error) { var l int _ = l if len(m.ClientIds) > 0 { - for iNdEx := len(m.ClientIds) - 1; iNdEx >= 0; iNdEx-- { - i -= 4 - encoding_binary.LittleEndian.PutUint32(dAtA[i:], uint32(m.ClientIds[iNdEx])) + dAtA6 := make([]byte, len(m.ClientIds)*10) + var j5 int + for _, num := range m.ClientIds { + for num >= 1<<7 { + dAtA6[j5] = uint8(uint64(num)&0x7f | 0x80) + num >>= 7 + j5++ + } + dAtA6[j5] = uint8(num) + j5++ } - i = encodeVarintTx(dAtA, i, uint64(len(m.ClientIds)*4)) + i -= j5 + copy(dAtA[i:], dAtA6[:j5]) + i = encodeVarintTx(dAtA, i, uint64(j5)) i-- dAtA[i] = 0x12 } @@ -2490,7 +2501,11 @@ func (m *OrderBatch) Size() (n int) { n += 1 + sovTx(uint64(m.ClobPairId)) } if len(m.ClientIds) > 0 { - n += 1 + sovTx(uint64(len(m.ClientIds)*4)) + len(m.ClientIds)*4 + l = 0 + for _, e := range m.ClientIds { + l += sovTx(uint64(e)) + } + n += 1 + sovTx(uint64(l)) + l } return n } @@ -3447,13 +3462,22 @@ func (m *OrderBatch) Unmarshal(dAtA []byte) error { } } case 2: - if wireType == 5 { + if wireType == 0 { var v uint32 - if (iNdEx + 4) > l { - return io.ErrUnexpectedEOF + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } } - v = uint32(encoding_binary.LittleEndian.Uint32(dAtA[iNdEx:])) - iNdEx += 4 m.ClientIds = append(m.ClientIds, v) } else if wireType == 2 { var packedLen int @@ -3482,17 +3506,32 @@ func (m *OrderBatch) Unmarshal(dAtA []byte) error { return io.ErrUnexpectedEOF } var elementCount int - elementCount = packedLen / 4 + var count int + for _, integer := range dAtA[iNdEx:postIndex] { + if integer < 128 { + count++ + } + } + elementCount = count if elementCount != 0 && len(m.ClientIds) == 0 { m.ClientIds = make([]uint32, 0, elementCount) } for iNdEx < postIndex { var v uint32 - if (iNdEx + 4) > l { - return io.ErrUnexpectedEOF + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } } - v = uint32(encoding_binary.LittleEndian.Uint32(dAtA[iNdEx:])) - iNdEx += 4 m.ClientIds = append(m.ClientIds, v) } } else {