mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-18 09:53:48 +00:00
eth, tests: prestate tracer, call test suite, rewinding
This commit is contained in:
parent
becec012ef
commit
4aecb6c273
25 changed files with 1495 additions and 230 deletions
|
|
@ -368,7 +368,7 @@ type storageEntry struct {
|
||||||
|
|
||||||
// StorageRangeAt returns the storage at the given block height and transaction index.
|
// StorageRangeAt returns the storage at the given block height and transaction index.
|
||||||
func (api *PrivateDebugAPI) StorageRangeAt(ctx context.Context, blockHash common.Hash, txIndex int, contractAddress common.Address, keyStart hexutil.Bytes, maxResult int) (StorageRangeResult, error) {
|
func (api *PrivateDebugAPI) StorageRangeAt(ctx context.Context, blockHash common.Hash, txIndex int, contractAddress common.Address, keyStart hexutil.Bytes, maxResult int) (StorageRangeResult, error) {
|
||||||
_, _, statedb, err := api.computeTxEnv(blockHash, txIndex)
|
_, _, statedb, err := api.computeTxEnv(blockHash, txIndex, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return StorageRangeResult{}, err
|
return StorageRangeResult{}, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -191,6 +191,12 @@ func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Bl
|
||||||
diskdb: api.eth.ChainDb(),
|
diskdb: api.eth.ChainDb(),
|
||||||
memdb: memdb,
|
memdb: memdb,
|
||||||
}
|
}
|
||||||
|
if number := start.NumberU64(); number > 0 {
|
||||||
|
start = api.eth.blockchain.GetBlock(start.ParentHash(), start.NumberU64()-1)
|
||||||
|
if start == nil {
|
||||||
|
return nil, fmt.Errorf("parent block #%d not found", number-1)
|
||||||
|
}
|
||||||
|
}
|
||||||
statedb, err := state.New(start.Root(), state.NewDatabase(db))
|
statedb, err := state.New(start.Root(), state.NewDatabase(db))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// If the starting state is missing, allow some number of blocks to be rewound
|
// If the starting state is missing, allow some number of blocks to be rewound
|
||||||
|
|
@ -210,7 +216,12 @@ func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Bl
|
||||||
}
|
}
|
||||||
// If we still don't have the state available, bail out
|
// If we still don't have the state available, bail out
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
switch err.(type) {
|
||||||
|
case *trie.MissingNodeError:
|
||||||
|
return nil, errors.New("required historical state unavailable")
|
||||||
|
default:
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Execute all the transaction contained within the chain concurrently for each block
|
// Execute all the transaction contained within the chain concurrently for each block
|
||||||
|
|
@ -346,10 +357,12 @@ func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Bl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// No more concurrent access at this point, prune the database
|
// No more concurrent access at this point, prune the database
|
||||||
log.Info("Pruning tracer database", "nodes", db.memdb.Len())
|
var (
|
||||||
start := time.Now()
|
nodes = db.memdb.Len()
|
||||||
|
start = time.Now()
|
||||||
|
)
|
||||||
db.Prune(root)
|
db.Prune(root)
|
||||||
log.Info("Tracer database pruned", "nodes", db.memdb.Len(), "elapsed", time.Since(start))
|
log.Info("Pruned tracer state entries", "deleted", nodes-db.memdb.Len(), "left", db.memdb.Len(), "elapsed", time.Since(start))
|
||||||
|
|
||||||
statedb, _ = state.New(root, state.NewDatabase(db))
|
statedb, _ = state.New(root, state.NewDatabase(db))
|
||||||
}
|
}
|
||||||
|
|
@ -448,6 +461,14 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block,
|
||||||
if parent == nil {
|
if parent == nil {
|
||||||
return nil, fmt.Errorf("parent %x not found", block.ParentHash())
|
return nil, fmt.Errorf("parent %x not found", block.ParentHash())
|
||||||
}
|
}
|
||||||
|
rewind := defaultTraceRewind
|
||||||
|
if config.Rewind != nil {
|
||||||
|
rewind = *config.Rewind
|
||||||
|
}
|
||||||
|
statedb, err := api.computeStateDB(parent, rewind)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
// Execute all the transaction contained within the block concurrently
|
// Execute all the transaction contained within the block concurrently
|
||||||
var (
|
var (
|
||||||
signer = types.MakeSigner(api.config, block.Number())
|
signer = types.MakeSigner(api.config, block.Number())
|
||||||
|
|
@ -482,10 +503,6 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block,
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
// Feed the transactions into the tracers and return
|
// Feed the transactions into the tracers and return
|
||||||
statedb, err := api.eth.blockchain.StateAt(parent.Root())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var failed error
|
var failed error
|
||||||
for i, tx := range txs {
|
for i, tx := range txs {
|
||||||
// Send the trace task over for execution
|
// Send the trace task over for execution
|
||||||
|
|
@ -513,6 +530,83 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block,
|
||||||
return results, nil
|
return results, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// computeStateDB retrieves the state database associated with a certain block.
|
||||||
|
// If no state is locally available for the given block, a number of blocks are
|
||||||
|
// attempted to be rewound and replayed to regenerate the desired state.
|
||||||
|
func (api *PrivateDebugAPI) computeStateDB(block *types.Block, rewind uint64) (*state.StateDB, error) {
|
||||||
|
// If we have the state fully available, use that
|
||||||
|
statedb, err := api.eth.blockchain.StateAt(block.Root())
|
||||||
|
if err == nil {
|
||||||
|
return statedb, nil
|
||||||
|
}
|
||||||
|
// Otherwise try to rewind blocks until we find a state or reach our limit
|
||||||
|
origin := block.NumberU64()
|
||||||
|
|
||||||
|
memdb, _ := ethdb.NewMemDatabase()
|
||||||
|
db := &ephemeralDatabase{
|
||||||
|
diskdb: api.eth.ChainDb(),
|
||||||
|
memdb: memdb,
|
||||||
|
}
|
||||||
|
for i := uint64(0); i < rewind; i++ {
|
||||||
|
block = api.eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1)
|
||||||
|
if block == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if statedb, err = state.New(block.Root(), state.NewDatabase(db)); err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
switch err.(type) {
|
||||||
|
case *trie.MissingNodeError:
|
||||||
|
return nil, errors.New("required historical state unavailable")
|
||||||
|
default:
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// State was available at historical point, regenerate
|
||||||
|
var (
|
||||||
|
start = time.Now()
|
||||||
|
logged time.Time
|
||||||
|
)
|
||||||
|
for block.NumberU64() < origin {
|
||||||
|
// Print progress logs if long enough time elapsed
|
||||||
|
if time.Since(logged) > 8*time.Second {
|
||||||
|
log.Info("Regenerating historical state", "block", block.NumberU64()+1, "target", origin, "elapsed", time.Since(start))
|
||||||
|
logged = time.Now()
|
||||||
|
}
|
||||||
|
// Retrieve the next block to regenerate and process it
|
||||||
|
if block = api.eth.blockchain.GetBlockByNumber(block.NumberU64() + 1); block == nil {
|
||||||
|
return nil, fmt.Errorf("block #%d not found", block.NumberU64()+1)
|
||||||
|
}
|
||||||
|
_, _, _, err := api.eth.blockchain.Processor().Process(block, statedb, vm.Config{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Finalize the state so any modifications are written to the trie
|
||||||
|
root, err := statedb.CommitTo(db, true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := statedb.Reset(root); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// After every N blocks, prune the database to only retain relevant data
|
||||||
|
if block.NumberU64()%4096 == 0 || block.NumberU64() == origin {
|
||||||
|
var (
|
||||||
|
nodes = db.memdb.Len()
|
||||||
|
begin = time.Now()
|
||||||
|
)
|
||||||
|
db.Prune(root)
|
||||||
|
log.Info("Pruned tracer state entries", "deleted", nodes-db.memdb.Len(), "left", db.memdb.Len(), "elapsed", time.Since(begin))
|
||||||
|
|
||||||
|
statedb, _ = state.New(root, state.NewDatabase(db))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Info("Historical state regenerated", "block", block.NumberU64(), "elapsed", time.Since(start))
|
||||||
|
return statedb, nil
|
||||||
|
}
|
||||||
|
|
||||||
// TraceTransaction returns the structured logs created during the execution of EVM
|
// TraceTransaction returns the structured logs created during the execution of EVM
|
||||||
// and returns them as a JSON object.
|
// and returns them as a JSON object.
|
||||||
func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, hash common.Hash, config *TraceConfig) (interface{}, error) {
|
func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, hash common.Hash, config *TraceConfig) (interface{}, error) {
|
||||||
|
|
@ -521,7 +615,11 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, hash common.Ha
|
||||||
if tx == nil {
|
if tx == nil {
|
||||||
return nil, fmt.Errorf("transaction %x not found", hash)
|
return nil, fmt.Errorf("transaction %x not found", hash)
|
||||||
}
|
}
|
||||||
msg, vmctx, statedb, err := api.computeTxEnv(blockHash, int(index))
|
rewind := defaultTraceRewind
|
||||||
|
if config.Rewind != nil {
|
||||||
|
rewind = *config.Rewind
|
||||||
|
}
|
||||||
|
msg, vmctx, statedb, err := api.computeTxEnv(blockHash, int(index), rewind)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -548,17 +646,14 @@ func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Constuct the JavaScript tracer to execute with
|
// Constuct the JavaScript tracer to execute with
|
||||||
if tracer, ok := tracers.Tracer(*config.Tracer); ok {
|
if tracer, err = tracers.New(*config.Tracer); err != nil {
|
||||||
*config.Tracer = tracer
|
|
||||||
}
|
|
||||||
if tracer, err = ethapi.NewJavascriptTracer(*config.Tracer); err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Handle timeouts and RPC cancellations
|
// Handle timeouts and RPC cancellations
|
||||||
deadlineCtx, cancel := context.WithTimeout(ctx, timeout)
|
deadlineCtx, cancel := context.WithTimeout(ctx, timeout)
|
||||||
go func() {
|
go func() {
|
||||||
<-deadlineCtx.Done()
|
<-deadlineCtx.Done()
|
||||||
tracer.(*ethapi.JavascriptTracer).Stop(errors.New("execution timeout"))
|
tracer.(*tracers.Tracer).Stop(errors.New("execution timeout"))
|
||||||
}()
|
}()
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
|
|
@ -585,7 +680,7 @@ func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, v
|
||||||
StructLogs: ethapi.FormatLogs(tracer.StructLogs()),
|
StructLogs: ethapi.FormatLogs(tracer.StructLogs()),
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
case *ethapi.JavascriptTracer:
|
case *tracers.Tracer:
|
||||||
return tracer.GetResult()
|
return tracer.GetResult()
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
@ -594,7 +689,7 @@ func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, v
|
||||||
}
|
}
|
||||||
|
|
||||||
// computeTxEnv returns the execution environment of a certain transaction.
|
// computeTxEnv returns the execution environment of a certain transaction.
|
||||||
func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int) (core.Message, vm.Context, *state.StateDB, error) {
|
func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int, rewind uint64) (core.Message, vm.Context, *state.StateDB, error) {
|
||||||
// Create the parent state database
|
// Create the parent state database
|
||||||
block := api.eth.blockchain.GetBlockByHash(blockHash)
|
block := api.eth.blockchain.GetBlockByHash(blockHash)
|
||||||
if block == nil {
|
if block == nil {
|
||||||
|
|
@ -604,7 +699,7 @@ func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int) (co
|
||||||
if parent == nil {
|
if parent == nil {
|
||||||
return nil, vm.Context{}, nil, fmt.Errorf("parent %x not found", block.ParentHash())
|
return nil, vm.Context{}, nil, fmt.Errorf("parent %x not found", block.ParentHash())
|
||||||
}
|
}
|
||||||
statedb, err := api.eth.blockchain.StateAt(parent.Root())
|
statedb, err := api.computeStateDB(parent, rewind)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, vm.Context{}, nil, err
|
return nil, vm.Context{}, nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -747,10 +747,11 @@ func (self *ProtocolManager) txBroadcastLoop() {
|
||||||
// EthNodeInfo represents a short summary of the Ethereum sub-protocol metadata known
|
// EthNodeInfo represents a short summary of the Ethereum sub-protocol metadata known
|
||||||
// about the host peer.
|
// about the host peer.
|
||||||
type EthNodeInfo struct {
|
type EthNodeInfo struct {
|
||||||
Network uint64 `json:"network"` // Ethereum network ID (1=Frontier, 2=Morden, Ropsten=3)
|
Network uint64 `json:"network"` // Ethereum network ID (1=Frontier, 2=Morden, Ropsten=3)
|
||||||
Difficulty *big.Int `json:"difficulty"` // Total difficulty of the host's blockchain
|
Difficulty *big.Int `json:"difficulty"` // Total difficulty of the host's blockchain
|
||||||
Genesis common.Hash `json:"genesis"` // SHA3 hash of the host's genesis block
|
Genesis common.Hash `json:"genesis"` // SHA3 hash of the host's genesis block
|
||||||
Head common.Hash `json:"head"` // SHA3 hash of the host's best owned block
|
Config *params.ChainConfig `json:"config"` // Chain configuration for the fork rules
|
||||||
|
Head common.Hash `json:"head"` // SHA3 hash of the host's best owned block
|
||||||
}
|
}
|
||||||
|
|
||||||
// NodeInfo retrieves some protocol metadata about the running host node.
|
// NodeInfo retrieves some protocol metadata about the running host node.
|
||||||
|
|
@ -760,6 +761,7 @@ func (self *ProtocolManager) NodeInfo() *EthNodeInfo {
|
||||||
Network: self.networkId,
|
Network: self.networkId,
|
||||||
Difficulty: self.blockchain.GetTd(currentBlock.Hash(), currentBlock.NumberU64()),
|
Difficulty: self.blockchain.GetTd(currentBlock.Hash(), currentBlock.NumberU64()),
|
||||||
Genesis: self.blockchain.Genesis().Hash(),
|
Genesis: self.blockchain.Genesis().Hash(),
|
||||||
|
Config: self.blockchain.Config(),
|
||||||
Head: currentBlock.Hash(),
|
Head: currentBlock.Hash(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -172,7 +172,7 @@
|
||||||
|
|
||||||
// result is invoked when all the opcodes have been iterated over and returns
|
// result is invoked when all the opcodes have been iterated over and returns
|
||||||
// the final result of the tracing.
|
// the final result of the tracing.
|
||||||
result: function(ctx) {
|
result: function(ctx, db) {
|
||||||
var result = {
|
var result = {
|
||||||
type: ctx.type,
|
type: ctx.type,
|
||||||
from: toHex(ctx.from),
|
from: toHex(ctx.from),
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/**
|
/**
|
||||||
This tracer returns sufficent information from a trace to
|
This tracer returns sufficent information from a trace to
|
||||||
perform evmdis-style disassembly
|
perform evmdis-style disassembly
|
||||||
|
|
||||||
@author Nick Johnson, Martin Swende
|
@author Nick Johnson, Martin Swende
|
||||||
|
|
|
||||||
|
|
@ -1,131 +0,0 @@
|
||||||
/**
|
|
||||||
This tracer-script outputs sufficient information to create a local
|
|
||||||
execution of the transaction.
|
|
||||||
|
|
||||||
@author Martin Holst Swende
|
|
||||||
**/
|
|
||||||
{
|
|
||||||
//The genesis that we're building
|
|
||||||
prestate : null,
|
|
||||||
|
|
||||||
lookupAccount: function(acc,db){
|
|
||||||
var addr = toHex(acc);
|
|
||||||
if(this.prestate[addr]) return;
|
|
||||||
|
|
||||||
this.prestate[addr] = {
|
|
||||||
'balance': db.getBalance(acc),
|
|
||||||
'nonce' : db.getNonce(acc),
|
|
||||||
'code' : toHex(db.getCode(acc)),
|
|
||||||
'storage' : {}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
lookupStorage: function(addr, index, db){
|
|
||||||
//No overwrite
|
|
||||||
if (!this.prestate[addr]['storage'][index]){
|
|
||||||
this.prestate[addr]['storage'][index] = db.getState(addr,key);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
result: function(ctx,db) {
|
|
||||||
// At this point, we need to deduct the 'value' from the
|
|
||||||
// outer transaction, and move it back to the origin
|
|
||||||
var from = toAddress(ctx.from);
|
|
||||||
var to = toAddress(ctx.to);
|
|
||||||
var v = ctx.value;
|
|
||||||
this.lookupAccount(from, db);
|
|
||||||
|
|
||||||
// Add back value to sender (bigints)
|
|
||||||
var from_bal = this.prestate[toHex(from)]['balance'];
|
|
||||||
var to_bal = this.prestate[toHex(to)]['balance'];
|
|
||||||
to_bal.Sub(to_bal, v);
|
|
||||||
from_bal.Add(from_bal, v);
|
|
||||||
|
|
||||||
// Set back nonce (uint64)
|
|
||||||
this.prestate[toHex(from)]['nonce']--;
|
|
||||||
|
|
||||||
//Note, the sender-account still may not be 100% correct,
|
|
||||||
// since we haven't accounted for the gas.
|
|
||||||
//Homestead
|
|
||||||
config = {
|
|
||||||
"eip150Block": 2000,
|
|
||||||
"eip158Block": 2000,
|
|
||||||
"eip155Block": 2000,
|
|
||||||
"homesteadBlock": 0,
|
|
||||||
"daoForkBlock": 0,
|
|
||||||
"byzantiumBlock" : 2000,
|
|
||||||
}
|
|
||||||
var blnum = ctx['blocknumber']
|
|
||||||
if (blnum > 2463000){
|
|
||||||
// Tangerine: disable
|
|
||||||
config["eip150Block"] = 0;
|
|
||||||
}
|
|
||||||
if (blnum > 2675000){
|
|
||||||
// Spurious
|
|
||||||
config["eip155Block"] = 0;
|
|
||||||
config["eip158Block"] = 0;
|
|
||||||
}
|
|
||||||
if (blnum > 4370000){
|
|
||||||
//Byzantium
|
|
||||||
config["byzantiumBlock"] = 0;
|
|
||||||
}
|
|
||||||
genesis ={
|
|
||||||
nonce: "0x0000000000000000",
|
|
||||||
difficulty: "0x020000",
|
|
||||||
mixhash: "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
coinbase: "0x0000000000000000000000000000000000000000",
|
|
||||||
timestamp: "0x00",
|
|
||||||
number: "0x00",
|
|
||||||
parentHash: "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
extraData: "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
gasLimit: toHex(ctx['gas'].Bytes()),
|
|
||||||
alloc: this.prestate,
|
|
||||||
config: config,
|
|
||||||
};
|
|
||||||
|
|
||||||
return genesis;
|
|
||||||
},
|
|
||||||
|
|
||||||
step: function(log, db) {
|
|
||||||
|
|
||||||
if (!this.prestate){
|
|
||||||
// Add the current account
|
|
||||||
this.prestate = {};
|
|
||||||
// Balance will potentially be wrong here,
|
|
||||||
// since this will include the value sent
|
|
||||||
// along with the message
|
|
||||||
// We fix that in 'result()'
|
|
||||||
this.lookupAccount(log.account,db);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(log.op.toString()){
|
|
||||||
case "EXTCODECOPY":
|
|
||||||
case "EXTCODESIZE":
|
|
||||||
case "BALANCE":
|
|
||||||
var addr = log.peek(0).Text(16);
|
|
||||||
this.lookupAccount(addr,db);
|
|
||||||
break;
|
|
||||||
case "CALL":
|
|
||||||
case "CALLCODE":
|
|
||||||
case "DELEGATECALL":
|
|
||||||
case "STATICCALL":
|
|
||||||
var addr = log.stack.peek(1).Text(16);
|
|
||||||
this.lookupAccount(addr,db);
|
|
||||||
break;
|
|
||||||
case 'SSTORE':
|
|
||||||
case 'SLOAD':
|
|
||||||
var key = log.stack.peek(0).Text(16);
|
|
||||||
this.lookupStorage(log.account.Text(16), key,db);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/** TODO
|
|
||||||
|
|
||||||
Missing BLOCKNUM
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Somehow get 'origin'-account in there.
|
|
||||||
**/
|
|
||||||
|
|
@ -9,5 +9,5 @@
|
||||||
|
|
||||||
// result is invoked when all the opcodes have been iterated over and returns
|
// result is invoked when all the opcodes have been iterated over and returns
|
||||||
// the final result of the tracing.
|
// the final result of the tracing.
|
||||||
result: function(ctx) { }
|
result: function(ctx, db) { }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,5 +12,5 @@
|
||||||
|
|
||||||
// result is invoked when all the opcodes have been iterated over and returns
|
// result is invoked when all the opcodes have been iterated over and returns
|
||||||
// the final result of the tracing.
|
// the final result of the tracing.
|
||||||
result: function(ctx) { return this.count }
|
result: function(ctx, db) { return this.count }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
87
eth/tracers/internal/tracers/prestate_tracer.js
Normal file
87
eth/tracers/internal/tracers/prestate_tracer.js
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
// prestateTracer outputs sufficient information to create a local execution of
|
||||||
|
// the transaction from a custom assembled genesis block.
|
||||||
|
{
|
||||||
|
// prestate is the genesis that we're building.
|
||||||
|
prestate: null,
|
||||||
|
|
||||||
|
// lookupAccount injects the specified account into the prestate object.
|
||||||
|
lookupAccount: function(addr, db){
|
||||||
|
var acc = toHex(addr);
|
||||||
|
if (this.prestate[acc] === undefined) {
|
||||||
|
this.prestate[acc] = {
|
||||||
|
balance: '0x' + db.getBalance(addr).toString(16),
|
||||||
|
nonce: db.getNonce(addr),
|
||||||
|
code: toHex(db.getCode(addr)),
|
||||||
|
storage: {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// lookupStorage injects the specified storage entry of the given account into
|
||||||
|
// the prestate object.
|
||||||
|
lookupStorage: function(addr, key, db){
|
||||||
|
var acc = toHex(addr);
|
||||||
|
var idx = toHex(key);
|
||||||
|
|
||||||
|
if (this.prestate[acc].storage[idx] === undefined) {
|
||||||
|
var val = toHex(db.getState(addr, key));
|
||||||
|
if (val != "0x0000000000000000000000000000000000000000000000000000000000000000") {
|
||||||
|
this.prestate[acc].storage[idx] = toHex(db.getState(addr, key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// result is invoked when all the opcodes have been iterated over and returns
|
||||||
|
// the final result of the tracing.
|
||||||
|
result: function(ctx, db) {
|
||||||
|
// At this point, we need to deduct the 'value' from the
|
||||||
|
// outer transaction, and move it back to the origin
|
||||||
|
this.lookupAccount(ctx.from, db);
|
||||||
|
|
||||||
|
var fromBal = bigInt(this.prestate[toHex(ctx.from)].balance.slice(2), 16);
|
||||||
|
var toBal = bigInt(this.prestate[toHex(ctx.to)].balance.slice(2), 16);
|
||||||
|
|
||||||
|
this.prestate[toHex(ctx.to)].balance = '0x'+toBal.subtract(ctx.value).toString(16);
|
||||||
|
this.prestate[toHex(ctx.from)].balance = '0x'+fromBal.add(ctx.value).toString(16);
|
||||||
|
|
||||||
|
// Decrement the caller's nonce, and remove empty create targets
|
||||||
|
this.prestate[toHex(ctx.from)].nonce--;
|
||||||
|
if (ctx.type == 'CREATE') {
|
||||||
|
// We can blibdly delete the contract prestate, as any existing state would
|
||||||
|
// have caused the transaction to be rejected as invalid in the first place.
|
||||||
|
delete this.prestate[toHex(ctx.to)];
|
||||||
|
}
|
||||||
|
// Return the assembled allocations (prestate)
|
||||||
|
return this.prestate;
|
||||||
|
},
|
||||||
|
|
||||||
|
// step is invoked for every opcode that the VM executes.
|
||||||
|
step: function(log, db) {
|
||||||
|
// Add the current account if we just started tracing
|
||||||
|
if (this.prestate === null){
|
||||||
|
this.prestate = {};
|
||||||
|
// Balance will potentially be wrong here, since this will include the value
|
||||||
|
// sent along with the message. We fix that in 'result()'.
|
||||||
|
this.lookupAccount(log.contract.getAddress(), db);
|
||||||
|
}
|
||||||
|
// Whenever new state is accessed, add it to the prestate
|
||||||
|
switch (log.op.toString()) {
|
||||||
|
case "EXTCODECOPY": case "EXTCODESIZE": case "BALANCE":
|
||||||
|
this.lookupAccount(toAddress(log.stack.peek(0).toString(16)), db);
|
||||||
|
break;
|
||||||
|
case "CREATE":
|
||||||
|
var from = log.contract.getAddress();
|
||||||
|
this.lookupAccount(toContract(from, db.getNonce(from)), db);
|
||||||
|
break;
|
||||||
|
case "CALL": case "CALLCODE": case "DELEGATECALL": case "STATICCALL":
|
||||||
|
this.lookupAccount(toAddress(log.stack.peek(1).toString(16)), db);
|
||||||
|
break;
|
||||||
|
case 'SSTORE':case 'SLOAD':
|
||||||
|
this.lookupStorage(log.contract.getAddress(), toWord(log.stack.peek(0).toString(16)), db);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// fault is invoked when the actual execution of an opcode fails.
|
||||||
|
fault: function(log, db) {}
|
||||||
|
}
|
||||||
58
eth/tracers/testdata/call_tracer_create.json
vendored
Normal file
58
eth/tracers/testdata/call_tracer_create.json
vendored
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
{
|
||||||
|
"context": {
|
||||||
|
"difficulty": "3755480783",
|
||||||
|
"gasLimit": "5401723",
|
||||||
|
"miner": "0xd049bfd667cb46aa3ef5df0da3e57db3be39e511",
|
||||||
|
"number": "2294702",
|
||||||
|
"timestamp": "1513676146"
|
||||||
|
},
|
||||||
|
"genesis": {
|
||||||
|
"alloc": {
|
||||||
|
"0x13e4acefe6a6700604929946e70e6443e4e73447": {
|
||||||
|
"balance": "0xcf3e0938579f000",
|
||||||
|
"code": "0x",
|
||||||
|
"nonce": "9",
|
||||||
|
"storage": {}
|
||||||
|
},
|
||||||
|
"0x7dc9c9730689ff0b0fd506c67db815f12d90a448": {
|
||||||
|
"balance": "0x0",
|
||||||
|
"code": "0x",
|
||||||
|
"nonce": "0",
|
||||||
|
"storage": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"byzantiumBlock": 1700000,
|
||||||
|
"chainId": 3,
|
||||||
|
"daoForkSupport": true,
|
||||||
|
"eip150Block": 0,
|
||||||
|
"eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d",
|
||||||
|
"eip155Block": 10,
|
||||||
|
"eip158Block": 10,
|
||||||
|
"ethash": {},
|
||||||
|
"homesteadBlock": 0
|
||||||
|
},
|
||||||
|
"difficulty": "3757315409",
|
||||||
|
"extraData": "0x566961425443",
|
||||||
|
"gasLimit": "5406414",
|
||||||
|
"hash": "0xae107f592eebdd9ff8d6ba00363676096e6afb0e1007a7d3d0af88173077378d",
|
||||||
|
"miner": "0xd049bfd667cb46aa3ef5df0da3e57db3be39e511",
|
||||||
|
"mixHash": "0xc927aa05a38bc3de864e95c33b3ae559d3f39c4ccd51cef6f113f9c50ba0caf1",
|
||||||
|
"nonce": "0x93363bbd2c95f410",
|
||||||
|
"number": "2294701",
|
||||||
|
"stateRoot": "0x6b6737d5bde8058990483e915866bd1578014baeff57bd5e4ed228a2bfad635c",
|
||||||
|
"timestamp": "1513676127",
|
||||||
|
"totalDifficulty": "7160808139332585"
|
||||||
|
},
|
||||||
|
"input": "0xf907ef098504e3b29200830897be8080b9079c606060405260405160208061077c83398101604052808051906020019091905050600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415151561007d57600080fd5b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506001600460006101000a81548160ff02191690831515021790555050610653806101296000396000f300606060405260043610610083576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806305e4382a146100855780631c02708d146100ae5780632e1a7d4d146100c35780635114cb52146100e6578063a37dda2c146100fe578063ae200e7914610153578063b5769f70146101a8575b005b341561009057600080fd5b6100986101d1565b6040518082815260200191505060405180910390f35b34156100b957600080fd5b6100c16101d7565b005b34156100ce57600080fd5b6100e460048080359060200190919050506102eb565b005b6100fc6004808035906020019091905050610513565b005b341561010957600080fd5b6101116105d6565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561015e57600080fd5b6101666105fc565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156101b357600080fd5b6101bb610621565b6040518082815260200191505060405180910390f35b60025481565b60011515600460009054906101000a900460ff1615151415156101f957600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806102a15750600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b15156102ac57600080fd5b6000600460006101000a81548160ff0219169083151502179055506003543073ffffffffffffffffffffffffffffffffffffffff163103600281905550565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806103935750600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b151561039e57600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561048357600060025411801561040757506002548111155b151561041257600080fd5b80600254036002819055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f19350505050151561047e57600080fd5b610510565b600060035411801561049757506003548111155b15156104a257600080fd5b8060035403600381905550600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f19350505050151561050f57600080fd5b5b50565b60011515600460009054906101000a900460ff16151514151561053557600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614801561059657506003548160035401115b80156105bd575080600354013073ffffffffffffffffffffffffffffffffffffffff163110155b15156105c857600080fd5b806003540160038190555050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600354815600a165627a7a72305820c3b849e8440987ce43eae3097b77672a69234d516351368b03fe5b7de03807910029000000000000000000000000c65e620a3a55451316168d57e268f5702ef56a1129a01060f46676a5dff6f407f0f51eb6f37f5c8c54e238c70221e18e65fc29d3ea65a0557b01c50ff4ffaac8ed6e5d31237a4ecbac843ab1bfe8bb0165a0060df7c54f",
|
||||||
|
"result": {
|
||||||
|
"from": "0x13e4acefe6a6700604929946e70e6443e4e73447",
|
||||||
|
"gas": "0x5e106",
|
||||||
|
"gasUsed": "0x5e106",
|
||||||
|
"input": "0x606060405260405160208061077c83398101604052808051906020019091905050600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415151561007d57600080fd5b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506001600460006101000a81548160ff02191690831515021790555050610653806101296000396000f300606060405260043610610083576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806305e4382a146100855780631c02708d146100ae5780632e1a7d4d146100c35780635114cb52146100e6578063a37dda2c146100fe578063ae200e7914610153578063b5769f70146101a8575b005b341561009057600080fd5b6100986101d1565b6040518082815260200191505060405180910390f35b34156100b957600080fd5b6100c16101d7565b005b34156100ce57600080fd5b6100e460048080359060200190919050506102eb565b005b6100fc6004808035906020019091905050610513565b005b341561010957600080fd5b6101116105d6565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561015e57600080fd5b6101666105fc565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156101b357600080fd5b6101bb610621565b6040518082815260200191505060405180910390f35b60025481565b60011515600460009054906101000a900460ff1615151415156101f957600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806102a15750600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b15156102ac57600080fd5b6000600460006101000a81548160ff0219169083151502179055506003543073ffffffffffffffffffffffffffffffffffffffff163103600281905550565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806103935750600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b151561039e57600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561048357600060025411801561040757506002548111155b151561041257600080fd5b80600254036002819055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f19350505050151561047e57600080fd5b610510565b600060035411801561049757506003548111155b15156104a257600080fd5b8060035403600381905550600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f19350505050151561050f57600080fd5b5b50565b60011515600460009054906101000a900460ff16151514151561053557600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614801561059657506003548160035401115b80156105bd575080600354013073ffffffffffffffffffffffffffffffffffffffff163110155b15156105c857600080fd5b806003540160038190555050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600354815600a165627a7a72305820c3b849e8440987ce43eae3097b77672a69234d516351368b03fe5b7de03807910029000000000000000000000000c65e620a3a55451316168d57e268f5702ef56a11",
|
||||||
|
"output": "0x606060405260043610610083576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806305e4382a146100855780631c02708d146100ae5780632e1a7d4d146100c35780635114cb52146100e6578063a37dda2c146100fe578063ae200e7914610153578063b5769f70146101a8575b005b341561009057600080fd5b6100986101d1565b6040518082815260200191505060405180910390f35b34156100b957600080fd5b6100c16101d7565b005b34156100ce57600080fd5b6100e460048080359060200190919050506102eb565b005b6100fc6004808035906020019091905050610513565b005b341561010957600080fd5b6101116105d6565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561015e57600080fd5b6101666105fc565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156101b357600080fd5b6101bb610621565b6040518082815260200191505060405180910390f35b60025481565b60011515600460009054906101000a900460ff1615151415156101f957600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806102a15750600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b15156102ac57600080fd5b6000600460006101000a81548160ff0219169083151502179055506003543073ffffffffffffffffffffffffffffffffffffffff163103600281905550565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806103935750600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b151561039e57600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561048357600060025411801561040757506002548111155b151561041257600080fd5b80600254036002819055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f19350505050151561047e57600080fd5b610510565b600060035411801561049757506003548111155b15156104a257600080fd5b8060035403600381905550600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f19350505050151561050f57600080fd5b5b50565b60011515600460009054906101000a900460ff16151514151561053557600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614801561059657506003548160035401115b80156105bd575080600354013073ffffffffffffffffffffffffffffffffffffffff163110155b15156105c857600080fd5b806003540160038190555050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600354815600a165627a7a72305820c3b849e8440987ce43eae3097b77672a69234d516351368b03fe5b7de03807910029",
|
||||||
|
"to": "0x7dc9c9730689ff0b0fd506c67db815f12d90a448",
|
||||||
|
"type": "CREATE",
|
||||||
|
"value": "0x0"
|
||||||
|
}
|
||||||
|
}
|
||||||
415
eth/tracers/testdata/call_tracer_deep_calls.json
vendored
Normal file
415
eth/tracers/testdata/call_tracer_deep_calls.json
vendored
Normal file
File diff suppressed because one or more lines are too long
97
eth/tracers/testdata/call_tracer_delegatecall.json
vendored
Normal file
97
eth/tracers/testdata/call_tracer_delegatecall.json
vendored
Normal file
File diff suppressed because one or more lines are too long
77
eth/tracers/testdata/call_tracer_inner_create_oog_outer_throw.json
vendored
Normal file
77
eth/tracers/testdata/call_tracer_inner_create_oog_outer_throw.json
vendored
Normal file
File diff suppressed because one or more lines are too long
81
eth/tracers/testdata/call_tracer_inner_throw_outer_revert.json
vendored
Normal file
81
eth/tracers/testdata/call_tracer_inner_throw_outer_revert.json
vendored
Normal file
File diff suppressed because one or more lines are too long
60
eth/tracers/testdata/call_tracer_oog.json
vendored
Normal file
60
eth/tracers/testdata/call_tracer_oog.json
vendored
Normal file
File diff suppressed because one or more lines are too long
58
eth/tracers/testdata/call_tracer_revert.json
vendored
Normal file
58
eth/tracers/testdata/call_tracer_revert.json
vendored
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
{
|
||||||
|
"context": {
|
||||||
|
"difficulty": "3665057456",
|
||||||
|
"gasLimit": "5232723",
|
||||||
|
"miner": "0xf4d8e706cfb25c0decbbdd4d2e2cc10c66376a3f",
|
||||||
|
"number": "2294501",
|
||||||
|
"timestamp": "1513673601"
|
||||||
|
},
|
||||||
|
"genesis": {
|
||||||
|
"alloc": {
|
||||||
|
"0x0f6cef2b7fbb504782e35aa82a2207e816a2b7a9": {
|
||||||
|
"balance": "0x2a3fc32bcc019283",
|
||||||
|
"code": "0x",
|
||||||
|
"nonce": "10",
|
||||||
|
"storage": {}
|
||||||
|
},
|
||||||
|
"0xabbcd5b340c80b5f1c0545c04c987b87310296ae": {
|
||||||
|
"balance": "0x0",
|
||||||
|
"code": "0x606060405236156100755763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416632d0335ab811461007a578063548db174146100ab5780637f649783146100fc578063b092145e1461014d578063c3f44c0a14610186578063c47cf5de14610203575b600080fd5b341561008557600080fd5b610099600160a060020a0360043516610270565b60405190815260200160405180910390f35b34156100b657600080fd5b6100fa600460248135818101908301358060208181020160405190810160405280939291908181526020018383602002808284375094965061028f95505050505050565b005b341561010757600080fd5b6100fa600460248135818101908301358060208181020160405190810160405280939291908181526020018383602002808284375094965061029e95505050505050565b005b341561015857600080fd5b610172600160a060020a03600435811690602435166102ad565b604051901515815260200160405180910390f35b341561019157600080fd5b6100fa6004803560ff1690602480359160443591606435600160a060020a0316919060a49060843590810190830135806020601f8201819004810201604051908101604052818152929190602084018383808284375094965050509235600160a060020a031692506102cd915050565b005b341561020e57600080fd5b61025460046024813581810190830135806020601f8201819004810201604051908101604052818152929190602084018383808284375094965061056a95505050505050565b604051600160a060020a03909116815260200160405180910390f35b600160a060020a0381166000908152602081905260409020545b919050565b61029a816000610594565b5b50565b61029a816001610594565b5b50565b600160209081526000928352604080842090915290825290205460ff1681565b60008080600160a060020a038416158061030d5750600160a060020a038085166000908152600160209081526040808320339094168352929052205460ff165b151561031857600080fd5b6103218561056a565b600160a060020a038116600090815260208190526040808220549295507f19000000000000000000000000000000000000000000000000000000000000009230918891908b908b90517fff000000000000000000000000000000000000000000000000000000000000008089168252871660018201526c01000000000000000000000000600160a060020a038088168202600284015286811682026016840152602a8301869052841602604a820152605e810182805190602001908083835b6020831061040057805182525b601f1990920191602091820191016103e0565b6001836020036101000a0380198251168184511617909252505050919091019850604097505050505050505051809103902091506001828a8a8a6040516000815260200160405260006040516020015260405193845260ff90921660208085019190915260408085019290925260608401929092526080909201915160208103908084039060008661646e5a03f1151561049957600080fd5b5050602060405103519050600160a060020a03838116908216146104bc57600080fd5b600160a060020a0380841660009081526020819052604090819020805460010190559087169086905180828051906020019080838360005b8381101561050d5780820151818401525b6020016104f4565b50505050905090810190601f16801561053a5780820380516001836020036101000a031916815260200191505b5091505060006040518083038160008661646e5a03f1915050151561055e57600080fd5b5b505050505050505050565b600060248251101561057e5750600061028a565b600160a060020a0360248301511690505b919050565b60005b825181101561060157600160a060020a033316600090815260016020526040812083918584815181106105c657fe5b90602001906020020151600160a060020a031681526020810191909152604001600020805460ff19169115159190911790555b600101610597565b5b5050505600a165627a7a723058200027e8b695e9d2dea9f3629519022a69f3a1d23055ce86406e686ea54f31ee9c0029",
|
||||||
|
"nonce": "1",
|
||||||
|
"storage": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"byzantiumBlock": 1700000,
|
||||||
|
"chainId": 3,
|
||||||
|
"daoForkSupport": true,
|
||||||
|
"eip150Block": 0,
|
||||||
|
"eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d",
|
||||||
|
"eip155Block": 10,
|
||||||
|
"eip158Block": 10,
|
||||||
|
"ethash": {},
|
||||||
|
"homesteadBlock": 0
|
||||||
|
},
|
||||||
|
"difficulty": "3672229776",
|
||||||
|
"extraData": "0x4554482e45544846414e532e4f52472d4641313738394444",
|
||||||
|
"gasLimit": "5227619",
|
||||||
|
"hash": "0xa07b3d6c6bf63f5f981016db9f2d1d93033833f2c17e8bf7209e85f1faf08076",
|
||||||
|
"miner": "0xbbf5029fd710d227630c8b7d338051b8e76d50b3",
|
||||||
|
"mixHash": "0x806e151ce2817be922e93e8d5921fa0f0d0fd213d6b2b9a3fa17458e74a163d0",
|
||||||
|
"nonce": "0xbc5d43adc2c30c7d",
|
||||||
|
"number": "2294500",
|
||||||
|
"stateRoot": "0xca645b335888352ef9d8b1ef083e9019648180b259026572e3139717270de97d",
|
||||||
|
"timestamp": "1513673552",
|
||||||
|
"totalDifficulty": "7160066586979149"
|
||||||
|
},
|
||||||
|
"input": "0xf9018b0a8505d21dba00832dc6c094abbcd5b340c80b5f1c0545c04c987b87310296ae80b9012473b40a5c000000000000000000000000400de2e016bda6577407dfc379faba9899bc73ef0000000000000000000000002cc31912b2b0f3075a87b3640923d45a26cef3ee000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000064d79d8e6c7265636f76657279416464726573730000000000000000000000000000000000000000000000000000000000383e3ec32dc0f66d8fe60dbdc2f6815bdf73a988383e3ec32dc0f66d8fe60dbdc2f6815bdf73a988000000000000000000000000000000000000000000000000000000000000000000000000000000001ba0fd659d76a4edbd2a823e324c93f78ad6803b30ff4a9c8bce71ba82798975c70ca06571eecc0b765688ec6c78942c5ee8b585e00988c0141b518287e9be919bc48a",
|
||||||
|
"result": {
|
||||||
|
"error": "execution reverted",
|
||||||
|
"from": "0x0f6cef2b7fbb504782e35aa82a2207e816a2b7a9",
|
||||||
|
"gas": "0x2d55e8",
|
||||||
|
"gasUsed": "0xc3",
|
||||||
|
"input": "0x73b40a5c000000000000000000000000400de2e016bda6577407dfc379faba9899bc73ef0000000000000000000000002cc31912b2b0f3075a87b3640923d45a26cef3ee000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000064d79d8e6c7265636f76657279416464726573730000000000000000000000000000000000000000000000000000000000383e3ec32dc0f66d8fe60dbdc2f6815bdf73a988383e3ec32dc0f66d8fe60dbdc2f6815bdf73a98800000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"to": "0xabbcd5b340c80b5f1c0545c04c987b87310296ae",
|
||||||
|
"type": "CALL",
|
||||||
|
"value": "0x0"
|
||||||
|
}
|
||||||
|
}
|
||||||
78
eth/tracers/testdata/call_tracer_simple.json
vendored
Normal file
78
eth/tracers/testdata/call_tracer_simple.json
vendored
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
{
|
||||||
|
"context": {
|
||||||
|
"difficulty": "3502894804",
|
||||||
|
"gasLimit": "4722976",
|
||||||
|
"miner": "0x1585936b53834b021f68cc13eeefdec2efc8e724",
|
||||||
|
"number": "2289806",
|
||||||
|
"timestamp": "1513601314"
|
||||||
|
},
|
||||||
|
"genesis": {
|
||||||
|
"alloc": {
|
||||||
|
"0x0024f658a46fbb89d8ac105e98d7ac7cbbaf27c5": {
|
||||||
|
"balance": "0x0",
|
||||||
|
"code": "0x",
|
||||||
|
"nonce": "22",
|
||||||
|
"storage": {}
|
||||||
|
},
|
||||||
|
"0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe": {
|
||||||
|
"balance": "0x4d87094125a369d9bd5",
|
||||||
|
"code": "0x606060405236156100935763ffffffff60e060020a60003504166311ee8382811461009c57806313af4035146100be5780631f5e8f4c146100ee57806324daddc5146101125780634921a91a1461013b57806363e4bff414610157578063764978f91461017f578063893d20e8146101a1578063ba40aaa1146101cd578063cebc9a82146101f4578063e177246e14610216575b61009a5b5b565b005b34156100a457fe5b6100ac61023d565b60408051918252519081900360200190f35b34156100c657fe5b6100da600160a060020a0360043516610244565b604080519115158252519081900360200190f35b34156100f657fe5b6100da610307565b604080519115158252519081900360200190f35b341561011a57fe5b6100da6004351515610318565b604080519115158252519081900360200190f35b6100da6103d6565b604080519115158252519081900360200190f35b6100da600160a060020a0360043516610420565b604080519115158252519081900360200190f35b341561018757fe5b6100ac61046c565b60408051918252519081900360200190f35b34156101a957fe5b6101b1610473565b60408051600160a060020a039092168252519081900360200190f35b34156101d557fe5b6100da600435610483565b604080519115158252519081900360200190f35b34156101fc57fe5b6100ac61050d565b60408051918252519081900360200190f35b341561021e57fe5b6100da600435610514565b604080519115158252519081900360200190f35b6003545b90565b60006000610250610473565b600160a060020a031633600160a060020a03161415156102705760006000fd5b600160a060020a03831615156102865760006000fd5b50600054600160a060020a0390811690831681146102fb57604051600160a060020a0380851691908316907ffcf23a92150d56e85e3a3d33b357493246e55783095eb6a733eb8439ffc752c890600090a360008054600160a060020a031916600160a060020a03851617905560019150610300565b600091505b5b50919050565b60005460a060020a900460ff165b90565b60006000610324610473565b600160a060020a031633600160a060020a03161415156103445760006000fd5b5060005460a060020a900460ff16801515831515146102fb576000546040805160a060020a90920460ff1615158252841515602083015280517fe6cd46a119083b86efc6884b970bfa30c1708f53ba57b86716f15b2f4551a9539281900390910190a16000805460a060020a60ff02191660a060020a8515150217905560019150610300565b600091505b5b50919050565b60006103e0610307565b801561040557506103ef610473565b600160a060020a031633600160a060020a031614155b156104105760006000fd5b610419336105a0565b90505b5b90565b600061042a610307565b801561044f5750610439610473565b600160a060020a031633600160a060020a031614155b1561045a5760006000fd5b610463826105a0565b90505b5b919050565b6001545b90565b600054600160a060020a03165b90565b6000600061048f610473565b600160a060020a031633600160a060020a03161415156104af5760006000fd5b506001548281146102fb57604080518281526020810185905281517f79a3746dde45672c9e8ab3644b8bb9c399a103da2dc94b56ba09777330a83509929181900390910190a160018381559150610300565b600091505b5b50919050565b6002545b90565b60006000610520610473565b600160a060020a031633600160a060020a03161415156105405760006000fd5b506002548281146102fb57604080518281526020810185905281517ff6991a728965fedd6e927fdf16bdad42d8995970b4b31b8a2bf88767516e2494929181900390910190a1600283905560019150610300565b600091505b5b50919050565b60006000426105ad61023d565b116102fb576105c46105bd61050d565b4201610652565b6105cc61046c565b604051909150600160a060020a038416908290600081818185876187965a03f1925050501561063d57604080518281529051600160a060020a038516917f9bca65ce52fdef8a470977b51f247a2295123a4807dfa9e502edf0d30722da3b919081900360200190a260019150610300565b6102fb42610652565b5b600091505b50919050565b60038190555b505600a165627a7a72305820f3c973c8b7ed1f62000b6701bd5b708469e19d0f1d73fde378a56c07fd0b19090029",
|
||||||
|
"nonce": "1",
|
||||||
|
"storage": {
|
||||||
|
"0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000001b436ba50d378d4bbc8660d312a13df6af6e89dfb",
|
||||||
|
"0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000006f05b59d3b20000",
|
||||||
|
"0x0000000000000000000000000000000000000000000000000000000000000002": "0x000000000000000000000000000000000000000000000000000000000000003c",
|
||||||
|
"0x0000000000000000000000000000000000000000000000000000000000000003": "0x000000000000000000000000000000000000000000000000000000005a37b834"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"0xb436ba50d378d4bbc8660d312a13df6af6e89dfb": {
|
||||||
|
"balance": "0x1780d77678137ac1b775",
|
||||||
|
"code": "0x",
|
||||||
|
"nonce": "29072",
|
||||||
|
"storage": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"byzantiumBlock": 1700000,
|
||||||
|
"chainId": 3,
|
||||||
|
"daoForkSupport": true,
|
||||||
|
"eip150Block": 0,
|
||||||
|
"eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d",
|
||||||
|
"eip155Block": 10,
|
||||||
|
"eip158Block": 10,
|
||||||
|
"ethash": {},
|
||||||
|
"homesteadBlock": 0
|
||||||
|
},
|
||||||
|
"difficulty": "3509749784",
|
||||||
|
"extraData": "0x4554482e45544846414e532e4f52472d4641313738394444",
|
||||||
|
"gasLimit": "4727564",
|
||||||
|
"hash": "0x609948ac3bd3c00b7736b933248891d6c901ee28f066241bddb28f4e00a9f440",
|
||||||
|
"miner": "0xbbf5029fd710d227630c8b7d338051b8e76d50b3",
|
||||||
|
"mixHash": "0xb131e4507c93c7377de00e7c271bf409ec7492767142ff0f45c882f8068c2ada",
|
||||||
|
"nonce": "0x4eb12e19c16d43da",
|
||||||
|
"number": "2289805",
|
||||||
|
"stateRoot": "0xc7f10f352bff82fac3c2999d3085093d12652e19c7fd32591de49dc5d91b4f1f",
|
||||||
|
"timestamp": "1513601261",
|
||||||
|
"totalDifficulty": "7143276353481064"
|
||||||
|
},
|
||||||
|
"input": "0xf88b8271908506fc23ac0083015f90943b873a919aa0512d5a0f09e6dcceaa4a6727fafe80a463e4bff40000000000000000000000000024f658a46fbb89d8ac105e98d7ac7cbbaf27c52aa0bdce0b59e8761854e857fe64015f06dd08a4fbb7624f6094893a79a72e6ad6bea01d9dde033cff7bb235a3163f348a6d7ab8d6b52bc0963a95b91612e40ca766a4",
|
||||||
|
"result": {
|
||||||
|
"calls": [
|
||||||
|
{
|
||||||
|
"from": "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe",
|
||||||
|
"input": "0x",
|
||||||
|
"to": "0x0024f658a46fbb89d8ac105e98d7ac7cbbaf27c5",
|
||||||
|
"type": "CALL",
|
||||||
|
"value": "0x6f05b59d3b20000"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"from": "0xb436ba50d378d4bbc8660d312a13df6af6e89dfb",
|
||||||
|
"gas": "0x10738",
|
||||||
|
"gasUsed": "0x3ef9",
|
||||||
|
"input": "0x63e4bff40000000000000000000000000024f658a46fbb89d8ac105e98d7ac7cbbaf27c5",
|
||||||
|
"output": "0x0000000000000000000000000000000000000000000000000000000000000001",
|
||||||
|
"to": "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe",
|
||||||
|
"type": "CALL",
|
||||||
|
"value": "0x0"
|
||||||
|
}
|
||||||
|
}
|
||||||
62
eth/tracers/testdata/call_tracer_throw.json
vendored
Normal file
62
eth/tracers/testdata/call_tracer_throw.json
vendored
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -14,7 +14,7 @@
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package ethapi
|
package tracers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
@ -28,6 +28,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"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/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
duktape "gopkg.in/olebedev/go-duktape.v3"
|
duktape "gopkg.in/olebedev/go-duktape.v3"
|
||||||
)
|
)
|
||||||
|
|
@ -273,9 +274,9 @@ func (cw *contractWrapper) pushObject(vm *duktape.Context) {
|
||||||
vm.PutPropString(obj, "getInput")
|
vm.PutPropString(obj, "getInput")
|
||||||
}
|
}
|
||||||
|
|
||||||
// JavascriptTracer provides an implementation of Tracer that evaluates a
|
// Tracer provides an implementation of Tracer that evaluates a Javascript
|
||||||
// Javascript function for each VM execution step.
|
// function for each VM execution step.
|
||||||
type JavascriptTracer struct {
|
type Tracer struct {
|
||||||
inited bool // Flag whether the context was already inited from the EVM
|
inited bool // Flag whether the context was already inited from the EVM
|
||||||
|
|
||||||
vm *duktape.Context // Javascript VM instance
|
vm *duktape.Context // Javascript VM instance
|
||||||
|
|
@ -302,11 +303,15 @@ type JavascriptTracer struct {
|
||||||
reason error // Textual reason for the interruption
|
reason error // Textual reason for the interruption
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewJavascriptTracer instantiates a new JavascriptTracer instance.
|
// New instantiates a new tracer instance. code specifies a Javascript snippet,
|
||||||
// code specifies a Javascript snippet, which must evaluate to an expression
|
// which must evaluate to an expression returning an object with 'step', 'fault'
|
||||||
// returning an object with 'step' and 'result' functions.
|
// and 'result' functions.
|
||||||
func NewJavascriptTracer(code string) (*JavascriptTracer, error) {
|
func New(code string) (*Tracer, error) {
|
||||||
tracer := &JavascriptTracer{
|
// Resolve any tracers by name and assemble the tracer object
|
||||||
|
if tracer, ok := tracer(code); ok {
|
||||||
|
code = tracer
|
||||||
|
}
|
||||||
|
tracer := &Tracer{
|
||||||
vm: duktape.New(),
|
vm: duktape.New(),
|
||||||
ctx: make(map[string]interface{}),
|
ctx: make(map[string]interface{}),
|
||||||
opWrapper: new(opWrapper),
|
opWrapper: new(opWrapper),
|
||||||
|
|
@ -324,6 +329,17 @@ func NewJavascriptTracer(code string) (*JavascriptTracer, error) {
|
||||||
ctx.PushString(hexutil.Encode(popSlice(ctx)))
|
ctx.PushString(hexutil.Encode(popSlice(ctx)))
|
||||||
return 1
|
return 1
|
||||||
})
|
})
|
||||||
|
tracer.vm.PushGlobalGoFunction("toWord", func(ctx *duktape.Context) int {
|
||||||
|
var word common.Hash
|
||||||
|
if ptr, size := ctx.GetBuffer(-1); ptr != nil {
|
||||||
|
word = common.BytesToHash(makeSlice(ptr, size))
|
||||||
|
} else {
|
||||||
|
word = common.HexToHash(ctx.GetString(-1))
|
||||||
|
}
|
||||||
|
ctx.Pop()
|
||||||
|
copy(makeSlice(ctx.PushFixedBuffer(32), 32), word[:])
|
||||||
|
return 1
|
||||||
|
})
|
||||||
tracer.vm.PushGlobalGoFunction("toAddress", func(ctx *duktape.Context) int {
|
tracer.vm.PushGlobalGoFunction("toAddress", func(ctx *duktape.Context) int {
|
||||||
var addr common.Address
|
var addr common.Address
|
||||||
if ptr, size := ctx.GetBuffer(-1); ptr != nil {
|
if ptr, size := ctx.GetBuffer(-1); ptr != nil {
|
||||||
|
|
@ -335,6 +351,20 @@ func NewJavascriptTracer(code string) (*JavascriptTracer, error) {
|
||||||
copy(makeSlice(ctx.PushFixedBuffer(20), 20), addr[:])
|
copy(makeSlice(ctx.PushFixedBuffer(20), 20), addr[:])
|
||||||
return 1
|
return 1
|
||||||
})
|
})
|
||||||
|
tracer.vm.PushGlobalGoFunction("toContract", func(ctx *duktape.Context) int {
|
||||||
|
var from common.Address
|
||||||
|
if ptr, size := ctx.GetBuffer(-2); ptr != nil {
|
||||||
|
from = common.BytesToAddress(makeSlice(ptr, size))
|
||||||
|
} else {
|
||||||
|
from = common.HexToAddress(ctx.GetString(-2))
|
||||||
|
}
|
||||||
|
nonce := uint64(ctx.GetInt(-1))
|
||||||
|
ctx.Pop2()
|
||||||
|
|
||||||
|
contract := crypto.CreateAddress(from, nonce)
|
||||||
|
copy(makeSlice(ctx.PushFixedBuffer(20), 20), contract[:])
|
||||||
|
return 1
|
||||||
|
})
|
||||||
tracer.vm.PushGlobalGoFunction("isPrecompiled", func(ctx *duktape.Context) int {
|
tracer.vm.PushGlobalGoFunction("isPrecompiled", func(ctx *duktape.Context) int {
|
||||||
_, ok := vm.PrecompiledContractsByzantium[common.BytesToAddress(popSlice(ctx))]
|
_, ok := vm.PrecompiledContractsByzantium[common.BytesToAddress(popSlice(ctx))]
|
||||||
ctx.PushBoolean(ok)
|
ctx.PushBoolean(ok)
|
||||||
|
|
@ -414,14 +444,14 @@ func NewJavascriptTracer(code string) (*JavascriptTracer, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop terminates execution of the tracer at the first opportune moment.
|
// Stop terminates execution of the tracer at the first opportune moment.
|
||||||
func (jst *JavascriptTracer) Stop(err error) {
|
func (jst *Tracer) Stop(err error) {
|
||||||
jst.reason = err
|
jst.reason = err
|
||||||
atomic.StoreUint32(&jst.interrupt, 1)
|
atomic.StoreUint32(&jst.interrupt, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// call executes a method on a JS object, catching any errors, formatting and
|
// call executes a method on a JS object, catching any errors, formatting and
|
||||||
// returning them as error objects.
|
// returning them as error objects.
|
||||||
func (jst *JavascriptTracer) call(method string, args ...string) (json.RawMessage, error) {
|
func (jst *Tracer) call(method string, args ...string) (json.RawMessage, error) {
|
||||||
// Execute the JavaScript call and return any error
|
// Execute the JavaScript call and return any error
|
||||||
jst.vm.PushString(method)
|
jst.vm.PushString(method)
|
||||||
for _, arg := range args {
|
for _, arg := range args {
|
||||||
|
|
@ -448,7 +478,7 @@ func wrapError(context string, err error) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureStart implements the Tracer interface to initialize the tracing operation.
|
// CaptureStart implements the Tracer interface to initialize the tracing operation.
|
||||||
func (jst *JavascriptTracer) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error {
|
func (jst *Tracer) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error {
|
||||||
jst.ctx["type"] = "CALL"
|
jst.ctx["type"] = "CALL"
|
||||||
if create {
|
if create {
|
||||||
jst.ctx["type"] = "CREATE"
|
jst.ctx["type"] = "CREATE"
|
||||||
|
|
@ -463,7 +493,7 @@ func (jst *JavascriptTracer) CaptureStart(from common.Address, to common.Address
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureState implements the Tracer interface to trace a single step of VM execution.
|
// CaptureState implements the Tracer interface to trace a single step of VM execution.
|
||||||
func (jst *JavascriptTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
|
func (jst *Tracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
|
||||||
if jst.err == nil {
|
if jst.err == nil {
|
||||||
// Initialize the context if it wasn't done yet
|
// Initialize the context if it wasn't done yet
|
||||||
if !jst.inited {
|
if !jst.inited {
|
||||||
|
|
@ -501,7 +531,7 @@ func (jst *JavascriptTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode,
|
||||||
|
|
||||||
// CaptureFault implements the Tracer interface to trace an execution fault
|
// CaptureFault implements the Tracer interface to trace an execution fault
|
||||||
// while running an opcode.
|
// while running an opcode.
|
||||||
func (jst *JavascriptTracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
|
func (jst *Tracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
|
||||||
if jst.err == nil {
|
if jst.err == nil {
|
||||||
// Apart from the error, everything matches the previous invocation
|
// Apart from the error, everything matches the previous invocation
|
||||||
jst.errorValue = new(string)
|
jst.errorValue = new(string)
|
||||||
|
|
@ -516,7 +546,7 @@ func (jst *JavascriptTracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode,
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureEnd is called after the call finishes to finalize the tracing.
|
// CaptureEnd is called after the call finishes to finalize the tracing.
|
||||||
func (jst *JavascriptTracer) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error {
|
func (jst *Tracer) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error {
|
||||||
jst.ctx["output"] = output
|
jst.ctx["output"] = output
|
||||||
jst.ctx["gasUsed"] = gasUsed
|
jst.ctx["gasUsed"] = gasUsed
|
||||||
jst.ctx["time"] = t.String()
|
jst.ctx["time"] = t.String()
|
||||||
|
|
@ -528,7 +558,7 @@ func (jst *JavascriptTracer) CaptureEnd(output []byte, gasUsed uint64, t time.Du
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetResult calls the Javascript 'result' function and returns its value, or any accumulated error
|
// GetResult calls the Javascript 'result' function and returns its value, or any accumulated error
|
||||||
func (jst *JavascriptTracer) GetResult() (json.RawMessage, error) {
|
func (jst *Tracer) GetResult() (json.RawMessage, error) {
|
||||||
// Transform the context into a JavaScript object and inject into the state
|
// Transform the context into a JavaScript object and inject into the state
|
||||||
obj := jst.vm.PushObject()
|
obj := jst.vm.PushObject()
|
||||||
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package ethapi
|
package tracers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
|
@ -43,7 +43,7 @@ func (account) ReturnGas(*big.Int) {}
|
||||||
func (account) SetCode(common.Hash, []byte) {}
|
func (account) SetCode(common.Hash, []byte) {}
|
||||||
func (account) ForEachStorage(cb func(key, value common.Hash) bool) {}
|
func (account) ForEachStorage(cb func(key, value common.Hash) bool) {}
|
||||||
|
|
||||||
func runTrace(tracer *JavascriptTracer) (json.RawMessage, error) {
|
func runTrace(tracer *Tracer) (json.RawMessage, error) {
|
||||||
env := vm.NewEVM(vm.Context{BlockNumber: big.NewInt(1)}, nil, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
|
env := vm.NewEVM(vm.Context{BlockNumber: big.NewInt(1)}, nil, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
|
||||||
|
|
||||||
contract := vm.NewContract(account{}, account{}, big.NewInt(0), 10000)
|
contract := vm.NewContract(account{}, account{}, big.NewInt(0), 10000)
|
||||||
|
|
@ -57,7 +57,7 @@ func runTrace(tracer *JavascriptTracer) (json.RawMessage, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTracing(t *testing.T) {
|
func TestTracing(t *testing.T) {
|
||||||
tracer, err := NewJavascriptTracer("{count: 0, step: function() { this.count += 1; }, fault: function() {}, result: function() { return this.count; }}")
|
tracer, err := New("{count: 0, step: function() { this.count += 1; }, fault: function() {}, result: function() { return this.count; }}")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -72,7 +72,7 @@ func TestTracing(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStack(t *testing.T) {
|
func TestStack(t *testing.T) {
|
||||||
tracer, err := NewJavascriptTracer("{depths: [], step: function(log) { this.depths.push(log.stack.length()); }, fault: function() {}, result: function() { return this.depths; }}")
|
tracer, err := New("{depths: [], step: function(log) { this.depths.push(log.stack.length()); }, fault: function() {}, result: function() { return this.depths; }}")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -87,7 +87,7 @@ func TestStack(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOpcodes(t *testing.T) {
|
func TestOpcodes(t *testing.T) {
|
||||||
tracer, err := NewJavascriptTracer("{opcodes: [], step: function(log) { this.opcodes.push(log.op.toString()); }, fault: function() {}, result: function() { return this.opcodes; }}")
|
tracer, err := New("{opcodes: [], step: function(log) { this.opcodes.push(log.op.toString()); }, fault: function() {}, result: function() { return this.opcodes; }}")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -105,7 +105,7 @@ func TestHalt(t *testing.T) {
|
||||||
t.Skip("duktape doesn't support abortion")
|
t.Skip("duktape doesn't support abortion")
|
||||||
|
|
||||||
timeout := errors.New("stahp")
|
timeout := errors.New("stahp")
|
||||||
tracer, err := NewJavascriptTracer("{step: function() { while(1); }, result: function() { return null; }}")
|
tracer, err := New("{step: function() { while(1); }, result: function() { return null; }}")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -121,7 +121,7 @@ func TestHalt(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHaltBetweenSteps(t *testing.T) {
|
func TestHaltBetweenSteps(t *testing.T) {
|
||||||
tracer, err := NewJavascriptTracer("{step: function() {}, fault: function() {}, result: function() { return null; }}")
|
tracer, err := New("{step: function() {}, fault: function() {}, result: function() { return null; }}")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -27,23 +27,25 @@ import (
|
||||||
// all contains all the built in JavaScript tracers by name.
|
// all contains all the built in JavaScript tracers by name.
|
||||||
var all = make(map[string]string)
|
var all = make(map[string]string)
|
||||||
|
|
||||||
|
// camel converts a snake cased input string into a camel cased output.
|
||||||
|
func camel(str string) string {
|
||||||
|
pieces := strings.Split(str, "_")
|
||||||
|
for i := 1; i < len(pieces); i++ {
|
||||||
|
pieces[i] = string(unicode.ToUpper(rune(pieces[i][0]))) + pieces[i][1:]
|
||||||
|
}
|
||||||
|
return strings.Join(pieces, "")
|
||||||
|
}
|
||||||
|
|
||||||
// init retrieves the JavaScript transaction tracers included in go-ethereum.
|
// init retrieves the JavaScript transaction tracers included in go-ethereum.
|
||||||
func init() {
|
func init() {
|
||||||
for _, file := range tracers.AssetNames() {
|
for _, file := range tracers.AssetNames() {
|
||||||
// Convert the underscored tracer file name into a camelcase tracer name
|
name := camel(strings.TrimSuffix(file, ".js"))
|
||||||
pieces := strings.Split(strings.TrimSuffix(file, ".js"), "_")
|
|
||||||
for i := 1; i < len(pieces); i++ {
|
|
||||||
pieces[i] = string(unicode.ToUpper(rune(pieces[i][0]))) + pieces[i][1:]
|
|
||||||
}
|
|
||||||
name := strings.Join(pieces, "")
|
|
||||||
|
|
||||||
// Retrieve and store the tracer
|
|
||||||
all[name] = string(tracers.MustAsset(file))
|
all[name] = string(tracers.MustAsset(file))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tracer retrieves a specific JavaScript tracer by name.
|
// tracer retrieves a specific JavaScript tracer by name.
|
||||||
func Tracer(name string) (string, bool) {
|
func tracer(name string) (string, bool) {
|
||||||
if tracer, ok := all[name]; ok {
|
if tracer, ok := all[name]; ok {
|
||||||
return tracer, true
|
return tracer, true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
194
eth/tracers/tracers_test.go
Normal file
194
eth/tracers/tracers_test.go
Normal file
|
|
@ -0,0 +1,194 @@
|
||||||
|
// Copyright 2017 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library 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 go-ethereum library is 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 go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package tracers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
"math/big"
|
||||||
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
"github.com/ethereum/go-ethereum/tests"
|
||||||
|
)
|
||||||
|
|
||||||
|
// To generate a new callTracer test, copy paste the makeTest method below into
|
||||||
|
// a Geth console and call it with a transaction hash you which to export.
|
||||||
|
|
||||||
|
/*
|
||||||
|
// makeTest generates a callTracer test by running a prestate reassembled and a
|
||||||
|
// call trace run, assembling all the gathered information into a test case.
|
||||||
|
var makeTest = function(tx, rewind) {
|
||||||
|
// Generate the genesis block from the block, transaction and prestate data
|
||||||
|
var block = eth.getBlock(eth.getTransaction(tx).blockHash);
|
||||||
|
var genesis = eth.getBlock(block.parentHash);
|
||||||
|
|
||||||
|
delete genesis.gasUsed;
|
||||||
|
delete genesis.logsBloom;
|
||||||
|
delete genesis.parentHash;
|
||||||
|
delete genesis.receiptsRoot;
|
||||||
|
delete genesis.sha3Uncles;
|
||||||
|
delete genesis.size;
|
||||||
|
delete genesis.transactions;
|
||||||
|
delete genesis.transactionsRoot;
|
||||||
|
delete genesis.uncles;
|
||||||
|
|
||||||
|
genesis.gasLimit = genesis.gasLimit.toString();
|
||||||
|
genesis.number = genesis.number.toString();
|
||||||
|
genesis.timestamp = genesis.timestamp.toString();
|
||||||
|
|
||||||
|
genesis.alloc = debug.traceTransaction(tx, {tracer: "prestateTracer", rewind: rewind});
|
||||||
|
for (var key in genesis.alloc) {
|
||||||
|
genesis.alloc[key].nonce = genesis.alloc[key].nonce.toString();
|
||||||
|
}
|
||||||
|
genesis.config = admin.nodeInfo.protocols.eth.config;
|
||||||
|
|
||||||
|
// Generate the call trace and produce the test input
|
||||||
|
var result = debug.traceTransaction(tx, {tracer: "callTracer", rewind: rewind});
|
||||||
|
delete result.time;
|
||||||
|
|
||||||
|
console.log(JSON.stringify({
|
||||||
|
genesis: genesis,
|
||||||
|
context: {
|
||||||
|
number: block.number.toString(),
|
||||||
|
difficulty: block.difficulty,
|
||||||
|
timestamp: block.timestamp.toString(),
|
||||||
|
gasLimit: block.gasLimit.toString(),
|
||||||
|
miner: block.miner,
|
||||||
|
},
|
||||||
|
input: eth.getRawTransaction(tx),
|
||||||
|
result: result,
|
||||||
|
}, null, 2));
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// callTrace is the result of a callTracer run.
|
||||||
|
type callTrace struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
From common.Address `json:"from"`
|
||||||
|
To common.Address `json:"to"`
|
||||||
|
Input hexutil.Bytes `json:"input"`
|
||||||
|
Output hexutil.Bytes `json:"output"`
|
||||||
|
Gas *hexutil.Uint64 `json:"gas,omitempty"`
|
||||||
|
GasUsed *hexutil.Uint64 `json:"gasUsed,omitempty"`
|
||||||
|
Value *hexutil.Big `json:"value,omitempty"`
|
||||||
|
Error string `json:"error,omitempty"`
|
||||||
|
Calls []callTrace `json:"calls,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type callContext struct {
|
||||||
|
Number math.HexOrDecimal64 `json:"number"`
|
||||||
|
Difficulty *math.HexOrDecimal256 `json:"difficulty"`
|
||||||
|
Time math.HexOrDecimal64 `json:"timestamp"`
|
||||||
|
GasLimit math.HexOrDecimal64 `json:"gasLimit"`
|
||||||
|
Miner common.Address `json:"miner"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// callTracerTest defines a single test to check the call tracer against.
|
||||||
|
type callTracerTest struct {
|
||||||
|
Genesis *core.Genesis `json:"genesis"`
|
||||||
|
Context *callContext `json:"context"`
|
||||||
|
Input string `json:"input"`
|
||||||
|
Result *callTrace `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterates over all the input-output datasets in the tracer test harness and
|
||||||
|
// runs the JavaScript tracers against them.
|
||||||
|
func TestCallTracer(t *testing.T) {
|
||||||
|
files, err := ioutil.ReadDir("testdata")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to retrieve tracer test suite: %v", err)
|
||||||
|
}
|
||||||
|
for _, file := range files {
|
||||||
|
if !strings.HasPrefix(file.Name(), "call_tracer_") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
file := file // capture range variable
|
||||||
|
t.Run(camel(strings.TrimSuffix(strings.TrimPrefix(file.Name(), "call_tracer_"), ".json")), func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Call tracer test found, read if from disk
|
||||||
|
blob, err := ioutil.ReadFile(filepath.Join("testdata", file.Name()))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to read testcase: %v", err)
|
||||||
|
}
|
||||||
|
test := new(callTracerTest)
|
||||||
|
if err := json.Unmarshal(blob, test); err != nil {
|
||||||
|
t.Fatalf("failed to parse testcase: %v", err)
|
||||||
|
}
|
||||||
|
// Configure a blockchain with the given prestate
|
||||||
|
tx := new(types.Transaction)
|
||||||
|
if err := rlp.DecodeBytes(common.FromHex(test.Input), tx); err != nil {
|
||||||
|
t.Fatalf("failed to parse testcase input: %v", err)
|
||||||
|
}
|
||||||
|
signer := types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)))
|
||||||
|
origin, _ := signer.Sender(tx)
|
||||||
|
|
||||||
|
context := vm.Context{
|
||||||
|
CanTransfer: core.CanTransfer,
|
||||||
|
Transfer: core.Transfer,
|
||||||
|
Origin: origin,
|
||||||
|
Coinbase: test.Context.Miner,
|
||||||
|
BlockNumber: new(big.Int).SetUint64(uint64(test.Context.Number)),
|
||||||
|
Time: new(big.Int).SetUint64(uint64(test.Context.Time)),
|
||||||
|
Difficulty: (*big.Int)(test.Context.Difficulty),
|
||||||
|
GasLimit: new(big.Int).SetUint64(uint64(test.Context.GasLimit)),
|
||||||
|
GasPrice: tx.GasPrice(),
|
||||||
|
}
|
||||||
|
db, _ := ethdb.NewMemDatabase()
|
||||||
|
statedb := tests.MakePreState(db, test.Genesis.Alloc)
|
||||||
|
|
||||||
|
// Create the tracer, the EVM environment and run it
|
||||||
|
tracer, err := New("callTracer")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create call tracer: %v", err)
|
||||||
|
}
|
||||||
|
evm := vm.NewEVM(context, statedb, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer})
|
||||||
|
|
||||||
|
msg, err := tx.AsMessage(signer)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to prepare transaction for tracing: %v", err)
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
// Retrieve the trace result and compare against the etalon
|
||||||
|
res, err := tracer.GetResult()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to retrieve trace result: %v", err)
|
||||||
|
}
|
||||||
|
ret := new(callTrace)
|
||||||
|
if err := json.Unmarshal(res, ret); err != nil {
|
||||||
|
t.Fatalf("failed to unmarshal trace result: %v", err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(ret, test.Result) {
|
||||||
|
t.Fatalf("trace mismatch: have %+v, want %+v", ret, test.Result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -127,7 +127,7 @@ func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config) (*state.StateD
|
||||||
}
|
}
|
||||||
block, _ := t.genesis(config).ToBlock()
|
block, _ := t.genesis(config).ToBlock()
|
||||||
db, _ := ethdb.NewMemDatabase()
|
db, _ := ethdb.NewMemDatabase()
|
||||||
statedb := makePreState(db, t.json.Pre)
|
statedb := MakePreState(db, t.json.Pre)
|
||||||
|
|
||||||
post := t.json.Post[subtest.Fork][subtest.Index]
|
post := t.json.Post[subtest.Fork][subtest.Index]
|
||||||
msg, err := t.json.Tx.toMessage(post)
|
msg, err := t.json.Tx.toMessage(post)
|
||||||
|
|
@ -158,7 +158,7 @@ func (t *StateTest) gasLimit(subtest StateSubtest) uint64 {
|
||||||
return t.json.Tx.GasLimit[t.json.Post[subtest.Fork][subtest.Index].Indexes.Gas]
|
return t.json.Tx.GasLimit[t.json.Post[subtest.Fork][subtest.Index].Indexes.Gas]
|
||||||
}
|
}
|
||||||
|
|
||||||
func makePreState(db ethdb.Database, accounts core.GenesisAlloc) *state.StateDB {
|
func MakePreState(db ethdb.Database, accounts core.GenesisAlloc) *state.StateDB {
|
||||||
sdb := state.NewDatabase(db)
|
sdb := state.NewDatabase(db)
|
||||||
statedb, _ := state.New(common.Hash{}, sdb)
|
statedb, _ := state.New(common.Hash{}, sdb)
|
||||||
for addr, a := range accounts {
|
for addr, a := range accounts {
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@ type vmExecMarshaling struct {
|
||||||
|
|
||||||
func (t *VMTest) Run(vmconfig vm.Config) error {
|
func (t *VMTest) Run(vmconfig vm.Config) error {
|
||||||
db, _ := ethdb.NewMemDatabase()
|
db, _ := ethdb.NewMemDatabase()
|
||||||
statedb := makePreState(db, t.json.Pre)
|
statedb := MakePreState(db, t.json.Pre)
|
||||||
ret, gasRemaining, err := t.exec(statedb, vmconfig)
|
ret, gasRemaining, err := t.exec(statedb, vmconfig)
|
||||||
|
|
||||||
if t.json.GasRemaining == nil {
|
if t.json.GasRemaining == nil {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue