aboutsummaryrefslogtreecommitdiffstats
path: root/eth
diff options
context:
space:
mode:
authorJeffrey Wilcke <jeffrey@ethereum.org>2016-03-24 06:20:51 +0800
committerJeffrey Wilcke <jeffrey@ethereum.org>2016-03-24 06:20:51 +0800
commit75c86f8646ed6a21116d6c5f5e400dd966bb218d (patch)
tree6c979fd121e7721328b9502f1855387b602dcd87 /eth
parent9866f19d6af607f629be311cb1c879e8f6472773 (diff)
parent0cfa21fc7f34d9da93abc41541dd4a98d70eb9dd (diff)
downloadgo-tangerine-75c86f8646ed6a21116d6c5f5e400dd966bb218d.tar
go-tangerine-75c86f8646ed6a21116d6c5f5e400dd966bb218d.tar.gz
go-tangerine-75c86f8646ed6a21116d6c5f5e400dd966bb218d.tar.bz2
go-tangerine-75c86f8646ed6a21116d6c5f5e400dd966bb218d.tar.lz
go-tangerine-75c86f8646ed6a21116d6c5f5e400dd966bb218d.tar.xz
go-tangerine-75c86f8646ed6a21116d6c5f5e400dd966bb218d.tar.zst
go-tangerine-75c86f8646ed6a21116d6c5f5e400dd966bb218d.zip
Merge pull request #2141 from obscuren/evm-init
core, core/vm, tests: changed the initialisation behaviour of the EVM
Diffstat (limited to 'eth')
-rw-r--r--eth/api.go290
-rw-r--r--eth/backend.go9
2 files changed, 206 insertions, 93 deletions
diff --git a/eth/api.go b/eth/api.go
index c16c3d142..347047332 100644
--- a/eth/api.go
+++ b/eth/api.go
@@ -22,6 +22,7 @@ import (
"errors"
"fmt"
"io"
+ "io/ioutil"
"math/big"
"os"
"sync"
@@ -669,7 +670,7 @@ func (s *PublicBlockChainAPI) doCall(args CallArgs, blockNr rpc.BlockNumber) (st
}
// Execute the call and return
- vmenv := core.NewEnv(stateDb, s.bc, msg, block.Header())
+ vmenv := core.NewEnv(stateDb, s.bc, msg, block.Header(), nil)
gp := new(core.GasPool).AddGas(common.MaxBig)
res, gas, err := core.ApplyMessage(vmenv, msg, gp)
@@ -980,6 +981,7 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(txHash common.Hash) (ma
}
fields := map[string]interface{}{
+ "root": common.Bytes2Hex(receipt.PostState),
"blockHash": txBlock,
"blockNumber": rpc.NewHexNumber(blockIndex),
"transactionHash": txHash,
@@ -1508,38 +1510,113 @@ func NewPrivateDebugAPI(eth *Ethereum) *PrivateDebugAPI {
return &PrivateDebugAPI{eth: eth}
}
-// ProcessBlock reprocesses an already owned block.
-func (api *PrivateDebugAPI) ProcessBlock(number uint64) (bool, error) {
+// BlockTraceResults is the returned value when replaying a block to check for
+// consensus results and full VM trace logs for all included transactions.
+type BlockTraceResult struct {
+ Validated bool `json: "validated"`
+ StructLogs []structLogRes `json:"structLogs"`
+ Error error `json:"error"`
+}
+
+// TraceBlock processes the given block's RLP but does not import the block in to
+// the chain.
+func (api *PrivateDebugAPI) TraceBlock(blockRlp []byte, config vm.Config) BlockTraceResult {
+ var block types.Block
+ err := rlp.Decode(bytes.NewReader(blockRlp), &block)
+ if err != nil {
+ return BlockTraceResult{Error: fmt.Errorf("could not decode block: %v", err)}
+ }
+
+ validated, logs, err := api.traceBlock(&block, config)
+ return BlockTraceResult{
+ Validated: validated,
+ StructLogs: formatLogs(logs),
+ Error: err,
+ }
+}
+
+// TraceBlockFromFile loads the block's RLP from the given file name and attempts to
+// process it but does not import the block in to the chain.
+func (api *PrivateDebugAPI) TraceBlockFromFile(file string, config vm.Config) BlockTraceResult {
+ blockRlp, err := ioutil.ReadFile(file)
+ if err != nil {
+ return BlockTraceResult{Error: fmt.Errorf("could not read file: %v", err)}
+ }
+ return api.TraceBlock(blockRlp, config)
+}
+
+// TraceProcessBlock processes the block by canonical block number.
+func (api *PrivateDebugAPI) TraceBlockByNumber(number uint64, config vm.Config) BlockTraceResult {
// Fetch the block that we aim to reprocess
block := api.eth.BlockChain().GetBlockByNumber(number)
if block == nil {
- return false, fmt.Errorf("block #%d not found", number)
+ return BlockTraceResult{Error: fmt.Errorf("block #%d not found", number)}
+ }
+
+ validated, logs, err := api.traceBlock(block, config)
+ return BlockTraceResult{
+ Validated: validated,
+ StructLogs: formatLogs(logs),
+ Error: err,
+ }
+}
+
+// TraceBlockByHash processes the block by hash.
+func (api *PrivateDebugAPI) TraceBlockByHash(hash common.Hash, config vm.Config) BlockTraceResult {
+ // Fetch the block that we aim to reprocess
+ block := api.eth.BlockChain().GetBlock(hash)
+ if block == nil {
+ return BlockTraceResult{Error: fmt.Errorf("block #%x not found", hash)}
}
- // Temporarily enable debugging
- defer func(old bool) { vm.Debug = old }(vm.Debug)
- vm.Debug = true
+ validated, logs, err := api.traceBlock(block, config)
+ return BlockTraceResult{
+ Validated: validated,
+ StructLogs: formatLogs(logs),
+ Error: err,
+ }
+}
+
+// TraceCollector collects EVM structered logs.
+//
+// TraceCollector implements vm.Collector
+type TraceCollector struct {
+ traces []vm.StructLog
+}
+
+// AddStructLog adds a structered log.
+func (t *TraceCollector) AddStructLog(slog vm.StructLog) {
+ t.traces = append(t.traces, slog)
+}
+
+// traceBlock processes the given block but does not save the state.
+func (api *PrivateDebugAPI) traceBlock(block *types.Block, config vm.Config) (bool, []vm.StructLog, error) {
// Validate and reprocess the block
var (
blockchain = api.eth.BlockChain()
validator = blockchain.Validator()
processor = blockchain.Processor()
+ collector = &TraceCollector{}
)
+ config.Debug = true // make sure debug is set.
+ config.Logger.Collector = collector
+
if err := core.ValidateHeader(blockchain.AuxValidator(), block.Header(), blockchain.GetHeader(block.ParentHash()), true, false); err != nil {
- return false, err
+ return false, collector.traces, err
}
statedb, err := state.New(blockchain.GetBlock(block.ParentHash()).Root(), api.eth.ChainDb())
if err != nil {
- return false, err
+ return false, collector.traces, err
}
- receipts, _, usedGas, err := processor.Process(block, statedb)
+
+ receipts, _, usedGas, err := processor.Process(block, statedb, &config)
if err != nil {
- return false, err
+ return false, collector.traces, err
}
if err := validator.ValidateState(block, blockchain.GetBlock(block.ParentHash()), statedb, receipts, usedGas); err != nil {
- return false, err
+ return false, collector.traces, err
}
- return true, nil
+ return true, collector.traces, nil
}
// SetHead rewinds the head of the blockchain to a previous block.
@@ -1547,51 +1624,93 @@ func (api *PrivateDebugAPI) SetHead(number uint64) {
api.eth.BlockChain().SetHead(number)
}
-// StructLogRes stores a structured log emitted by the EVM while replaying a
+// ExecutionResult groups all structured logs emitted by the EVM
+// while replaying a transaction in debug mode as well as the amount of
+// gas used and the return value
+type ExecutionResult struct {
+ Gas *big.Int `json:"gas"`
+ ReturnValue string `json:"returnValue"`
+ StructLogs []structLogRes `json:"structLogs"`
+}
+
+// structLogRes stores a structured log emitted by the EVM while replaying a
// transaction in debug mode
type structLogRes struct {
Pc uint64 `json:"pc"`
Op string `json:"op"`
Gas *big.Int `json:"gas"`
GasCost *big.Int `json:"gasCost"`
+ Depth int `json:"depth"`
Error error `json:"error"`
Stack []string `json:"stack"`
- Memory map[string]string `json:"memory"`
+ Memory []string `json:"memory"`
Storage map[string]string `json:"storage"`
}
-// TransactionExecutionRes groups all structured logs emitted by the EVM
-// while replaying a transaction in debug mode as well as the amount of
-// gas used and the return value
-type TransactionExecutionResult struct {
- Gas *big.Int `json:"gas"`
- ReturnValue string `json:"returnValue"`
- StructLogs []structLogRes `json:"structLogs"`
+// VmLoggerOptions are the options used for debugging transactions and capturing
+// specific data.
+type VmLoggerOptions struct {
+ DisableMemory bool // disable memory capture
+ DisableStack bool // disable stack capture
+ DisableStorage bool // disable storage capture
+ FullStorage bool // show full storage (slow)
}
-func (s *PrivateDebugAPI) doReplayTransaction(txHash common.Hash) ([]vm.StructLog, []byte, *big.Int, error) {
+// formatLogs formats EVM returned structured logs for json output
+func formatLogs(structLogs []vm.StructLog) []structLogRes {
+ formattedStructLogs := make([]structLogRes, len(structLogs))
+ for index, trace := range structLogs {
+ formattedStructLogs[index] = structLogRes{
+ Pc: trace.Pc,
+ Op: trace.Op.String(),
+ Gas: trace.Gas,
+ GasCost: trace.GasCost,
+ Depth: trace.Depth,
+ Error: trace.Err,
+ Stack: make([]string, len(trace.Stack)),
+ Storage: make(map[string]string),
+ }
+
+ for i, stackValue := range trace.Stack {
+ formattedStructLogs[index].Stack[i] = fmt.Sprintf("%x", common.LeftPadBytes(stackValue.Bytes(), 32))
+ }
+
+ for i := 0; i+32 <= len(trace.Memory); i += 32 {
+ formattedStructLogs[index].Memory = append(formattedStructLogs[index].Memory, fmt.Sprintf("%x", trace.Memory[i:i+32]))
+ }
+
+ for i, storageValue := range trace.Storage {
+ formattedStructLogs[index].Storage[fmt.Sprintf("%x", i)] = fmt.Sprintf("%x", storageValue)
+ }
+ }
+ return formattedStructLogs
+}
+
+// TraceTransaction returns the structured logs created during the execution of EVM
+// and returns them as a JSON object.
+func (s *PrivateDebugAPI) TraceTransaction(txHash common.Hash, logger vm.LogConfig) (*ExecutionResult, error) {
// Retrieve the tx from the chain
tx, _, blockIndex, _ := core.GetTransaction(s.eth.ChainDb(), txHash)
if tx == nil {
- return nil, nil, nil, fmt.Errorf("Transaction not found")
+ return nil, fmt.Errorf("Transaction not found")
}
block := s.eth.BlockChain().GetBlockByNumber(blockIndex - 1)
if block == nil {
- return nil, nil, nil, fmt.Errorf("Unable to retrieve prior block")
+ return nil, fmt.Errorf("Unable to retrieve prior block")
}
// Create the state database
stateDb, err := state.New(block.Root(), s.eth.ChainDb())
if err != nil {
- return nil, nil, nil, err
+ return nil, err
}
txFrom, err := tx.FromFrontier()
if err != nil {
- return nil, nil, nil, fmt.Errorf("Unable to create transaction sender")
+ return nil, fmt.Errorf("Unable to create transaction sender")
}
from := stateDb.GetOrNewStateObject(txFrom)
msg := callmsg{
@@ -1603,87 +1722,72 @@ func (s *PrivateDebugAPI) doReplayTransaction(txHash common.Hash) ([]vm.StructLo
data: tx.Data(),
}
- vmenv := core.NewEnv(stateDb, s.eth.BlockChain(), msg, block.Header())
+ vmenv := core.NewEnv(stateDb, s.eth.BlockChain(), msg, block.Header(), &vm.Config{
+ Debug: true,
+ Logger: logger,
+ })
gp := new(core.GasPool).AddGas(block.GasLimit())
- vm.GenerateStructLogs = true
- defer func() { vm.GenerateStructLogs = false }()
ret, gas, err := core.ApplyMessage(vmenv, msg, gp)
if err != nil {
- return nil, nil, nil, fmt.Errorf("Error executing transaction %v", err)
+ return nil, fmt.Errorf("Error executing transaction %v", err)
}
- return vmenv.StructLogs(), ret, gas, nil
+ return &ExecutionResult{
+ Gas: gas,
+ ReturnValue: fmt.Sprintf("%x", ret),
+ StructLogs: formatLogs(vmenv.StructLogs()),
+ }, nil
}
-// Executes a transaction and returns the structured logs of the EVM
-// gathered during the execution
-func (s *PrivateDebugAPI) ReplayTransaction(txHash common.Hash, stackDepth int, memorySize int, storageSize int) (*TransactionExecutionResult, error) {
-
- structLogs, ret, gas, err := s.doReplayTransaction(txHash)
-
- if err != nil {
+func (s *PublicBlockChainAPI) TraceCall(args CallArgs, blockNr rpc.BlockNumber) (*ExecutionResult, error) {
+ // Fetch the state associated with the block number
+ stateDb, block, err := stateAndBlockByNumber(s.miner, s.bc, blockNr, s.chainDb)
+ if stateDb == nil || err != nil {
return nil, err
}
+ stateDb = stateDb.Copy()
- res := TransactionExecutionResult{
- Gas: gas,
- ReturnValue: fmt.Sprintf("%x", ret),
- StructLogs: make([]structLogRes, len(structLogs)),
- }
-
- for index, trace := range structLogs {
-
- stackLength := len(trace.Stack)
-
- // Return full stack by default
- if stackDepth != -1 && stackDepth < stackLength {
- stackLength = stackDepth
- }
-
- res.StructLogs[index] = structLogRes{
- Pc: trace.Pc,
- Op: trace.Op.String(),
- Gas: trace.Gas,
- GasCost: trace.GasCost,
- Error: trace.Err,
- Stack: make([]string, stackLength),
- Memory: make(map[string]string),
- Storage: make(map[string]string),
- }
-
- for i := 0; i < stackLength; i++ {
- res.StructLogs[index].Stack[i] = fmt.Sprintf("%x", common.LeftPadBytes(trace.Stack[i].Bytes(), 32))
- }
-
- addr := 0
- memorySizeLocal := memorySize
-
- // Return full memory by default
- if memorySize == -1 {
- memorySizeLocal = len(trace.Memory)
+ // Retrieve the account state object to interact with
+ var from *state.StateObject
+ if args.From == (common.Address{}) {
+ accounts, err := s.am.Accounts()
+ if err != nil || len(accounts) == 0 {
+ from = stateDb.GetOrNewStateObject(common.Address{})
+ } else {
+ from = stateDb.GetOrNewStateObject(accounts[0].Address)
}
+ } else {
+ from = stateDb.GetOrNewStateObject(args.From)
+ }
+ from.SetBalance(common.MaxBig)
- for i := 0; i+16 <= len(trace.Memory) && addr < memorySizeLocal; i += 16 {
- res.StructLogs[index].Memory[fmt.Sprintf("%04d", addr*16)] = fmt.Sprintf("%x", trace.Memory[i:i+16])
- addr++
- }
+ // Assemble the CALL invocation
+ msg := callmsg{
+ from: from,
+ to: args.To,
+ gas: args.Gas.BigInt(),
+ gasPrice: args.GasPrice.BigInt(),
+ value: args.Value.BigInt(),
+ data: common.FromHex(args.Data),
+ }
+ if msg.gas.Cmp(common.Big0) == 0 {
+ msg.gas = big.NewInt(50000000)
+ }
+ if msg.gasPrice.Cmp(common.Big0) == 0 {
+ msg.gasPrice = new(big.Int).Mul(big.NewInt(50), common.Shannon)
+ }
- storageLength := len(trace.Stack)
- if storageSize != -1 && storageSize < storageLength {
- storageLength = storageSize
- }
+ // Execute the call and return
+ vmenv := core.NewEnv(stateDb, s.bc, msg, block.Header(), nil)
+ gp := new(core.GasPool).AddGas(common.MaxBig)
- i := 0
- for storageIndex, storageValue := range trace.Storage {
- if i >= storageLength {
- break
- }
- res.StructLogs[index].Storage[fmt.Sprintf("%x", storageIndex)] = fmt.Sprintf("%x", storageValue)
- i++
- }
- }
- return &res, nil
+ ret, gas, err := core.ApplyMessage(vmenv, msg, gp)
+ return &ExecutionResult{
+ Gas: gas,
+ ReturnValue: fmt.Sprintf("%x", ret),
+ StructLogs: formatLogs(vmenv.StructLogs()),
+ }, nil
}
// PublicNetAPI offers network related RPC methods
diff --git a/eth/backend.go b/eth/backend.go
index d807f8ae8..4f3e11a50 100644
--- a/eth/backend.go
+++ b/eth/backend.go
@@ -35,6 +35,7 @@ import (
"github.com/ethereum/go-ethereum/common/registrar/ethreg"
"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/eth/downloader"
"github.com/ethereum/go-ethereum/eth/filters"
"github.com/ethereum/go-ethereum/ethdb"
@@ -91,6 +92,9 @@ type Config struct {
GpobaseStepUp int
GpobaseCorrectionFactor int
+ EnableJit bool
+ ForceJit bool
+
TestGenesisBlock *types.Block // Genesis block to seed the chain database with (testing only!)
TestGenesisState ethdb.Database // Genesis state to seed the database with (testing only!)
}
@@ -225,6 +229,11 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
}
//genesis := core.GenesisBlock(uint64(config.GenesisNonce), stateDb)
eth.blockchain, err = core.NewBlockChain(chainDb, eth.pow, eth.EventMux())
+ eth.blockchain.SetConfig(&vm.Config{
+ EnableJit: config.EnableJit,
+ ForceJit: config.ForceJit,
+ })
+
if err != nil {
if err == core.ErrNoGenesis {
return nil, fmt.Errorf(`Genesis block not found. Please supply a genesis block with the "--genesis /path/to/file" argument`)