@ -31,7 +31,6 @@ import (
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rlp"
@ -260,75 +259,121 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) {
}
}
}
}
// TestZeroValueToNotExitCall tests the calltracer(s) on the following:
func TestInternals ( t * testing . T ) {
// Tx to A, A calls B with zero value. B does not already exist.
var (
// Expected: that enter/exit is invoked and the inner call is shown in the result
to = common . HexToAddress ( "0x00000000000000000000000000000000deadbeef" )
func TestZeroValueToNotExitCall ( t * testing . T ) {
origin = common . HexToAddress ( "0x00000000000000000000000000000000feed" )
var to = common . HexToAddress ( "0x00000000000000000000000000000000deadbeef" )
txContext = vm . TxContext {
privkey , err := crypto . HexToECDSA ( "0000000000000000deadbeef00000000000000000000000000000000deadbeef" )
Origin : origin ,
if err != nil {
GasPrice : big . NewInt ( 1 ) ,
t . Fatalf ( "err %v" , err )
}
}
context = vm . BlockContext {
signer := types . NewEIP155Signer ( big . NewInt ( 1 ) )
CanTransfer : core . CanTransfer ,
tx , err := types . SignNewTx ( privkey , signer , & types . LegacyTx {
Transfer : core . Transfer ,
GasPrice : big . NewInt ( 0 ) ,
Coinbase : common . Address { } ,
Gas : 50000 ,
BlockNumber : new ( big . Int ) . SetUint64 ( 8000000 ) ,
To : & to ,
Time : 5 ,
} )
Difficulty : big . NewInt ( 0x30000 ) ,
if err != nil {
GasLimit : uint64 ( 6000000 ) ,
t . Fatalf ( "err %v" , err )
}
}
)
origin , _ := signer . Sender ( tx )
mkTracer := func ( name string , cfg json . RawMessage ) tracers . Tracer {
txContext := vm . TxContext {
tr , err := tracers . DefaultDirectory . New ( name , nil , cfg )
Origin : origin ,
if err != nil {
GasPrice : big . NewInt ( 1 ) ,
t . Fatalf ( "failed to create call tracer: %v" , err )
}
}
context := vm . BlockContext {
return tr
CanTransfer : core . CanTransfer ,
Transfer : core . Transfer ,
Coinbase : common . Address { } ,
BlockNumber : new ( big . Int ) . SetUint64 ( 8000000 ) ,
Time : 5 ,
Difficulty : big . NewInt ( 0x30000 ) ,
GasLimit : uint64 ( 6000000 ) ,
}
var code = [ ] byte {
byte ( vm . PUSH1 ) , 0x0 , byte ( vm . DUP1 ) , byte ( vm . DUP1 ) , byte ( vm . DUP1 ) , // in and outs zero
byte ( vm . DUP1 ) , byte ( vm . PUSH1 ) , 0xff , byte ( vm . GAS ) , // value=0,address=0xff, gas=GAS
byte ( vm . CALL ) ,
}
}
var alloc = core . GenesisAlloc {
to : core . GenesisAccount {
for _ , tc := range [ ] struct {
Nonce : 1 ,
name string
Code : code ,
code [ ] byte
tracer tracers . Tracer
want string
} {
{
// TestZeroValueToNotExitCall tests the calltracer(s) on the following:
// Tx to A, A calls B with zero value. B does not already exist.
// Expected: that enter/exit is invoked and the inner call is shown in the result
name : "ZeroValueToNotExitCall" ,
code : [ ] byte {
byte ( vm . PUSH1 ) , 0x0 , byte ( vm . DUP1 ) , byte ( vm . DUP1 ) , byte ( vm . DUP1 ) , // in and outs zero
byte ( vm . DUP1 ) , byte ( vm . PUSH1 ) , 0xff , byte ( vm . GAS ) , // value=0,address=0xff, gas=GAS
byte ( vm . CALL ) ,
} ,
tracer : mkTracer ( "callTracer" , nil ) ,
want : ` { "from":"0x000000000000000000000000000000000000feed","gas":"0x7148","gasUsed":"0x54d8","to":"0x00000000000000000000000000000000deadbeef","input":"0x","calls":[ { "from":"0x00000000000000000000000000000000deadbeef","gas":"0x6cbf","gasUsed":"0x0","to":"0x00000000000000000000000000000000000000ff","input":"0x","value":"0x0","type":"CALL"}],"value":"0x0","type":"CALL"} ` ,
} ,
} ,
origin : core . GenesisAccount {
{
Nonce : 0 ,
name : "Stack depletion in LOG0" ,
Balance : big . NewInt ( 500000000000000 ) ,
code : [ ] byte { byte ( vm . LOG3 ) } ,
tracer : mkTracer ( "callTracer" , json . RawMessage ( ` { "withLog": true } ` ) ) ,
want : ` { "from":"0x000000000000000000000000000000000000feed","gas":"0x7148","gasUsed":"0xc350","to":"0x00000000000000000000000000000000deadbeef","input":"0x","error":"stack underflow (0 \u003c=\u003e 5)","value":"0x0","type":"CALL"} ` ,
} ,
} ,
}
{
_ , statedb := tests . MakePreState ( rawdb . NewMemoryDatabase ( ) , alloc , false )
name : "Mem expansion in LOG0" ,
// Create the tracer, the EVM environment and run it
code : [ ] byte {
tracer , err := tracers . DefaultDirectory . New ( "callTracer" , nil , nil )
byte ( vm . PUSH1 ) , 0x1 ,
if err != nil {
byte ( vm . PUSH1 ) , 0x0 ,
t . Fatalf ( "failed to create call tracer: %v" , err )
byte ( vm . MSTORE ) ,
}
byte ( vm . PUSH1 ) , 0xff ,
evm := vm . NewEVM ( context , txContext , statedb , params . MainnetChainConfig , vm . Config { Debug : true , Tracer : tracer } )
byte ( vm . PUSH1 ) , 0x0 ,
msg , err := core . TransactionToMessage ( tx , signer , nil )
byte ( vm . LOG0 ) ,
if err != nil {
} ,
t . Fatalf ( "failed to prepare transaction for tracing: %v" , err )
tracer : mkTracer ( "callTracer" , json . RawMessage ( ` { "withLog": true } ` ) ) ,
}
want : ` { "from":"0x000000000000000000000000000000000000feed","gas":"0x7148","gasUsed":"0x5b9e","to":"0x00000000000000000000000000000000deadbeef","input":"0x","logs":[ { "address":"0x00000000000000000000000000000000deadbeef","topics":[],"data":"0x000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}],"value":"0x0","type":"CALL"} ` ,
st := core . NewStateTransition ( evm , msg , new ( core . GasPool ) . AddGas ( tx . Gas ( ) ) )
} ,
if _ , err = st . TransitionDb ( ) ; err != nil {
{
t . Fatalf ( "failed to execute transaction: %v" , err )
// Leads to OOM on the prestate tracer
}
name : "Prestate-tracer - mem expansion in CREATE2" ,
// Retrieve the trace result and compare against the etalon
code : [ ] byte {
res , err := tracer . GetResult ( )
byte ( vm . PUSH1 ) , 0x1 ,
if err != nil {
byte ( vm . PUSH1 ) , 0x0 ,
t . Fatalf ( "failed to retrieve trace result: %v" , err )
byte ( vm . MSTORE ) ,
}
byte ( vm . PUSH1 ) , 0x1 ,
wantStr := ` { "from":"0x682a80a6f560eec50d54e63cbeda1c324c5f8d1b","gas":"0x7148","gasUsed":"0x54d8","to":"0x00000000000000000000000000000000deadbeef","input":"0x","calls":[ { "from":"0x00000000000000000000000000000000deadbeef","gas":"0x6cbf","gasUsed":"0x0","to":"0x00000000000000000000000000000000000000ff","input":"0x","value":"0x0","type":"CALL"}],"value":"0x0","type":"CALL"} `
byte ( vm . PUSH5 ) , 0xff , 0xff , 0xff , 0xff , 0xff ,
if string ( res ) != wantStr {
byte ( vm . PUSH1 ) , 0x1 ,
t . Fatalf ( "trace mismatch\n have: %v\n want: %v\n" , string ( res ) , wantStr )
byte ( vm . PUSH1 ) , 0x0 ,
byte ( vm . CREATE2 ) ,
byte ( vm . PUSH1 ) , 0xff ,
byte ( vm . PUSH1 ) , 0x0 ,
byte ( vm . LOG0 ) ,
} ,
tracer : mkTracer ( "prestateTracer" , json . RawMessage ( ` { "withLog": true } ` ) ) ,
want : ` { "0x0000000000000000000000000000000000000000": { "balance":"0x0"},"0x000000000000000000000000000000000000feed": { "balance":"0x1c6bf52640350"},"0x00000000000000000000000000000000deadbeef": { "balance":"0x0","code":"0x6001600052600164ffffffffff60016000f560ff6000a0"}} ` ,
} ,
} {
_ , statedb := tests . MakePreState ( rawdb . NewMemoryDatabase ( ) ,
core . GenesisAlloc {
to : core . GenesisAccount {
Code : tc . code ,
} ,
origin : core . GenesisAccount {
Balance : big . NewInt ( 500000000000000 ) ,
} ,
} , false )
evm := vm . NewEVM ( context , txContext , statedb , params . MainnetChainConfig , vm . Config { Debug : true , Tracer : tc . tracer } )
msg := & core . Message {
To : & to ,
From : origin ,
Value : big . NewInt ( 0 ) ,
GasLimit : 50000 ,
GasPrice : big . NewInt ( 0 ) ,
GasFeeCap : big . NewInt ( 0 ) ,
GasTipCap : big . NewInt ( 0 ) ,
SkipAccountChecks : false ,
}
st := core . NewStateTransition ( evm , msg , new ( core . GasPool ) . AddGas ( msg . GasLimit ) )
if _ , err := st . TransitionDb ( ) ; err != nil {
t . Fatalf ( "test %v: failed to execute transaction: %v" , tc . name , err )
}
// Retrieve the trace result and compare against the expected
res , err := tc . tracer . GetResult ( )
if err != nil {
t . Fatalf ( "test %v: failed to retrieve trace result: %v" , tc . name , err )
}
if string ( res ) != tc . want {
t . Fatalf ( "test %v: trace mismatch\n have: %v\n want: %v\n" , tc . name , string ( res ) , tc . want )
}
}
}
}
}