Commit 6609d45e authored by Jeffrey Wilcke's avatar Jeffrey Wilcke

Merge pull request #1228 from obscuren/vm-optimisations

core/vm: optimisations
parents 13bd452f 37111aa4
...@@ -59,6 +59,7 @@ func main() { ...@@ -59,6 +59,7 @@ func main() {
logger.AddLogSystem(logger.NewStdLogSystem(os.Stdout, log.LstdFlags, logger.LogLevel(*loglevel))) logger.AddLogSystem(logger.NewStdLogSystem(os.Stdout, log.LstdFlags, logger.LogLevel(*loglevel)))
vm.Debug = true
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
statedb := state.New(common.Hash{}, db) statedb := state.New(common.Hash{}, db)
sender := statedb.CreateAccount(common.StringToAddress("sender")) sender := statedb.CreateAccount(common.StringToAddress("sender"))
...@@ -80,6 +81,8 @@ func main() { ...@@ -80,6 +81,8 @@ func main() {
fmt.Println(string(statedb.Dump())) fmt.Println(string(statedb.Dump()))
} }
vm.StdErrFormat(vmenv.StructLogs())
var mem runtime.MemStats var mem runtime.MemStats
runtime.ReadMemStats(&mem) runtime.ReadMemStats(&mem)
fmt.Printf("vm took %v\n", time.Since(tstart)) fmt.Printf("vm took %v\n", time.Since(tstart))
...@@ -104,6 +107,7 @@ type VMEnv struct { ...@@ -104,6 +107,7 @@ type VMEnv struct {
depth int depth int
Gas *big.Int Gas *big.Int
time int64 time int64
logs []vm.StructLog
} }
func NewEnv(state *state.StateDB, transactor common.Address, value *big.Int) *VMEnv { func NewEnv(state *state.StateDB, transactor common.Address, value *big.Int) *VMEnv {
...@@ -133,6 +137,12 @@ func (self *VMEnv) GetHash(n uint64) common.Hash { ...@@ -133,6 +137,12 @@ func (self *VMEnv) GetHash(n uint64) common.Hash {
} }
return common.Hash{} return common.Hash{}
} }
func (self *VMEnv) AddStructLog(log vm.StructLog) {
self.logs = append(self.logs, log)
}
func (self *VMEnv) StructLogs() []vm.StructLog {
return self.logs
}
func (self *VMEnv) AddLog(log *state.Log) { func (self *VMEnv) AddLog(log *state.Log) {
self.state.AddLog(log) self.state.AddLog(log)
} }
......
...@@ -271,9 +271,12 @@ func (js *jsre) debugBlock(call otto.FunctionCall) otto.Value { ...@@ -271,9 +271,12 @@ func (js *jsre) debugBlock(call otto.FunctionCall) otto.Value {
} }
tstart := time.Now() tstart := time.Now()
old := vm.Debug old := vm.Debug
vm.Debug = true
if len(call.ArgumentList) > 1 {
vm.Debug, _ = call.Argument(1).ToBoolean()
}
_, err = js.ethereum.BlockProcessor().RetryProcess(block) _, err = js.ethereum.BlockProcessor().RetryProcess(block)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
......
...@@ -151,11 +151,17 @@ func (sm *BlockProcessor) RetryProcess(block *types.Block) (logs state.Logs, err ...@@ -151,11 +151,17 @@ func (sm *BlockProcessor) RetryProcess(block *types.Block) (logs state.Logs, err
return nil, ParentError(header.ParentHash) return nil, ParentError(header.ParentHash)
} }
parent := sm.bc.GetBlock(header.ParentHash) parent := sm.bc.GetBlock(header.ParentHash)
if !sm.Pow.Verify(block) {
// FIXME Change to full header validation. See #1225
errch := make(chan bool)
go func() { errch <- sm.Pow.Verify(block) }()
logs, err = sm.processWithParent(block, parent)
if !<-errch {
return nil, ValidationError("Block's nonce is invalid (= %x)", block.Nonce) return nil, ValidationError("Block's nonce is invalid (= %x)", block.Nonce)
} }
return sm.processWithParent(block, parent) return logs, err
} }
// Process block will attempt to process the given block's transactions and applies them // Process block will attempt to process the given block's transactions and applies them
......
...@@ -567,6 +567,7 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) { ...@@ -567,6 +567,7 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) {
go verifyNonces(self.pow, chain, nonceQuit, nonceDone) go verifyNonces(self.pow, chain, nonceQuit, nonceDone)
defer close(nonceQuit) defer close(nonceQuit)
txcount := 0
for i, block := range chain { for i, block := range chain {
bstart := time.Now() bstart := time.Now()
// Wait for block i's nonce to be verified before processing // Wait for block i's nonce to be verified before processing
...@@ -625,6 +626,8 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) { ...@@ -625,6 +626,8 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) {
return i, err return i, err
} }
txcount += len(block.Transactions())
cblock := self.currentBlock cblock := self.currentBlock
// Compare the TD of the last known block in the canonical chain to make sure it's greater. // Compare the TD of the last known block in the canonical chain to make sure it's greater.
// At this point it's possible that a different chain (fork) becomes the new canonical chain. // At this point it's possible that a different chain (fork) becomes the new canonical chain.
...@@ -683,7 +686,7 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) { ...@@ -683,7 +686,7 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) {
if (stats.queued > 0 || stats.processed > 0 || stats.ignored > 0) && bool(glog.V(logger.Info)) { if (stats.queued > 0 || stats.processed > 0 || stats.ignored > 0) && bool(glog.V(logger.Info)) {
tend := time.Since(tstart) tend := time.Since(tstart)
start, end := chain[0], chain[len(chain)-1] start, end := chain[0], chain[len(chain)-1]
glog.Infof("imported %d block(s) (%d queued %d ignored) in %v. #%v [%x / %x]\n", stats.processed, stats.queued, stats.ignored, tend, end.Number(), start.Hash().Bytes()[:4], end.Hash().Bytes()[:4]) glog.Infof("imported %d block(s) (%d queued %d ignored) including %d txs in %v. #%v [%x / %x]\n", stats.processed, stats.queued, stats.ignored, txcount, tend, end.Number(), start.Hash().Bytes()[:4], end.Hash().Bytes()[:4])
} }
go self.eventMux.Post(queueEvent) go self.eventMux.Post(queueEvent)
......
...@@ -2,7 +2,6 @@ package core ...@@ -2,7 +2,6 @@ package core
import ( import (
"math/big" "math/big"
"time"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
...@@ -49,8 +48,6 @@ func (self *Execution) Create(caller vm.ContextRef) (ret []byte, err error, acco ...@@ -49,8 +48,6 @@ func (self *Execution) Create(caller vm.ContextRef) (ret []byte, err error, acco
} }
func (self *Execution) exec(contextAddr *common.Address, code []byte, caller vm.ContextRef) (ret []byte, err error) { func (self *Execution) exec(contextAddr *common.Address, code []byte, caller vm.ContextRef) (ret []byte, err error) {
start := time.Now()
env := self.env env := self.env
evm := self.evm evm := self.evm
if env.Depth() > int(params.CallCreateDepth.Int64()) { if env.Depth() > int(params.CallCreateDepth.Int64()) {
...@@ -96,7 +93,6 @@ func (self *Execution) exec(contextAddr *common.Address, code []byte, caller vm. ...@@ -96,7 +93,6 @@ func (self *Execution) exec(contextAddr *common.Address, code []byte, caller vm.
context.SetCallCode(contextAddr, code) context.SetCallCode(contextAddr, code)
ret, err = evm.Run(context, self.input) ret, err = evm.Run(context, self.input)
evm.Printf("message call took %v", time.Since(start)).Endl()
if err != nil { if err != nil {
env.State().Set(snapshot) env.State().Set(snapshot)
} }
......
...@@ -336,6 +336,22 @@ func (self *StateObject) Nonce() uint64 { ...@@ -336,6 +336,22 @@ func (self *StateObject) Nonce() uint64 {
return self.nonce return self.nonce
} }
func (self *StateObject) EachStorage(cb func(key, value []byte)) {
// When iterating over the storage check the cache first
for h, v := range self.storage {
cb([]byte(h), v.Bytes())
}
it := self.State.trie.Iterator()
for it.Next() {
// ignore cached values
key := self.State.trie.GetKey(it.Key)
if _, ok := self.storage[string(key)]; !ok {
cb(key, it.Value)
}
}
}
// //
// Encoding // Encoding
// //
......
...@@ -223,6 +223,10 @@ func (self *StateTransition) transitionState() (ret []byte, usedGas *big.Int, er ...@@ -223,6 +223,10 @@ func (self *StateTransition) transitionState() (ret []byte, usedGas *big.Int, er
return nil, nil, InvalidTxError(err) return nil, nil, InvalidTxError(err)
} }
if vm.Debug {
vm.StdErrFormat(vmenv.StructLogs())
}
self.refundGas() self.refundGas()
self.state.AddBalance(self.coinbase, new(big.Int).Mul(self.gasUsed(), self.gasPrice)) self.state.AddBalance(self.coinbase, new(big.Int).Mul(self.gasUsed(), self.gasPrice))
......
...@@ -49,13 +49,13 @@ func NewContext(caller ContextRef, object ContextRef, value, gas, price *big.Int ...@@ -49,13 +49,13 @@ func NewContext(caller ContextRef, object ContextRef, value, gas, price *big.Int
return c return c
} }
func (c *Context) GetOp(n *big.Int) OpCode { func (c *Context) GetOp(n uint64) OpCode {
return OpCode(c.GetByte(n)) return OpCode(c.GetByte(n))
} }
func (c *Context) GetByte(n *big.Int) byte { func (c *Context) GetByte(n uint64) byte {
if n.Cmp(big.NewInt(int64(len(c.Code)))) < 0 { if n < uint64(len(c.Code)) {
return c.Code[n.Int64()] return c.Code[n]
} }
return 0 return 0
......
...@@ -8,6 +8,8 @@ import ( ...@@ -8,6 +8,8 @@ import (
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
) )
// Environment is is required by the virtual machine to get information from
// it's own isolated environment. For an example see `core.VMEnv`
type Environment interface { type Environment interface {
State() *state.StateDB State() *state.StateDB
...@@ -20,6 +22,8 @@ type Environment interface { ...@@ -20,6 +22,8 @@ type Environment interface {
GasLimit() *big.Int GasLimit() *big.Int
Transfer(from, to Account, amount *big.Int) error Transfer(from, to Account, amount *big.Int) error
AddLog(*state.Log) AddLog(*state.Log)
AddStructLog(StructLog)
StructLogs() []StructLog
VmType() Type VmType() Type
...@@ -31,6 +35,18 @@ type Environment interface { ...@@ -31,6 +35,18 @@ type Environment interface {
Create(me ContextRef, data []byte, gas, price, value *big.Int) ([]byte, error, ContextRef) Create(me ContextRef, data []byte, gas, price, value *big.Int) ([]byte, error, ContextRef)
} }
// StructLog is emited to the Environment each cycle and lists information about the curent internal state
// prior to the execution of the statement.
type StructLog struct {
Pc uint64
Op OpCode
Gas *big.Int
GasCost *big.Int
Memory []byte
Stack []*big.Int
Storage map[common.Hash][]byte
}
type Account interface { type Account interface {
SubBalance(amount *big.Int) SubBalance(amount *big.Int)
AddBalance(amount *big.Int) AddBalance(amount *big.Int)
......
package vm
import (
"fmt"
"os"
"unicode/utf8"
"github.com/ethereum/go-ethereum/common"
)
func StdErrFormat(logs []StructLog) {
fmt.Fprintf(os.Stderr, "VM Stats %d ops\n", len(logs))
for _, log := range logs {
fmt.Fprintf(os.Stderr, "PC %08d: %s GAS: %v COST: %v\n", log.Pc, log.Op, log.Gas, log.GasCost)
fmt.Fprintln(os.Stderr, "STACK =", len(log.Stack))
for i := len(log.Stack) - 1; i >= 0; i-- {
fmt.Fprintf(os.Stderr, "%04d: %x\n", len(log.Stack)-i-1, common.LeftPadBytes(log.Stack[i].Bytes(), 32))
}
const maxMem = 10
addr := 0
fmt.Fprintln(os.Stderr, "MEM =", len(log.Memory))
for i := 0; i+16 <= len(log.Memory) && addr < maxMem; i += 16 {
data := log.Memory[i : i+16]
str := fmt.Sprintf("%04d: % x ", addr*16, data)
for _, r := range data {
if r == 0 {
str += "."
} else if utf8.ValidRune(rune(r)) {
str += fmt.Sprintf("%s", string(r))
} else {
str += "?"
}
}
addr++
fmt.Fprintln(os.Stderr, str)
}
fmt.Fprintln(os.Stderr, "STORAGE =", len(log.Storage))
for h, item := range log.Storage {
fmt.Fprintf(os.Stderr, "%x: %x\n", h, common.LeftPadBytes(item, 32))
}
fmt.Fprintln(os.Stderr)
}
}
...@@ -5,7 +5,7 @@ import ( ...@@ -5,7 +5,7 @@ import (
"math/big" "math/big"
) )
func newStack() *stack { func newstack() *stack {
return &stack{} return &stack{}
} }
...@@ -14,6 +14,10 @@ type stack struct { ...@@ -14,6 +14,10 @@ type stack struct {
ptr int ptr int
} }
func (st *stack) Data() []*big.Int {
return st.data[:st.ptr]
}
func (st *stack) push(d *big.Int) { func (st *stack) push(d *big.Int) {
// NOTE push limit (1024) is checked in baseCheck // NOTE push limit (1024) is checked in baseCheck
stackItem := new(big.Int).Set(d) stackItem := new(big.Int).Set(d)
......
...@@ -3,6 +3,4 @@ package vm ...@@ -3,6 +3,4 @@ package vm
type VirtualMachine interface { type VirtualMachine interface {
Env() Environment Env() Environment
Run(context *Context, data []byte) ([]byte, error) Run(context *Context, data []byte) ([]byte, error)
Printf(string, ...interface{}) VirtualMachine
Endl() VirtualMachine
} }
This diff is collapsed.
...@@ -16,6 +16,8 @@ type VMEnv struct { ...@@ -16,6 +16,8 @@ type VMEnv struct {
depth int depth int
chain *ChainManager chain *ChainManager
typ vm.Type typ vm.Type
// structured logging
logs []vm.StructLog
} }
func NewEnv(state *state.StateDB, chain *ChainManager, msg Message, block *types.Block) *VMEnv { func NewEnv(state *state.StateDB, chain *ChainManager, msg Message, block *types.Block) *VMEnv {
...@@ -47,6 +49,7 @@ func (self *VMEnv) GetHash(n uint64) common.Hash { ...@@ -47,6 +49,7 @@ func (self *VMEnv) GetHash(n uint64) common.Hash {
return common.Hash{} return common.Hash{}
} }
func (self *VMEnv) AddLog(log *state.Log) { func (self *VMEnv) AddLog(log *state.Log) {
self.state.AddLog(log) self.state.AddLog(log)
} }
...@@ -68,3 +71,11 @@ func (self *VMEnv) Create(me vm.ContextRef, data []byte, gas, price, value *big. ...@@ -68,3 +71,11 @@ func (self *VMEnv) Create(me vm.ContextRef, data []byte, gas, price, value *big.
exe := NewExecution(self, nil, data, gas, price, value) exe := NewExecution(self, nil, data, gas, price, value)
return exe.Create(me) return exe.Create(me)
} }
func (self *VMEnv) StructLogs() []vm.StructLog {
return self.logs
}
func (self *VMEnv) AddStructLog(log vm.StructLog) {
self.logs = append(self.logs, log)
}
...@@ -270,7 +270,6 @@ func (self *worker) wait() { ...@@ -270,7 +270,6 @@ func (self *worker) wait() {
func (self *worker) push() { func (self *worker) push() {
if atomic.LoadInt32(&self.mining) == 1 { if atomic.LoadInt32(&self.mining) == 1 {
self.current.block.Header().GasUsed = self.current.totalUsedGas
self.current.block.SetRoot(self.current.state.Root()) self.current.block.SetRoot(self.current.state.Root())
// push new work to agents // push new work to agents
...@@ -510,6 +509,8 @@ func (self *worker) commitTransactions(transactions types.Transactions) { ...@@ -510,6 +509,8 @@ func (self *worker) commitTransactions(transactions types.Transactions) {
current.tcount++ current.tcount++
} }
} }
self.current.block.Header().GasUsed = self.current.totalUsedGas
} }
func (self *worker) commitTransaction(tx *types.Transaction) error { func (self *worker) commitTransaction(tx *types.Transaction) error {
......
...@@ -27,9 +27,8 @@ type Env struct { ...@@ -27,9 +27,8 @@ type Env struct {
difficulty *big.Int difficulty *big.Int
gasLimit *big.Int gasLimit *big.Int
logs state.Logs
vmTest bool vmTest bool
logs []vm.StructLog
} }
func NewEnv(state *state.StateDB) *Env { func NewEnv(state *state.StateDB) *Env {
...@@ -38,6 +37,14 @@ func NewEnv(state *state.StateDB) *Env { ...@@ -38,6 +37,14 @@ func NewEnv(state *state.StateDB) *Env {
} }
} }
func (self *Env) StructLogs() []vm.StructLog {
return self.logs
}
func (self *Env) AddStructLog(log vm.StructLog) {
self.logs = append(self.logs, log)
}
func NewEnvFromMap(state *state.StateDB, envValues map[string]string, exeValues map[string]string) *Env { func NewEnvFromMap(state *state.StateDB, envValues map[string]string, exeValues map[string]string) *Env {
env := NewEnv(state) env := NewEnv(state)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment