Files
evm/eth/tracers/api_extra_test.go
2024-01-03 09:44:09 +01:00

418 lines
14 KiB
Go

// (c) 2021-2024, Lux Partners Limited. All rights reserved.
// See the file LICENSE for licensing terms.
package tracers
import (
"context"
"encoding/json"
"fmt"
"math/big"
"reflect"
"sync/atomic"
"testing"
"github.com/luxdefi/evm/core"
"github.com/luxdefi/evm/core/types"
"github.com/luxdefi/evm/eth/tracers/logger"
"github.com/luxdefi/evm/internal/ethapi"
"github.com/luxdefi/evm/params"
"github.com/luxdefi/evm/precompile/contracts/txallowlist"
"github.com/luxdefi/evm/rpc"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/common/math"
"github.com/stretchr/testify/require"
)
func TestTraceBlockPrecompileActivation(t *testing.T) {
t.Parallel()
// Initialize test accounts
accounts := newAccounts(3)
copyConfig := *params.TestChainConfig
genesis := &core.Genesis{
Config: &copyConfig,
Alloc: core.GenesisAlloc{
accounts[0].addr: {Balance: big.NewInt(params.Ether)},
accounts[1].addr: {Balance: big.NewInt(params.Ether)},
accounts[2].addr: {Balance: big.NewInt(params.Ether)},
},
}
activateAllowlistBlock := 3
// assumes gap is 10 sec
activateAllowListTime := uint64(activateAllowlistBlock * 10)
activateTxAllowListConfig := params.PrecompileUpgrade{
Config: txallowlist.NewConfig(&activateAllowListTime, []common.Address{accounts[0].addr}, nil, nil),
}
deactivateAllowlistBlock := activateAllowlistBlock + 3
deactivateAllowListTime := uint64(deactivateAllowlistBlock) * 10
deactivateTxAllowListConfig := params.PrecompileUpgrade{
Config: txallowlist.NewDisableConfig(&deactivateAllowListTime),
}
genesis.Config.PrecompileUpgrades = []params.PrecompileUpgrade{
activateTxAllowListConfig,
deactivateTxAllowListConfig,
}
genBlocks := 10
signer := types.HomesteadSigner{}
txHashes := make([]common.Hash, genBlocks)
backend := newTestBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) {
// Transfer from account[0] to account[1]
// value: 1000 wei
// fee: 0 wei
tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[0].key)
b.AddTx(tx)
txHashes[i] = tx.Hash()
})
defer backend.chain.Stop()
api := NewAPI(backend)
testSuite := []struct {
blockNumber rpc.BlockNumber
config *TraceConfig
want string
expectErr error
}{
// Trace head block
{
blockNumber: rpc.BlockNumber(genBlocks),
want: fmt.Sprintf(`[{"txHash":"%v","result":{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}}]`, txHashes[genBlocks-1]),
},
// Trace block before activation
{
blockNumber: rpc.BlockNumber(activateAllowlistBlock - 1),
want: fmt.Sprintf(`[{"txHash":"%v","result":{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}}]`, txHashes[activateAllowlistBlock-2]),
},
// Trace block activation
{
blockNumber: rpc.BlockNumber(activateAllowlistBlock),
want: fmt.Sprintf(`[{"txHash":"%v","result":{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}}]`, txHashes[activateAllowlistBlock-1]),
},
// Trace block after activation
{
blockNumber: rpc.BlockNumber(activateAllowlistBlock + 1),
want: fmt.Sprintf(`[{"txHash":"%v","result":{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}}]`, txHashes[activateAllowlistBlock]),
},
// Trace block deactivation
{
blockNumber: rpc.BlockNumber(deactivateAllowlistBlock),
want: fmt.Sprintf(`[{"txHash":"%v","result":{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}}]`, txHashes[deactivateAllowlistBlock-1]),
},
// Trace block after deactivation
{
blockNumber: rpc.BlockNumber(deactivateAllowlistBlock + 1),
want: fmt.Sprintf(`[{"txHash":"%v","result":{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}}]`, txHashes[deactivateAllowlistBlock]),
},
}
for i, tc := range testSuite {
result, err := api.TraceBlockByNumber(context.Background(), tc.blockNumber, tc.config)
if tc.expectErr != nil {
if err == nil {
t.Errorf("test %d, want error %v", i, tc.expectErr)
continue
}
if !reflect.DeepEqual(err, tc.expectErr) {
t.Errorf("test %d: error mismatch, want %v, get %v", i, tc.expectErr, err)
}
continue
}
if err != nil {
t.Errorf("test %d, want no error, have %v", i, err)
continue
}
have, _ := json.Marshal(result)
want := tc.want
if string(have) != want {
t.Errorf("test %d, result mismatch, have\n%v\n, want\n%v\n", i, string(have), want)
}
}
}
func TestTraceTransactionPrecompileActivation(t *testing.T) {
t.Parallel()
// Initialize test accounts
accounts := newAccounts(3)
copyConfig := *params.TestChainConfig
genesis := &core.Genesis{
Config: &copyConfig,
Alloc: core.GenesisAlloc{
accounts[0].addr: {Balance: big.NewInt(params.Ether)},
accounts[1].addr: {Balance: big.NewInt(params.Ether)},
accounts[2].addr: {Balance: big.NewInt(params.Ether)},
},
}
activateAllowlistBlock := uint64(2)
// assumes gap is 10 sec
activateAllowListTime := activateAllowlistBlock * 10
activateTxAllowListConfig := params.PrecompileUpgrade{
Config: txallowlist.NewConfig(&activateAllowListTime, []common.Address{accounts[0].addr}, nil, nil),
}
deactivateAllowlistBlock := activateAllowlistBlock + 2
deactivateAllowListTime := deactivateAllowlistBlock * 10
deactivateTxAllowListConfig := params.PrecompileUpgrade{
Config: txallowlist.NewDisableConfig(&deactivateAllowListTime),
}
genesis.Config.PrecompileUpgrades = []params.PrecompileUpgrade{
activateTxAllowListConfig,
deactivateTxAllowListConfig,
}
signer := types.HomesteadSigner{}
blockNoTxMap := make(map[uint64]common.Hash)
backend := newTestBackend(t, 5, genesis, func(i int, b *core.BlockGen) {
// Transfer from account[0] to account[1]
// value: 1000 wei
// fee: 0 wei
tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, new(big.Int).Add(b.BaseFee(), big.NewInt(int64(500*params.GWei))), nil), signer, accounts[0].key)
b.AddTx(tx)
blockNoTxMap[uint64(i)] = tx.Hash()
})
defer backend.chain.Stop()
api := NewAPI(backend)
for i, target := range blockNoTxMap {
t.Run(fmt.Sprintf("blockNumber %d", i), func(t *testing.T) {
result, err := api.TraceTransaction(context.Background(), target, nil)
require := require.New(t)
require.NoError(err)
var have *logger.ExecutionResult
err = json.Unmarshal(result.(json.RawMessage), &have)
require.NoError(err)
expected := &logger.ExecutionResult{
Gas: params.TxGas,
Failed: false,
ReturnValue: "",
StructLogs: []logger.StructLogRes{},
}
eq := reflect.DeepEqual(have, expected)
require.True(eq, "have %v, want %v", have, expected)
})
}
}
func TestTraceChainPrecompileActivation(t *testing.T) {
// Initialize test accounts
// Note: the balances in this test have been increased compared to go-ethereum.
accounts := newAccounts(3)
copyConfig := *params.TestChainConfig
genesis := &core.Genesis{
Config: &copyConfig,
Alloc: core.GenesisAlloc{
accounts[0].addr: {Balance: big.NewInt(5 * params.Ether)},
accounts[1].addr: {Balance: big.NewInt(5 * params.Ether)},
accounts[2].addr: {Balance: big.NewInt(5 * params.Ether)},
},
}
activateAllowlistBlock := uint64(20)
// assumes gap is 10 sec
activateAllowListTime := activateAllowlistBlock * 10
activateTxAllowListConfig := params.PrecompileUpgrade{
Config: txallowlist.NewConfig(&activateAllowListTime, []common.Address{accounts[0].addr}, nil, nil),
}
deactivateAllowlistBlock := activateAllowlistBlock + 10
deactivateAllowListTime := deactivateAllowlistBlock * 10
deactivateTxAllowListConfig := params.PrecompileUpgrade{
Config: txallowlist.NewDisableConfig(&deactivateAllowListTime),
}
genesis.Config.PrecompileUpgrades = []params.PrecompileUpgrade{
activateTxAllowListConfig,
deactivateTxAllowListConfig,
}
genBlocks := 50
signer := types.HomesteadSigner{}
var (
ref atomic.Uint32 // total refs has made
rel atomic.Uint32 // total rels has made
nonce uint64
)
backend := newTestBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) {
// Transfer from account[0] to account[1]
// value: 1000 wei
// fee: 0 wei
for j := 0; j < i+1; j++ {
tx, _ := types.SignTx(types.NewTransaction(nonce, accounts[1].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[0].key)
b.AddTx(tx)
nonce += 1
}
})
backend.refHook = func() { ref.Add(1) }
backend.relHook = func() { rel.Add(1) }
api := NewAPI(backend)
single := `{"txHash":"0x0000000000000000000000000000000000000000000000000000000000000000","result":{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}}`
cases := []struct {
start uint64
end uint64
config *TraceConfig
}{
{0, 50, nil}, // the entire chain range, blocks [1, 50]
{10, 20, nil}, // the middle chain range, blocks [11, 20]
}
for _, c := range cases {
ref.Store(0)
rel.Store(0)
from, _ := api.blockByNumber(context.Background(), rpc.BlockNumber(c.start))
to, _ := api.blockByNumber(context.Background(), rpc.BlockNumber(c.end))
resCh := api.traceChain(from, to, c.config, nil)
next := c.start + 1
for result := range resCh {
if have, want := uint64(result.Block), next; have != want {
t.Fatalf("unexpected tracing block, have %d want %d", have, want)
}
if have, want := len(result.Traces), int(next); have != want {
t.Fatalf("unexpected result length, have %d want %d", have, want)
}
for _, trace := range result.Traces {
trace.TxHash = common.Hash{}
blob, _ := json.Marshal(trace)
if have, want := string(blob), single; have != want {
t.Fatalf("unexpected tracing result, have\n%v\nwant:\n%v", have, want)
}
}
next += 1
}
if next != c.end+1 {
t.Error("Missing tracing block")
}
if nref, nrel := ref.Load(), rel.Load(); nref != nrel {
t.Errorf("Ref and deref actions are not equal, ref %d rel %d", nref, nrel)
}
}
}
func TestTraceCallWithOverridesStateUpgrade(t *testing.T) {
t.Parallel()
// Initialize test accounts
accounts := newAccounts(3)
copyConfig := *params.TestChainConfig
genesis := &core.Genesis{
Config: &copyConfig,
Alloc: core.GenesisAlloc{
accounts[0].addr: {Balance: big.NewInt(5 * params.Ether)},
accounts[1].addr: {Balance: big.NewInt(5 * params.Ether)},
accounts[2].addr: {Balance: big.NewInt(5 * params.Ether)},
},
}
activateStateUpgradeBlock := uint64(2)
// assumes gap is 10 sec
activateStateUpgradeTime := activateStateUpgradeBlock * 10
activateStateUpgradeConfig := params.StateUpgrade{
BlockTimestamp: &activateStateUpgradeTime,
StateUpgradeAccounts: map[common.Address]params.StateUpgradeAccount{
accounts[2].addr: {
// deplete all balance
BalanceChange: (*math.HexOrDecimal256)(new(big.Int).Neg(big.NewInt(5 * params.Ether))),
},
},
}
genesis.Config.StateUpgrades = []params.StateUpgrade{
activateStateUpgradeConfig,
}
genBlocks := 3
// assumes gap is 10 sec
signer := types.HomesteadSigner{}
fastForwardTime := activateStateUpgradeTime + 10
backend := newTestBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) {
// Transfer from account[0] to account[1]
// value: 1000 wei
// fee: 0 wei
tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[0].key)
b.AddTx(tx)
})
defer backend.teardown()
api := NewAPI(backend)
testSuite := []struct {
blockNumber rpc.BlockNumber
call ethapi.TransactionArgs
config *TraceCallConfig
expectErr error
expect string
}{
{
blockNumber: rpc.BlockNumber(0),
call: ethapi.TransactionArgs{
From: &accounts[0].addr,
To: &accounts[1].addr,
Value: (*hexutil.Big)(big.NewInt(1000)),
},
config: nil,
expectErr: nil,
expect: `{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}`,
},
{
blockNumber: rpc.BlockNumber(activateStateUpgradeBlock - 1),
call: ethapi.TransactionArgs{
From: &accounts[2].addr,
To: &accounts[1].addr,
Value: (*hexutil.Big)(big.NewInt(1000)),
},
config: nil,
expectErr: nil,
expect: `{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}`,
},
{
blockNumber: rpc.BlockNumber(activateStateUpgradeBlock + 1),
call: ethapi.TransactionArgs{
From: &accounts[2].addr,
To: &accounts[1].addr,
Value: (*hexutil.Big)(big.NewInt(1000)),
},
config: nil,
expectErr: core.ErrInsufficientFunds,
expect: `{"gas":21000,"failed":true,"returnValue":"","structLogs":[]}`,
},
{
blockNumber: rpc.BlockNumber(activateStateUpgradeBlock - 1),
call: ethapi.TransactionArgs{
From: &accounts[2].addr,
To: &accounts[1].addr,
Value: (*hexutil.Big)(big.NewInt(1000)),
},
config: &TraceCallConfig{
BlockOverrides: &ethapi.BlockOverrides{
Time: (*hexutil.Uint64)(&fastForwardTime),
},
},
expectErr: core.ErrInsufficientFunds,
expect: `{"gas":21000,"failed":true,"returnValue":"","structLogs":[]}`,
},
}
for i, testspec := range testSuite {
result, err := api.TraceCall(context.Background(), testspec.call, rpc.BlockNumberOrHash{BlockNumber: &testspec.blockNumber}, testspec.config)
if testspec.expectErr != nil {
require.ErrorIs(t, err, testspec.expectErr, "test %d", i)
continue
} else {
if err != nil {
t.Errorf("test %d: expect no error, got %v", i, err)
continue
}
var have *logger.ExecutionResult
if err := json.Unmarshal(result.(json.RawMessage), &have); err != nil {
t.Errorf("test %d: failed to unmarshal result %v", i, err)
}
var want *logger.ExecutionResult
if err := json.Unmarshal([]byte(testspec.expect), &want); err != nil {
t.Errorf("test %d: failed to unmarshal result %v", i, err)
}
if !reflect.DeepEqual(have, want) {
t.Errorf("test %d: result mismatch, want %v, got %v", i, testspec.expect, string(result.(json.RawMessage)))
}
}
}
}