-
Notifications
You must be signed in to change notification settings - Fork 188
/
Copy pathcall.go
277 lines (252 loc) · 7.65 KB
/
call.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
package types
import (
"fmt"
"math/big"
gethCommon "github.com/onflow/go-ethereum/common"
gethCore "github.com/onflow/go-ethereum/core"
gethTypes "github.com/onflow/go-ethereum/core/types"
gethParams "github.com/onflow/go-ethereum/params"
"github.com/onflow/go-ethereum/rlp"
)
const (
// tx type 255 is used for direct calls from COAs
DirectCallTxType = byte(255)
UnknownCallSubType = byte(0)
DepositCallSubType = byte(1)
WithdrawCallSubType = byte(2)
TransferCallSubType = byte(3)
DeployCallSubType = byte(4)
ContractCallSubType = byte(5)
// Note that these gas values might need to change if we
// change the transaction (e.g. add access list),
// then it has to be updated to use Intrinsic function
// to calculate the minimum gas needed to run the transaction.
IntrinsicFeeForTokenTransfer = gethParams.TxGas
// 21_000 is the minimum for a transaction + max gas allowed for receive/fallback methods
DefaultGasLimitForTokenTransfer = IntrinsicFeeForTokenTransfer + 2_300
// the value is set to the gas limit for transfer to facilitate transfers
// to smart contract addresses.
DepositCallGasLimit = DefaultGasLimitForTokenTransfer
WithdrawCallGasLimit = DefaultGasLimitForTokenTransfer
)
// DirectCall captures all the data related to a direct call to evm
// direct calls are similar to transactions but they don't have
// signatures and don't need sequence number checks
// Note that while we don't check the nonce, it impacts
// hash calculation and also impacts the address of resulting contract
// when deployed through direct calls.
// Users don't have the worry about the nonce, handler sets
// it to the right value.
type DirectCall struct {
Type byte
SubType byte
From Address
To Address
Data []byte
Value *big.Int
GasLimit uint64
Nonce uint64
}
// DirectCallFromEncoded constructs a DirectCall from encoded data
func DirectCallFromEncoded(encoded []byte) (*DirectCall, error) {
if encoded[0] != DirectCallTxType {
return nil, fmt.Errorf("tx type mismatch")
}
dc := &DirectCall{}
return dc, rlp.DecodeBytes(encoded[1:], dc)
}
// Encode encodes the direct call it also adds the type
// as the very first byte, similar to how evm encodes types.
func (dc *DirectCall) Encode() ([]byte, error) {
encoded, err := rlp.EncodeToBytes(dc)
return append([]byte{dc.Type}, encoded...), err
}
// Hash computes the hash of a direct call
func (dc *DirectCall) Hash() gethCommon.Hash {
// we use geth transaction hash calculation since direct call hash is included in the
// block transaction hashes, and thus observed as any other transaction
// We construct this Legacy tx type so the external 3rd party tools
// don't have to support a new type for the purpose of hash computation
return dc.Transaction().Hash()
}
// Message constructs a core.Message from the direct call
func (dc *DirectCall) Message() *gethCore.Message {
return &gethCore.Message{
From: dc.From.ToCommon(),
To: dc.to(),
Value: dc.Value,
Data: dc.Data,
Nonce: dc.Nonce,
GasLimit: dc.GasLimit,
GasPrice: big.NewInt(0), // price is set to zero fo direct calls
GasTipCap: big.NewInt(0), // also known as maxPriorityFeePerGas (in GWei)
GasFeeCap: big.NewInt(0), // also known as maxFeePerGas (in GWei)
// TODO: maybe revisit setting the access list
// AccessList: tx.AccessList(),
// When SkipAccountChecks is true, the message nonce is not checked against the
// account nonce in state. It also disables checking that the sender is an EOA.
// Since we use the direct calls for COAs, we set the nonce and the COA is an smart contract.
// SkipNonceChecks & SkipFromEOACheck was previously a single setting, namely
// SkipAccountChecks. And since we had SkipAccountChecks = true, we now
// have both SkipNonceChecks = SkipFromEOACheck = true
SkipNonceChecks: true,
SkipFromEOACheck: true,
}
}
// Transaction constructs a geth.Transaction from the direct call
func (dc *DirectCall) Transaction() *gethTypes.Transaction {
// Since a direct call doesn't have a valid signature
// and we need to somehow include the From field for the purpose
// of hash calculation. we define the canonical format by
// using the FROM bytes to set the bytes for the R part of the tx (big endian),
// S captures the subtype of transaction and V is set to DirectCallTxType (255).
return gethTypes.NewTx(&gethTypes.LegacyTx{
GasPrice: big.NewInt(0),
Gas: dc.GasLimit,
To: dc.to(),
Value: dc.Value,
Data: dc.Data,
Nonce: dc.Nonce,
R: new(big.Int).SetBytes(dc.From.Bytes()),
S: new(big.Int).SetBytes([]byte{dc.SubType}),
V: new(big.Int).SetBytes([]byte{DirectCallTxType}),
})
}
// EmptyToField returns true if `to` field contains an empty address
func (dc *DirectCall) EmptyToField() bool {
return dc.To == EmptyAddress
}
func (dc *DirectCall) to() *gethCommon.Address {
if !dc.EmptyToField() {
ct := dc.To.ToCommon()
return &ct
}
return nil
}
// NewDepositCall constructs a new deposit direct call
func NewDepositCall(
bridge Address,
address Address,
amount *big.Int,
nonce uint64,
) *DirectCall {
return &DirectCall{
Type: DirectCallTxType,
SubType: DepositCallSubType,
From: bridge,
To: address,
Data: nil,
Value: amount,
GasLimit: DepositCallGasLimit,
Nonce: nonce,
}
}
// NewDepositCall constructs a new withdraw direct call
func NewWithdrawCall(
bridge Address,
address Address,
amount *big.Int,
nonce uint64,
) *DirectCall {
return &DirectCall{
Type: DirectCallTxType,
SubType: WithdrawCallSubType,
From: address,
To: bridge,
Data: nil,
Value: amount,
GasLimit: WithdrawCallGasLimit,
Nonce: nonce,
}
}
func NewTransferCall(
from Address,
to Address,
amount *big.Int,
nonce uint64,
) *DirectCall {
return &DirectCall{
Type: DirectCallTxType,
SubType: TransferCallSubType,
From: from,
To: to,
Data: nil,
Value: amount,
GasLimit: DefaultGasLimitForTokenTransfer,
Nonce: nonce,
}
}
// NewDeployCall constructs a new deploy direct call
func NewDeployCall(
caller Address,
code Code,
gasLimit uint64,
value *big.Int,
nonce uint64,
) *DirectCall {
return &DirectCall{
Type: DirectCallTxType,
SubType: DeployCallSubType,
From: caller,
To: EmptyAddress,
Data: code,
Value: value,
GasLimit: gasLimit,
Nonce: nonce,
}
}
// NewDeployCallWithTargetAddress constructs a new deployment call
// for the given target address
//
// Warning! This subtype should only be used internally for
// deploying contracts at given addresses (e.g. COA account init setup)
// should not be used for other means.
func NewDeployCallWithTargetAddress(
caller Address,
to Address,
code Code,
gasLimit uint64,
value *big.Int,
nonce uint64,
) *DirectCall {
return &DirectCall{
Type: DirectCallTxType,
SubType: DeployCallSubType,
From: caller,
To: to,
Data: code,
Value: value,
GasLimit: gasLimit,
Nonce: nonce,
}
}
// NewContractCall constructs a new contract call
func NewContractCall(
caller Address,
to Address,
data Data,
gasLimit uint64,
value *big.Int,
nonce uint64,
) *DirectCall {
return &DirectCall{
Type: DirectCallTxType,
SubType: ContractCallSubType,
From: caller,
To: to,
Data: data,
Value: value,
GasLimit: gasLimit,
Nonce: nonce,
}
}
// GasLimit sets the limit for the total gas used by a transaction
type GasLimit uint64
// Code holds an smart contract code
type Code []byte
// Data holds the data passed as part of a call
type Data []byte
// AsBigInt process the data and return it as a big integer
func (d Data) AsBigInt() *big.Int {
return new(big.Int).SetBytes(d)
}