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

Move clob hydration to preblocker #1412

Merged
merged 11 commits into from
Apr 29, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
53 changes: 2 additions & 51 deletions protocol/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -1164,7 +1164,8 @@ func New(
)

app.ModuleManager.SetOrderPreBlockers(
vincentwschau marked this conversation as resolved.
Show resolved Hide resolved
upgradetypes.ModuleName,
upgradetypes.ModuleName, // Must be first since upgrades may be state schema breaking.
clobmoduletypes.ModuleName,
)

// During begin block slashing happens after distr.BeginBlocker so that
Expand Down Expand Up @@ -1400,16 +1401,6 @@ func New(
tmos.Exit(err.Error())
}

// Hydrate memStores used for caching state.
app.hydrateMemStores()

// Hydrate the `memclob` with all ordersbooks from state,
// and hydrate the next `checkState` as well as the `memclob` with stateful orders.
app.hydrateMemclobWithOrderbooksAndStatefulOrders()

// Hydrate the keeper in-memory data structures.
app.hydrateKeeperInMemoryDataStructures()

// load the x/prices keeper currency-pair ID cache
app.loadCurrencyPairIDsForMarkets()
}
Expand Down Expand Up @@ -1627,15 +1618,6 @@ func (app *App) loadCurrencyPairIDsForMarkets() {
app.PricesKeeper.LoadCurrencyPairIDCache(uncachedCtx)
}

// hydrateMemStores hydrates the memStores used for caching state.
func (app *App) hydrateMemStores() {
// Create an `uncachedCtx` where the underlying MultiStore is the `rootMultiStore`.
// We use this to hydrate the `memStore` state with values from the underlying `rootMultiStore`.
uncachedCtx := app.BaseApp.NewUncachedContext(true, tmproto.Header{})
// Initialize memstore in clobKeeper with order fill amounts and stateful orders.
app.ClobKeeper.InitMemStore(uncachedCtx)
}

