-
Notifications
You must be signed in to change notification settings - Fork 142
/
requestimpl.go
273 lines (224 loc) · 6.69 KB
/
requestimpl.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
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
package isc
import (
"errors"
"fmt"
"io"
"github.com/ethereum/go-ethereum/common"
iotago "github.com/iotaledger/iota.go/v3"
"github.com/iotaledger/wasp/packages/hashing"
"github.com/iotaledger/wasp/packages/kv/dict"
"github.com/iotaledger/wasp/packages/util/rwutil"
)
type RequestKind rwutil.Kind
const (
requestKindOnLedger RequestKind = iota
requestKindOffLedgerISC
requestKindOffLedgerEVMTx
requestKindOffLedgerEVMCall
)
func IsOffledgerKind(b byte) bool {
switch RequestKind(b) {
case requestKindOffLedgerISC, requestKindOffLedgerEVMTx:
return true
}
return false
}
func RequestFromBytes(data []byte) (Request, error) {
rr := rwutil.NewBytesReader(data)
return RequestFromReader(rr), rr.Err
}
func RequestFromReader(rr *rwutil.Reader) (ret Request) {
kind := rr.ReadKind()
switch RequestKind(kind) {
case requestKindOnLedger:
ret = new(onLedgerRequestData)
case requestKindOffLedgerISC:
ret = new(OffLedgerRequestData)
case requestKindOffLedgerEVMTx:
ret = new(evmOffLedgerTxRequest)
case requestKindOffLedgerEVMCall:
ret = new(evmOffLedgerCallRequest)
default:
if rr.Err == nil {
rr.Err = errors.New("invalid Request kind")
return nil
}
}
rr.PushBack().WriteKind(kind)
rr.Read(ret)
return ret
}
// region RequestID //////////////////////////////////////////////////////////////////
type RequestID iotago.OutputID
const RequestIDDigestLen = 6
type RequestRef struct {
ID RequestID
Hash hashing.HashValue
}
const RequestRefKeyLen = iotago.OutputIDLength + 32
type RequestRefKey [RequestRefKeyLen]byte
func (rrk RequestRefKey) String() string {
return iotago.EncodeHex(rrk[:])
}
func RequestRefFromBytes(data []byte) (*RequestRef, error) {
return rwutil.ReadFromBytes(data, new(RequestRef))
}
func RequestRefFromRequest(req Request) *RequestRef {
return &RequestRef{ID: req.ID(), Hash: RequestHash(req)}
}
func RequestRefsFromRequests(reqs []Request) []*RequestRef {
rr := make([]*RequestRef, len(reqs))
for i := range rr {
rr[i] = RequestRefFromRequest(reqs[i])
}
return rr
}
func (ref *RequestRef) AsKey() RequestRefKey {
var key RequestRefKey
copy(key[:], ref.Bytes())
return key
}
func (ref *RequestRef) IsFor(req Request) bool {
if ref.ID != req.ID() {
return false
}
return ref.Hash == RequestHash(req)
}
func (ref *RequestRef) Bytes() []byte {
return rwutil.WriteToBytes(ref)
}
func (ref *RequestRef) String() string {
return fmt.Sprintf("{requestRef, id=%v, hash=%v}", ref.ID.String(), ref.Hash.Hex())
}
func (ref *RequestRef) Read(r io.Reader) error {
rr := rwutil.NewReader(r)
rr.ReadN(ref.Hash[:])
rr.ReadN(ref.ID[:])
return rr.Err
}
func (ref *RequestRef) Write(w io.Writer) error {
ww := rwutil.NewWriter(w)
ww.WriteN(ref.Hash[:])
ww.WriteN(ref.ID[:])
return ww.Err
}
// RequestLookupDigest is shortened version of the request id. It is guaranteed to be unique
// within one block, however it may collide globally. Used for quick checking for most requests
// if it was never seen
type RequestLookupDigest [RequestIDDigestLen + 2]byte
func NewRequestID(txid iotago.TransactionID, index uint16) RequestID {
return RequestID(iotago.OutputIDFromTransactionIDAndIndex(txid, index))
}
func RequestIDFromBytes(data []byte) (ret RequestID, err error) {
_, err = rwutil.ReadFromBytes(data, &ret)
return ret, err
}
func RequestIDFromEVMTxHash(txHash common.Hash) RequestID {
return NewRequestID(iotago.TransactionID(txHash), 0)
}
func RequestIDFromString(s string) (ret RequestID, err error) {
data, err := iotago.DecodeHex(s)
if err != nil {
return RequestID{}, err
}
if len(data) != iotago.OutputIDLength {
return ret, errors.New("error parsing requestID: wrong length")
}
requestID := RequestID{}
copy(requestID[:], data)
return requestID, nil
}
func (rid RequestID) OutputID() iotago.OutputID {
return iotago.OutputID(rid)
}
func (rid RequestID) LookupDigest() RequestLookupDigest {
ret := RequestLookupDigest{}
copy(ret[:RequestIDDigestLen], rid[:RequestIDDigestLen])
// last 2 bytes are the outputindex
copy(ret[RequestIDDigestLen:RequestIDDigestLen+2], rid[len(rid)-2:])
return ret
}
func (rid RequestID) Bytes() []byte {
return rid[:]
}
func (rid RequestID) Equals(other RequestID) bool {
return rid == other
}
func (rid RequestID) String() string {
return iotago.EncodeHex(rid[:])
}
func (rid RequestID) Short() string {
ridString := rid.String()
return fmt.Sprintf("%s..%s", ridString[2:6], ridString[len(ridString)-4:])
}
func (rid *RequestID) Read(r io.Reader) error {
return rwutil.ReadN(r, rid[:])
}
func (rid *RequestID) Write(w io.Writer) error {
return rwutil.WriteN(w, rid[:])
}
// endregion ////////////////////////////////////////////////////////////
// region RequestMetadata //////////////////////////////////////////////////
type RequestMetadata struct {
SenderContract ContractIdentity `json:"senderContract"`
// ID of the target smart contract
TargetContract Hname `json:"targetContract"`
// entry point code
EntryPoint Hname `json:"entryPoint"`
// request arguments
Params dict.Dict `json:"params"`
// Allowance intended to the target contract to take. Nil means zero allowance
Allowance *Assets `json:"allowance"`
// gas budget
GasBudget uint64 `json:"gasBudget"`
}
func requestMetadataFromFeatureSet(set iotago.FeatureSet) (*RequestMetadata, error) {
metadataFeatBlock := set.MetadataFeature()
if metadataFeatBlock == nil {
// IMPORTANT: this cannot return an empty `&RequestMetadata{}` object because that could cause `isInternalUTXO` check to fail
return nil, nil
}
return RequestMetadataFromBytes(metadataFeatBlock.Data)
}
func RequestMetadataFromBytes(data []byte) (*RequestMetadata, error) {
return rwutil.ReadFromBytes(data, new(RequestMetadata))
}
// returns nil if nil pointer receiver is cloned
func (meta *RequestMetadata) Clone() *RequestMetadata {
if meta == nil {
return nil
}
return &RequestMetadata{
SenderContract: meta.SenderContract,
TargetContract: meta.TargetContract,
EntryPoint: meta.EntryPoint,
Params: meta.Params.Clone(),
Allowance: meta.Allowance.Clone(),
GasBudget: meta.GasBudget,
}
}
func (meta *RequestMetadata) Bytes() []byte {
return rwutil.WriteToBytes(meta)
}
func (meta *RequestMetadata) Read(r io.Reader) error {
rr := rwutil.NewReader(r)
rr.Read(&meta.SenderContract)
rr.Read(&meta.TargetContract)
rr.Read(&meta.EntryPoint)
meta.GasBudget = rr.ReadGas64()
meta.Params = dict.New()
rr.Read(&meta.Params)
meta.Allowance = NewEmptyAssets()
rr.Read(meta.Allowance)
return rr.Err
}
func (meta *RequestMetadata) Write(w io.Writer) error {
ww := rwutil.NewWriter(w)
ww.Write(&meta.SenderContract)
ww.Write(&meta.TargetContract)
ww.Write(&meta.EntryPoint)
ww.WriteGas64(meta.GasBudget)
ww.Write(&meta.Params)
ww.Write(meta.Allowance)
return ww.Err
}