code.vegaprotocol.io/vega@v0.79.0/core/execution/engine.go (about)

     1  // Copyright (C) 2023 Gobalsky Labs Limited
     2  //
     3  // This program is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU Affero General Public License as
     5  // published by the Free Software Foundation, either version 3 of the
     6  // License, or (at your option) any later version.
     7  //
     8  // This program is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  // GNU Affero General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU Affero General Public License
    14  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    15  
    16  package execution
    17  
    18  import (
    19  	"context"
    20  	"errors"
    21  	"fmt"
    22  	"sort"
    23  	"sync"
    24  	"time"
    25  
    26  	"code.vegaprotocol.io/vega/core/events"
    27  	"code.vegaprotocol.io/vega/core/execution/common"
    28  	"code.vegaprotocol.io/vega/core/execution/future"
    29  	"code.vegaprotocol.io/vega/core/execution/spot"
    30  	"code.vegaprotocol.io/vega/core/fee"
    31  	"code.vegaprotocol.io/vega/core/metrics"
    32  	"code.vegaprotocol.io/vega/core/monitor"
    33  	"code.vegaprotocol.io/vega/core/types"
    34  	"code.vegaprotocol.io/vega/libs/crypto"
    35  	"code.vegaprotocol.io/vega/libs/num"
    36  	"code.vegaprotocol.io/vega/logging"
    37  	"code.vegaprotocol.io/vega/protos/vega"
    38  
    39  	"golang.org/x/exp/maps"
    40  )
    41  
    42  var (
    43  	// ErrMarketDoesNotExist is returned when the market does not exist.
    44  	ErrMarketDoesNotExist = errors.New("market does not exist")
    45  
    46  	// ErrNotAFutureMarket is returned when the market isn't a future market.
    47  	ErrNotAFutureMarket = errors.New("not a future market")
    48  
    49  	// ErrNoMarketID is returned when invalid (empty) market id was supplied during market creation.
    50  	ErrNoMarketID = errors.New("no valid market id was supplied")
    51  
    52  	// ErrInvalidOrderCancellation is returned when an incomplete order cancellation request is used.
    53  	ErrInvalidOrderCancellation = errors.New("invalid order cancellation")
    54  
    55  	// ErrSuccessorMarketDoesNotExists is returned when SucceedMarket call is made with an invalid successor market ID.
    56  	ErrSuccessorMarketDoesNotExist = errors.New("successor market does not exist")
    57  
    58  	// ErrParentMarketNotEnactedYet is returned when trying to enact a successor market that is still in proposed state.
    59  	ErrParentMarketNotEnactedYet = errors.New("parent market in proposed state, can't enact successor")
    60  
    61  	// ErrInvalidStopOrdersCancellation is returned when an incomplete stop orders cancellation request is used.
    62  	ErrInvalidStopOrdersCancellation = errors.New("invalid stop orders cancellation")
    63  
    64  	// ErrMarketIDRequiredWhenOrderIDSpecified is returned when a stop order cancellation is emitted without an order id.
    65  	ErrMarketIDRequiredWhenOrderIDSpecified = errors.New("market id required when order id specified")
    66  
    67  	// ErrStopOrdersNotAcceptedDuringOpeningAuction is returned if a stop order is submitted when the market is in the opening auction.
    68  	ErrStopOrdersNotAcceptedDuringOpeningAuction = errors.New("stop orders are not accepted during the opening auction")
    69  )
    70  
    71  // Engine is the execution engine.
    72  type Engine struct {
    73  	Config
    74  	log *logging.Logger
    75  
    76  	futureMarkets    map[string]*future.Market
    77  	futureMarketsCpy []*future.Market
    78  
    79  	spotMarkets    map[string]*spot.Market
    80  	spotMarketsCpy []*spot.Market
    81  
    82  	allMarkets    map[string]common.CommonMarket
    83  	allMarketsCpy []common.CommonMarket
    84  
    85  	collateral                    common.Collateral
    86  	assets                        common.Assets
    87  	referralDiscountRewardService fee.ReferralDiscountRewardService
    88  	volumeDiscountService         fee.VolumeDiscountService
    89  	volumeRebateService           fee.VolumeRebateService
    90  
    91  	banking common.Banking
    92  	parties common.Parties
    93  
    94  	broker                common.Broker
    95  	timeService           common.TimeService
    96  	stateVarEngine        common.StateVarEngine
    97  	marketActivityTracker *common.MarketActivityTracker
    98  
    99  	oracle common.OracleEngine
   100  
   101  	npv netParamsValues
   102  
   103  	snapshotSerialised    []byte
   104  	newGeneratedProviders []types.StateProvider // new providers generated during the last state change
   105  
   106  	// Map of all active snapshot providers that the execution engine has generated
   107  	generatedProviders map[string]struct{}
   108  
   109  	maxPeggedOrders        uint64
   110  	totalPeggedOrdersCount int64
   111  
   112  	marketCPStates map[string]*types.CPMarketState
   113  	// a map of all successor markets under parent ID
   114  	// used to manage pending markets once a successor takes over
   115  	successors      map[string][]string
   116  	isSuccessor     map[string]string
   117  	successorWindow time.Duration
   118  	// only used once, during CP restore, this doesn't need to be included in a snapshot or checkpoint.
   119  	skipRestoreSuccessors                 map[string]struct{}
   120  	minMaintenanceMarginQuantumMultiplier num.Decimal
   121  	minHoldingQuantumMultiplier           num.Decimal
   122  
   123  	lock sync.RWMutex
   124  
   125  	delayTransactionsTarget common.DelayTransactionsTarget
   126  }
   127  
   128  // NewEngine takes stores and engines and returns
   129  // a new execution engine to process new orders, etc.
   130  func NewEngine(
   131  	log *logging.Logger,
   132  	executionConfig Config,
   133  	ts common.TimeService,
   134  	collateral common.Collateral,
   135  	oracle common.OracleEngine,
   136  	broker common.Broker,
   137  	stateVarEngine common.StateVarEngine,
   138  	marketActivityTracker *common.MarketActivityTracker,
   139  	assets common.Assets,
   140  	referralDiscountRewardService fee.ReferralDiscountRewardService,
   141  	volumeDiscountService fee.VolumeDiscountService,
   142  	volumeRebateService fee.VolumeRebateService,
   143  	banking common.Banking,
   144  	parties common.Parties,
   145  	delayTransactionsTarget common.DelayTransactionsTarget,
   146  ) *Engine {
   147  	// setup logger
   148  	log = log.Named(namedLogger)
   149  	log.SetLevel(executionConfig.Level.Get())
   150  	e := &Engine{
   151  		log:                           log,
   152  		Config:                        executionConfig,
   153  		futureMarkets:                 map[string]*future.Market{},
   154  		spotMarkets:                   map[string]*spot.Market{},
   155  		allMarkets:                    map[string]common.CommonMarket{},
   156  		timeService:                   ts,
   157  		collateral:                    collateral,
   158  		assets:                        assets,
   159  		broker:                        broker,
   160  		oracle:                        oracle,
   161  		npv:                           defaultNetParamsValues(),
   162  		generatedProviders:            map[string]struct{}{},
   163  		stateVarEngine:                stateVarEngine,
   164  		marketActivityTracker:         marketActivityTracker,
   165  		marketCPStates:                map[string]*types.CPMarketState{},
   166  		successors:                    map[string][]string{},
   167  		isSuccessor:                   map[string]string{},
   168  		skipRestoreSuccessors:         map[string]struct{}{},
   169  		referralDiscountRewardService: referralDiscountRewardService,
   170  		volumeDiscountService:         volumeDiscountService,
   171  		volumeRebateService:           volumeRebateService,
   172  
   173  		banking:                 banking,
   174  		parties:                 parties,
   175  		delayTransactionsTarget: delayTransactionsTarget,
   176  	}
   177  
   178  	// set the eligibility for proposer bonus checker
   179  	e.marketActivityTracker.SetEligibilityChecker(e)
   180  
   181  	return e
   182  }
   183  
   184  // ReloadConf updates the internal configuration of the execution
   185  // engine and its dependencies.
   186  func (e *Engine) ReloadConf(cfg Config) {
   187  	e.log.Debug("reloading configuration")
   188  
   189  	if e.log.GetLevel() != cfg.Level.Get() {
   190  		e.log.Info("updating log level",
   191  			logging.String("old", e.log.GetLevel().String()),
   192  			logging.String("new", cfg.Level.String()),
   193  		)
   194  		e.log.SetLevel(cfg.Level.Get())
   195  	}
   196  
   197  	e.Config = cfg
   198  	for _, mkt := range e.futureMarketsCpy {
   199  		mkt.ReloadConf(e.Matching, e.Risk, e.Position, e.Settlement, e.Fee)
   200  	}
   201  
   202  	for _, mkt := range e.spotMarketsCpy {
   203  		mkt.ReloadConf(e.Matching, e.Fee)
   204  	}
   205  }
   206  
   207  func (e *Engine) OnEpochEvent(ctx context.Context, epoch types.Epoch) {
   208  	for _, m := range e.allMarketsCpy {
   209  		// propagate SLA parameters to markets at a start of a epoch
   210  		if epoch.Action == vega.EpochAction_EPOCH_ACTION_START {
   211  			e.propagateSLANetParams(ctx, m, false)
   212  		}
   213  
   214  		m.OnEpochEvent(ctx, epoch)
   215  	}
   216  }
   217  
   218  func (e *Engine) OnEpochRestore(ctx context.Context, epoch types.Epoch) {
   219  	for _, m := range e.allMarketsCpy {
   220  		m.OnEpochRestore(ctx, epoch)
   221  	}
   222  }
   223  
   224  func (e *Engine) Hash() []byte {
   225  	e.log.Debug("hashing markets")
   226  
   227  	hashes := make([]string, 0, len(e.allMarketsCpy))
   228  
   229  	for _, m := range e.allMarketsCpy {
   230  		hash := m.Hash()
   231  		e.log.Debug("market app state hash", logging.Hash(hash), logging.String("market-id", m.GetID()))
   232  		hashes = append(hashes, string(hash))
   233  	}
   234  	sort.Strings(hashes)
   235  
   236  	// get the accounts hash + add it at end of all markets hash
   237  	accountsHash := e.collateral.Hash()
   238  	e.log.Debug("accounts state hash", logging.Hash(accountsHash))
   239  
   240  	bytes := []byte{}
   241  	for _, h := range append(hashes, string(accountsHash)) {
   242  		bytes = append(bytes, []byte(h)...)
   243  	}
   244  
   245  	return crypto.Hash(bytes)
   246  }
   247  
   248  func (e *Engine) ensureIsFutureMarket(market string) error {
   249  	if _, exist := e.allMarkets[market]; !exist {
   250  		return ErrMarketDoesNotExist
   251  	}
   252  
   253  	if _, isFuture := e.futureMarkets[market]; !isFuture {
   254  		return ErrNotAFutureMarket
   255  	}
   256  
   257  	return nil
   258  }
   259  
   260  func (e *Engine) SubmitAMM(
   261  	ctx context.Context,
   262  	submit *types.SubmitAMM,
   263  	deterministicID string,
   264  ) error {
   265  	if err := e.ensureIsFutureMarket(submit.MarketID); err != nil {
   266  		return err
   267  	}
   268  
   269  	return e.allMarkets[submit.MarketID].SubmitAMM(ctx, submit, deterministicID)
   270  }
   271  
   272  func (e *Engine) AmendAMM(
   273  	ctx context.Context,
   274  	submit *types.AmendAMM,
   275  	deterministicID string,
   276  ) error {
   277  	if err := e.ensureIsFutureMarket(submit.MarketID); err != nil {
   278  		return err
   279  	}
   280  
   281  	return e.allMarkets[submit.MarketID].AmendAMM(ctx, submit, deterministicID)
   282  }
   283  
   284  func (e *Engine) CancelAMM(
   285  	ctx context.Context,
   286  	cancel *types.CancelAMM,
   287  	deterministicID string,
   288  ) error {
   289  	if err := e.ensureIsFutureMarket(cancel.MarketID); err != nil {
   290  		return err
   291  	}
   292  
   293  	return e.allMarkets[cancel.MarketID].CancelAMM(ctx, cancel, deterministicID)
   294  }
   295  
   296  // RejectMarket will stop the execution of the market
   297  // and refund into the general account any funds in margins accounts from any parties
   298  // This works only if the market is in a PROPOSED STATE.
   299  func (e *Engine) RejectMarket(ctx context.Context, marketID string) error {
   300  	if e.log.IsDebug() {
   301  		e.log.Debug("reject market", logging.MarketID(marketID))
   302  	}
   303  
   304  	_, isFuture := e.futureMarkets[marketID]
   305  	if _, ok := e.allMarkets[marketID]; !ok {
   306  		return ErrMarketDoesNotExist
   307  	}
   308  	mkt := e.allMarkets[marketID]
   309  	if err := mkt.Reject(ctx); err != nil {
   310  		return err
   311  	}
   312  
   313  	// send market data event so market data and markets API are consistent.
   314  	e.broker.Send(events.NewMarketDataEvent(ctx, mkt.GetMarketData()))
   315  	e.removeMarket(marketID)
   316  
   317  	if !isFuture {
   318  		return nil
   319  	}
   320  
   321  	// a market rejection can have a knock-on effect for proposed markets which were supposed to succeed this market
   322  	// they should be purged here, and @TODO handle any errors
   323  	if successors, ok := e.successors[marketID]; ok {
   324  		delete(e.successors, marketID)
   325  		for _, sID := range successors {
   326  			e.RejectMarket(ctx, sID)
   327  			delete(e.isSuccessor, sID)
   328  		}
   329  	}
   330  	// remove entries in succession maps
   331  	delete(e.isSuccessor, marketID)
   332  	// and clear out any state that may exist
   333  	delete(e.marketCPStates, marketID)
   334  	return nil
   335  }
   336  
   337  // StartOpeningAuction will start the opening auction of the given market.
   338  // This will work only if the market is currently in a PROPOSED state.
   339  func (e *Engine) StartOpeningAuction(ctx context.Context, marketID string) error {
   340  	if e.log.IsDebug() {
   341  		e.log.Debug("start opening auction", logging.MarketID(marketID))
   342  	}
   343  
   344  	if mkt, ok := e.allMarkets[marketID]; ok {
   345  		return mkt.StartOpeningAuction(ctx)
   346  	}
   347  
   348  	return ErrMarketDoesNotExist
   349  }
   350  
   351  func (e *Engine) EnterLongBlockAuction(ctx context.Context, duration int64) {
   352  	for _, mkt := range e.allMarkets {
   353  		mkt.EnterLongBlockAuction(ctx, duration)
   354  	}
   355  }
   356  
   357  func (e *Engine) SucceedMarket(ctx context.Context, successor, parent string) error {
   358  	return e.succeedOrRestore(ctx, successor, parent, false)
   359  }
   360  
   361  func (e *Engine) restoreOwnState(ctx context.Context, mID string) (bool, error) {
   362  	mkt, ok := e.futureMarkets[mID]
   363  	if !ok {
   364  		return false, ErrMarketDoesNotExist
   365  	}
   366  	if state, ok := e.marketCPStates[mID]; ok {
   367  		// set ELS state and the like
   368  		mkt.RestoreELS(ctx, state)
   369  		// if there was state of the market to restore, then check if this is a successor market
   370  		if pid := mkt.GetParentMarketID(); len(pid) > 0 {
   371  			// mark parent market as being succeeded
   372  			if pMkt, ok := e.futureMarkets[pid]; ok {
   373  				pMkt.SetSucceeded()
   374  			}
   375  			for _, pending := range e.successors[pid] {
   376  				if pending == mID {
   377  					continue
   378  				}
   379  				e.RejectMarket(ctx, pending)
   380  			}
   381  			delete(e.successors, pid)
   382  			delete(e.isSuccessor, mID)
   383  		}
   384  		return true, nil
   385  	}
   386  	return false, nil
   387  }
   388  
   389  func (e *Engine) succeedOrRestore(ctx context.Context, successor, parent string, restore bool) error {
   390  	mkt, ok := e.futureMarkets[successor]
   391  	if !ok {
   392  		// this can happen if a proposal vote closed, but the proposal had an enactment time in the future.
   393  		// Between the proposal being accepted and enacted, another proposal may be enacted first.
   394  		// Whenever the parent is succeeded, all other markets are rejected and removed from the map here,
   395  		// nevertheless the proposal is still valid, and updated by the governance engine.
   396  		return ErrMarketDoesNotExist
   397  	}
   398  	if restore {
   399  		// first up: when restoring markets, check to see if this successor should be rejected
   400  		if _, ok := e.skipRestoreSuccessors[parent]; ok {
   401  			_ = e.RejectMarket(ctx, successor)
   402  			delete(e.successors, parent)
   403  			delete(e.isSuccessor, successor)
   404  			// no error: we just do not care about this market anymore
   405  			return nil
   406  		}
   407  	}
   408  	// if this is a market restore, first check to see if there is some state already
   409  	_, ok = e.GetMarket(parent, true)
   410  	if !ok && !restore {
   411  		// a successor market that has passed the vote, but the parent market either already was succeeded
   412  		// or the proposal vote closed when the parent market was still around, but it wasn't enacted until now
   413  		// and since then the parent market state expired. This shouldn't really happen save for checkpoints,
   414  		// but then the proposal will be rejected/closed later on.
   415  		mkt.ResetParentIDAndInsurancePoolFraction()
   416  		return nil
   417  	}
   418  	_, sok := e.marketCPStates[parent]
   419  	// restoring a market, but no state of the market nor parent market exists. Treat market as parent.
   420  	if restore && !sok && !ok {
   421  		// restoring a market, but the market state and parent market both are missing
   422  		// this market, upon leaving opening auction, cannot possibly succeed a market that no longer exists
   423  		// now we should reset
   424  		mkt.ResetParentIDAndInsurancePoolFraction()
   425  		// remove from maps
   426  		delete(e.successors, parent)
   427  		delete(e.isSuccessor, successor)
   428  		return nil
   429  	}
   430  	// succeeding a parent market before it was enacted is not allowed
   431  	if pmo, ok := e.futureMarkets[parent]; ok && !restore && pmo.Mkt().State == types.MarketStateProposed {
   432  		e.RejectMarket(ctx, successor)
   433  		return ErrParentMarketNotEnactedYet
   434  	}
   435  	// successor market set up accordingly, clean up the state
   436  	// first reject all pending successors proposed for the same parent
   437  	return nil
   438  }
   439  
   440  // IsEligibleForProposerBonus checks if the given value is greater than that market quantum * quantum_multiplier.
   441  func (e *Engine) IsEligibleForProposerBonus(marketID string, value *num.Uint) bool {
   442  	if mkt, ok := e.allMarkets[marketID]; ok {
   443  		quantum, err := e.collateral.GetAssetQuantum(mkt.GetAssetForProposerBonus())
   444  		if err != nil {
   445  			return false
   446  		}
   447  		return value.ToDecimal().GreaterThan(quantum.Mul(e.npv.marketCreationQuantumMultiple))
   448  	}
   449  	return false
   450  }
   451  
   452  // SubmitMarket submits a new market configuration to the network.
   453  func (e *Engine) SubmitMarket(ctx context.Context, marketConfig *types.Market, proposer string, oos time.Time) error {
   454  	return e.submitOrRestoreMarket(ctx, marketConfig, proposer, true, oos)
   455  }
   456  
   457  // SubmitSpotMarket submits a new spot market configuration to the network.
   458  func (e *Engine) SubmitSpotMarket(ctx context.Context, marketConfig *types.Market, proposer string, oos time.Time) error {
   459  	return e.submitOrRestoreSpotMarket(ctx, marketConfig, proposer, true, oos)
   460  }
   461  
   462  // RestoreMarket restores a new market from proposal checkpoint.
   463  func (e *Engine) RestoreMarket(ctx context.Context, marketConfig *types.Market) error {
   464  	proposer := e.marketActivityTracker.GetProposer(marketConfig.ID)
   465  	if len(proposer) == 0 {
   466  		return ErrMarketDoesNotExist
   467  	}
   468  	// restoring a market means starting it as though the proposal was accepted now.
   469  	if err := e.submitOrRestoreMarket(ctx, marketConfig, "", false, e.timeService.GetTimeNow()); err != nil {
   470  		return err
   471  	}
   472  	// attempt to restore market state from checkpoint, returns true if state (ELS) was restored
   473  	// error if the market doesn't exist
   474  	ok, err := e.restoreOwnState(ctx, marketConfig.ID)
   475  	if err != nil {
   476  		return err
   477  	}
   478  	if ok {
   479  		// existing state has been restored. This means a potential parent market has been succeeded
   480  		// the parent market may no longer be present. In that case, remove the reference to the parent market
   481  		if len(marketConfig.ParentMarketID) == 0 {
   482  			return nil
   483  		}
   484  		// successor had state to restore, meaning it left opening auction, and no other successors with the same parent market
   485  		// can be restored after this point.
   486  		e.skipRestoreSuccessors[marketConfig.ParentMarketID] = struct{}{}
   487  		// any pending successors that didn't manage to leave opening auction should be rejected at this point:
   488  		pendingSuccessors := e.successors[marketConfig.ParentMarketID]
   489  		for _, sid := range pendingSuccessors {
   490  			_ = e.RejectMarket(ctx, sid)
   491  		}
   492  		// check to see if the parent market can be found, remove from the successor maps if the parent is gone
   493  		// the market itself should still hold the reference because state was restored
   494  		pmkt, ok := e.futureMarkets[marketConfig.ParentMarketID]
   495  		if ok {
   496  			// market parent as having been succeeded
   497  			pmkt.SetSucceeded()
   498  		}
   499  		// remove the parent from the successors map
   500  		delete(e.successors, marketConfig.ParentMarketID)
   501  		// remove from the isSuccessor map, do not reset the parent ID reference to preserve the reference in the events.
   502  		delete(e.isSuccessor, marketConfig.ID)
   503  		return nil
   504  	}
   505  	// this is a successor market, handle accordingly
   506  	if pid := marketConfig.ParentMarketID; len(pid) > 0 {
   507  		return e.succeedOrRestore(ctx, marketConfig.ID, pid, true)
   508  	}
   509  	return nil
   510  }
   511  
   512  func (e *Engine) submitOrRestoreMarket(ctx context.Context, marketConfig *types.Market, proposer string, isNewMarket bool, oos time.Time) error {
   513  	if e.log.IsDebug() {
   514  		msg := "submit market"
   515  		if !isNewMarket {
   516  			msg = "restore market"
   517  		}
   518  		e.log.Debug(msg, logging.Market(*marketConfig))
   519  	}
   520  
   521  	if err := e.submitMarket(ctx, marketConfig, oos); err != nil {
   522  		return err
   523  	}
   524  	if pid := marketConfig.ParentMarketID; len(pid) > 0 {
   525  		ss, ok := e.successors[pid]
   526  		if !ok {
   527  			ss = make([]string, 0, 5)
   528  		}
   529  		id := marketConfig.ID
   530  		// add successor market to the successors, to track which markets to get rid off once one successor is enacted
   531  		e.successors[pid] = append(ss, id)
   532  		e.isSuccessor[id] = pid
   533  	}
   534  
   535  	if isNewMarket {
   536  		assets, err := marketConfig.GetAssets()
   537  		if err != nil {
   538  			e.log.Panic("failed to get asset from market config", logging.String("market", marketConfig.ID), logging.String("error", err.Error()))
   539  		}
   540  		e.marketActivityTracker.MarketProposed(assets[0], marketConfig.ID, proposer)
   541  	}
   542  
   543  	// keep state in pending, opening auction is triggered when proposal is enacted
   544  	mkt := e.futureMarkets[marketConfig.ID]
   545  	e.publishNewMarketInfos(ctx, mkt.GetMarketData(), *mkt.Mkt())
   546  	return nil
   547  }
   548  
   549  func (e *Engine) submitOrRestoreSpotMarket(ctx context.Context, marketConfig *types.Market, proposer string, isNewMarket bool, oos time.Time) error {
   550  	if e.log.IsDebug() {
   551  		msg := "submit spot market"
   552  		if !isNewMarket {
   553  			msg = "restore spot market"
   554  		}
   555  		e.log.Debug(msg, logging.Market(*marketConfig))
   556  	}
   557  
   558  	if err := e.submitSpotMarket(ctx, marketConfig, oos); err != nil {
   559  		return err
   560  	}
   561  
   562  	if isNewMarket {
   563  		assets, err := marketConfig.GetAssets()
   564  		if err != nil {
   565  			e.log.Panic("failed to get asset from market config", logging.String("market", marketConfig.ID), logging.String("error", err.Error()))
   566  		}
   567  		e.marketActivityTracker.MarketProposed(assets[1], marketConfig.ID, proposer)
   568  	}
   569  
   570  	// keep state in pending, opening auction is triggered when proposal is enacted
   571  	mkt := e.spotMarkets[marketConfig.ID]
   572  	e.publishNewMarketInfos(ctx, mkt.GetMarketData(), *mkt.Mkt())
   573  	return nil
   574  }
   575  
   576  // UpdateSpotMarket will update an existing market configuration.
   577  func (e *Engine) UpdateSpotMarket(ctx context.Context, marketConfig *types.Market) error {
   578  	e.log.Info("update spot market", logging.Market(*marketConfig))
   579  
   580  	mkt := e.spotMarkets[marketConfig.ID]
   581  	if err := mkt.Update(ctx, marketConfig); err != nil {
   582  		return err
   583  	}
   584  	e.delayTransactionsTarget.MarketDelayRequiredUpdated(mkt.GetID(), marketConfig.EnableTxReordering)
   585  	e.publishUpdateMarketInfos(ctx, mkt.GetMarketData(), *mkt.Mkt())
   586  	return nil
   587  }
   588  
   589  func (e *Engine) VerifyUpdateMarketState(changes *types.MarketStateUpdateConfiguration) error {
   590  	// futures or perps market
   591  	if market, ok := e.futureMarkets[changes.MarketID]; ok {
   592  		if changes.SettlementPrice == nil && changes.UpdateType == types.MarketStateUpdateTypeTerminate {
   593  			return fmt.Errorf("missing settlement price for governance initiated futures market termination")
   594  		}
   595  		state := market.GetMarketState()
   596  		if state == types.MarketStateCancelled || state == types.MarketStateClosed || state == types.MarketStateRejected || state == types.MarketStateSettled || state == types.MarketStateTradingTerminated {
   597  			return fmt.Errorf("invalid state update request. Market is already in a terminal state")
   598  		}
   599  		if changes.UpdateType == types.MarketStateUpdateTypeSuspend && state == types.MarketStateSuspendedViaGovernance {
   600  			return fmt.Errorf("invalid state update request. Market for suspend is already suspended")
   601  		}
   602  		if changes.UpdateType == types.MarketStateUpdateTypeResume && state != types.MarketStateSuspendedViaGovernance {
   603  			return fmt.Errorf("invalid state update request. Market for resume is not suspended")
   604  		}
   605  		return nil
   606  	}
   607  
   608  	// spot market
   609  	if market, ok := e.spotMarkets[changes.MarketID]; ok {
   610  		if changes.SettlementPrice != nil && changes.UpdateType == types.MarketStateUpdateTypeTerminate {
   611  			return fmt.Errorf("settlement price is not needed for governance initiated spot market termination")
   612  		}
   613  		state := market.GetMarketState()
   614  		if state == types.MarketStateCancelled || state == types.MarketStateClosed || state == types.MarketStateRejected || state == types.MarketStateTradingTerminated {
   615  			return fmt.Errorf("invalid state update request. Market is already in a terminal state")
   616  		}
   617  		if changes.UpdateType == types.MarketStateUpdateTypeResume && state != types.MarketStateSuspendedViaGovernance {
   618  			return fmt.Errorf("invalid state update request. Market for resume is not suspended")
   619  		}
   620  		return nil
   621  	}
   622  	return ErrMarketDoesNotExist
   623  }
   624  
   625  func (e *Engine) UpdateMarketState(ctx context.Context, changes *types.MarketStateUpdateConfiguration) error {
   626  	if market, ok := e.allMarkets[changes.MarketID]; ok {
   627  		if err := e.VerifyUpdateMarketState(changes); err != nil {
   628  			return err
   629  		}
   630  		return market.UpdateMarketState(ctx, changes)
   631  	}
   632  	return ErrMarketDoesNotExist
   633  }
   634  
   635  // UpdateMarket will update an existing market configuration.
   636  func (e *Engine) UpdateMarket(ctx context.Context, marketConfig *types.Market) error {
   637  	e.log.Info("update market", logging.Market(*marketConfig))
   638  	mkt := e.futureMarkets[marketConfig.ID]
   639  	if err := mkt.Update(ctx, marketConfig, e.oracle); err != nil {
   640  		return err
   641  	}
   642  	e.delayTransactionsTarget.MarketDelayRequiredUpdated(mkt.GetID(), marketConfig.EnableTxReordering)
   643  	e.publishUpdateMarketInfos(ctx, mkt.GetMarketData(), *mkt.Mkt())
   644  	return nil
   645  }
   646  
   647  func (e *Engine) publishNewMarketInfos(ctx context.Context, data types.MarketData, mkt types.Market) {
   648  	// we send a market data event for this market when it's created so graphql does not fail
   649  	e.broker.Send(events.NewMarketDataEvent(ctx, data))
   650  	e.broker.Send(events.NewMarketCreatedEvent(ctx, mkt))
   651  	e.broker.Send(events.NewMarketUpdatedEvent(ctx, mkt))
   652  }
   653  
   654  func (e *Engine) publishUpdateMarketInfos(ctx context.Context, data types.MarketData, mkt types.Market) {
   655  	// we send a market data event for this market when it's created so graphql does not fail
   656  	e.broker.Send(events.NewMarketDataEvent(ctx, data))
   657  	e.broker.Send(events.NewMarketUpdatedEvent(ctx, mkt))
   658  }
   659  
   660  // submitMarket will submit a new market configuration to the network.
   661  func (e *Engine) submitMarket(ctx context.Context, marketConfig *types.Market, oos time.Time) error {
   662  	if len(marketConfig.ID) == 0 {
   663  		return ErrNoMarketID
   664  	}
   665  
   666  	// ensure the asset for this new market exists
   667  	assets, err := marketConfig.GetAssets()
   668  	if err != nil {
   669  		return err
   670  	}
   671  	asset := assets[0]
   672  
   673  	if !e.collateral.AssetExists(asset) {
   674  		e.log.Error("unable to create a market with an invalid asset",
   675  			logging.MarketID(marketConfig.ID),
   676  			logging.AssetID(asset))
   677  	}
   678  
   679  	// ignore the response, this cannot fail as the asset
   680  	// is already proven to exists a few line before
   681  	_, _, _ = e.collateral.CreateMarketAccounts(ctx, marketConfig.ID, asset)
   682  
   683  	// create market auction state
   684  	mas := monitor.NewAuctionState(marketConfig, oos)
   685  	ad, err := e.assets.Get(asset)
   686  	if err != nil {
   687  		e.log.Error("Failed to create a new market, unknown asset",
   688  			logging.MarketID(marketConfig.ID),
   689  			logging.String("asset-id", asset),
   690  			logging.Error(err),
   691  		)
   692  		return err
   693  	}
   694  	mkt, err := future.NewMarket(
   695  		ctx,
   696  		e.log,
   697  		e.Risk,
   698  		e.Position,
   699  		e.Settlement,
   700  		e.Matching,
   701  		e.Fee,
   702  		e.Liquidity,
   703  		e.collateral,
   704  		e.oracle,
   705  		marketConfig,
   706  		e.timeService,
   707  		e.broker,
   708  		mas,
   709  		e.stateVarEngine,
   710  		e.marketActivityTracker,
   711  		ad,
   712  		e.peggedOrderCountUpdated,
   713  		e.referralDiscountRewardService,
   714  		e.volumeDiscountService,
   715  		e.volumeRebateService,
   716  		e.banking,
   717  		e.parties,
   718  	)
   719  	if err != nil {
   720  		e.log.Error("failed to instantiate market",
   721  			logging.MarketID(marketConfig.ID),
   722  			logging.Error(err),
   723  		)
   724  		return err
   725  	}
   726  
   727  	e.lock.Lock()
   728  	e.delayTransactionsTarget.MarketDelayRequiredUpdated(mkt.GetID(), marketConfig.EnableTxReordering)
   729  	e.futureMarkets[marketConfig.ID] = mkt
   730  	e.futureMarketsCpy = append(e.futureMarketsCpy, mkt)
   731  	e.allMarkets[marketConfig.ID] = mkt
   732  	e.allMarketsCpy = append(e.allMarketsCpy, mkt)
   733  	e.lock.Unlock()
   734  	return e.propagateInitialNetParamsToFutureMarket(ctx, mkt, false)
   735  }
   736  
   737  // submitMarket will submit a new market configuration to the network.
   738  func (e *Engine) submitSpotMarket(ctx context.Context, marketConfig *types.Market, oos time.Time) error {
   739  	if len(marketConfig.ID) == 0 {
   740  		return ErrNoMarketID
   741  	}
   742  
   743  	// ensure the asset for this new market exists
   744  	assets, err := marketConfig.GetAssets()
   745  	if err != nil {
   746  		return err
   747  	}
   748  	baseAsset := assets[spot.BaseAssetIndex]
   749  	if !e.collateral.AssetExists(baseAsset) {
   750  		e.log.Error("unable to create a spot market with an invalid base asset",
   751  			logging.MarketID(marketConfig.ID),
   752  			logging.AssetID(baseAsset))
   753  	}
   754  
   755  	quoteAsset := assets[spot.QuoteAssetIndex]
   756  	if !e.collateral.AssetExists(quoteAsset) {
   757  		e.log.Error("unable to create a spot market with an invalid quote asset",
   758  			logging.MarketID(marketConfig.ID),
   759  			logging.AssetID(quoteAsset))
   760  	}
   761  
   762  	// create market auction state
   763  	mas := monitor.NewAuctionState(marketConfig, oos)
   764  	bad, err := e.assets.Get(baseAsset)
   765  	if err != nil {
   766  		e.log.Error("Failed to create a new market, unknown asset",
   767  			logging.MarketID(marketConfig.ID),
   768  			logging.String("asset-id", baseAsset),
   769  			logging.Error(err),
   770  		)
   771  		return err
   772  	}
   773  	qad, err := e.assets.Get(quoteAsset)
   774  	if err != nil {
   775  		e.log.Error("Failed to create a new market, unknown asset",
   776  			logging.MarketID(marketConfig.ID),
   777  			logging.String("asset-id", quoteAsset),
   778  			logging.Error(err),
   779  		)
   780  		return err
   781  	}
   782  	mkt, err := spot.NewMarket(
   783  		e.log,
   784  		e.Matching,
   785  		e.Fee,
   786  		e.Liquidity,
   787  		e.collateral,
   788  		marketConfig,
   789  		e.timeService,
   790  		e.broker,
   791  		mas,
   792  		e.stateVarEngine,
   793  		e.marketActivityTracker,
   794  		bad,
   795  		qad,
   796  		e.peggedOrderCountUpdated,
   797  		e.referralDiscountRewardService,
   798  		e.volumeDiscountService,
   799  		e.volumeRebateService,
   800  		e.banking,
   801  	)
   802  	if err != nil {
   803  		e.log.Error("failed to instantiate market",
   804  			logging.MarketID(marketConfig.ID),
   805  			logging.Error(err),
   806  		)
   807  		return err
   808  	}
   809  	e.lock.Lock()
   810  	e.delayTransactionsTarget.MarketDelayRequiredUpdated(mkt.GetID(), marketConfig.EnableTxReordering)
   811  	e.spotMarkets[marketConfig.ID] = mkt
   812  	e.spotMarketsCpy = append(e.spotMarketsCpy, mkt)
   813  	e.allMarkets[marketConfig.ID] = mkt
   814  	e.allMarketsCpy = append(e.allMarketsCpy, mkt)
   815  	e.lock.Unlock()
   816  	e.collateral.CreateSpotMarketAccounts(ctx, marketConfig.ID, quoteAsset)
   817  
   818  	if err := e.propagateSpotInitialNetParams(ctx, mkt, false); err != nil {
   819  		return err
   820  	}
   821  
   822  	return nil
   823  }
   824  
   825  func (e *Engine) removeMarket(mktID string) {
   826  	e.log.Debug("removing market", logging.String("id", mktID))
   827  	e.lock.Lock()
   828  	defer e.lock.Unlock()
   829  	delete(e.allMarkets, mktID)
   830  	for i, mkt := range e.allMarketsCpy {
   831  		if mkt.GetID() == mktID {
   832  			copy(e.allMarketsCpy[i:], e.allMarketsCpy[i+1:])
   833  			e.allMarketsCpy[len(e.allMarketsCpy)-1] = nil
   834  			e.allMarketsCpy = e.allMarketsCpy[:len(e.allMarketsCpy)-1]
   835  			break
   836  		}
   837  	}
   838  	if _, ok := e.futureMarkets[mktID]; ok {
   839  		delete(e.futureMarkets, mktID)
   840  		for i, mkt := range e.futureMarketsCpy {
   841  			if mkt.GetID() == mktID {
   842  				mkt.StopSnapshots()
   843  
   844  				copy(e.futureMarketsCpy[i:], e.futureMarketsCpy[i+1:])
   845  				e.futureMarketsCpy[len(e.futureMarketsCpy)-1] = nil
   846  				e.futureMarketsCpy = e.futureMarketsCpy[:len(e.futureMarketsCpy)-1]
   847  				e.marketActivityTracker.RemoveMarket(mkt.GetSettlementAsset(), mktID)
   848  				e.log.Debug("removed in total", logging.String("id", mktID))
   849  				return
   850  			}
   851  		}
   852  		return
   853  	}
   854  	if _, ok := e.spotMarkets[mktID]; ok {
   855  		delete(e.spotMarkets, mktID)
   856  		for i, mkt := range e.spotMarketsCpy {
   857  			if mkt.GetID() == mktID {
   858  				mkt.StopSnapshots()
   859  				copy(e.spotMarketsCpy[i:], e.spotMarketsCpy[i+1:])
   860  				e.spotMarketsCpy[len(e.spotMarketsCpy)-1] = nil
   861  				e.spotMarketsCpy = e.spotMarketsCpy[:len(e.spotMarketsCpy)-1]
   862  				e.marketActivityTracker.RemoveMarket(mkt.GetAssetForProposerBonus(), mktID)
   863  				e.log.Debug("removed in total", logging.String("id", mktID))
   864  				return
   865  			}
   866  		}
   867  	}
   868  }
   869  
   870  func (e *Engine) peggedOrderCountUpdated(added int64) {
   871  	e.totalPeggedOrdersCount += added
   872  }
   873  
   874  func (e *Engine) canSubmitPeggedOrder() bool {
   875  	return uint64(e.totalPeggedOrdersCount) < e.maxPeggedOrders
   876  }
   877  
   878  func (e *Engine) SubmitStopOrders(
   879  	ctx context.Context,
   880  	submission *types.StopOrdersSubmission,
   881  	party string,
   882  	idgen common.IDGenerator,
   883  	fallsBelowID *string,
   884  	risesAboveID *string,
   885  ) (*types.OrderConfirmation, error) {
   886  	var market string
   887  	if submission.FallsBelow != nil {
   888  		market = submission.FallsBelow.OrderSubmission.MarketID
   889  	} else {
   890  		market = submission.RisesAbove.OrderSubmission.MarketID
   891  	}
   892  
   893  	if mkt, ok := e.allMarkets[market]; ok {
   894  		conf, err := mkt.SubmitStopOrdersWithIDGeneratorAndOrderIDs(
   895  			ctx, submission, party, idgen, fallsBelowID, risesAboveID)
   896  		if err != nil {
   897  			return nil, err
   898  		}
   899  
   900  		// not necessary going to trade on submission, could be nil
   901  		if conf != nil {
   902  			// increasing the gauge, just because we reuse the
   903  			// decrement function, and it required the order + passive
   904  			metrics.OrderGaugeAdd(1, market)
   905  			e.decrementOrderGaugeMetrics(market, conf.Order, conf.PassiveOrdersAffected)
   906  		}
   907  
   908  		return conf, nil
   909  	}
   910  	return nil, ErrMarketDoesNotExist
   911  }
   912  
   913  func (e *Engine) CancelStopOrders(ctx context.Context, cancel *types.StopOrdersCancellation, party string, idgen common.IDGenerator) error {
   914  	// ensure that if orderID is specified marketId is as well
   915  	if len(cancel.OrderID) > 0 && len(cancel.MarketID) <= 0 {
   916  		return ErrMarketIDRequiredWhenOrderIDSpecified
   917  	}
   918  
   919  	if len(cancel.MarketID) > 0 {
   920  		if len(cancel.OrderID) > 0 {
   921  			return e.cancelStopOrders(ctx, party, cancel.MarketID, cancel.OrderID, idgen)
   922  		}
   923  		return e.cancelStopOrdersByMarket(ctx, party, cancel.MarketID)
   924  	}
   925  	return e.cancelAllPartyStopOrders(ctx, party)
   926  }
   927  
   928  func (e *Engine) cancelStopOrders(ctx context.Context, party, market, orderID string, _ common.IDGenerator) error {
   929  	if mkt, ok := e.allMarkets[market]; ok {
   930  		err := mkt.CancelStopOrder(ctx, party, orderID)
   931  		if err != nil {
   932  			return err
   933  		}
   934  		return nil
   935  	}
   936  	return types.ErrInvalidMarketID
   937  }
   938  
   939  func (e *Engine) cancelStopOrdersByMarket(ctx context.Context, party, market string) error {
   940  	if mkt, ok := e.allMarkets[market]; ok {
   941  		err := mkt.CancelAllStopOrders(ctx, party)
   942  		if err != nil {
   943  			return err
   944  		}
   945  	}
   946  	return types.ErrInvalidMarketID
   947  }
   948  
   949  func (e *Engine) cancelAllPartyStopOrders(ctx context.Context, party string) error {
   950  	for _, mkt := range e.allMarketsCpy {
   951  		err := mkt.CancelAllStopOrders(ctx, party)
   952  		if err != nil && err != common.ErrTradingNotAllowed {
   953  			return err
   954  		}
   955  	}
   956  	return nil
   957  }
   958  
   959  // SubmitOrder checks the incoming order and submits it to a Vega market.
   960  func (e *Engine) SubmitOrder(ctx context.Context, submission *types.OrderSubmission, party string, idgen common.IDGenerator, orderID string) (*types.OrderConfirmation, error) {
   961  	timer := metrics.NewTimeCounter(submission.MarketID, "execution", "SubmitOrder")
   962  	defer func() {
   963  		timer.EngineTimeCounterAdd()
   964  	}()
   965  
   966  	if e.log.IsDebug() {
   967  		e.log.Debug("submit order", logging.OrderSubmission(submission))
   968  	}
   969  
   970  	if mkt, ok := e.allMarkets[submission.MarketID]; ok {
   971  		if submission.PeggedOrder != nil && !e.canSubmitPeggedOrder() {
   972  			return nil, types.ErrTooManyPeggedOrders
   973  		}
   974  
   975  		metrics.OrderGaugeAdd(1, submission.MarketID)
   976  		conf, err := mkt.SubmitOrderWithIDGeneratorAndOrderID(
   977  			ctx, submission, party, idgen, orderID, true)
   978  		if err != nil {
   979  			return nil, err
   980  		}
   981  
   982  		e.decrementOrderGaugeMetrics(submission.MarketID, conf.Order, conf.PassiveOrdersAffected)
   983  		return conf, nil
   984  	}
   985  	return nil, types.ErrInvalidMarketID
   986  }
   987  
   988  func (e *Engine) ValidateSettlementData(mID string, data *num.Uint) bool {
   989  	mkt, ok := e.allMarkets[mID]
   990  	if !ok {
   991  		return false
   992  	}
   993  	return mkt.ValidateSettlementData(data)
   994  }
   995  
   996  // AmendOrder takes order amendment details and attempts to amend the order
   997  // if it exists and is in a editable state.
   998  func (e *Engine) AmendOrder(ctx context.Context, amendment *types.OrderAmendment, party string, idgen common.IDGenerator) (*types.OrderConfirmation, error) {
   999  	timer := metrics.NewTimeCounter(amendment.MarketID, "execution", "AmendOrder")
  1000  	defer func() {
  1001  		timer.EngineTimeCounterAdd()
  1002  	}()
  1003  
  1004  	if e.log.IsDebug() {
  1005  		e.log.Debug("amend order", logging.OrderAmendment(amendment))
  1006  	}
  1007  
  1008  	if mkt, ok := e.allMarkets[amendment.MarketID]; ok {
  1009  		conf, err := mkt.AmendOrderWithIDGenerator(ctx, amendment, party, idgen)
  1010  		if err != nil {
  1011  			return nil, err
  1012  		}
  1013  
  1014  		e.decrementOrderGaugeMetrics(amendment.MarketID, conf.Order, conf.PassiveOrdersAffected)
  1015  		return conf, nil
  1016  	}
  1017  	return nil, types.ErrInvalidMarketID
  1018  }
  1019  
  1020  func (e *Engine) decrementOrderGaugeMetrics(
  1021  	market string,
  1022  	order *types.Order,
  1023  	passive []*types.Order,
  1024  ) {
  1025  	// order was active, not anymore -> decrement gauge
  1026  	if order.Status != types.OrderStatusActive {
  1027  		metrics.OrderGaugeAdd(-1, market)
  1028  	}
  1029  	var passiveCount int
  1030  	for _, v := range passive {
  1031  		if v.IsFinished() {
  1032  			passiveCount++
  1033  		}
  1034  	}
  1035  	if passiveCount > 0 {
  1036  		metrics.OrderGaugeAdd(-passiveCount, market)
  1037  	}
  1038  }
  1039  
  1040  // CancelOrder takes order details and attempts to cancel if it exists in matching engine, stores etc.
  1041  func (e *Engine) CancelOrder(
  1042  	ctx context.Context,
  1043  	cancel *types.OrderCancellation,
  1044  	party string,
  1045  	idgen common.IDGenerator,
  1046  ) (_ []*types.OrderCancellationConfirmation, returnedErr error) {
  1047  	timer := metrics.NewTimeCounter(cancel.MarketID, "execution", "CancelOrder")
  1048  	defer func() {
  1049  		timer.EngineTimeCounterAdd()
  1050  	}()
  1051  
  1052  	if e.log.IsDebug() {
  1053  		e.log.Debug("cancel order", logging.OrderCancellation(cancel))
  1054  	}
  1055  
  1056  	// ensure that if orderID is specified marketId is as well
  1057  	if len(cancel.OrderID) > 0 && len(cancel.MarketID) <= 0 {
  1058  		return nil, ErrInvalidOrderCancellation
  1059  	}
  1060  
  1061  	if len(cancel.MarketID) > 0 {
  1062  		if len(cancel.OrderID) > 0 {
  1063  			return e.cancelOrder(ctx, party, cancel.MarketID, cancel.OrderID, idgen)
  1064  		}
  1065  		return e.cancelOrderByMarket(ctx, party, cancel.MarketID)
  1066  	}
  1067  	return e.cancelAllPartyOrders(ctx, party)
  1068  }
  1069  
  1070  func (e *Engine) cancelOrder(ctx context.Context, party, market, orderID string, idgen common.IDGenerator) ([]*types.OrderCancellationConfirmation, error) {
  1071  	if mkt, ok := e.allMarkets[market]; ok {
  1072  		conf, err := mkt.CancelOrderWithIDGenerator(ctx, party, orderID, idgen)
  1073  		if err != nil {
  1074  			return nil, err
  1075  		}
  1076  		if conf.Order.Status == types.OrderStatusCancelled {
  1077  			metrics.OrderGaugeAdd(-1, market)
  1078  		}
  1079  		return []*types.OrderCancellationConfirmation{conf}, nil
  1080  	}
  1081  	return nil, types.ErrInvalidMarketID
  1082  }
  1083  
  1084  func (e *Engine) cancelOrderByMarket(ctx context.Context, party, market string) ([]*types.OrderCancellationConfirmation, error) {
  1085  	if mkt, ok := e.allMarkets[market]; ok {
  1086  		confirmations, err := mkt.CancelAllOrders(ctx, party)
  1087  		if err != nil {
  1088  			return nil, err
  1089  		}
  1090  		var confirmed int
  1091  		for _, conf := range confirmations {
  1092  			if conf.Order.Status == types.OrderStatusCancelled {
  1093  				confirmed++
  1094  			}
  1095  		}
  1096  		metrics.OrderGaugeAdd(-confirmed, market)
  1097  		return confirmations, nil
  1098  	}
  1099  	return nil, types.ErrInvalidMarketID
  1100  }
  1101  
  1102  func (e *Engine) cancelAllPartyOrdersForMarket(ctx context.Context, ID, party string, mkt common.CommonMarket) ([]*types.OrderCancellationConfirmation, error) {
  1103  	confs, err := mkt.CancelAllOrders(ctx, party)
  1104  	if err != nil && err != common.ErrTradingNotAllowed {
  1105  		return nil, err
  1106  	}
  1107  	var confirmed int
  1108  	for _, conf := range confs {
  1109  		if conf.Order.Status == types.OrderStatusCancelled {
  1110  			confirmed++
  1111  		}
  1112  	}
  1113  	metrics.OrderGaugeAdd(-confirmed, ID)
  1114  	return confs, nil
  1115  }
  1116  
  1117  func (e *Engine) cancelAllPartyOrders(ctx context.Context, party string) ([]*types.OrderCancellationConfirmation, error) {
  1118  	confirmations := []*types.OrderCancellationConfirmation{}
  1119  
  1120  	for _, mkt := range e.allMarketsCpy {
  1121  		confs, err := e.cancelAllPartyOrdersForMarket(ctx, mkt.GetID(), party, mkt)
  1122  		if err != nil && err != common.ErrTradingNotAllowed {
  1123  			return nil, err
  1124  		}
  1125  		confirmations = append(confirmations, confs...)
  1126  	}
  1127  	return confirmations, nil
  1128  }
  1129  
  1130  func (e *Engine) SubmitLiquidityProvision(ctx context.Context, sub *types.LiquidityProvisionSubmission, party, deterministicID string) error {
  1131  	timer := metrics.NewTimeCounter(sub.MarketID, "execution", "LiquidityProvisionSubmission")
  1132  	defer func() {
  1133  		timer.EngineTimeCounterAdd()
  1134  	}()
  1135  
  1136  	if e.log.IsDebug() {
  1137  		e.log.Debug("submit liquidity provision",
  1138  			logging.LiquidityProvisionSubmission(*sub),
  1139  			logging.PartyID(party),
  1140  			logging.LiquidityID(deterministicID),
  1141  		)
  1142  	}
  1143  
  1144  	if mkt, ok := e.allMarkets[sub.MarketID]; ok {
  1145  		return mkt.SubmitLiquidityProvision(ctx, sub, party, deterministicID)
  1146  	}
  1147  	return types.ErrInvalidMarketID
  1148  }
  1149  
  1150  func (e *Engine) AmendLiquidityProvision(ctx context.Context, lpa *types.LiquidityProvisionAmendment, party string, deterministicID string) error {
  1151  	timer := metrics.NewTimeCounter(lpa.MarketID, "execution", "LiquidityProvisionAmendment")
  1152  	defer func() {
  1153  		timer.EngineTimeCounterAdd()
  1154  	}()
  1155  
  1156  	if e.log.IsDebug() {
  1157  		e.log.Debug("amend liquidity provision",
  1158  			logging.LiquidityProvisionAmendment(*lpa),
  1159  			logging.PartyID(party),
  1160  			logging.MarketID(lpa.MarketID),
  1161  		)
  1162  	}
  1163  
  1164  	if mkt, ok := e.allMarkets[lpa.MarketID]; ok {
  1165  		return mkt.AmendLiquidityProvision(ctx, lpa, party, deterministicID)
  1166  	}
  1167  	return types.ErrInvalidMarketID
  1168  }
  1169  
  1170  func (e *Engine) CancelLiquidityProvision(ctx context.Context, cancel *types.LiquidityProvisionCancellation, party string) error {
  1171  	timer := metrics.NewTimeCounter(cancel.MarketID, "execution", "LiquidityProvisionCancellation")
  1172  	defer func() {
  1173  		timer.EngineTimeCounterAdd()
  1174  	}()
  1175  
  1176  	if e.log.IsDebug() {
  1177  		e.log.Debug("cancel liquidity provision",
  1178  			logging.LiquidityProvisionCancellation(*cancel),
  1179  			logging.PartyID(party),
  1180  			logging.MarketID(cancel.MarketID),
  1181  		)
  1182  	}
  1183  
  1184  	if mkt, ok := e.allMarkets[cancel.MarketID]; ok {
  1185  		return mkt.CancelLiquidityProvision(ctx, cancel, party)
  1186  	}
  1187  	return types.ErrInvalidMarketID
  1188  }
  1189  
  1190  func (e *Engine) OnTick(ctx context.Context, t time.Time) {
  1191  	timer := metrics.NewTimeCounter("-", "execution", "OnTick")
  1192  
  1193  	e.log.Debug("updating engine on new time update")
  1194  
  1195  	// notify markets of the time expiration
  1196  	toDelete := []string{}
  1197  	parentStates := e.getParentStates()
  1198  	evts := make([]events.Event, 0, len(e.futureMarketsCpy))
  1199  	toSkip := map[int]string{}
  1200  	for i, mkt := range e.futureMarketsCpy {
  1201  		// we can skip successor markets which reference a parent market that has been succeeded
  1202  		if _, ok := toSkip[i]; ok {
  1203  			continue
  1204  		}
  1205  		mkt := mkt
  1206  		id := mkt.GetID()
  1207  		mdef := mkt.Mkt()
  1208  		pstate, isSuccessor := parentStates[id]
  1209  		inOA := isSuccessor && mdef.State == types.MarketStatePending
  1210  		// this market was a successor, but has no parent state (parent state likely expired
  1211  		// although this currently is not possible, better check here.
  1212  		if isSuccessor && inOA {
  1213  			if pstate == nil {
  1214  				delete(e.isSuccessor, id)
  1215  				delete(e.successors, mdef.ParentMarketID)
  1216  				mkt.ResetParentIDAndInsurancePoolFraction()
  1217  				isSuccessor = false
  1218  			} else {
  1219  				// update parent state in market prior to potentially leaving opening auction
  1220  				mkt.InheritParent(ctx, pstate)
  1221  			}
  1222  		}
  1223  		closing := mkt.OnTick(ctx, t)
  1224  		// successor market has left opening auction
  1225  		leftOA := inOA && mdef.State == types.MarketStateActive
  1226  		if closing {
  1227  			e.log.Info("market is closed, removing from execution engine",
  1228  				logging.MarketID(id))
  1229  			toDelete = append(toDelete, id)
  1230  		}
  1231  		// this can only be true if mkt was a successor, and the successor market has left the opening auction
  1232  		if leftOA {
  1233  			pid := mdef.ParentMarketID
  1234  			// transfer insurance pool balance
  1235  			if !mdef.InsurancePoolFraction.IsZero() {
  1236  				lm := e.collateral.SuccessorInsuranceFraction(ctx, id, pid, mkt.GetSettlementAsset(), mdef.InsurancePoolFraction)
  1237  				if lm != nil {
  1238  					e.broker.Send(events.NewLedgerMovements(ctx, []*types.LedgerMovement{lm}))
  1239  				}
  1240  			}
  1241  			// set parent market as succeeded, clear insurance pool account if needed
  1242  			if pmkt, ok := e.futureMarkets[pid]; ok {
  1243  				pmkt.SetSucceeded()
  1244  			} else {
  1245  				asset := mkt.GetSettlementAsset()
  1246  				// clear parent market insurance pool
  1247  				if clearTransfers, _ := e.collateral.ClearInsurancepool(ctx, pid, asset, true); len(clearTransfers) > 0 {
  1248  					e.broker.Send(events.NewLedgerMovements(ctx, clearTransfers))
  1249  				}
  1250  			}
  1251  			// add other markets that need to be rejected to the skip list
  1252  			toSkip = e.getPendingSuccessorsToReject(pid, id, toSkip)
  1253  			// remove data used to indicate that the parent market has pending successors
  1254  			delete(e.isSuccessor, id)
  1255  			delete(e.successors, pid)
  1256  			delete(e.marketCPStates, pid)
  1257  		} else if isSuccessor {
  1258  			// this call can be made even if the market has left opening auction, but checking this here, too, is better than
  1259  			// relying on how this is implemented
  1260  			mkt.RollbackInherit(ctx)
  1261  		}
  1262  		if !mkt.IsSucceeded() {
  1263  			// the market was not yet succeeded -> capture state
  1264  			cps := mkt.GetCPState()
  1265  			// set until what time this state is considered valid.
  1266  			cps.TTL = t.Add(e.successorWindow)
  1267  			e.marketCPStates[id] = cps
  1268  		} else {
  1269  			// market was succeeded
  1270  			delete(e.marketCPStates, id)
  1271  		}
  1272  		evts = append(evts, events.NewMarketDataEvent(ctx, mkt.GetMarketData()))
  1273  	}
  1274  
  1275  	for _, mkt := range e.spotMarketsCpy {
  1276  		closing := mkt.OnTick(ctx, t)
  1277  		if closing {
  1278  			e.log.Info("spot market is closed, removing from execution engine",
  1279  				logging.MarketID(mkt.GetID()))
  1280  			toDelete = append(toDelete, mkt.GetID())
  1281  		}
  1282  		evts = append(evts, events.NewMarketDataEvent(ctx, mkt.GetMarketData()))
  1283  	}
  1284  	e.broker.SendBatch(evts)
  1285  
  1286  	// reject successor markets in the toSkip list
  1287  	mids := maps.Values(toSkip)
  1288  	sort.Strings(mids)
  1289  	for _, mid := range mids {
  1290  		e.RejectMarket(ctx, mid)
  1291  	}
  1292  
  1293  	rmCPStates := make([]string, 0, len(toDelete))
  1294  	for _, id := range toDelete {
  1295  		// a cancelled market cannot be succeeded, so remove it from the CP state immediately
  1296  		if m, ok := e.futureMarkets[id]; ok && m.Mkt().State == types.MarketStateCancelled {
  1297  			rmCPStates = append(rmCPStates, id)
  1298  		}
  1299  		e.removeMarket(id)
  1300  	}
  1301  
  1302  	// sort the marketCPStates by ID since the order we clear insurance pools
  1303  	// changes the division when we split it across remaining markets and
  1304  	// who the remainder ends up with if it doesn't divide equally.
  1305  	allIDs := []string{}
  1306  	for id := range e.marketCPStates {
  1307  		allIDs = append(allIDs, id)
  1308  	}
  1309  	sort.Strings(allIDs)
  1310  
  1311  	// find state that should expire
  1312  	for _, id := range allIDs {
  1313  		// market field will be nil if the market is still current (ie not closed/settled)
  1314  		cpm := e.marketCPStates[id]
  1315  		if !cpm.TTL.Before(t) {
  1316  			// CP data has not expired yet
  1317  			continue
  1318  		}
  1319  		if cpm.Market == nil {
  1320  			// expired, and yet somehow the market is gone, this is stale data, must be removed
  1321  			if _, ok := e.futureMarkets[id]; !ok {
  1322  				rmCPStates = append(rmCPStates, id)
  1323  			}
  1324  		} else {
  1325  			// market state was set, so this is a closed/settled market that was not succeeded in time
  1326  			rmCPStates = append(rmCPStates, id)
  1327  			assets, _ := cpm.Market.GetAssets()
  1328  			if clearTransfers, _ := e.collateral.ClearInsurancepool(ctx, id, assets[0], true); len(clearTransfers) > 0 {
  1329  				e.broker.Send(events.NewLedgerMovements(ctx, clearTransfers))
  1330  			}
  1331  		}
  1332  	}
  1333  	for _, id := range rmCPStates {
  1334  		delete(e.marketCPStates, id)
  1335  		if ss, ok := e.successors[id]; ok {
  1336  			// parent market expired, remove parent ID
  1337  			for _, s := range ss {
  1338  				delete(e.isSuccessor, s)
  1339  				if mkt, ok := e.futureMarkets[s]; ok {
  1340  					mkt.ResetParentIDAndInsurancePoolFraction()
  1341  				}
  1342  			}
  1343  		}
  1344  		delete(e.successors, id)
  1345  	}
  1346  
  1347  	timer.EngineTimeCounterAdd()
  1348  }
  1349  
  1350  func (e *Engine) getPendingSuccessorsToReject(parent, successor string, toSkip map[int]string) map[int]string {
  1351  	ss, ok := e.successors[parent]
  1352  	if !ok {
  1353  		return toSkip
  1354  	}
  1355  	// iterate over all pending successors for the given parent
  1356  	for _, sid := range ss {
  1357  		// ignore the actual successor
  1358  		if sid == successor {
  1359  			continue
  1360  		}
  1361  		if _, ok := e.futureMarkets[sid]; !ok {
  1362  			continue
  1363  		}
  1364  		for i, mkt := range e.futureMarketsCpy {
  1365  			if mkt.GetID() == sid {
  1366  				toSkip[i] = sid
  1367  			}
  1368  		}
  1369  	}
  1370  	return toSkip
  1371  }
  1372  
  1373  func (e *Engine) getParentStates() map[string]*types.CPMarketState {
  1374  	// all successor markets need to have a reference to the parent state
  1375  	states := make(map[string]*types.CPMarketState, len(e.isSuccessor))
  1376  	// for each parent market, get the successors
  1377  	for pid, successors := range e.successors {
  1378  		state, sok := e.marketCPStates[pid]
  1379  		if !sok {
  1380  			if pmkt, ok := e.futureMarkets[pid]; ok {
  1381  				state = pmkt.GetCPState()
  1382  			}
  1383  		}
  1384  		// if the state does not exist, then there is nothing to inherit. This is handled elsewhere
  1385  		// include nil states in the map
  1386  		for _, sid := range successors {
  1387  			states[sid] = state
  1388  		}
  1389  	}
  1390  	return states
  1391  }
  1392  
  1393  func (e *Engine) BlockEnd(ctx context.Context) {
  1394  	for _, mkt := range e.allMarketsCpy {
  1395  		mkt.BlockEnd(ctx)
  1396  	}
  1397  }
  1398  
  1399  func (e *Engine) BeginBlock(ctx context.Context, prevBlockDuration time.Duration) {
  1400  	for _, mkt := range e.allMarketsCpy {
  1401  		mkt.BeginBlock(ctx)
  1402  	}
  1403  	longBlockAuctionDuration := e.npv.lbadTable.GetLongBlockAuctionDurationForBlockDuration(prevBlockDuration)
  1404  	if longBlockAuctionDuration == nil {
  1405  		return
  1406  	}
  1407  	auctionDurationInSeconds := int64(longBlockAuctionDuration.Seconds())
  1408  	for _, mkt := range e.allMarketsCpy {
  1409  		mkt.EnterLongBlockAuction(ctx, auctionDurationInSeconds)
  1410  	}
  1411  }
  1412  
  1413  func (e *Engine) GetMarketState(mktID string) (types.MarketState, error) {
  1414  	if mkt, ok := e.allMarkets[mktID]; ok {
  1415  		return mkt.GetMarketState(), nil
  1416  	}
  1417  	return types.MarketStateUnspecified, types.ErrInvalidMarketID
  1418  }
  1419  
  1420  func (e *Engine) IsSucceeded(mktID string) bool {
  1421  	if mkt, ok := e.futureMarkets[mktID]; ok {
  1422  		return mkt.IsSucceeded()
  1423  	}
  1424  	// checking marketCPStates is pointless. The parent market could not be found to validate the proposal, so it will be rejected outright
  1425  	// if the market is no longer in e.markets, it will be set in marketCPStates, and therefore the successor proposal must be accepted.
  1426  	return false
  1427  }
  1428  
  1429  func (e *Engine) GetMarketData(mktID string) (types.MarketData, error) {
  1430  	if mkt, ok := e.allMarkets[mktID]; ok {
  1431  		return mkt.GetMarketData(), nil
  1432  	}
  1433  	return types.MarketData{}, types.ErrInvalidMarketID
  1434  }
  1435  
  1436  func (e *Engine) MarketExists(market string) bool {
  1437  	_, ok := e.allMarkets[market]
  1438  	return ok
  1439  }
  1440  
  1441  func (e *Engine) GetMarket(market string, settled bool) (types.Market, bool) {
  1442  	if mkt, ok := e.allMarkets[market]; ok {
  1443  		return mkt.IntoType(), true
  1444  	}
  1445  	// market wasn't found in the markets map, if a successor market was proposed after parent market
  1446  	// was settled/closed, then we should check the checkpoint states map for the parent market definition.
  1447  	if settled {
  1448  		if mcp, ok := e.marketCPStates[market]; ok && mcp.Market != nil {
  1449  			cpy := mcp.Market.DeepClone()
  1450  			return *cpy, true
  1451  		}
  1452  	}
  1453  	return types.Market{}, false
  1454  }
  1455  
  1456  // GetEquityLikeShareForMarketAndParty return the equity-like shares of the given
  1457  // party in the given market. If the market doesn't exist, it returns false.
  1458  func (e *Engine) GetEquityLikeShareForMarketAndParty(market, party string) (num.Decimal, bool) {
  1459  	if mkt, ok := e.allMarkets[market]; ok {
  1460  		return mkt.GetEquitySharesForParty(party), true
  1461  	}
  1462  	return num.DecimalZero(), false
  1463  }
  1464  
  1465  // GetMarketCounters returns the per-market counts used for gas estimation.
  1466  func (e *Engine) GetMarketCounters() map[string]*types.MarketCounters {
  1467  	counters := map[string]*types.MarketCounters{}
  1468  	for k, m := range e.allMarkets {
  1469  		counters[k] = m.GetMarketCounters()
  1470  	}
  1471  	return counters
  1472  }
  1473  
  1474  func (e *Engine) GetMarketStats() map[string]*types.MarketStats {
  1475  	stats := map[string]*types.MarketStats{}
  1476  	for id, cm := range e.allMarkets {
  1477  		if s := cm.GetPartiesStats(); s != nil {
  1478  			stats[id] = s
  1479  		}
  1480  	}
  1481  
  1482  	return stats
  1483  }
  1484  
  1485  func (e *Engine) OnSuccessorMarketTimeWindowUpdate(ctx context.Context, window time.Duration) error {
  1486  	// change in succession window length
  1487  	delta := window - e.successorWindow
  1488  	if delta != 0 {
  1489  		for _, cpm := range e.marketCPStates {
  1490  			cpm.TTL = cpm.TTL.Add(delta)
  1491  		}
  1492  	}
  1493  	e.successorWindow = window
  1494  	return nil
  1495  }
  1496  
  1497  func (e *Engine) OnChainIDUpdate(cID uint64) error {
  1498  	e.npv.chainID = cID
  1499  	return nil
  1500  }
  1501  
  1502  func (e *Engine) UpdateMarginMode(ctx context.Context, party, marketID string, marginMode types.MarginMode, marginFactor num.Decimal) error {
  1503  	if _, ok := e.futureMarkets[marketID]; !ok {
  1504  		return types.ErrInvalidMarketID
  1505  	}
  1506  	market := e.futureMarkets[marketID]
  1507  	if marginMode == types.MarginModeIsolatedMargin {
  1508  		riskFactors := market.GetRiskFactors()
  1509  		rf := num.MaxD(riskFactors.Long, riskFactors.Short).Add(market.Mkt().LinearSlippageFactor)
  1510  		if marginFactor.LessThanOrEqual(rf) {
  1511  			return fmt.Errorf("margin factor (%s) must be greater than max(riskFactorLong (%s), riskFactorShort (%s)) + linearSlippageFactor (%s)", marginFactor.String(), riskFactors.Long.String(), riskFactors.Short.String(), market.Mkt().LinearSlippageFactor.String())
  1512  		}
  1513  	}
  1514  
  1515  	return market.UpdateMarginMode(ctx, party, marginMode, marginFactor)
  1516  }
  1517  
  1518  func (e *Engine) OnMinimalMarginQuantumMultipleUpdate(_ context.Context, multiplier num.Decimal) error {
  1519  	e.minMaintenanceMarginQuantumMultiplier = multiplier
  1520  	for _, mkt := range e.futureMarketsCpy {
  1521  		mkt.OnMinimalMarginQuantumMultipleUpdate(multiplier)
  1522  	}
  1523  	return nil
  1524  }
  1525  
  1526  func (e *Engine) OnMinimalHoldingQuantumMultipleUpdate(_ context.Context, multiplier num.Decimal) error {
  1527  	e.minHoldingQuantumMultiplier = multiplier
  1528  	for _, mkt := range e.spotMarketsCpy {
  1529  		mkt.OnMinimalHoldingQuantumMultipleUpdate(multiplier)
  1530  	}
  1531  	return nil
  1532  }
  1533  
  1534  func (e *Engine) CheckCanSubmitOrderOrLiquidityCommitment(party, market string) error {
  1535  	if len(market) == 0 {
  1536  		return e.collateral.CheckOrderSpamAllMarkets(party)
  1537  	}
  1538  	e.lock.RLock()
  1539  	defer e.lock.RUnlock()
  1540  	mkt, ok := e.allMarkets[market]
  1541  	if !ok {
  1542  		return fmt.Errorf("market does not exist")
  1543  	}
  1544  	assets := mkt.GetAssets()
  1545  	return e.collateral.CheckOrderSpam(party, market, assets)
  1546  }
  1547  
  1548  func (e *Engine) CheckOrderSubmissionForSpam(orderSubmission *types.OrderSubmission, party string) error {
  1549  	e.lock.RLock()
  1550  	defer e.lock.RUnlock()
  1551  	if mkt := e.allMarkets[orderSubmission.MarketID]; mkt == nil {
  1552  		return types.ErrInvalidMarketID
  1553  	}
  1554  	if ftr := e.futureMarkets[orderSubmission.MarketID]; ftr != nil {
  1555  		return ftr.CheckOrderSubmissionForSpam(orderSubmission, party, e.minMaintenanceMarginQuantumMultiplier)
  1556  	}
  1557  	return e.spotMarkets[orderSubmission.MarketID].CheckOrderSubmissionForSpam(orderSubmission, party, e.minHoldingQuantumMultiplier)
  1558  }
  1559  
  1560  func (e *Engine) GetFillPriceForMarket(marketID string, volume uint64, side types.Side) (*num.Uint, error) {
  1561  	if mkt, ok := e.allMarkets[marketID]; ok {
  1562  		return mkt.GetFillPrice(volume, side)
  1563  	}
  1564  	return nil, types.ErrInvalidMarketID
  1565  }
  1566  
  1567  func (e *Engine) NewProtocolAutomatedPurchase(ctx context.Context, ID string, automatedPurchaseConfig *types.NewProtocolAutomatedPurchaseChanges) error {
  1568  	if _, ok := e.spotMarkets[automatedPurchaseConfig.MarketID]; !ok {
  1569  		return types.ErrInvalidMarketID
  1570  	}
  1571  	return e.spotMarkets[automatedPurchaseConfig.MarketID].NewProtocolAutomatedPurchase(ctx, ID, automatedPurchaseConfig, e.oracle)
  1572  }
  1573  
  1574  func (e *Engine) MarketHasActivePAP(marketID string) (bool, error) {
  1575  	if _, ok := e.spotMarkets[marketID]; !ok {
  1576  		return false, types.ErrInvalidMarketID
  1577  	}
  1578  	return e.spotMarkets[marketID].MarketHasActivePAP(), nil
  1579  }