diff options
author | Jeffrey Wilcke <jeffrey@ethereum.org> | 2016-03-24 06:20:51 +0800 |
---|---|---|
committer | Jeffrey Wilcke <jeffrey@ethereum.org> | 2016-03-24 06:20:51 +0800 |
commit | 75c86f8646ed6a21116d6c5f5e400dd966bb218d (patch) | |
tree | 6c979fd121e7721328b9502f1855387b602dcd87 /eth | |
parent | 9866f19d6af607f629be311cb1c879e8f6472773 (diff) | |
parent | 0cfa21fc7f34d9da93abc41541dd4a98d70eb9dd (diff) | |
download | go-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.go | 290 | ||||
-rw-r--r-- | eth/backend.go | 9 |
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`) |