-
Notifications
You must be signed in to change notification settings - Fork 0
/
eth.go
396 lines (339 loc) · 13.7 KB
/
eth.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
// Copyright 2022 Hekas Foundation
// This file is part of the Hekas Network packages.
//
// Hekas is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The Hekas packages are distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the Hekas packages. If not, see https://github.com/hekas-network/hekas/blob/main/LICENSE
package evm
import (
"math"
"math/big"
errorsmod "cosmossdk.io/errors"
sdkmath "cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
errortypes "github.com/cosmos/cosmos-sdk/types/errors"
anteutils "github.com/hekas-network/hekas/app/ante/utils"
"github.com/hekas-network/hekas/types"
"github.com/hekas-network/hekas/x/evm/keeper"
"github.com/hekas-network/hekas/x/evm/statedb"
evmtypes "github.com/hekas-network/hekas/x/evm/types"
"github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types"
)
// EthAccountVerificationDecorator validates an account balance checks
type EthAccountVerificationDecorator struct {
ak evmtypes.AccountKeeper
evmKeeper EVMKeeper
}
// NewEthAccountVerificationDecorator creates a new EthAccountVerificationDecorator
func NewEthAccountVerificationDecorator(ak evmtypes.AccountKeeper, ek EVMKeeper) EthAccountVerificationDecorator {
return EthAccountVerificationDecorator{
ak: ak,
evmKeeper: ek,
}
}
// AnteHandle validates checks that the sender balance is greater than the total transaction cost.
// The account will be set to store if it doesn't exist, i.e. cannot be found on store.
// This AnteHandler decorator will fail if:
// - any of the msgs is not a MsgEthereumTx
// - from address is empty
// - account balance is lower than the transaction cost
func (avd EthAccountVerificationDecorator) AnteHandle(
ctx sdk.Context,
tx sdk.Tx,
simulate bool,
next sdk.AnteHandler,
) (newCtx sdk.Context, err error) {
if !ctx.IsCheckTx() {
return next(ctx, tx, simulate)
}
for i, msg := range tx.GetMsgs() {
msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx)
if !ok {
return ctx, errorsmod.Wrapf(errortypes.ErrUnknownRequest, "invalid message type %T, expected %T", msg, (*evmtypes.MsgEthereumTx)(nil))
}
txData, err := evmtypes.UnpackTxData(msgEthTx.Data)
if err != nil {
return ctx, errorsmod.Wrapf(err, "failed to unpack tx data any for tx %d", i)
}
// sender address should be in the tx cache from the previous AnteHandle call
from := msgEthTx.GetFrom()
if from.Empty() {
return ctx, errorsmod.Wrap(errortypes.ErrInvalidAddress, "from address cannot be empty")
}
// check whether the sender address is EOA
fromAddr := common.BytesToAddress(from)
acct := avd.evmKeeper.GetAccount(ctx, fromAddr)
if acct == nil {
acc := avd.ak.NewAccountWithAddress(ctx, from)
avd.ak.SetAccount(ctx, acc)
acct = statedb.NewEmptyAccount()
} else if acct.IsContract() {
return ctx, errorsmod.Wrapf(errortypes.ErrInvalidType,
"the sender is not EOA: address %s, codeHash <%s>", fromAddr, acct.CodeHash)
}
if err := keeper.CheckSenderBalance(sdkmath.NewIntFromBigInt(acct.Balance), txData); err != nil {
return ctx, errorsmod.Wrap(err, "failed to check sender balance")
}
}
return next(ctx, tx, simulate)
}
// EthGasConsumeDecorator validates enough intrinsic gas for the transaction and
// gas consumption.
type EthGasConsumeDecorator struct {
bankKeeper anteutils.BankKeeper
distributionKeeper anteutils.DistributionKeeper
evmKeeper EVMKeeper
stakingKeeper anteutils.StakingKeeper
maxGasWanted uint64
}
// NewEthGasConsumeDecorator creates a new EthGasConsumeDecorator
func NewEthGasConsumeDecorator(
bankKeeper anteutils.BankKeeper,
distributionKeeper anteutils.DistributionKeeper,
evmKeeper EVMKeeper,
stakingKeeper anteutils.StakingKeeper,
maxGasWanted uint64,
) EthGasConsumeDecorator {
return EthGasConsumeDecorator{
bankKeeper,
distributionKeeper,
evmKeeper,
stakingKeeper,
maxGasWanted,
}
}
// AnteHandle validates that the Ethereum tx message has enough to cover intrinsic gas
// (during CheckTx only) and that the sender has enough balance to pay for the gas cost.
// If the balance is not sufficient, it will be attempted to withdraw enough staking rewards
// for the payment.
//
// Intrinsic gas for a transaction is the amount of gas that the transaction uses before the
// transaction is executed. The gas is a constant value plus any cost incurred by additional bytes
// of data supplied with the transaction.
//
// This AnteHandler decorator will fail if:
// - the message is not a MsgEthereumTx
// - sender account cannot be found
// - transaction's gas limit is lower than the intrinsic gas
// - user has neither enough balance nor staking rewards to deduct the transaction fees (gas_limit * gas_price)
// - transaction or block gas meter runs out of gas
// - sets the gas meter limit
// - gas limit is greater than the block gas meter limit
func (egcd EthGasConsumeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) {
gasWanted := uint64(0)
// gas consumption limit already checked during CheckTx so there's no need to
// verify it again during ReCheckTx
if ctx.IsReCheckTx() {
// Use new context with gasWanted = 0
// Otherwise, there's an error on txmempool.postCheck (tendermint)
// that is not bubbled up. Thus, the Tx never runs on DeliverMode
// Error: "gas wanted -1 is negative"
// For more information, see issue #1554
// https://github.com/hekas-network/ethermint/issues/1554
newCtx := ctx.WithGasMeter(types.NewInfiniteGasMeterWithLimit(gasWanted))
return next(newCtx, tx, simulate)
}
evmParams := egcd.evmKeeper.GetParams(ctx)
evmDenom := evmParams.GetEvmDenom()
chainCfg := evmParams.GetChainConfig()
ethCfg := chainCfg.EthereumConfig(egcd.evmKeeper.ChainID())
blockHeight := big.NewInt(ctx.BlockHeight())
homestead := ethCfg.IsHomestead(blockHeight)
istanbul := ethCfg.IsIstanbul(blockHeight)
var events sdk.Events
// Use the lowest priority of all the messages as the final one.
minPriority := int64(math.MaxInt64)
baseFee := egcd.evmKeeper.GetBaseFee(ctx, ethCfg)
for _, msg := range tx.GetMsgs() {
msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx)
if !ok {
return ctx, errorsmod.Wrapf(errortypes.ErrUnknownRequest, "invalid message type %T, expected %T", msg, (*evmtypes.MsgEthereumTx)(nil))
}
from := msgEthTx.GetFrom()
txData, err := evmtypes.UnpackTxData(msgEthTx.Data)
if err != nil {
return ctx, errorsmod.Wrap(err, "failed to unpack tx data")
}
if ctx.IsCheckTx() && egcd.maxGasWanted != 0 {
// We can't trust the tx gas limit, because we'll refund the unused gas.
if txData.GetGas() > egcd.maxGasWanted {
gasWanted += egcd.maxGasWanted
} else {
gasWanted += txData.GetGas()
}
} else {
gasWanted += txData.GetGas()
}
fees, err := keeper.VerifyFee(txData, evmDenom, baseFee, homestead, istanbul, ctx.IsCheckTx())
if err != nil {
return ctx, errorsmod.Wrapf(err, "failed to verify the fees")
}
// If the account balance is not sufficient, try to withdraw enough staking rewards
err = anteutils.ClaimStakingRewardsIfNecessary(ctx, egcd.bankKeeper, egcd.distributionKeeper, egcd.stakingKeeper, from, fees)
if err != nil {
return ctx, err
}
err = egcd.evmKeeper.DeductTxCostsFromUserBalance(ctx, fees, common.HexToAddress(msgEthTx.From))
if err != nil {
return ctx, errorsmod.Wrapf(err, "failed to deduct transaction costs from user balance")
}
events = append(events,
sdk.NewEvent(
sdk.EventTypeTx,
sdk.NewAttribute(sdk.AttributeKeyFee, fees.String()),
),
)
priority := evmtypes.GetTxPriority(txData, baseFee)
if priority < minPriority {
minPriority = priority
}
}
ctx.EventManager().EmitEvents(events)
blockGasLimit := types.BlockGasLimit(ctx)
// return error if the tx gas is greater than the block limit (max gas)
// NOTE: it's important here to use the gas wanted instead of the gas consumed
// from the tx gas pool. The latter only has the value so far since the
// EthSetupContextDecorator, so it will never exceed the block gas limit.
if gasWanted > blockGasLimit {
return ctx, errorsmod.Wrapf(
errortypes.ErrOutOfGas,
"tx gas (%d) exceeds block gas limit (%d)",
gasWanted,
blockGasLimit,
)
}
// Set tx GasMeter with a limit of GasWanted (i.e. gas limit from the Ethereum tx).
// The gas consumed will be then reset to the gas used by the state transition
// in the EVM.
// FIXME: use a custom gas configuration that doesn't add any additional gas and only
// takes into account the gas consumed at the end of the EVM transaction.
newCtx := ctx.
WithGasMeter(types.NewInfiniteGasMeterWithLimit(gasWanted)).
WithPriority(minPriority)
// we know that we have enough gas on the pool to cover the intrinsic gas
return next(newCtx, tx, simulate)
}
// CanTransferDecorator checks if the sender is allowed to transfer funds according to the EVM block
// context rules.
type CanTransferDecorator struct {
evmKeeper EVMKeeper
}
// NewCanTransferDecorator creates a new CanTransferDecorator instance.
func NewCanTransferDecorator(evmKeeper EVMKeeper) CanTransferDecorator {
return CanTransferDecorator{
evmKeeper: evmKeeper,
}
}
// AnteHandle creates an EVM from the message and calls the BlockContext CanTransfer function to
// see if the address can execute the transaction.
func (ctd CanTransferDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) {
params := ctd.evmKeeper.GetParams(ctx)
ethCfg := params.ChainConfig.EthereumConfig(ctd.evmKeeper.ChainID())
signer := ethtypes.MakeSigner(ethCfg, big.NewInt(ctx.BlockHeight()))
for _, msg := range tx.GetMsgs() {
msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx)
if !ok {
return ctx, errorsmod.Wrapf(errortypes.ErrUnknownRequest, "invalid message type %T, expected %T", msg, (*evmtypes.MsgEthereumTx)(nil))
}
baseFee := ctd.evmKeeper.GetBaseFee(ctx, ethCfg)
coreMsg, err := msgEthTx.AsMessage(signer, baseFee)
if err != nil {
return ctx, errorsmod.Wrapf(
err,
"failed to create an ethereum core.Message from signer %T", signer,
)
}
if evmtypes.IsLondon(ethCfg, ctx.BlockHeight()) {
if baseFee == nil {
return ctx, errorsmod.Wrap(
evmtypes.ErrInvalidBaseFee,
"base fee is supported but evm block context value is nil",
)
}
if coreMsg.GasFeeCap().Cmp(baseFee) < 0 {
return ctx, errorsmod.Wrapf(
errortypes.ErrInsufficientFee,
"max fee per gas less than block base fee (%s < %s)",
coreMsg.GasFeeCap(), baseFee,
)
}
}
// NOTE: pass in an empty coinbase address and nil tracer as we don't need them for the check below
cfg := &statedb.EVMConfig{
ChainConfig: ethCfg,
Params: params,
CoinBase: common.Address{},
BaseFee: baseFee,
}
stateDB := statedb.New(ctx, ctd.evmKeeper, statedb.NewEmptyTxConfig(common.BytesToHash(ctx.HeaderHash().Bytes())))
evm := ctd.evmKeeper.NewEVM(ctx, coreMsg, cfg, evmtypes.NewNoOpTracer(), stateDB)
// check that caller has enough balance to cover asset transfer for **topmost** call
// NOTE: here the gas consumed is from the context with the infinite gas meter
if coreMsg.Value().Sign() > 0 && !evm.Context.CanTransfer(stateDB, coreMsg.From(), coreMsg.Value()) {
return ctx, errorsmod.Wrapf(
errortypes.ErrInsufficientFunds,
"failed to transfer %s from address %s using the EVM block context transfer function",
coreMsg.Value(),
coreMsg.From(),
)
}
}
return next(ctx, tx, simulate)
}
// EthIncrementSenderSequenceDecorator increments the sequence of the signers.
type EthIncrementSenderSequenceDecorator struct {
ak evmtypes.AccountKeeper
}
// NewEthIncrementSenderSequenceDecorator creates a new EthIncrementSenderSequenceDecorator.
func NewEthIncrementSenderSequenceDecorator(ak evmtypes.AccountKeeper) EthIncrementSenderSequenceDecorator {
return EthIncrementSenderSequenceDecorator{
ak: ak,
}
}
// AnteHandle handles incrementing the sequence of the signer (i.e. sender). If the transaction is a
// contract creation, the nonce will be incremented during the transaction execution and not within
// this AnteHandler decorator.
func (issd EthIncrementSenderSequenceDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) {
for _, msg := range tx.GetMsgs() {
msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx)
if !ok {
return ctx, errorsmod.Wrapf(errortypes.ErrUnknownRequest, "invalid message type %T, expected %T", msg, (*evmtypes.MsgEthereumTx)(nil))
}
txData, err := evmtypes.UnpackTxData(msgEthTx.Data)
if err != nil {
return ctx, errorsmod.Wrap(err, "failed to unpack tx data")
}
// increase sequence of sender
acc := issd.ak.GetAccount(ctx, msgEthTx.GetFrom())
if acc == nil {
return ctx, errorsmod.Wrapf(
errortypes.ErrUnknownAddress,
"account %s is nil", common.BytesToAddress(msgEthTx.GetFrom().Bytes()),
)
}
nonce := acc.GetSequence()
// we merged the nonce verification to nonce increment, so when tx includes multiple messages
// with same sender, they'll be accepted.
if txData.GetNonce() != nonce {
return ctx, errorsmod.Wrapf(
errortypes.ErrInvalidSequence,
"invalid nonce; got %d, expected %d", txData.GetNonce(), nonce,
)
}
if err := acc.SetSequence(nonce + 1); err != nil {
return ctx, errorsmod.Wrapf(err, "failed to set sequence to %d", acc.GetSequence()+1)
}
issd.ak.SetAccount(ctx, acc)
}
return next(ctx, tx, simulate)
}