-
Notifications
You must be signed in to change notification settings - Fork 1.7k
/
encoder.go
231 lines (189 loc) · 6.22 KB
/
encoder.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
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
package evm
import (
"fmt"
"math/big"
"reflect"
"github.com/ethereum/go-ethereum/accounts/abi"
ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v2"
"github.com/smartcontractkit/chainlink-automation/pkg/v2/encoding"
)
type EVMAutomationEncoder20 struct {
encoding.BasicEncoder
}
var (
Uint256, _ = abi.NewType("uint256", "", nil)
Uint256Arr, _ = abi.NewType("uint256[]", "", nil)
PerformDataMarshalingArgs = []abi.ArgumentMarshaling{
{Name: "checkBlockNumber", Type: "uint32"},
{Name: "checkBlockhash", Type: "bytes32"},
{Name: "performData", Type: "bytes"},
}
PerformDataArr, _ = abi.NewType("tuple(uint32,bytes32,bytes)[]", "", PerformDataMarshalingArgs)
ErrUnexpectedResult = fmt.Errorf("unexpected result struct")
packFn = reportArgs.Pack
unpackIntoMapFn = reportArgs.UnpackIntoMap
mKeys = []string{"fastGasWei", "linkNative", "upkeepIds", "wrappedPerformDatas"}
reportArgs = abi.Arguments{
{Name: mKeys[0], Type: Uint256},
{Name: mKeys[1], Type: Uint256},
{Name: mKeys[2], Type: Uint256Arr},
{Name: mKeys[3], Type: PerformDataArr},
}
)
type EVMAutomationUpkeepResult20 struct {
// Block is the block number used to build an UpkeepKey for this result
Block uint32
// ID is the unique identifier for the upkeep
ID *big.Int
Eligible bool
FailureReason uint8
GasUsed *big.Int
PerformData []byte
FastGasWei *big.Int
LinkNative *big.Int
// CheckBlockNumber is the block number that the contract indicates the
// upkeep was checked on
CheckBlockNumber uint32
CheckBlockHash [32]byte
ExecuteGas uint32
}
func (enc EVMAutomationEncoder20) EncodeReport(toReport []ocr2keepers.UpkeepResult) ([]byte, error) {
if len(toReport) == 0 {
return nil, nil
}
var (
fastGas *big.Int
link *big.Int
)
ids := make([]*big.Int, len(toReport))
data := make([]wrappedPerform, len(toReport))
for i, result := range toReport {
res, ok := result.(EVMAutomationUpkeepResult20)
if !ok {
return nil, fmt.Errorf("unexpected upkeep result struct")
}
// only take these values from the first result
// TODO: find a new way to get these values
if i == 0 {
fastGas = res.FastGasWei
link = res.LinkNative
}
ids[i] = res.ID
data[i] = wrappedPerform{
CheckBlockNumber: res.CheckBlockNumber,
CheckBlockhash: res.CheckBlockHash,
PerformData: res.PerformData,
}
}
bts, err := packFn(fastGas, link, ids, data)
if err != nil {
return []byte{}, fmt.Errorf("%w: failed to pack report data", err)
}
return bts, nil
}
func (enc EVMAutomationEncoder20) DecodeReport(report []byte) ([]ocr2keepers.UpkeepResult, error) {
m := make(map[string]interface{})
if err := unpackIntoMapFn(m, report); err != nil {
return nil, err
}
for _, key := range mKeys {
if _, ok := m[key]; !ok {
return nil, fmt.Errorf("decoding error: %s missing from struct", key)
}
}
res := []ocr2keepers.UpkeepResult{}
var (
ok bool
upkeepIds []*big.Int
wei *big.Int
link *big.Int
)
if upkeepIds, ok = m[mKeys[2]].([]*big.Int); !ok {
return res, fmt.Errorf("upkeep ids of incorrect type in report")
}
// TODO: a type assertion on `wrappedPerform` did not work, even with the
// exact same struct definition as what follows. reflect was used to get the
// struct definition. not sure yet how to clean this up.
// ex:
// t := reflect.TypeOf(rawPerforms)
// fmt.Printf("%v\n", t)
performs, ok := m[mKeys[3]].([]struct {
CheckBlockNumber uint32 `json:"checkBlockNumber"`
CheckBlockhash [32]byte `json:"checkBlockhash"`
PerformData []byte `json:"performData"`
})
if !ok {
return res, fmt.Errorf("performs of incorrect structure in report")
}
if len(upkeepIds) != len(performs) {
return res, fmt.Errorf("upkeep ids and performs should have matching length")
}
if wei, ok = m[mKeys[0]].(*big.Int); !ok {
return res, fmt.Errorf("fast gas as wrong type")
}
if link, ok = m[mKeys[1]].(*big.Int); !ok {
return res, fmt.Errorf("link native as wrong type")
}
res = make([]ocr2keepers.UpkeepResult, len(upkeepIds))
for i := 0; i < len(upkeepIds); i++ {
r := EVMAutomationUpkeepResult20{
Block: performs[i].CheckBlockNumber,
ID: upkeepIds[i],
Eligible: true,
PerformData: performs[i].PerformData,
FastGasWei: wei,
LinkNative: link,
CheckBlockNumber: performs[i].CheckBlockNumber,
CheckBlockHash: performs[i].CheckBlockhash,
}
res[i] = ocr2keepers.UpkeepResult(r)
}
return res, nil
}
func (enc EVMAutomationEncoder20) Eligible(result ocr2keepers.UpkeepResult) (bool, error) {
res, ok := result.(EVMAutomationUpkeepResult20)
if !ok {
tp := reflect.TypeOf(result)
return false, fmt.Errorf("%s: name: %s, kind: %s", ErrUnexpectedResult, tp.Name(), tp.Kind())
}
return res.Eligible, nil
}
func (enc EVMAutomationEncoder20) Detail(result ocr2keepers.UpkeepResult) (ocr2keepers.UpkeepKey, uint32, error) {
res, ok := result.(EVMAutomationUpkeepResult20)
if !ok {
return nil, 0, ErrUnexpectedResult
}
str := fmt.Sprintf("%d%s%s", res.Block, separator, res.ID)
return ocr2keepers.UpkeepKey([]byte(str)), res.ExecuteGas, nil
}
func (enc EVMAutomationEncoder20) KeysFromReport(b []byte) ([]ocr2keepers.UpkeepKey, error) {
results, err := enc.DecodeReport(b)
if err != nil {
return nil, err
}
keys := make([]ocr2keepers.UpkeepKey, 0, len(results))
for _, result := range results {
res, ok := result.(EVMAutomationUpkeepResult20)
if !ok {
return nil, fmt.Errorf("unexpected result struct")
}
str := fmt.Sprintf("%d%s%s", res.Block, separator, res.ID)
keys = append(keys, ocr2keepers.UpkeepKey([]byte(str)))
}
return keys, nil
}
type wrappedPerform struct {
CheckBlockNumber uint32 `abi:"checkBlockNumber"`
CheckBlockhash [32]byte `abi:"checkBlockhash"`
PerformData []byte `abi:"performData"`
}
type BlockKeyHelper[T uint32 | int64] struct {
}
func (kh BlockKeyHelper[T]) MakeBlockKey(b T) ocr2keepers.BlockKey {
return ocr2keepers.BlockKey(fmt.Sprintf("%d", b))
}
type UpkeepKeyHelper[T uint32 | int64] struct {
}
func (kh UpkeepKeyHelper[T]) MakeUpkeepKey(b T, id *big.Int) ocr2keepers.UpkeepKey {
return ocr2keepers.UpkeepKey(fmt.Sprintf("%d%s%s", b, separator, id))
}