Skip to content

Commit

Permalink
[dex] only process contract if there is related order/cancel in the b…
Browse files Browse the repository at this point in the history
…lock
  • Loading branch information
codchen committed May 11, 2023
1 parent 969d198 commit 2514a53
Show file tree
Hide file tree
Showing 8 changed files with 128 additions and 11 deletions.
15 changes: 13 additions & 2 deletions x/dex/ante.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/sei-protocol/sei-chain/x/dex/keeper"
"github.com/sei-protocol/sei-chain/x/dex/types"
"github.com/sei-protocol/sei-chain/x/dex/utils"
)

// TickSizeMultipleDecorator check if the place order tx's price is multiple of
Expand Down Expand Up @@ -115,12 +116,22 @@ func (d CheckDexGasDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bo
}
params := d.dexKeeper.GetParams(ctx)
dexGasRequired := uint64(0)
memState := utils.GetMemState(ctx.Context())
contractLoader := func(addr string) *types.ContractInfoV2 {
contract, err := d.dexKeeper.GetContract(ctx, addr)
if err != nil {
return nil
}
return &contract
}
for _, msg := range tx.GetMsgs() {
switch m := msg.(type) {
case *types.MsgPlaceOrders:
dexGasRequired += params.DefaultGasPerOrder * uint64(len(m.Orders))
numDependencies := len(memState.GetContractToDependencies(m.ContractAddr, contractLoader))
dexGasRequired += params.DefaultGasPerOrder * uint64(len(m.Orders)*numDependencies)
case *types.MsgCancelOrders:
dexGasRequired += params.DefaultGasPerCancel * uint64(len(m.Cancellations))
numDependencies := len(memState.GetContractToDependencies(m.ContractAddr, contractLoader))
dexGasRequired += params.DefaultGasPerCancel * uint64(len(m.Cancellations)*numDependencies)
}
}
if dexGasRequired == 0 {
Expand Down
86 changes: 80 additions & 6 deletions x/dex/cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package dex

import (
"fmt"
"sync"
"time"

"github.com/cosmos/cosmos-sdk/store/prefix"
Expand All @@ -14,14 +15,20 @@ import (
const SynchronizationTimeoutInSeconds = 5

type MemState struct {
storeKey sdk.StoreKey
depositInfo *datastructures.TypedSyncMap[types.ContractAddress, *DepositInfo]
storeKey sdk.StoreKey

contractsToProcess *datastructures.SyncSet[string]
contractsToDepsMtx *sync.Mutex
contractsToDependencies *datastructures.TypedSyncMap[string, []string]
}

func NewMemState(storeKey sdk.StoreKey) *MemState {
contractsToProcess := datastructures.NewSyncSet[string]([]string{})
return &MemState{
storeKey: storeKey,
depositInfo: datastructures.NewTypedSyncMap[types.ContractAddress, *DepositInfo](),
storeKey: storeKey,
contractsToProcess: &contractsToProcess,
contractsToDepsMtx: &sync.Mutex{},
contractsToDependencies: datastructures.NewTypedSyncMap[string, []string](),
}
}

Expand Down Expand Up @@ -81,10 +88,39 @@ func (s *MemState) GetDepositInfo(ctx sdk.Context, contractAddr types.ContractAd
)
}

func (s *MemState) GetContractToDependencies(contractAddress string, loader func(addr string) *types.ContractInfoV2) []string {
s.contractsToDepsMtx.Lock()
defer s.contractsToDepsMtx.Unlock()
if deps, ok := s.contractsToDependencies.Load(contractAddress); ok {
return deps
}
loadedDownstreams := GetAllDownstreamContracts(contractAddress, loader)
s.contractsToDependencies.Store(contractAddress, loadedDownstreams)
return loadedDownstreams
}

func (s *MemState) ClearContractToDependencies() {
s.contractsToDepsMtx.Lock()
defer s.contractsToDepsMtx.Unlock()

s.contractsToDependencies = datastructures.NewTypedSyncMap[string, []string]()
}

func (s *MemState) SetDownstreamsToProcess(contractAddress string, loader func(addr string) *types.ContractInfoV2) {
s.contractsToProcess.AddAll(s.GetContractToDependencies(contractAddress, loader))
}

func (s *MemState) GetContractToProcess() *datastructures.SyncSet[string] {
return s.contractsToProcess
}

func (s *MemState) Clear(ctx sdk.Context) {
DeepDelete(ctx.KVStore(s.storeKey), types.KeyPrefix(types.MemOrderKey), func(_ []byte) bool { return true })
DeepDelete(ctx.KVStore(s.storeKey), types.KeyPrefix(types.MemCancelKey), func(_ []byte) bool { return true })
DeepDelete(ctx.KVStore(s.storeKey), types.KeyPrefix(types.MemDepositKey), func(_ []byte) bool { return true })

newContractToDependencies := datastructures.NewSyncSet([]string{})
s.contractsToProcess = &newContractToDependencies
}

func (s *MemState) ClearCancellationForPair(ctx sdk.Context, contractAddr types.ContractAddress, pair types.PairString) {
Expand All @@ -102,8 +138,12 @@ func (s *MemState) ClearCancellationForPair(ctx sdk.Context, contractAddr types.
}

func (s *MemState) DeepCopy() *MemState {
copy := NewMemState(s.storeKey)
return copy
return &MemState{
storeKey: s.storeKey,
contractsToProcess: s.contractsToProcess,
contractsToDepsMtx: s.contractsToDepsMtx, // passing by pointer
contractsToDependencies: s.contractsToDependencies,
}
}

func (s *MemState) DeepFilterAccount(ctx sdk.Context, account string) {
Expand Down Expand Up @@ -202,3 +242,37 @@ func DeepDelete(kvStore sdk.KVStore, storePrefix []byte, matcher func([]byte) bo
}
}
}

// BFS traversal over a acyclic graph
// Includes the root contract itself.
func GetAllDownstreamContracts(contractAddress string, loader func(addr string) *types.ContractInfoV2) []string {
res := []string{contractAddress}
seen := datastructures.NewSyncSet(res)
downstreams := []*types.ContractInfoV2{}
populater := func(target *types.ContractInfoV2) {
for _, dep := range target.Dependencies {
if downstream := loader(dep.Dependency); downstream != nil && !seen.Contains(downstream.ContractAddr) {
downstreams = append(downstreams, downstream)
seen.Add(downstream.ContractAddr)
} else {
// either getting the dependency returned an error, or there is a cycle in the graph. Either way
// is bad and should cause the triggering tx to fail
panic(fmt.Sprintf("getting dependency %s for %s returned an error, or there is a cycle in the dependency graph", dep.Dependency, target.ContractAddr))
}
}
}
// init first layer downstreams
if contract := loader(contractAddress); contract != nil {
populater(contract)
} else {
return res
}

for len(downstreams) > 0 {
downstream := downstreams[0]
res = append(res, downstream.ContractAddr)
populater(downstream)
downstreams = downstreams[1:]
}
return res
}
15 changes: 12 additions & 3 deletions x/dex/contract/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"time"

"github.com/cosmos/cosmos-sdk/telemetry"
seiutils "github.com/sei-protocol/sei-chain/utils"
"github.com/sei-protocol/sei-chain/utils/logging"
"github.com/sei-protocol/sei-chain/utils/tracing"

Expand Down Expand Up @@ -53,7 +52,8 @@ func EndBlockerAtomic(ctx sdk.Context, keeper *keeper.Keeper, validContractsInfo
env := newEnv(ctx, validContractsInfo, keeper)
cachedCtx, msCached := cacheContext(ctx, env)
memStateCopy := dexutils.GetMemState(cachedCtx.Context()).DeepCopy()
preRunRents := keeper.GetRentsForContracts(cachedCtx, seiutils.Map(validContractsInfo, func(c types.ContractInfoV2) string { return c.ContractAddr }))
contractsToProcess := memStateCopy.GetContractToProcess().ToOrderedSlice(datastructures.StringComparator)
preRunRents := keeper.GetRentsForContracts(cachedCtx, contractsToProcess)

handleDeposits(spanCtx, cachedCtx, env, keeper, tracer)

Expand All @@ -76,7 +76,7 @@ func EndBlockerAtomic(ctx sdk.Context, keeper *keeper.Keeper, validContractsInfo
telemetry.IncrCounter(float32(env.failedContractAddresses.Size()), "dex", "total_failed_contracts")
// No error is thrown for any contract. This should happen most of the time.
if env.failedContractAddresses.Size() == 0 {
postRunRents := keeper.GetRentsForContracts(cachedCtx, seiutils.Map(validContractsInfo, func(c types.ContractInfoV2) string { return c.ContractAddr }))
postRunRents := keeper.GetRentsForContracts(cachedCtx, contractsToProcess)
TransferRentFromDexToCollector(ctx, keeper.BankKeeper, preRunRents, postRunRents)
msCached.Write()
return env.validContractsInfo, []types.ContractInfoV2{}, ctx, true
Expand Down Expand Up @@ -165,6 +165,9 @@ func handleDeposits(spanCtx context.Context, ctx sdk.Context, env *environment,
defer telemetry.MeasureSince(time.Now(), "dex", "handle_deposits")
keeperWrapper := dexkeeperabci.KeeperWrapper{Keeper: keeper}
for _, contract := range env.validContractsInfo {
if !dexutils.GetMemState(ctx.Context()).GetContractToProcess().Contains(contract.ContractAddr) {
continue
}
if !contract.NeedOrderMatching {
continue
}
Expand Down Expand Up @@ -200,6 +203,9 @@ func handleUnfulfilledMarketOrders(ctx context.Context, sdkCtx sdk.Context, env
// Cancel unfilled market orders
defer telemetry.MeasureSince(time.Now(), "dex", "handle_unfulfilled_market_orders")
for _, contract := range env.validContractsInfo {
if !dexutils.GetMemState(sdkCtx.Context()).GetContractToProcess().Contains(contract.ContractAddr) {
return
}
if contract.NeedOrderMatching {
registeredPairs, found := env.registeredPairs.Load(contract.ContractAddr)
if !found {
Expand Down Expand Up @@ -229,6 +235,9 @@ func orderMatchingRunnable(ctx context.Context, sdkContext sdk.Context, env *env
}
}
}()
if !dexutils.GetMemState(sdkContext.Context()).GetContractToProcess().Contains(contractInfo.ContractAddr) {
return
}
if !contractInfo.NeedOrderMatching {
return
}
Expand Down
8 changes: 8 additions & 0 deletions x/dex/keeper/msgserver/msg_server_cancel_orders.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/sei-protocol/sei-chain/x/dex/types"
"github.com/sei-protocol/sei-chain/x/dex/utils"
dexutils "github.com/sei-protocol/sei-chain/x/dex/utils"
)

Expand Down Expand Up @@ -56,5 +57,12 @@ func (k msgServer) CancelOrders(goCtx context.Context, msg *types.MsgCancelOrder
}
}
ctx.EventManager().EmitEvents(events)
utils.GetMemState(ctx.Context()).SetDownstreamsToProcess(msg.ContractAddr, func(addr string) *types.ContractInfoV2 {
contract, err := k.GetContract(ctx, addr)
if err != nil {
return nil
}
return &contract
})
return &types.MsgCancelOrdersResponse{}, nil
}
9 changes: 9 additions & 0 deletions x/dex/keeper/msgserver/msg_server_place_orders.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/sei-protocol/sei-chain/x/dex/types"
"github.com/sei-protocol/sei-chain/x/dex/utils"
dexutils "github.com/sei-protocol/sei-chain/x/dex/utils"
)

Expand Down Expand Up @@ -82,6 +83,14 @@ func (k msgServer) PlaceOrders(goCtx context.Context, msg *types.MsgPlaceOrders)
}
k.SetNextOrderID(ctx, msg.ContractAddr, nextID)
ctx.EventManager().EmitEvents(events)

utils.GetMemState(ctx.Context()).SetDownstreamsToProcess(msg.ContractAddr, func(addr string) *types.ContractInfoV2 {
contract, err := k.GetContract(ctx, addr)
if err != nil {
return nil
}
return &contract
})
return &types.MsgPlaceOrdersResponse{
OrderIds: idsInResp,
}, nil
Expand Down
2 changes: 2 additions & 0 deletions x/dex/keeper/msgserver/msg_server_register_contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/sei-protocol/sei-chain/utils/datastructures"
"github.com/sei-protocol/sei-chain/x/dex/contract"
"github.com/sei-protocol/sei-chain/x/dex/types"
dexutils "github.com/sei-protocol/sei-chain/x/dex/utils"
)

func (k msgServer) RegisterContract(goCtx context.Context, msg *types.MsgRegisterContract) (*types.MsgRegisterContractResponse, error) {
Expand Down Expand Up @@ -70,6 +71,7 @@ func (k msgServer) RegisterContract(goCtx context.Context, msg *types.MsgRegiste
sdk.NewAttribute(types.AttributeKeyContractAddress, msg.Contract.ContractAddr),
))

dexutils.GetMemState(ctx.Context()).ClearContractToDependencies()
return &types.MsgRegisterContractResponse{}, nil
}

Expand Down
3 changes: 3 additions & 0 deletions x/dex/keeper/msgserver/msg_server_unregister_contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/sei-protocol/sei-chain/x/dex/types"
dexutils "github.com/sei-protocol/sei-chain/x/dex/utils"
)

func (k msgServer) UnregisterContract(goCtx context.Context, msg *types.MsgUnregisterContract) (*types.MsgUnregisterContractResponse, error) {
Expand All @@ -32,5 +33,7 @@ func (k msgServer) UnregisterContract(goCtx context.Context, msg *types.MsgUnreg
types.EventTypeUnregisterContract,
sdk.NewAttribute(types.AttributeKeyContractAddress, msg.ContractAddr),
))

dexutils.GetMemState(ctx.Context()).ClearContractToDependencies()
return &types.MsgUnregisterContractResponse{}, nil
}
1 change: 1 addition & 0 deletions x/dex/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@ func (am AppModule) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) (ret []abc
ctx.Logger().Error(fmt.Sprintf("Unregistering invalid contract %s", oldValidContract.ContractAddr))
am.keeper.DoUnregisterContract(ctx, oldValidContract)
telemetry.IncrCounter(float32(1), am.Name(), "total_unregistered_contracts")
dexutils.GetMemState(ctx.Context()).ClearContractToDependencies()
}
validContractsInfo = am.getAllContractInfo(ctx) // reload contract info to get updated dependencies due to unregister above

Expand Down

0 comments on commit 2514a53

Please sign in to comment.