This repository has been archived by the owner on May 13, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 29
/
operations.go
491 lines (431 loc) · 15.5 KB
/
operations.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
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
// Copyright (c) 2020-2023 Blockwatch Data Inc.
// Author: alex@blockwatch.cc
package rpc
import (
"bytes"
"context"
"encoding/hex"
"encoding/json"
"fmt"
"blockwatch.cc/tzgo/micheline"
"blockwatch.cc/tzgo/tezos"
)
type MetadataMode string
const (
MetadataModeUnset MetadataMode = ""
MetadataModeNever MetadataMode = "never"
MetadataModeAlways MetadataMode = "always"
)
// Operation represents a single operation or batch of operations included in a block
type Operation struct {
Protocol tezos.ProtocolHash `json:"protocol"`
ChainID tezos.ChainIdHash `json:"chain_id"`
Hash tezos.OpHash `json:"hash"`
Branch tezos.BlockHash `json:"branch"`
Contents OperationList `json:"contents"`
Signature tezos.Signature `json:"signature"`
Errors []OperationError `json:"error,omitempty"` // mempool only
Metadata string `json:"metadata,omitempty"` // contains `too large` when stripped, this is BAD!!
}
// TotalCosts returns the sum of costs across all batched and internal operations.
func (o Operation) TotalCosts() tezos.Costs {
var c tezos.Costs
for _, op := range o.Contents {
c = c.Add(op.Costs())
}
return c
}
// Costs returns ta list of individual costs for all batched operations.
func (o Operation) Costs() []tezos.Costs {
list := make([]tezos.Costs, len(o.Contents))
for i, op := range o.Contents {
list[i] = op.Costs()
}
return list
}
// TypedOperation must be implemented by all operations
type TypedOperation interface {
Kind() tezos.OpType
Meta() OperationMetadata
Result() OperationResult
Costs() tezos.Costs
Limits() tezos.Limits
}
// OperationError represents data describing an error conditon that lead to a
// failed operation execution.
type OperationError struct {
GenericError
Contract *tezos.Address `json:"contract,omitempty"`
Raw json.RawMessage `json:"-"`
}
// OperationMetadata contains execution receipts for successful and failed
// operations.
type OperationMetadata struct {
BalanceUpdates BalanceUpdates `json:"balance_updates"` // fee-related
Result OperationResult `json:"operation_result"`
// transaction only
InternalResults []*InternalResult `json:"internal_operation_results,omitempty"`
// endorsement only
Delegate tezos.Address `json:"delegate"`
Slots []int `json:"slots,omitempty"`
EndorsementPower int `json:"endorsement_power,omitempty"` // v12+
PreendorsementPower int `json:"preendorsement_power,omitempty"` // v12+
// some rollup ops only, FIXME: is this correct here or is this field in result?
Level int64 `json:"level"`
}
// Address returns the delegate address for endorsements.
func (m OperationMetadata) Address() tezos.Address {
return m.Delegate
}
// OperationResult contains receipts for executed operations, both success and failed.
// This type is a generic container for all possible results. Which fields are actually
// used depends on operation type and performed actions.
type OperationResult struct {
Status tezos.OpStatus `json:"status"`
BalanceUpdates BalanceUpdates `json:"balance_updates"`
ConsumedGas int64 `json:"consumed_gas,string"` // deprecated in v015
ConsumedMilliGas int64 `json:"consumed_milligas,string"` // v007+
Errors []OperationError `json:"errors,omitempty"`
Allocated bool `json:"allocated_destination_contract"` // tx only
Storage *micheline.Prim `json:"storage,omitempty"` // tx, orig
OriginatedContracts []tezos.Address `json:"originated_contracts"` // orig only
StorageSize int64 `json:"storage_size,string"` // tx, orig, const
PaidStorageSizeDiff int64 `json:"paid_storage_size_diff,string"` // tx, orig
BigmapDiff json.RawMessage `json:"big_map_diff,omitempty"` // tx, orig, <v013
LazyStorageDiff json.RawMessage `json:"lazy_storage_diff,omitempty"` // v008+ tx, orig
GlobalAddress tezos.ExprHash `json:"global_address"` // const
TicketUpdatesCorrect []TicketUpdate `json:"ticket_updates"` // v015
TicketReceipts []TicketUpdate `json:"ticket_receipt"` // v015, name on internal
// v013 tx rollup
TxRollupResult
// v016 smart rollup
SmartRollupResult
}
// Always use this helper to retrieve Ticket updates. This is because due to
// lack of quality control Tezos Lima protocol ended up with 2 distinct names
// for ticket updates in external call receipts versus internal call receipts.
func (r OperationResult) TicketUpdates() []TicketUpdate {
if len(r.TicketUpdatesCorrect) > 0 {
return r.TicketUpdatesCorrect
}
return r.TicketReceipts
}
func (r OperationResult) BigmapEvents() micheline.BigmapEvents {
if r.LazyStorageDiff != nil {
res := make(micheline.LazyEvents, 0)
if err := json.Unmarshal(r.LazyStorageDiff, &res); err != nil {
log.Debugf("rpc: lazy decode: %v", err)
}
return res.BigmapEvents()
}
if r.BigmapDiff != nil {
res := make(micheline.BigmapEvents, 0)
if err := json.Unmarshal(r.BigmapDiff, &res); err != nil {
log.Debugf("rpc: bigmap decode: %v", err)
}
return res
}
return nil
}
func (r OperationResult) IsSuccess() bool {
return r.Status == tezos.OpStatusApplied
}
func (r OperationResult) Gas() int64 {
if r.ConsumedMilliGas > 0 {
return r.ConsumedMilliGas / 1000
}
return r.ConsumedGas
}
func (r OperationResult) MilliGas() int64 {
if r.ConsumedMilliGas > 0 {
return r.ConsumedMilliGas
}
return r.ConsumedGas * 1000
}
func (o OperationError) MarshalJSON() ([]byte, error) {
return o.Raw, nil
}
func (o *OperationError) UnmarshalJSON(data []byte) error {
type alias OperationError
if err := json.Unmarshal(data, (*alias)(o)); err != nil {
return err
}
o.Raw = make([]byte, len(data))
copy(o.Raw, data)
return nil
}
// Generic is the most generic operation type.
type Generic struct {
OpKind tezos.OpType `json:"kind"`
Metadata OperationMetadata `json:"metadata"`
}
// Kind returns the operation's type. Implements TypedOperation interface.
func (e Generic) Kind() tezos.OpType {
return e.OpKind
}
// Meta returns an empty operation metadata to implement TypedOperation interface.
func (e Generic) Meta() OperationMetadata {
return e.Metadata
}
// Result returns an empty operation result to implement TypedOperation interface.
func (e Generic) Result() OperationResult {
return e.Metadata.Result
}
// Costs returns empty operation costs to implement TypedOperation interface.
func (e Generic) Costs() tezos.Costs {
return tezos.Costs{}
}
// Limits returns empty operation limits to implement TypedOperation interface.
func (e Generic) Limits() tezos.Limits {
return tezos.Limits{}
}
// Manager represents data common for all manager operations.
type Manager struct {
Generic
Source tezos.Address `json:"source"`
Fee int64 `json:"fee,string"`
Counter int64 `json:"counter,string"`
GasLimit int64 `json:"gas_limit,string"`
StorageLimit int64 `json:"storage_limit,string"`
}
// Limits returns manager operation limits to implement TypedOperation interface.
func (e Manager) Limits() tezos.Limits {
return tezos.Limits{
Fee: e.Fee,
GasLimit: e.GasLimit,
StorageLimit: e.StorageLimit,
}
}
// OperationList is a slice of TypedOperation (interface type) with custom JSON unmarshaller
type OperationList []TypedOperation
// Contains returns true when the list contains an operation of kind typ.
func (o OperationList) Contains(typ tezos.OpType) bool {
for _, v := range o {
if v.Kind() == typ {
return true
}
}
return false
}
func (o OperationList) Select(typ tezos.OpType, n int) TypedOperation {
var cnt int
for _, v := range o {
if v.Kind() != typ {
continue
}
if cnt == n {
return v
}
cnt++
}
return nil
}
func (o OperationList) Len() int {
return len(o)
}
func (o OperationList) N(n int) TypedOperation {
if n < 0 {
n += len(o)
}
return o[n]
}
// UnmarshalJSON implements json.Unmarshaler
func (e *OperationList) UnmarshalJSON(data []byte) error {
if len(data) <= 2 {
return nil
}
if data[0] != '[' {
return fmt.Errorf("rpc: expected operation array")
}
// fmt.Printf("Decoding ops: %s\n", string(data))
dec := json.NewDecoder(bytes.NewReader(data))
// read open bracket
_, err := dec.Token()
if err != nil {
return fmt.Errorf("rpc: %v", err)
}
for dec.More() {
// peek into `{"kind":"...",` field
start := int(dec.InputOffset()) + 9
// after first JSON object, decoder pos is at `,`
if data[start] == '"' {
start += 1
}
end := start + bytes.IndexByte(data[start:], '"')
kind := tezos.ParseOpType(string(data[start:end]))
var op TypedOperation
switch kind {
// anonymous operations
case tezos.OpTypeActivateAccount:
op = &Activation{}
case tezos.OpTypeDoubleBakingEvidence:
op = &DoubleBaking{}
case tezos.OpTypeDoubleEndorsementEvidence,
tezos.OpTypeDoublePreendorsementEvidence:
op = &DoubleEndorsement{}
case tezos.OpTypeSeedNonceRevelation:
op = &SeedNonce{}
case tezos.OpTypeDrainDelegate:
op = &DrainDelegate{}
// consensus operations
case tezos.OpTypeEndorsement,
tezos.OpTypeEndorsementWithSlot,
tezos.OpTypePreendorsement:
op = &Endorsement{}
// amendment operations
case tezos.OpTypeProposals:
op = &Proposals{}
case tezos.OpTypeBallot:
op = &Ballot{}
// manager operations
case tezos.OpTypeTransaction:
op = &Transaction{}
case tezos.OpTypeOrigination:
op = &Origination{}
case tezos.OpTypeDelegation:
op = &Delegation{}
case tezos.OpTypeReveal:
op = &Reveal{}
case tezos.OpTypeRegisterConstant:
op = &ConstantRegistration{}
case tezos.OpTypeSetDepositsLimit:
op = &SetDepositsLimit{}
case tezos.OpTypeIncreasePaidStorage:
op = &IncreasePaidStorage{}
case tezos.OpTypeVdfRevelation:
op = &VdfRevelation{}
case tezos.OpTypeTransferTicket:
op = &TransferTicket{}
case tezos.OpTypeUpdateConsensusKey:
op = &UpdateConsensusKey{}
// DEPRECATED: tx rollup operations, kept for testnet backward compatibility
case tezos.OpTypeTxRollupOrigination,
tezos.OpTypeTxRollupSubmitBatch,
tezos.OpTypeTxRollupCommit,
tezos.OpTypeTxRollupReturnBond,
tezos.OpTypeTxRollupFinalizeCommitment,
tezos.OpTypeTxRollupRemoveCommitment,
tezos.OpTypeTxRollupRejection,
tezos.OpTypeTxRollupDispatchTickets:
op = &TxRollup{}
case tezos.OpTypeSmartRollupOriginate:
op = &SmartRollupOriginate{}
case tezos.OpTypeSmartRollupAddMessages:
op = &SmartRollupAddMessages{}
case tezos.OpTypeSmartRollupCement:
op = &SmartRollupCement{}
case tezos.OpTypeSmartRollupPublish:
op = &SmartRollupPublish{}
case tezos.OpTypeSmartRollupRefute:
op = &SmartRollupRefute{}
case tezos.OpTypeSmartRollupTimeout:
op = &SmartRollupTimeout{}
case tezos.OpTypeSmartRollupExecuteOutboxMessage:
op = &SmartRollupExecuteOutboxMessage{}
case tezos.OpTypeSmartRollupRecoverBond:
op = &SmartRollupRecoverBond{}
case tezos.OpTypeDalAttestation:
op = &DalAttestation{}
case tezos.OpTypeDalPublishSlotHeader:
op = &DalPublishSlotHeader{}
default:
return fmt.Errorf("rpc: unsupported op %q", string(data[start:end]))
}
if err := dec.Decode(op); err != nil {
return fmt.Errorf("rpc: operation kind %s: %v", kind, err)
}
(*e) = append(*e, op)
}
return nil
}
// GetBlockOperationHash returns a single operation hashes included in block
// https://tezos.gitlab.io/active/rpc.html#get-block-id-operation-hashes-list-offset-operation-offset
func (c *Client) GetBlockOperationHash(ctx context.Context, id BlockID, l, n int) (tezos.OpHash, error) {
var hash tezos.OpHash
u := fmt.Sprintf("chains/main/blocks/%s/operation_hashes/%d/%d", id, l, n)
err := c.Get(ctx, u, &hash)
return hash, err
}
// GetBlockOperationHashes returns a list of list of operation hashes included in block
// https://tezos.gitlab.io/active/rpc.html#get-block-id-operation-hashes
func (c *Client) GetBlockOperationHashes(ctx context.Context, id BlockID) ([][]tezos.OpHash, error) {
hashes := make([][]tezos.OpHash, 0)
u := fmt.Sprintf("chains/main/blocks/%s/operation_hashes", id)
if err := c.Get(ctx, u, &hashes); err != nil {
return nil, err
}
return hashes, nil
}
// GetBlockOperationListHashes returns a list of operation hashes included in block
// at a specified list position (i.e. validation pass) [0..3]
// https://tezos.gitlab.io/active/rpc.html#get-block-id-operation-hashes-list-offset
func (c *Client) GetBlockOperationListHashes(ctx context.Context, id BlockID, l int) ([]tezos.OpHash, error) {
hashes := make([]tezos.OpHash, 0)
u := fmt.Sprintf("chains/main/blocks/%s/operation_hashes/%d", id, l)
if err := c.Get(ctx, u, &hashes); err != nil {
return nil, err
}
return hashes, nil
}
// GetBlockOperation returns information about a single validated Tezos operation group
// (i.e. a single operation or a batch of operations) at list l and position n
// https://tezos.gitlab.io/active/rpc.html#get-block-id-operations-list-offset-operation-offset
func (c *Client) GetBlockOperation(ctx context.Context, id BlockID, l, n int) (*Operation, error) {
var op Operation
u := fmt.Sprintf("chains/main/blocks/%s/operations/%d/%d", id, l, n)
if c.MetadataMode != "" {
u += "?metadata=" + string(c.MetadataMode)
}
if err := c.Get(ctx, u, &op); err != nil {
return nil, err
}
return &op, nil
}
// GetBlockOperationList returns information about all validated Tezos operation group
// inside operation list l (i.e. validation pass) [0..3].
// https://tezos.gitlab.io/active/rpc.html#get-block-id-operations-list-offset
func (c *Client) GetBlockOperationList(ctx context.Context, id BlockID, l int) ([]Operation, error) {
ops := make([]Operation, 0)
u := fmt.Sprintf("chains/main/blocks/%s/operations/%d", id, l)
if c.MetadataMode != "" {
u += "?metadata=" + string(c.MetadataMode)
}
if err := c.Get(ctx, u, &ops); err != nil {
return nil, err
}
return ops, nil
}
// GetBlockOperations returns information about all validated Tezos operation groups
// from all operation lists in block.
// https://tezos.gitlab.io/active/rpc.html#get-block-id-operations
func (c *Client) GetBlockOperations(ctx context.Context, id BlockID) ([][]Operation, error) {
ops := make([][]Operation, 0)
u := fmt.Sprintf("chains/main/blocks/%s/operations", id)
if c.MetadataMode != "" {
u += "?metadata=" + string(c.MetadataMode)
}
if err := c.Get(ctx, u, &ops); err != nil {
return nil, err
}
return ops, nil
}
// BroadcastOperation sends a signed operation to the network (injection).
// The call returns the operation hash on success. If theoperation was rejected
// by the node error is of type RPCError.
func (c *Client) BroadcastOperation(ctx context.Context, body []byte) (hash tezos.OpHash, err error) {
err = c.Post(ctx, "injection/operation", hex.EncodeToString(body), &hash)
return
}
// RunOperation simulates executing an operation without requiring a valid signature.
// The call returns the execution result as regular operation receipt.
func (c *Client) RunOperation(ctx context.Context, id BlockID, body, resp interface{}) error {
u := fmt.Sprintf("chains/main/blocks/%s/helpers/scripts/run_operation", id)
return c.Post(ctx, u, body, resp)
}
// ForgeOperation uses a remote node to serialize an operation to its binary format.
// The result of this call SHOULD NEVER be used for signing the operation, it is only
// meant for validating the locally generated serialized output.
func (c *Client) ForgeOperation(ctx context.Context, id BlockID, body, resp interface{}) error {
u := fmt.Sprintf("chains/main/blocks/%s/helpers/forge/operations", id)
return c.Post(ctx, u, body, resp)
}