code.vegaprotocol.io/vega@v0.79.0/core/datasource/spec/spec.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 spec 17 18 import ( 19 "strconv" 20 "strings" 21 "time" 22 23 "code.vegaprotocol.io/vega/core/datasource" 24 "code.vegaprotocol.io/vega/core/datasource/common" 25 datapb "code.vegaprotocol.io/vega/protos/vega/data/v1" 26 ) 27 28 type SpecID string 29 30 type Spec struct { 31 // id is a unique identifier for the Spec 32 id SpecID 33 34 // signers list all the authorized public keys from where a Data can 35 // come from. 36 signers map[string]struct{} 37 38 // any time triggers on the spec 39 triggers common.InternalTimeTriggers 40 41 // filters holds all the expected property keys with the conditions they 42 // should match. 43 filters common.Filters 44 // OriginalSpec is the protobuf description of Spec 45 OriginalSpec *datasource.Spec 46 } 47 48 // New builds a new Spec from a common.Spec (currently uses one level below - common.ExternalDataSourceSpec) in a form that 49 // suits the processing of the filters. 50 // Spec allows the existence of one and only one. 51 // Currently VEGA network utilises internal triggers in the oracle function path, even though 52 // the oracles are treated as external data sources. 53 // For this reason this function checks if the provided external type of data source definition 54 // contains a key name that indicates a builtin type of logic 55 // and if the given data source definition is an internal type of data source, for more context refer to 56 // https://github.com/vegaprotocol/specs/blob/master/protocol/0048-DSRI-data_source_internal.md#13-vega-time-changed 57 func New(originalSpec datasource.Spec) (*Spec, error) { 58 filtersFromSpec := []*common.SpecFilter{} 59 signersFromSpec := []*common.Signer{} 60 var triggersFromSpec common.InternalTimeTriggers 61 62 isExtType := false 63 var err error 64 // if originalSpec != nil { 65 if originalSpec.Data != nil { 66 filtersFromSpec = originalSpec.Data.GetFilters() 67 isExtType, err = originalSpec.Data.IsExternal() 68 if err != nil { 69 return nil, err 70 } 71 } 72 //} 73 74 builtInKey := false 75 for _, f := range filtersFromSpec { 76 if isExtType { 77 if strings.HasPrefix(f.Key.Name, "vegaprotocol.builtin") && f.Key.Type == datapb.PropertyKey_TYPE_TIMESTAMP { 78 builtInKey = true 79 } 80 } 81 } 82 83 builtInTrigger := false 84 for _, f := range filtersFromSpec { 85 if strings.HasPrefix(f.Key.Name, "vegaprotocol.builtin.timetrigger") && f.Key.Type == datapb.PropertyKey_TYPE_TIMESTAMP { 86 builtInTrigger = true 87 } 88 } 89 90 typedFilters, err := common.NewFilters(filtersFromSpec, isExtType) 91 if err != nil { 92 return nil, err 93 } 94 // We check if the filters list is empty in the proposal submission step. 95 // We do not need to double that logic here. 96 97 signers := map[string]struct{}{} 98 if !builtInTrigger && !builtInKey && isExtType { 99 // if originalSpec != nil { 100 if originalSpec.Data != nil { 101 src := *originalSpec.Data 102 103 signersFromSpec = src.GetSigners() 104 } 105 //} 106 107 // We check if the signers list is empty h in the proposal submission step. 108 // We do not need to duble that logic here. 109 110 for _, pk := range signersFromSpec { 111 signers[pk.String()] = struct{}{} 112 } 113 } 114 115 if builtInTrigger { 116 triggersFromSpec = originalSpec.Data.GetTimeTriggers() 117 } 118 119 os := &Spec{ 120 id: SpecID(originalSpec.ID), 121 signers: signers, 122 filters: typedFilters, 123 triggers: triggersFromSpec, 124 OriginalSpec: &originalSpec, 125 } 126 127 return os, nil 128 } 129 130 func (s Spec) EnsureBoundableProperty(property string, propType datapb.PropertyKey_Type) error { 131 return s.filters.EnsureBoundableProperty(property, propType) 132 } 133 134 func isInternalData(data common.Data) bool { 135 for k := range data.Data { 136 if !strings.HasPrefix(k, BuiltinPrefix) { 137 return false 138 } 139 } 140 141 return true 142 } 143 144 func isInternalTimeTrigger(data common.Data) (bool, time.Time) { 145 for k, v := range data.Data { 146 if k == BuiltinTimeTrigger { 147 // convert string to time 148 if t, err := strconv.ParseInt(v, 10, 0); err == nil { 149 return true, time.Unix(t, 0) 150 } 151 } 152 } 153 return false, time.Time{} 154 } 155 156 // MatchSigners tries to match the public keys from the provided Data object with the ones 157 // present in the Spec. 158 func (s *Spec) MatchSigners(data common.Data) bool { 159 return containsRequiredSigners(data.Signers, s.signers) 160 } 161 162 // MatchData indicates if a given Data matches the spec or not. 163 func (s *Spec) MatchData(data common.Data) (bool, error) { 164 // if the data contains the internal source timestamp key, and only that key, 165 // then we do not need to verify the public keys as there will not be one 166 167 if !isInternalData(data) && !containsRequiredSigners(data.Signers, s.signers) { 168 return false, nil 169 } 170 171 // Don't broadcast ethcall data based unless it's 'EthKey' matches 172 // (which is currently the SpecID - see comment on the datasource.common.Data struct) 173 if data.EthKey != "" && data.EthKey != string(s.id) { 174 return false, nil 175 } 176 177 // if it is internal time data and we have a time-trigger check that we're past it 178 if ok, tt := isInternalTimeTrigger(data); ok && s.triggers[0] != nil { 179 if !s.triggers.IsTriggered(tt) { 180 return false, nil 181 } 182 } 183 184 return s.filters.Match(data.Data) 185 } 186 187 // containsRequiredSigners verifies if all the public keys in the Data 188 // are within the list of currently authorized by the Spec. 189 func containsRequiredSigners(dataSigners []*common.Signer, authPks map[string]struct{}) bool { 190 for _, signer := range dataSigners { 191 if _, ok := authPks[signer.String()]; !ok { 192 return false 193 } 194 } 195 return true 196 }