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 }