-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
bridge_type.go
201 lines (174 loc) · 5.98 KB
/
bridge_type.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
200
201
package bridges
import (
"crypto/subtle"
"database/sql/driver"
"encoding/json"
"fmt"
"math/big"
"regexp"
"strings"
"time"
"github.com/smartcontractkit/chainlink/core/assets"
"github.com/smartcontractkit/chainlink/core/store/models"
"github.com/smartcontractkit/chainlink/core/utils"
)
// BridgeTypeRequest is the incoming record used to create a BridgeType
type BridgeTypeRequest struct {
Name BridgeName `json:"name"`
URL models.WebURL `json:"url"`
Confirmations uint32 `json:"confirmations"`
MinimumContractPayment *assets.Link `json:"minimumContractPayment"`
}
// GetID returns the ID of this structure for jsonapi serialization.
func (bt BridgeTypeRequest) GetID() string {
return bt.Name.String()
}
// GetName returns the pluralized "type" of this structure for jsonapi serialization.
func (bt BridgeTypeRequest) GetName() string {
return "bridges"
}
// SetID is used to set the ID of this structure when deserializing from jsonapi documents.
func (bt *BridgeTypeRequest) SetID(value string) error {
name, err := ParseBridgeName(value)
bt.Name = name
return err
}
// BridgeTypeAuthentication is the record returned in response to a request to create a BridgeType
type BridgeTypeAuthentication struct {
Name BridgeName
URL models.WebURL
Confirmations uint32
IncomingToken string
OutgoingToken string
MinimumContractPayment *assets.Link
}
// BridgeType is used for external adapters and has fields for
// the name of the adapter and its URL.
type BridgeType struct {
Name BridgeName
URL models.WebURL
Confirmations uint32
IncomingTokenHash string
Salt string
OutgoingToken string
MinimumContractPayment *assets.Link
CreatedAt time.Time
UpdatedAt time.Time
}
// NewBridgeType returns a bridge type authentication (with plaintext
// password) and a bridge type (with hashed password, for persisting)
func NewBridgeType(btr *BridgeTypeRequest) (*BridgeTypeAuthentication,
*BridgeType, error) {
incomingToken := utils.NewSecret(24)
outgoingToken := utils.NewSecret(24)
salt := utils.NewSecret(24)
hash, err := incomingTokenHash(incomingToken, salt)
if err != nil {
return nil, nil, err
}
return &BridgeTypeAuthentication{
Name: btr.Name,
URL: btr.URL,
Confirmations: btr.Confirmations,
IncomingToken: incomingToken,
OutgoingToken: outgoingToken,
MinimumContractPayment: btr.MinimumContractPayment,
}, &BridgeType{
Name: btr.Name,
URL: btr.URL,
Confirmations: btr.Confirmations,
IncomingTokenHash: hash,
Salt: salt,
OutgoingToken: outgoingToken,
MinimumContractPayment: btr.MinimumContractPayment,
}, nil
}
// AuthenticateBridgeType returns true if the passed token matches its
// IncomingToken, or returns false with an error.
func AuthenticateBridgeType(bt *BridgeType, token string) (bool, error) {
hash, err := incomingTokenHash(token, bt.Salt)
if err != nil {
return false, err
}
return subtle.ConstantTimeCompare([]byte(hash), []byte(bt.IncomingTokenHash)) == 1, nil
}
func incomingTokenHash(token, salt string) (string, error) {
input := fmt.Sprintf("%s-%s", token, salt)
hash, err := utils.Sha256(input)
if err != nil {
return "", err
}
return hash, nil
}
// NOTE: latestAnswer and updatedAt is the only metadata used.
// Currently market closer adapter and outlier detection depend latestAnswer.
// https://github.com/smartcontractkit/external-adapters-js/tree/f474bd2e2de13ebe5c9dc3df36ebb7018817005e/composite/market-closure
// https://github.com/smartcontractkit/external-adapters-js/tree/5abb8e5ec2024f724fd39122897baa63c3cd0167/composite/outlier-detection
type BridgeMetaData struct {
LatestAnswer *big.Int `json:"latestAnswer"`
UpdatedAt *big.Int `json:"updatedAt"` // A unix timestamp
}
type BridgeMetaDataJSON struct {
Meta BridgeMetaData
}
func MarshalBridgeMetaData(latestAnswer *big.Int, updatedAt *big.Int) (map[string]interface{}, error) {
b, err := json.Marshal(&BridgeMetaData{LatestAnswer: latestAnswer, UpdatedAt: updatedAt})
if err != nil {
return nil, err
}
var mp map[string]interface{}
err = json.Unmarshal(b, &mp)
if err != nil {
return nil, err
}
return mp, nil
}
// BridgeName defines what Adapter a TaskSpec will use.
type BridgeName string
var bridgeNameRegex = regexp.MustCompile("^[a-zA-Z0-9-_]*$")
// ParseBridgeName returns a formatted Task type.
func ParseBridgeName(val string) (BridgeName, error) {
if !bridgeNameRegex.MatchString(val) {
return "", fmt.Errorf("task type validation: name %v contains invalid characters", val)
}
return BridgeName(strings.ToLower(val)), nil
}
// MustParseBridgeName instantiates a new BridgeName, and panics if a bad input is provided.
func MustParseBridgeName(val string) BridgeName {
tt, err := ParseBridgeName(val)
if err != nil {
panic(fmt.Sprintf("%v is not a valid BridgeName", val))
}
return tt
}
// UnmarshalJSON converts a bytes slice of JSON to a BridgeName.
func (t *BridgeName) UnmarshalJSON(input []byte) error {
var aux string
if err := json.Unmarshal(input, &aux); err != nil {
return err
}
tt, err := ParseBridgeName(aux)
*t = tt
return err
}
// MarshalJSON converts a BridgeName to a JSON byte slice.
func (t BridgeName) MarshalJSON() ([]byte, error) {
return json.Marshal(t.String())
}
// String returns this BridgeName as a string.
func (t BridgeName) String() string {
return string(t)
}
// Value returns this instance serialized for database storage.
func (t BridgeName) Value() (driver.Value, error) {
return string(t), nil
}
// Scan reads the database value and returns an instance.
func (t *BridgeName) Scan(value interface{}) error {
temp, ok := value.(string)
if !ok {
return fmt.Errorf("unable to convert %v of %T to BridgeName", value, value)
}
*t = BridgeName(temp)
return nil
}