// initializeRateLimiters initializes the rate limiters from state if the application is
// not started from genesis.
func (app *App) initializeRateLimiters() {
Expand All @@ -1645,37 +1627,6 @@ func (app *App) initializeRateLimiters() {
app.ClobKeeper.InitalizeBlockRateLimitFromStateIfExists(uncachedCtx)
}

// hydrateMemclobWithOrderbooksAndStatefulOrders hydrates the memclob with orderbooks and stateful orders
// from state.
func (app *App) hydrateMemclobWithOrderbooksAndStatefulOrders() {
// Create a `checkStateCtx` where the underlying MultiStore is the `CacheMultiStore` for
// the `checkState`. We do this to avoid performing any state writes to the `rootMultiStore`
// directly.
checkStateCtx := app.BaseApp.NewContext(true)

// Initialize memclob in clobKeeper with orderbooks using `ClobPairs` in state.
app.ClobKeeper.InitMemClobOrderbooks(checkStateCtx)
// Initialize memclob with all existing stateful orders.
// TODO(DEC-1348): Emit indexer messages to indicate that application restarted.
app.ClobKeeper.InitStatefulOrders(checkStateCtx)
}

// hydrateKeeperInMemoryDataStructures hydrates the keeper with ClobPairId and PerpetualId mapping
// and untriggered conditional orders from state.
func (app *App) hydrateKeeperInMemoryDataStructures() {
// Create a `checkStateCtx` where the underlying MultiStore is the `CacheMultiStore` for
// the `checkState`. We do this to avoid performing any state writes to the `rootMultiStore`
// directly.
checkStateCtx := app.BaseApp.NewContext(true)

// Initialize the untriggered conditional orders data structure with untriggered
// conditional orders in state.
app.ClobKeeper.HydrateClobPairAndPerpetualMapping(checkStateCtx)
// Initialize the untriggered conditional orders data structure with untriggered
// conditional orders in state.
app.ClobKeeper.HydrateUntriggeredConditionalOrders(checkStateCtx)
}

// GetBaseApp returns the base app of the application
func (app *App) GetBaseApp() *baseapp.BaseApp { return app.BaseApp }

Expand Down
1 change: 1 addition & 0 deletions protocol/lib/metrics/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ const (
OriginalNumTxs = "original_num_txs"
OtherTxs = "other_txs"
RemoveDisallowMsgs = "remove_disallow_msgs"
PreBlocker = "pre_blocker"
PrepareProposalTxs = "prepare_proposal_txs"
PrepareCheckState = "prepare_check_state"
PricesTx = "prices_tx"
Expand Down
23 changes: 23 additions & 0 deletions protocol/mocks/ClobKeeper.go

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

5 changes: 5 additions & 0 deletions protocol/mocks/MemClob.go

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

8 changes: 8 additions & 0 deletions protocol/x/clob/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ import (
"github.com/dydxprotocol/v4-chain/protocol/x/clob/types"
)

// PreBlocker executes all ABCI PreBlock logic respective to the clob module.
func PreBlocker(
ctx sdk.Context,
keeper types.ClobKeeper,
) {
keeper.Initialize(ctx)
}

// BeginBlocker executes all ABCI BeginBlock logic respective to the clob module.
func BeginBlocker(
ctx sdk.Context,
Expand Down
8 changes: 8 additions & 0 deletions protocol/x/clob/ante/clob.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,14 @@ func (cd ClobDecorator) AnteHandle(
return next(ctx, tx, simulate)
}

// Disable order placement and cancelation processing if the clob keeper is not hydrated.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Replace hydrated with initialized.

if !cd.clobKeeper.IsInitialized() {
return ctx, errorsmod.Wrap(
types.ErrClobNotInitialized,
"clob keeper is not initialized. Please wait for the next block.",
)
}

msgs := tx.GetMsgs()
var msg = msgs[0]

Expand Down
9 changes: 7 additions & 2 deletions protocol/x/clob/keeper/clob_pair.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,12 @@ func (k Keeper) validateClobPair(ctx sdk.Context, clobPair *types.ClobPair) erro
return nil
}

// maybeCreateOrderbook creates a new orderbook in the memclob.
func (k Keeper) maybeCreateOrderbook(ctx sdk.Context, clobPair types.ClobPair) {
// Create the corresponding orderbook in the memclob.
k.MemClob.MaybeCreateOrderbook(ctx, clobPair)
}
Comment on lines +158 to +162
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need maybe create orderbook here since orderbooks might have already been created by InitGenesis


// createOrderbook creates a new orderbook in the memclob.
func (k Keeper) createOrderbook(ctx sdk.Context, clobPair types.ClobPair) {
// Create the corresponding orderbook in the memclob.
Expand Down Expand Up @@ -192,12 +198,11 @@ func (k Keeper) setClobPair(ctx sdk.Context, clobPair types.ClobPair) {
}

// InitMemClobOrderbooks initializes the memclob with `ClobPair`s from state.
// This is called during app initialization in `app.go`, before any ABCI calls are received.
func (k Keeper) InitMemClobOrderbooks(ctx sdk.Context) {
clobPairs := k.GetAllClobPairs(ctx)
for _, clobPair := range clobPairs {
// Create the corresponding orderbook in the memclob.
k.createOrderbook(
k.maybeCreateOrderbook(
ctx,
clobPair,
)
Expand Down
39 changes: 39 additions & 0 deletions protocol/x/clob/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ type (
indexerEventManager indexer_manager.IndexerEventManager
streamingManager streamingtypes.GrpcStreamingManager

initialized *atomic.Bool
memStoreInitialized *atomic.Bool

Flags flags.ClobFlags
Expand Down Expand Up @@ -111,6 +112,7 @@ func NewKeeper(
indexerEventManager: indexerEventManager,
streamingManager: grpcStreamingManager,
memStoreInitialized: &atomic.Bool{},
initialized: &atomic.Bool{},
txDecoder: txDecoder,
mevTelemetryConfig: MevTelemetryConfig{
Enabled: clobFlags.MevTelemetryEnabled,
Expand Down Expand Up @@ -154,6 +156,43 @@ func (k Keeper) Logger(ctx sdk.Context) log.Logger {
func (k Keeper) InitializeForGenesis(ctx sdk.Context) {
}

// IsInitialized returns whether the clob keeper has been hydrated.
func (k Keeper) IsInitialized() bool {
return k.initialized.Load()
}

// Initialize hydrates the clob keeper with the necessary in memory data structures.
func (k Keeper) Initialize(ctx sdk.Context) {
alreadyInitialized := k.initialized.Swap(true)
if alreadyInitialized {
return
}

// Initialize memstore in clobKeeper with order fill amounts and stateful orders.
k.InitMemStore(ctx)

// Branch the context for hydration.
// This means that new order matches from hydration will get added to the operations
// queue but the corresponding state changes will be discarded.
// This is because we are currently in the deliver state so writing optimistic matches
// breaks consensus.
checkCtx, _ := ctx.CacheContext()
checkCtx = checkCtx.WithIsCheckTx(true)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

someone give me a quick sanity check here

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logic / comment makes sense, however seems hard to track. Maybe an additional comment that this is currently only run in Preblocker which is why this is needed?


// Initialize memclob in clobKeeper with orderbooks using `ClobPairs` in state.
k.InitMemClobOrderbooks(checkCtx)
// Initialize memclob with all existing stateful orders.
// TODO(DEC-1348): Emit indexer messages to indicate that application restarted.
k.InitStatefulOrders(checkCtx)

// Initialize the untriggered conditional orders data structure with untriggered
// conditional orders in state.
k.HydrateClobPairAndPerpetualMapping(checkCtx)
// Initialize the untriggered conditional orders data structure with untriggered
// conditional orders in state.
k.HydrateUntriggeredConditionalOrders(checkCtx)
}

// InitMemStore initializes the memstore of the `clob` keeper.
// This is called during app initialization in `app.go`, before any ABCI calls are received.
func (k Keeper) InitMemStore(ctx sdk.Context) {
Expand Down
11 changes: 11 additions & 0 deletions protocol/x/clob/memclob/memclob.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,17 @@ func (m *MemClobPriceTimePriority) CancelOrder(
return offchainUpdates, nil
}

// MaybeCreateOrderbook is used for updating memclob internal data structures to mark an orderbook as created.
func (m *MemClobPriceTimePriority) MaybeCreateOrderbook(
ctx sdk.Context,
clobPair types.ClobPair,
) {
if _, exists := m.openOrders.orderbooksMap[clobPair.GetClobPairId()]; exists {
return
}
m.CreateOrderbook(ctx, clobPair)
}

// CreateOrderbook is used for updating memclob internal data structures to mark an orderbook as created.
// This function will panic if `clobPairId` already exists in any of the memclob's internal data structures.
func (m *MemClobPriceTimePriority) CreateOrderbook(
Expand Down
13 changes: 13 additions & 0 deletions protocol/x/clob/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
"github.com/dydxprotocol/v4-chain/protocol/lib"
"github.com/dydxprotocol/v4-chain/protocol/lib/metrics"
"github.com/dydxprotocol/v4-chain/protocol/x/clob/client/cli"
"github.com/dydxprotocol/v4-chain/protocol/x/clob/keeper"
"github.com/dydxprotocol/v4-chain/protocol/x/clob/types"
Expand Down Expand Up @@ -164,6 +165,18 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.Raw
// ConsensusVersion implements ConsensusVersion.
func (AppModule) ConsensusVersion() uint64 { return 1 }

// PreBlock executes all ABCI PreBlock logic respective to the clob module.
func (am AppModule) PreBlock(ctx context.Context) (appmodule.ResponsePreBlock, error) {
defer telemetry.ModuleMeasureSince(am.Name(), time.Now(), metrics.PreBlocker)
PreBlocker(
lib.UnwrapSDKContext(ctx, types.ModuleName),
am.keeper,
)
return sdk.ResponsePreBlock{
ConsensusParamsChanged: false,
}, nil
}

// BeginBlock executes all ABCI BeginBlock logic respective to the clob module.
func (am AppModule) BeginBlock(ctx context.Context) error {
defer telemetry.ModuleMeasureSince(am.Name(), time.Now(), telemetry.MetricKeyBeginBlocker)
Expand Down
3 changes: 3 additions & 0 deletions protocol/x/clob/types/clob_keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ type ClobKeeper interface {
LiquidationsKeeper
LiquidationsConfigKeeper

IsInitialized() bool
Initialize(ctx sdk.Context)

AddOrderToOrderbookSubaccountUpdatesCheck(
ctx sdk.Context,
clobPairId ClobPairId,
Expand Down
5 changes: 5 additions & 0 deletions protocol/x/clob/types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,11 @@ var (
46,
"Batch cancel has failed",
)
ErrClobNotInitialized = errorsmod.Register(
ModuleName,
47,
"CLOB has not been initialized",
)

// Liquidations errors.
ErrInvalidLiquidationsConfig = errorsmod.Register(
Expand Down
4 changes: 4 additions & 0 deletions protocol/x/clob/types/memclob.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ type MemClob interface {
ctx sdk.Context,
clobPair ClobPair,
)
MaybeCreateOrderbook(
ctx sdk.Context,
clobPair ClobPair,
)
CountSubaccountShortTermOrders(
ctx sdk.Context,
subaccountId satypes.SubaccountId,
Expand Down
Loading