Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion plugin/dapp/evm/executor/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package executor
import (
"bytes"
"fmt"
"math"
"math/big"
"os"
"reflect"
Expand Down Expand Up @@ -209,14 +210,29 @@ func (evm *EVMExecutor) createEvmContractAddress(b common.Address, nonce uint64)

// CheckTx 校验交易
func (evm *EVMExecutor) CheckTx(tx *types.Transaction, index int) error {
if evm.GetAPI().GetConfig().IsPara() {
cfg := evm.GetAPI().GetConfig()
if cfg.IsPara() {
return nil
}

if tx == nil {
return fmt.Errorf("tx empty")
}

// 分叉后拒绝金额超过 int64 上限的交易:
// EVMContractAction.Amount 为 uint64,下游记账体系为 int64,
// 超过 math.MaxInt64 会回绕为负数,曾导致伪 msg.value 攻击(详见 ForkEVMFixOverflow)
if cfg.IsDappFork(evm.GetMainHeight(), "evm", evmtypes.ForkEVMFixOverflow) {
var action evmtypes.EVMContractAction
if err := types.Decode(tx.Payload, &action); err != nil {
return err
}
if action.GetAmount() > math.MaxInt64 {
log.Error("CheckTx", "reject evm tx with amount overflow", "amount", action.GetAmount())
return types.ErrAmount
}
}

return state.ProcessCheck(evm.GetMainHeight(), tx.Hash())
}

Expand Down
61 changes: 61 additions & 0 deletions plugin/dapp/evm/executor/evm_checktx_amount_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package executor

import (
"math"
"testing"

ctypes "github.com/33cn/chain33/types"
evmtypes "github.com/33cn/plugin/plugin/dapp/evm/types"
"github.com/stretchr/testify/require"
)

// makeCallTxWithAmount builds a signed evm call tx carrying the given amount.
func makeCallTxWithAmount(cfg *ctypes.Chain33Config, amount uint64) *ctypes.Transaction {
action := &evmtypes.EVMContractAction{
Amount: amount, GasLimit: 100000, GasPrice: 1,
Code: nil, Para: []byte("test"),
ContractAddr: "0x0000000000000000000000000000000000000000",
}
tx := &ctypes.Transaction{
ChainID: cfg.GetChainID(),
Execer: []byte(cfg.ExecName(evmtypes.ExecutorName)),
Payload: ctypes.Encode(action),
Fee: 1e6, Nonce: 0,
}
signTx(tx, roleAttacker)
return tx
}

// After ForkEVMFixOverflow, CheckTx must reject evm txs whose uint64 Amount
// exceeds math.MaxInt64 (the value that used to wrap to -1 and bypass the
// balance check, enabling the fake msg.value attack). Before the fork the
// historical behavior is preserved (only replay check runs).
func TestCheckTxRejectsAmountOverflow(t *testing.T) {
cfg := newTestConfig(t) // ForkEVMFixOverflow = 1000

// post-fork: overflow amount rejected at CheckTx
exec := newTestExecutor(t, cfg, 1000)
tx := makeCallTxWithAmount(cfg, math.MaxUint64)
require.Equal(t, ctypes.ErrAmount, exec.CheckTx(tx, 0))

tx = makeCallTxWithAmount(cfg, math.MaxInt64+1)
require.Equal(t, ctypes.ErrAmount, exec.CheckTx(tx, 0))

// post-fork: boundary and normal amounts pass
tx = makeCallTxWithAmount(cfg, math.MaxInt64)
require.NoError(t, exec.CheckTx(tx, 0))
tx = makeCallTxWithAmount(cfg, 1e8)
require.NoError(t, exec.CheckTx(tx, 0))
tx = makeCallTxWithAmount(cfg, 0)
require.NoError(t, exec.CheckTx(tx, 0))

// pre-fork: historical behavior preserved (overflow amount NOT rejected here;
// the exec-layer fork gate handles it on such chains)
execPre := newTestExecutor(t, cfg, 999)
tx = makeCallTxWithAmount(cfg, math.MaxUint64)
require.NoError(t, execPre.CheckTx(tx, 0))
}
Loading