Skip to content

Commit

Permalink
do not unregister if out of rent
Browse files Browse the repository at this point in the history
  • Loading branch information
codchen committed May 2, 2023
1 parent b3f7928 commit f4eaf7a
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 15 deletions.
42 changes: 33 additions & 9 deletions x/dex/contract/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package contract

import (
"context"
"errors"
"fmt"
"sync"
"time"
Expand Down Expand Up @@ -33,6 +34,7 @@ const LogExecSigSendAfter = 2 * time.Second
type environment struct {
validContractsInfo []types.ContractInfoV2
failedContractAddresses datastructures.SyncSet[string]
outOfRentContractAddresses datastructures.SyncSet[string]
settlementsByContract *datastructures.TypedSyncMap[string, []*types.SettlementEntry]
executionTerminationSignals *datastructures.TypedSyncMap[string, chan struct{}]
registeredPairs *datastructures.TypedSyncMap[string, []types.Pair]
Expand All @@ -42,7 +44,7 @@ type environment struct {
eventManagerMutex *sync.Mutex
}

func EndBlockerAtomic(ctx sdk.Context, keeper *keeper.Keeper, validContractsInfo []types.ContractInfoV2, tracingInfo *tracing.Info) ([]types.ContractInfoV2, sdk.Context, bool) {
func EndBlockerAtomic(ctx sdk.Context, keeper *keeper.Keeper, validContractsInfo []types.ContractInfoV2, tracingInfo *tracing.Info) ([]types.ContractInfoV2, []types.ContractInfoV2, sdk.Context, bool) {
tracer := tracingInfo.Tracer
spanCtx, span := tracingInfo.Start("DexEndBlockerAtomic")
defer span.End()
Expand Down Expand Up @@ -74,7 +76,7 @@ func EndBlockerAtomic(ctx sdk.Context, keeper *keeper.Keeper, validContractsInfo
postRunRents := keeper.GetRentsForContracts(cachedCtx, seiutils.Map(validContractsInfo, func(c types.ContractInfoV2) string { return c.ContractAddr }))
TransferRentFromDexToCollector(ctx, keeper.BankKeeper, preRunRents, postRunRents)
msCached.Write()
return env.validContractsInfo, ctx, true
return env.validContractsInfo, []types.ContractInfoV2{}, ctx, true
}

// persistent contract rent charges for failed contracts and discard everything else
Expand All @@ -99,7 +101,7 @@ func EndBlockerAtomic(ctx sdk.Context, keeper *keeper.Keeper, validContractsInfo

// restore keeper in-memory state
newGoContext := context.WithValue(ctx.Context(), dexutils.DexMemStateContextKey, memStateCopy)
return filterNewValidContracts(ctx, env), ctx.WithContext(newGoContext), false
return filterNewValidContracts(ctx, env), getOutOfRentContracts(ctx, env), ctx.WithContext(newGoContext), false
}

func newEnv(ctx sdk.Context, validContractsInfo []types.ContractInfoV2, keeper *keeper.Keeper) *environment {
Expand All @@ -119,6 +121,7 @@ func newEnv(ctx sdk.Context, validContractsInfo []types.ContractInfoV2, keeper *
return &environment{
validContractsInfo: validContractsInfo,
failedContractAddresses: datastructures.NewSyncSet([]string{}),
outOfRentContractAddresses: datastructures.NewSyncSet([]string{}),
settlementsByContract: settlementsByContract,
executionTerminationSignals: executionTerminationSignals,
registeredPairs: registeredPairs,
Expand All @@ -128,6 +131,14 @@ func newEnv(ctx sdk.Context, validContractsInfo []types.ContractInfoV2, keeper *
}
}

func (e *environment) addError(contractAddr string, err error) {
if err == types.ErrInsufficientRent {
e.outOfRentContractAddresses.Add(contractAddr)
return
}
e.failedContractAddresses.Add(contractAddr)
}

func cacheContext(ctx sdk.Context, env *environment) (sdk.Context, sdk.CacheMultiStore) {
cachedCtx, msCached := store.GetCachedContext(ctx)
goCtx := context.WithValue(cachedCtx.Context(), dexcache.CtxKeyExecTermSignal, env.executionTerminationSignals)
Expand All @@ -154,7 +165,7 @@ func handleDeposits(spanCtx context.Context, ctx sdk.Context, env *environment,
continue
}
if err := keeperWrapper.HandleEBDeposit(spanCtx, ctx, tracer, contract.ContractAddr); err != nil {
env.failedContractAddresses.Add(contract.ContractAddr)
env.addError(contract.ContractAddr, err)
}
}
}
Expand All @@ -174,7 +185,7 @@ func handleSettlements(ctx context.Context, sdkCtx sdk.Context, env *environment
}
if err := HandleSettlements(sdkCtx, contractAddr, keeper, settlements); err != nil {
sdkCtx.Logger().Error(fmt.Sprintf("Error handling settlements for %s", contractAddr))
env.failedContractAddresses.Add(contractAddr)
env.addError(contractAddr, err)
}
return true
})
Expand All @@ -190,7 +201,7 @@ func handleUnfulfilledMarketOrders(ctx context.Context, sdkCtx sdk.Context, env
}
if err := CancelUnfulfilledMarketOrders(ctx, sdkCtx, contract.ContractAddr, keeper, registeredPairs, tracer); err != nil {
sdkCtx.Logger().Error(fmt.Sprintf("Error cancelling unfulfilled market orders for %s", contract.ContractAddr))
env.failedContractAddresses.Add(contract.ContractAddr)
env.addError(contract.ContractAddr, err)
}
}
}
Expand Down Expand Up @@ -222,10 +233,10 @@ func orderMatchingRunnable(ctx context.Context, sdkContext sdk.Context, env *env

if !pairFound || !found {
sdkContext.Logger().Error(fmt.Sprintf("No pair or order book for %s", contractInfo.ContractAddr))
env.failedContractAddresses.Add(contractInfo.ContractAddr)
env.addError(contractInfo.ContractAddr, errors.New("no pair found (internal error)"))
} else if settlements, err := HandleExecutionForContract(ctx, sdkContext, contractInfo, keeper, pairs, orderBooks, tracer); err != nil {
sdkContext.Logger().Error(fmt.Sprintf("Error for EndBlock of %s", contractInfo.ContractAddr))
env.failedContractAddresses.Add(contractInfo.ContractAddr)
env.addError(contractInfo.ContractAddr, err)
} else {
env.settlementsByContract.Store(contractInfo.ContractAddr, settlements)
}
Expand All @@ -239,16 +250,29 @@ func orderMatchingRunnable(ctx context.Context, sdkContext sdk.Context, env *env
func filterNewValidContracts(ctx sdk.Context, env *environment) []types.ContractInfoV2 {
newValidContracts := []types.ContractInfoV2{}
for _, contract := range env.validContractsInfo {
if !env.failedContractAddresses.Contains(contract.ContractAddr) {
if !env.failedContractAddresses.Contains(contract.ContractAddr) && !env.outOfRentContractAddresses.Contains(contract.ContractAddr) {
newValidContracts = append(newValidContracts, contract)
}
}
for _, failedContractAddress := range env.failedContractAddresses.ToOrderedSlice(datastructures.StringComparator) {
dexutils.GetMemState(ctx.Context()).DeepFilterAccount(ctx, failedContractAddress)
}
for _, outOfRentContractAddress := range env.outOfRentContractAddresses.ToOrderedSlice(datastructures.StringComparator) {
dexutils.GetMemState(ctx.Context()).DeepFilterAccount(ctx, outOfRentContractAddress)
}
return newValidContracts
}

func getOutOfRentContracts(ctx sdk.Context, env *environment) []types.ContractInfoV2 {
outOfRentContracts := []types.ContractInfoV2{}
for _, contract := range env.validContractsInfo {
if env.outOfRentContractAddresses.Contains(contract.ContractAddr) {
outOfRentContracts = append(outOfRentContracts, contract)
}
}
return outOfRentContracts
}

func TransferRentFromDexToCollector(ctx sdk.Context, bankKeeper bankkeeper.Keeper, preRents map[string]uint64, postRents map[string]uint64) {
total := uint64(0)
for addr, preRent := range preRents {
Expand Down
2 changes: 1 addition & 1 deletion x/dex/keeper/contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ func (k Keeper) ChargeRentForGas(ctx sdk.Context, contractAddr string, gasUsed u
if err := k.SetContract(ctx, &contract); err != nil {
return err
}
return errors.New("insufficient rent")
return types.ErrInsufficientRent
}
contract.RentBalance -= uint64(gasPrice)
return k.SetContract(ctx, &contract)
Expand Down
19 changes: 14 additions & 5 deletions x/dex/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -274,17 +274,19 @@ func (am AppModule) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) (ret []abc

validContractsInfo := am.getAllContractInfo(ctx)
validContractsInfoAtBeginning := validContractsInfo
outOfRentContractsInfo := []types.ContractInfoV2{}
// Each iteration is atomic. If an iteration finishes without any error, it will return,
// otherwise it will rollback any state change, filter out contracts that cause the error,
// and proceed to the next iteration. The loop is guaranteed to finish since
// `validContractAddresses` will always decrease in size every iteration.
iterCounter := len(validContractsInfo)
for len(validContractsInfo) > 0 {
newValidContractsInfo, ctx, ok := contract.EndBlockerAtomic(ctx, &am.keeper, validContractsInfo, am.tracingInfo)
newValidContractsInfo, newOutOfRentContractsInfo, ctx, ok := contract.EndBlockerAtomic(ctx, &am.keeper, validContractsInfo, am.tracingInfo)
if ok {
break
}
validContractsInfo = newValidContractsInfo
outOfRentContractsInfo = newOutOfRentContractsInfo

// technically we don't really need this if `EndBlockerAtomic` guarantees that `validContractsInfo` size will
// always shrink if not `ok`, but just in case, we decided to have an explicit termination criteria here to
Expand All @@ -295,15 +297,22 @@ func (am AppModule) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) (ret []abc
break
}
}
validContractAddrs := map[string]struct{}{}
validContractAddrs, outOfRentContractAddrs := map[string]struct{}{}, map[string]struct{}{}
for _, c := range validContractsInfo {
validContractAddrs[c.ContractAddr] = struct{}{}
}
for _, c := range outOfRentContractsInfo {
outOfRentContractAddrs[c.ContractAddr] = struct{}{}
}
for _, c := range validContractsInfoAtBeginning {
if _, ok := validContractAddrs[c.ContractAddr]; !ok {
ctx.Logger().Error(fmt.Sprintf("Unregistering invalid contract %s", c.ContractAddr))
am.keeper.DoUnregisterContract(ctx, c)
if _, ok := validContractAddrs[c.ContractAddr]; ok {
continue
}
if _, ok := outOfRentContractAddrs[c.ContractAddr]; ok {
continue
}
ctx.Logger().Error(fmt.Sprintf("Unregistering invalid contract %s", c.ContractAddr))
am.keeper.DoUnregisterContract(ctx, c)
}

return []abci.ValidatorUpdate{}
Expand Down
1 change: 1 addition & 0 deletions x/dex/types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,6 @@ var (
ErrPairNotRegistered = sdkerrors.Register(ModuleName, 16, "pair is not registered")
ErrContractNotExists = sdkerrors.Register(ModuleName, 17, "Error finding contract info")
ErrParsingContractInfo = sdkerrors.Register(ModuleName, 18, "Error parsing contract info")
ErrInsufficientRent = sdkerrors.Register(ModuleName, 19, "Error contract does not have sufficient fee")
ErrCircularContractDependency = sdkerrors.Register(ModuleName, 1103, "circular contract dependency detected")
)

0 comments on commit f4eaf7a

Please sign in to comment.