/
output.go
169 lines (140 loc) · 6.08 KB
/
output.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
package vmcommon
import (
"encoding/hex"
"fmt"
"math/big"
)
// StorageUpdate represents a change in the account storage (insert, update or delete)
// Note: current implementation might also return unmodified storage entries.
type StorageUpdate struct {
// Offset is the storage key.
// The VM treats this as a big.Int.
Offset []byte
// Data is the new storage value.
// The VM treats this as a big.Int.
// Zero indicates missing data for the key (or even a missing key),
// therefore a value of zero here indicates that
// the storage map entry with the given key can be deleted.
Data []byte
}
// OutputAccount shows the state of an account after contract execution.
// It can be an existing account or a new account created by the transaction.
// Note: the current implementation might also return unmodified accounts.
type OutputAccount struct {
// Address is the public key of the account.
Address []byte
// Nonce is the new account nonce.
Nonce uint64
// Balance is the account balance after running a SC.
// Only used for some tests now, please ignore. Might be removed in the future.
Balance *big.Int
// BalanceDelta is by how much the balance should change following the SC execution.
// A negative value indicates that balance should decrease.
BalanceDelta *big.Int
// StorageUpdates is a map containing pointers to StorageUpdate structs,
// indexed with strings produced by `string(StorageUpdate.Offset)`, for fast
// access by the Offset of the StorageUpdate. These StorageUpdate structs
// will be processed by the Node to modify the storage of the SmartContract.
// Please note that it is likely that not all existing account storage keys
// show up here.
StorageUpdates map[string]*StorageUpdate
// Code is the assembled code of a smart contract account.
// This field will be populated when a new SC must be created after the transaction.
Code []byte
// CodeMetadata is the metadata of the code
// Like "Code", this field will be populated when a new SC must be created after the transaction.
CodeMetadata []byte
// Data will be populated if there is a transfer to this output account which has to
// be further interpreted or verified
Data []byte
// GasLimit will be populated if the call is a smart contract call for another shard
GasLimit uint64
// CallType will be set if the output account must be invoked as a smart contract
CallType CallType
}
// LogEntry represents an entry in the contract execution log.
// TODO: document all fields.
type LogEntry struct {
Identifier []byte
Address []byte
Topics [][]byte
Data []byte
}
// VMOutput is the return data and final account state after a SC execution.
type VMOutput struct {
// ReturnData is the function call returned result.
// This value does not influence the account state in any way.
// The value should be accessible in a UI.
// ReturnData is part of the transaction receipt.
ReturnData [][]byte
// ReturnCode is the function call error code.
// If it is not `Ok`, the transaction failed in some way - gas is, however, consumed anyway.
// This value does not influence the account state in any way.
// The value should be accessible to a UI.
// ReturnCode is part of the transaction receipt.
ReturnCode ReturnCode
// ReturnMessage is a message set by the SmartContract, destined for the
// caller
ReturnMessage string
// GasRemaining = VMInput.GasProvided - gas used.
// It is necessary to compute how much to charge the sender for the transaction.
GasRemaining uint64
// GasRefund is how much gas the sender earned during the transaction.
// Certain operations, like freeing up storage, actually return gas instead of consuming it.
// Based on GasRefund, the sender could in principle be rewarded instead of taxed.
GasRefund *big.Int
// OutputAccounts contains data about all accounts changed as a result of the
// Transaction. It is a map containing pointers to OutputAccount structs,
// indexed with strings produced by `string(OutputAccount.Address)`, for fast
// access by the Address of the OutputAccount.
// This information tells the Node how to update the account data.
// It can contain new accounts or existing changed accounts.
// Note: the current implementation might also retrieve accounts that were not changed.
OutputAccounts map[string]*OutputAccount
// DeletedAccounts is a list of public keys of accounts that need to be deleted
// as a result of the transaction.
DeletedAccounts [][]byte
// TouchedAccounts is a list of public keys of accounts that were somehow involved in the VM execution.
// TODO: investigate what we need to to about these.
TouchedAccounts [][]byte
// Logs is a list of event data logged by the VM.
// Smart contracts can choose to log certain events programatically.
// There are 3 main use cases for events and logs:
// 1. smart contract return values for the user interface;
// 2. asynchronous triggers with data;
// 3. a cheaper form of storage (e.g. storing historical data that can be rendered by the frontend).
// The logs should be accessible to the UI.
// The logs are part of the transaction receipt.
Logs []*LogEntry
}
// ReturnDataKind specifies how to interpret VMOutputs's return data.
// More specifically, how to interpret returned data's first item.
type ReturnDataKind int
const (
// AsBigInt to interpret as big int
AsBigInt ReturnDataKind = 1 << iota
// AsBigIntString to interpret as big int string
AsBigIntString
// AsString to interpret as string
AsString
// AsHex to interpret as hex
AsHex
)
// GetFirstReturnData is a helper function that returns the first ReturnData of VMOutput, interpreted as specified.
func (vmOutput *VMOutput) GetFirstReturnData(asType ReturnDataKind) (interface{}, error) {
if len(vmOutput.ReturnData) == 0 {
return nil, fmt.Errorf("no return data")
}
returnData := vmOutput.ReturnData[0]
switch asType {
case AsBigInt:
return big.NewInt(0).SetBytes(returnData), nil
case AsBigIntString:
return big.NewInt(0).SetBytes(returnData).String(), nil
case AsString:
return string(returnData), nil
case AsHex:
return hex.EncodeToString(returnData), nil
}
return nil, fmt.Errorf("can't interpret return data")
}