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