code.vegaprotocol.io/vega@v0.79.0/core/products/oracles.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 products 17 18 import ( 19 "context" 20 "errors" 21 "fmt" 22 "strings" 23 24 "code.vegaprotocol.io/vega/core/datasource" 25 "code.vegaprotocol.io/vega/core/datasource/spec" 26 "code.vegaprotocol.io/vega/core/types" 27 "code.vegaprotocol.io/vega/libs/num" 28 datapb "code.vegaprotocol.io/vega/protos/vega/data/v1" 29 ) 30 31 type scheduledOracle struct { 32 settlementSubscriptionID spec.SubscriptionID 33 scheduleSubscriptionID spec.SubscriptionID 34 binding scheduledBinding 35 settleUnsub spec.Unsubscriber 36 scheduleUnsub spec.Unsubscriber 37 } 38 39 type terminatingOracle struct { 40 settlementSubscriptionID spec.SubscriptionID 41 terminationSubscriptionID spec.SubscriptionID 42 settleUnsub spec.Unsubscriber 43 terminationUnsub spec.Unsubscriber 44 binding terminatingBinding 45 data oracleData 46 } 47 48 type scheduledBinding struct { 49 settlementProperty string 50 settlementType datapb.PropertyKey_Type 51 settlementDecimals uint64 52 53 scheduleProperty string 54 scheduleType datapb.PropertyKey_Type 55 } 56 57 type terminatingBinding struct { 58 settlementProperty string 59 settlementType datapb.PropertyKey_Type 60 settlementDecimals uint64 61 62 terminationProperty string 63 terminationType datapb.PropertyKey_Type 64 } 65 66 type oracleData struct { 67 settlData *num.Numeric 68 tradingTerminated bool 69 } 70 71 func newFutureOracle(f *types.Future) (terminatingOracle, error) { 72 bind, err := newFutureBinding(f) 73 if err != nil { 74 return terminatingOracle{}, err 75 } 76 return terminatingOracle{ 77 binding: bind, 78 }, nil 79 } 80 81 func newFutureBinding(f *types.Future) (terminatingBinding, error) { 82 settlementProperty := strings.TrimSpace(f.DataSourceSpecBinding.SettlementDataProperty) 83 if len(settlementProperty) == 0 { 84 return terminatingBinding{}, errors.New("binding for settlement data cannot be blank") 85 } 86 tradingTerminationProperty := strings.TrimSpace(f.DataSourceSpecBinding.TradingTerminationProperty) 87 if len(tradingTerminationProperty) == 0 { 88 return terminatingBinding{}, errors.New("binding for trading termination market cannot be blank") 89 } 90 // assume bool for now, check for built-in timestamp 91 // this can be set to anything else by the caller 92 termType := datapb.PropertyKey_TYPE_BOOLEAN 93 if tradingTerminationProperty == spec.BuiltinTimestamp { 94 termType = datapb.PropertyKey_TYPE_TIMESTAMP 95 } 96 t, dec := getSettleTypeAndDec(f.DataSourceSpecForSettlementData) 97 98 return terminatingBinding{ 99 settlementProperty: settlementProperty, 100 settlementType: t, 101 settlementDecimals: dec, 102 terminationProperty: tradingTerminationProperty, 103 terminationType: termType, 104 }, nil 105 } 106 107 func (t *terminatingOracle) bindAll(ctx context.Context, oe OracleEngine, settle, term *spec.Spec, settleCB, termCB spec.OnMatchedData) error { 108 if err := t.bindSettlement(ctx, oe, settle, settleCB); err != nil { 109 return err 110 } 111 return t.bindTermination(ctx, oe, term, termCB) 112 } 113 114 func (t *terminatingOracle) bindSettlement(ctx context.Context, oe OracleEngine, osForSettle *spec.Spec, cb spec.OnMatchedData) error { 115 err := osForSettle.EnsureBoundableProperty(t.binding.settlementProperty, t.binding.settlementType) 116 if err != nil { 117 return fmt.Errorf("invalid oracle spec binding for settlement data: %w", err) 118 } 119 if t.settlementSubscriptionID, t.settleUnsub, err = oe.Subscribe(ctx, *osForSettle, cb); err != nil { 120 return fmt.Errorf("could not subscribe to oracle engine for settlement data: %w", err) 121 } 122 return nil 123 } 124 125 func (t *terminatingOracle) bindTermination(ctx context.Context, oe OracleEngine, osForTerm *spec.Spec, cb spec.OnMatchedData) error { 126 err := osForTerm.EnsureBoundableProperty(t.binding.terminationProperty, t.binding.terminationType) 127 if err != nil { 128 return fmt.Errorf("invalid oracle spec binding for trading termination: %w", err) 129 } 130 if t.terminationSubscriptionID, t.terminationUnsub, err = oe.Subscribe(ctx, *osForTerm, cb); err != nil { 131 return fmt.Errorf("could not subscribe to oracle engine for trading termination: %w", err) 132 } 133 return nil 134 } 135 136 func (t *terminatingOracle) unsubSettle(ctx context.Context) { 137 if t.settleUnsub != nil { 138 t.settleUnsub(ctx, t.settlementSubscriptionID) 139 t.settleUnsub = nil 140 } 141 } 142 143 func (t *terminatingOracle) unsubTerm(ctx context.Context) { 144 if t.terminationUnsub != nil { 145 t.terminationUnsub(ctx, t.terminationSubscriptionID) 146 t.terminationUnsub = nil 147 } 148 } 149 150 func newPerpOracle(p *types.Perps) (scheduledOracle, error) { 151 bind, err := newPerpBinding(p) 152 if err != nil { 153 return scheduledOracle{}, err 154 } 155 return scheduledOracle{ 156 binding: bind, 157 }, nil 158 } 159 160 func newPerpBinding(p *types.Perps) (scheduledBinding, error) { 161 settleDataProp := strings.TrimSpace(p.DataSourceSpecBinding.SettlementDataProperty) 162 settleScheduleProp := strings.TrimSpace(p.DataSourceSpecBinding.SettlementScheduleProperty) 163 if len(settleDataProp) == 0 { 164 return scheduledBinding{}, errors.New("binding for settlement data cannot be blank") 165 } 166 if len(settleScheduleProp) == 0 { 167 return scheduledBinding{}, errors.New("binding for settlement schedule cannot be blank") 168 } 169 setT, dec := getSettleTypeAndDec(p.DataSourceSpecForSettlementData) 170 171 return scheduledBinding{ 172 settlementProperty: settleDataProp, 173 settlementType: setT, 174 settlementDecimals: dec, 175 scheduleProperty: settleScheduleProp, 176 scheduleType: datapb.PropertyKey_TYPE_TIMESTAMP, // default to timestamp 177 }, nil 178 } 179 180 func (s *scheduledOracle) bindAll(ctx context.Context, oe OracleEngine, settle, schedule *spec.Spec, settleCB, scheduleCB spec.OnMatchedData) error { 181 if err := s.bindSettlement(ctx, oe, settle, settleCB); err != nil { 182 return err 183 } 184 return s.bindSchedule(ctx, oe, schedule, scheduleCB) 185 } 186 187 func (s *scheduledOracle) bindSettlement(ctx context.Context, oe OracleEngine, osForSettle *spec.Spec, cb spec.OnMatchedData) error { 188 err := osForSettle.EnsureBoundableProperty(s.binding.settlementProperty, s.binding.settlementType) 189 if err != nil { 190 return fmt.Errorf("invalid oracle spec binding for settlement data: %w", err) 191 } 192 if s.settlementSubscriptionID, s.settleUnsub, err = oe.Subscribe(ctx, *osForSettle, cb); err != nil { 193 return fmt.Errorf("could not subscribe to oracle engine for settlement data: %w", err) 194 } 195 return nil 196 } 197 198 func (s *scheduledOracle) bindSchedule(ctx context.Context, oe OracleEngine, osForSchedule *spec.Spec, cb spec.OnMatchedData) error { 199 err := osForSchedule.EnsureBoundableProperty(s.binding.scheduleProperty, s.binding.scheduleType) 200 if err != nil { 201 return fmt.Errorf("invalid oracle spec binding for schedule data: %w", err) 202 } 203 if s.scheduleSubscriptionID, s.scheduleUnsub, err = oe.Subscribe(ctx, *osForSchedule, cb); err != nil { 204 return fmt.Errorf("could not subscribe to oracle engine for schedule data: %w", err) 205 } 206 return nil 207 } 208 209 func (s *scheduledOracle) unsubAll(ctx context.Context) { 210 if s.settleUnsub != nil { 211 s.settleUnsub(ctx, s.settlementSubscriptionID) 212 s.settleUnsub = nil 213 } 214 if s.scheduleUnsub != nil { 215 s.scheduleUnsub(ctx, s.scheduleSubscriptionID) 216 s.scheduleUnsub = nil 217 } 218 } 219 220 // SettlementData returns oracle data settlement data scaled as Uint. 221 func (o *oracleData) SettlementData(op, ap uint32) (*num.Uint, error) { 222 if o.settlData.Decimal() == nil && o.settlData.Uint() == nil { 223 return nil, ErrDataSourceSettlementDataNotSet 224 } 225 226 if !o.settlData.SupportDecimalPlaces(int64(ap)) { 227 return nil, ErrSettlementDataDecimalsNotSupportedByAsset 228 } 229 230 // scale to given target decimals by multiplying by 10^(targetDP - oracleDP) 231 // if targetDP > oracleDP - this scales up the decimals of settlement data 232 // if targetDP < oracleDP - this scaled down the decimals of settlement data and can lead to loss of accuracy 233 // if there're equal - no scaling happens 234 return o.settlData.ScaleTo(int64(op), int64(ap)) 235 } 236 237 // IsTradingTerminated returns true when oracle has signalled termination of trading. 238 func (o *oracleData) IsTradingTerminated() bool { 239 return o.tradingTerminated 240 } 241 242 func getSettleTypeAndDec(s *datasource.Spec) (datapb.PropertyKey_Type, uint64) { 243 def := s.GetDefinition() 244 defType := datapb.PropertyKey_TYPE_INTEGER 245 dec := uint64(0) 246 for _, f := range def.GetFilters() { 247 if f.Key.Type == datapb.PropertyKey_TYPE_INTEGER && f.Key.NumberDecimalPlaces != nil { 248 dec = *f.Key.NumberDecimalPlaces 249 break 250 } else if f.Key.Type == datapb.PropertyKey_TYPE_DECIMAL { 251 defType = f.Key.Type 252 if f.Key.NumberDecimalPlaces != nil { 253 dec = *f.Key.NumberDecimalPlaces 254 } 255 break 256 } 257 } 258 return defType, dec 259 }