-
Notifications
You must be signed in to change notification settings - Fork 1.7k
/
validate.go
199 lines (181 loc) · 6.15 KB
/
validate.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
197
198
199
package validate
import (
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"github.com/lib/pq"
"github.com/pelletier/go-toml"
pkgerrors "github.com/pkg/errors"
libocr2 "github.com/smartcontractkit/libocr/offchainreporting2plus"
"github.com/smartcontractkit/chainlink-relay/pkg/types"
"github.com/smartcontractkit/chainlink/v2/core/services/job"
dkgconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/dkg/config"
mercuryconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/mercury/config"
ocr2vrfconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2vrf/config"
"github.com/smartcontractkit/chainlink/v2/core/services/ocrcommon"
"github.com/smartcontractkit/chainlink/v2/core/services/relay"
)
// ValidatedOracleSpecToml validates an oracle spec that came from TOML
func ValidatedOracleSpecToml(config OCR2Config, insConf InsecureConfig, tomlString string) (job.Job, error) {
var jb = job.Job{}
var spec job.OCR2OracleSpec
tree, err := toml.Load(tomlString)
if err != nil {
return jb, pkgerrors.Wrap(err, "toml error on load")
}
// Note this validates all the fields which implement an UnmarshalText
// i.e. TransmitterAddress, PeerID...
err = tree.Unmarshal(&spec)
if err != nil {
return jb, pkgerrors.Wrap(err, "toml unmarshal error on spec")
}
err = tree.Unmarshal(&jb)
if err != nil {
return jb, pkgerrors.Wrap(err, "toml unmarshal error on job")
}
jb.OCR2OracleSpec = &spec
if jb.OCR2OracleSpec.P2PV2Bootstrappers == nil {
// Empty but non-null, field is non-nullable.
jb.OCR2OracleSpec.P2PV2Bootstrappers = pq.StringArray{}
}
if jb.Type != job.OffchainReporting2 {
return jb, pkgerrors.Errorf("the only supported type is currently 'offchainreporting2', got %s", jb.Type)
}
if _, ok := relay.SupportedRelays[spec.Relay]; !ok {
return jb, pkgerrors.Errorf("no such relay %v supported", spec.Relay)
}
if len(spec.P2PV2Bootstrappers) > 0 {
_, err = ocrcommon.ParseBootstrapPeers(spec.P2PV2Bootstrappers)
if err != nil {
return jb, err
}
}
if err = validateSpec(tree, jb); err != nil {
return jb, err
}
if err = validateTimingParameters(config, insConf, spec); err != nil {
return jb, err
}
return jb, nil
}
// Parameters that must be explicitly set by the operator.
var (
params = map[string]struct{}{
"type": {},
"schemaVersion": {},
"contractID": {},
"relay": {},
"relayConfig": {},
"pluginType": {},
"pluginConfig": {},
}
notExpectedParams = map[string]struct{}{
"isBootstrapPeer": {},
"juelsPerFeeCoinSource": {},
}
)
func validateTimingParameters(ocr2Conf OCR2Config, insConf InsecureConfig, spec job.OCR2OracleSpec) error {
lc, err := ToLocalConfig(ocr2Conf, insConf, spec)
if err != nil {
return err
}
return libocr2.SanityCheckLocalConfig(lc)
}
func validateSpec(tree *toml.Tree, spec job.Job) error {
expected, notExpected := ocrcommon.CloneSet(params), ocrcommon.CloneSet(notExpectedParams)
if err := ocrcommon.ValidateExplicitlySetKeys(tree, expected, notExpected, "ocr2"); err != nil {
return err
}
switch spec.OCR2OracleSpec.PluginType {
case types.Median:
if spec.Pipeline.Source == "" {
return errors.New("no pipeline specified")
}
case types.DKG:
return validateDKGSpec(spec.OCR2OracleSpec.PluginConfig)
case types.OCR2VRF:
return validateOCR2VRFSpec(spec.OCR2OracleSpec.PluginConfig)
case types.OCR2Keeper:
return validateOCR2KeeperSpec(spec.OCR2OracleSpec.PluginConfig)
case types.Functions:
// TODO validator for DR-OCR spec: https://app.shortcut.com/chainlinklabs/story/54054/ocr-plugin-for-directrequest-ocr
return nil
case types.Mercury:
return validateOCR2MercurySpec(spec.OCR2OracleSpec.PluginConfig, *spec.OCR2OracleSpec.FeedID)
case "":
return errors.New("no plugin specified")
default:
return pkgerrors.Errorf("invalid pluginType %s", spec.OCR2OracleSpec.PluginType)
}
return nil
}
func validateDKGSpec(jsonConfig job.JSONConfig) error {
if jsonConfig == nil {
return errors.New("pluginConfig is empty")
}
var pluginConfig dkgconfig.PluginConfig
err := json.Unmarshal(jsonConfig.Bytes(), &pluginConfig)
if err != nil {
return pkgerrors.Wrap(err, "error while unmarshaling plugin config")
}
err = validateHexString(pluginConfig.EncryptionPublicKey, 32)
if err != nil {
return pkgerrors.Wrap(err, "validation error for encryptedPublicKey")
}
err = validateHexString(pluginConfig.SigningPublicKey, 32)
if err != nil {
return pkgerrors.Wrap(err, "validation error for signingPublicKey")
}
err = validateHexString(pluginConfig.KeyID, 32)
if err != nil {
return pkgerrors.Wrap(err, "validation error for keyID")
}
return nil
}
func validateHexString(val string, expectedLengthInBytes uint) error {
decoded, err := hex.DecodeString(val)
if err != nil {
return pkgerrors.Wrapf(err, "expected hex string but received %s", val)
}
if len(decoded) != int(expectedLengthInBytes) {
return fmt.Errorf("value: %s has unexpected length. Expected %d bytes", val, expectedLengthInBytes)
}
return nil
}
func validateOCR2VRFSpec(jsonConfig job.JSONConfig) error {
if jsonConfig == nil {
return errors.New("pluginConfig is empty")
}
var cfg ocr2vrfconfig.PluginConfig
err := json.Unmarshal(jsonConfig.Bytes(), &cfg)
if err != nil {
return pkgerrors.Wrap(err, "json unmarshal plugin config")
}
err = validateDKGSpec(job.JSONConfig{
"encryptionPublicKey": cfg.DKGEncryptionPublicKey,
"signingPublicKey": cfg.DKGSigningPublicKey,
"keyID": cfg.DKGKeyID,
})
if err != nil {
return err
}
if cfg.LinkEthFeedAddress == "" {
return errors.New("linkEthFeedAddress must be provided")
}
if cfg.DKGContractAddress == "" {
return errors.New("dkgContractAddress must be provided")
}
return nil
}
func validateOCR2KeeperSpec(jsonConfig job.JSONConfig) error {
return nil
}
func validateOCR2MercurySpec(jsonConfig job.JSONConfig, feedId [32]byte) error {
var pluginConfig mercuryconfig.PluginConfig
err := json.Unmarshal(jsonConfig.Bytes(), &pluginConfig)
if err != nil {
return pkgerrors.Wrap(err, "error while unmarshaling plugin config")
}
return pkgerrors.Wrap(mercuryconfig.ValidatePluginConfig(pluginConfig, feedId), "Mercury PluginConfig is invalid")
}