code.vegaprotocol.io/vega@v0.79.0/core/limits/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 limits
    17  
    18  import (
    19  	"context"
    20  	"fmt"
    21  	"time"
    22  
    23  	"code.vegaprotocol.io/vega/core/events"
    24  	"code.vegaprotocol.io/vega/logging"
    25  	"code.vegaprotocol.io/vega/protos/vega"
    26  )
    27  
    28  type Engine struct {
    29  	log    *logging.Logger
    30  	cfg    Config
    31  	broker Broker
    32  
    33  	timeService TimeService
    34  
    35  	// are these action possible?
    36  	canProposeMarket, canProposeAsset bool
    37  
    38  	// Settings from the genesis state
    39  	proposeMarketEnabled, proposeAssetEnabled, proposeSpotMarketEnabled, proposePerpsMarketEnabled, useAMMEnabled bool
    40  	proposeMarketEnabledFrom, proposeAssetEnabledFrom                                                             time.Time
    41  
    42  	genesisLoaded bool
    43  
    44  	// snapshot state
    45  	lss *limitsSnapshotState
    46  }
    47  
    48  type Broker interface {
    49  	Send(event events.Event)
    50  }
    51  
    52  // TimeService provide the time of the vega node using the tm time.
    53  //
    54  //go:generate go run github.com/golang/mock/mockgen -destination mocks/time_service_mock.go -package mocks code.vegaprotocol.io/vega/core/limits TimeService
    55  type TimeService interface {
    56  	GetTimeNow() time.Time
    57  }
    58  
    59  func New(log *logging.Logger, cfg Config, tm TimeService, broker Broker) *Engine {
    60  	log = log.Named(namedLogger)
    61  	log.SetLevel(cfg.Level.Get())
    62  
    63  	return &Engine{
    64  		log:         log,
    65  		cfg:         cfg,
    66  		lss:         &limitsSnapshotState{},
    67  		broker:      broker,
    68  		timeService: tm,
    69  	}
    70  }
    71  
    72  // UponGenesis load the limits from the genesis state.
    73  func (e *Engine) UponGenesis(ctx context.Context, rawState []byte) (err error) {
    74  	e.log.Debug("Entering limits.Engine.UponGenesis")
    75  	defer func() {
    76  		if err != nil {
    77  			e.log.Debug("Failure in limits.Engine.UponGenesis", logging.Error(err))
    78  		} else {
    79  			e.log.Debug("Leaving limits.Engine.UponGenesis without error")
    80  		}
    81  		e.genesisLoaded = true
    82  	}()
    83  
    84  	state, err := LoadGenesisState(rawState)
    85  	if err != nil && err != ErrNoLimitsGenesisState {
    86  		e.log.Error("unable to load genesis state",
    87  			logging.Error(err))
    88  		return err
    89  	}
    90  
    91  	defer func() {
    92  		e.sendEvent(ctx)
    93  	}()
    94  
    95  	if err == ErrNoLimitsGenesisState {
    96  		defaultState := DefaultGenesisState()
    97  		state = &defaultState
    98  	}
    99  
   100  	// set enabled by default if not genesis state
   101  	if state == nil {
   102  		e.proposeAssetEnabled = true
   103  		e.proposeMarketEnabled = true
   104  	} else {
   105  		e.proposeAssetEnabled = state.ProposeAssetEnabled
   106  		e.proposeMarketEnabled = state.ProposeMarketEnabled
   107  	}
   108  
   109  	// at this point we only know about the genesis state
   110  	// of the limits, so we should set the can* fields to
   111  	// this state
   112  	e.canProposeAsset = e.proposeAssetEnabled
   113  	e.canProposeMarket = e.proposeMarketEnabled
   114  
   115  	e.log.Info("loaded limits genesis state",
   116  		logging.String("state", fmt.Sprintf("%#v", *state)))
   117  
   118  	return nil
   119  }
   120  
   121  func (e *Engine) OnLimitsProposeMarketEnabledFromUpdate(ctx context.Context, date string) error {
   122  	// already validated by the netparams
   123  	// no need to check it again, this is a valid date
   124  	if len(date) <= 0 {
   125  		e.proposeMarketEnabledFrom = time.Time{}
   126  	} else {
   127  		t, _ := time.Parse(time.RFC3339, date)
   128  		e.proposeMarketEnabledFrom = t
   129  	}
   130  	e.onUpdate(e.timeService.GetTimeNow())
   131  	e.sendEvent(ctx)
   132  
   133  	return nil
   134  }
   135  
   136  func (e *Engine) OnLimitsProposeSpotMarketEnabledFromUpdate(ctx context.Context, enabled int64) error {
   137  	e.proposeSpotMarketEnabled = enabled == 1
   138  	e.sendEvent(ctx)
   139  	return nil
   140  }
   141  
   142  func (e *Engine) OnLimitsProposePerpsMarketEnabledFromUpdate(ctx context.Context, enabled int64) error {
   143  	e.proposePerpsMarketEnabled = enabled == 1
   144  	e.sendEvent(ctx)
   145  	return nil
   146  }
   147  
   148  func (e *Engine) OnLimitsProposeAMMEnabledUpdate(ctx context.Context, enabled int64) error {
   149  	e.useAMMEnabled = enabled == 1
   150  	e.sendEvent(ctx)
   151  	return nil
   152  }
   153  
   154  func (e *Engine) OnLimitsProposeAssetEnabledFromUpdate(ctx context.Context, date string) error {
   155  	// already validated by the netparams
   156  	// no need to check it again, this is a valid date
   157  	if len(date) <= 0 {
   158  		e.proposeAssetEnabledFrom = time.Time{}
   159  	} else {
   160  		t, _ := time.Parse(time.RFC3339, date)
   161  		e.proposeAssetEnabledFrom = t
   162  	}
   163  
   164  	e.onUpdate(e.timeService.GetTimeNow())
   165  	e.sendEvent(ctx)
   166  
   167  	return nil
   168  }
   169  
   170  func (e *Engine) OnTick(ctx context.Context, t time.Time) {
   171  	canProposeAsset, canProposeMarket := e.canProposeAsset, e.canProposeMarket
   172  	defer func() {
   173  		if canProposeAsset != e.canProposeAsset || canProposeMarket != e.canProposeMarket {
   174  			e.sendEvent(ctx)
   175  		}
   176  	}()
   177  	e.onUpdate(t)
   178  }
   179  
   180  func (e *Engine) onUpdate(t time.Time) {
   181  	//  if propose market enabled in genesis
   182  	if e.proposeMarketEnabled {
   183  		// we can propose a market and a new date have been set in the future
   184  		if e.canProposeMarket && t.Before(e.proposeMarketEnabledFrom) {
   185  			e.log.Info("proposing market is now disabled")
   186  			e.canProposeMarket = false
   187  		}
   188  
   189  		// we can't propose a market for now, is the date in the past?
   190  		if !e.canProposeMarket && t.After(e.proposeMarketEnabledFrom) {
   191  			e.log.Info("all required conditions are met, proposing markets is now allowed")
   192  			e.canProposeMarket = true
   193  		}
   194  	}
   195  
   196  	//  if propose market enabled in genesis
   197  	if e.proposeAssetEnabled {
   198  		// we can propose a market and a new date have been set in the future
   199  		if e.canProposeAsset && t.Before(e.proposeAssetEnabledFrom) {
   200  			e.log.Info("proposing asset have been disabled")
   201  			e.canProposeAsset = false
   202  		}
   203  
   204  		if !e.canProposeAsset && t.After(e.proposeAssetEnabledFrom) {
   205  			e.log.Info("all required conditions are met, proposing assets is now allowed")
   206  			e.canProposeAsset = true
   207  		}
   208  	}
   209  }
   210  
   211  func (e *Engine) CanProposeMarket() bool {
   212  	return e.canProposeMarket
   213  }
   214  
   215  func (e *Engine) CanProposeAsset() bool {
   216  	return e.canProposeAsset
   217  }
   218  
   219  func (e *Engine) CanTrade() bool {
   220  	return e.canProposeAsset && e.canProposeMarket
   221  }
   222  
   223  func (e *Engine) CanProposeSpotMarket() bool {
   224  	return e.proposeSpotMarketEnabled
   225  }
   226  
   227  func (e *Engine) CanProposePerpsMarket() bool {
   228  	return e.proposePerpsMarketEnabled
   229  }
   230  
   231  func (e *Engine) CanUseAMMPool() bool {
   232  	return e.useAMMEnabled
   233  }
   234  
   235  func (e *Engine) sendEvent(ctx context.Context) {
   236  	limits := vega.NetworkLimits{
   237  		CanProposeMarket:          e.canProposeMarket,
   238  		CanProposeAsset:           e.canProposeAsset,
   239  		ProposeMarketEnabled:      e.proposeMarketEnabled,
   240  		ProposeAssetEnabled:       e.proposeAssetEnabled,
   241  		GenesisLoaded:             e.genesisLoaded,
   242  		CanProposeSpotMarket:      e.proposeSpotMarketEnabled,
   243  		CanProposePerpetualMarket: e.proposePerpsMarketEnabled,
   244  		CanUseAmm:                 e.useAMMEnabled,
   245  	}
   246  
   247  	if !e.proposeMarketEnabledFrom.IsZero() {
   248  		limits.ProposeMarketEnabledFrom = e.proposeAssetEnabledFrom.UnixNano()
   249  	}
   250  
   251  	if !e.proposeAssetEnabledFrom.IsZero() {
   252  		limits.ProposeAssetEnabledFrom = e.proposeAssetEnabledFrom.UnixNano()
   253  	}
   254  
   255  	event := events.NewNetworkLimitsEvent(ctx, &limits)
   256  	e.broker.Send(event)
   257  }