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 }