cmd/evm, core/vm, test: refactored VM and core

* Moved `vm.Transfer` to `core` package and changed execution to call
`env.Transfer` instead of `core.Transfer` directly.
* core/vm: byte code VM moved to jump table instead of switch
* Moved `vm.Transfer` to `core` package and changed execution to call
  `env.Transfer` instead of `core.Transfer` directly.
* Byte code VM now shares the same code as the JITVM
* Renamed Context to Contract
* Changed initialiser of state transition & unexported methods
* Removed the Execution object and refactor `Call`, `CallCode` &
  `Create` in to their own functions instead of being methods.
* Removed the hard dep on the state for the VM. The VM now
  depends on a Database interface returned by the environment. In the
  process the core now depends less on the statedb by usage of the env
* Moved `Log` from package `core/state` to package `core/vm`.
This commit is contained in:
Jeffrey Wilcke 2015-08-30 10:19:10 +02:00
parent f7a71996fb
commit 361082ec4b
39 changed files with 966 additions and 1082 deletions

View File

@ -80,12 +80,17 @@ var (
Name: "sysstat", Name: "sysstat",
Usage: "display system stats", Usage: "display system stats",
} }
VerbosityFlag = cli.IntFlag{
Name: "verbosity",
Usage: "sets the verbosity level",
}
) )
func init() { func init() {
app = utils.NewApp("0.2", "the evm command line interface") app = utils.NewApp("0.2", "the evm command line interface")
app.Flags = []cli.Flag{ app.Flags = []cli.Flag{
DebugFlag, DebugFlag,
VerbosityFlag,
ForceJitFlag, ForceJitFlag,
DisableJitFlag, DisableJitFlag,
SysStatFlag, SysStatFlag,
@ -105,6 +110,7 @@ func run(ctx *cli.Context) {
vm.EnableJit = !ctx.GlobalBool(DisableJitFlag.Name) vm.EnableJit = !ctx.GlobalBool(DisableJitFlag.Name)
glog.SetToStderr(true) glog.SetToStderr(true)
glog.SetV(ctx.GlobalInt(VerbosityFlag.Name))
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
statedb := state.New(common.Hash{}, db) statedb := state.New(common.Hash{}, db)
@ -179,18 +185,20 @@ func NewEnv(state *state.StateDB, transactor common.Address, value *big.Int) *VM
} }
} }
func (self *VMEnv) State() *state.StateDB { return self.state } func (self *VMEnv) Db() vm.Database { return self.state }
func (self *VMEnv) Origin() common.Address { return *self.transactor } func (self *VMEnv) MakeSnapshot() vm.Database { return self.state.Copy() }
func (self *VMEnv) BlockNumber() *big.Int { return common.Big0 } func (self *VMEnv) SetSnapshot(db vm.Database) { self.state.Set(db.(*state.StateDB)) }
func (self *VMEnv) Coinbase() common.Address { return *self.transactor } func (self *VMEnv) Origin() common.Address { return *self.transactor }
func (self *VMEnv) Time() *big.Int { return self.time } func (self *VMEnv) BlockNumber() *big.Int { return common.Big0 }
func (self *VMEnv) Difficulty() *big.Int { return common.Big1 } func (self *VMEnv) Coinbase() common.Address { return *self.transactor }
func (self *VMEnv) BlockHash() []byte { return make([]byte, 32) } func (self *VMEnv) Time() *big.Int { return self.time }
func (self *VMEnv) Value() *big.Int { return self.value } func (self *VMEnv) Difficulty() *big.Int { return common.Big1 }
func (self *VMEnv) GasLimit() *big.Int { return big.NewInt(1000000000) } func (self *VMEnv) BlockHash() []byte { return make([]byte, 32) }
func (self *VMEnv) VmType() vm.Type { return vm.StdVmTy } func (self *VMEnv) Value() *big.Int { return self.value }
func (self *VMEnv) Depth() int { return 0 } func (self *VMEnv) GasLimit() *big.Int { return big.NewInt(1000000000) }
func (self *VMEnv) SetDepth(i int) { self.depth = i } func (self *VMEnv) VmType() vm.Type { return vm.StdVmTy }
func (self *VMEnv) Depth() int { return 0 }
func (self *VMEnv) SetDepth(i int) { self.depth = i }
func (self *VMEnv) GetHash(n uint64) common.Hash { func (self *VMEnv) GetHash(n uint64) common.Hash {
if self.block.Number().Cmp(big.NewInt(int64(n))) == 0 { if self.block.Number().Cmp(big.NewInt(int64(n))) == 0 {
return self.block.Hash() return self.block.Hash()
@ -203,34 +211,24 @@ func (self *VMEnv) AddStructLog(log vm.StructLog) {
func (self *VMEnv) StructLogs() []vm.StructLog { func (self *VMEnv) StructLogs() []vm.StructLog {
return self.logs return self.logs
} }
func (self *VMEnv) AddLog(log *state.Log) { func (self *VMEnv) AddLog(log *vm.Log) {
self.state.AddLog(log) self.state.AddLog(log)
} }
func (self *VMEnv) CanTransfer(from vm.Account, balance *big.Int) bool { func (self *VMEnv) CanTransfer(from common.Address, balance *big.Int) bool {
return from.Balance().Cmp(balance) >= 0 return self.state.GetBalance(from).Cmp(balance) >= 0
} }
func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) error { func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) error {
return vm.Transfer(from, to, amount) return core.Transfer(from, to, amount)
} }
func (self *VMEnv) vm(addr *common.Address, data []byte, gas, price, value *big.Int) *core.Execution { func (self *VMEnv) Call(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
return core.NewExecution(self, addr, data, gas, price, value) self.Gas = gas
return core.Call(self, caller, addr, data, gas, price, value)
}
func (self *VMEnv) CallCode(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
return core.CallCode(self, caller, addr, data, gas, price, value)
} }
func (self *VMEnv) Call(caller vm.ContextRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) { func (self *VMEnv) Create(caller vm.ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) {
exe := self.vm(&addr, data, gas, price, value) return core.Create(self, caller, data, gas, price, value)
ret, err := exe.Call(addr, caller)
self.Gas = exe.Gas
return ret, err
}
func (self *VMEnv) CallCode(caller vm.ContextRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
a := caller.Address()
exe := self.vm(&a, data, gas, price, value)
return exe.Call(addr, caller)
}
func (self *VMEnv) Create(caller vm.ContextRef, data []byte, gas, price, value *big.Int) ([]byte, error, vm.ContextRef) {
exe := self.vm(nil, data, gas, price, value)
return exe.Create(caller)
} }

View File

@ -25,6 +25,7 @@ import (
"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"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
@ -163,7 +164,7 @@ func (self *BlockProcessor) ApplyTransactions(gp GasPool, statedb *state.StateDB
return receipts, err return receipts, err
} }
func (sm *BlockProcessor) RetryProcess(block *types.Block) (logs state.Logs, err error) { func (sm *BlockProcessor) RetryProcess(block *types.Block) (logs vm.Logs, err error) {
// Processing a blocks may never happen simultaneously // Processing a blocks may never happen simultaneously
sm.mutex.Lock() sm.mutex.Lock()
defer sm.mutex.Unlock() defer sm.mutex.Unlock()
@ -188,7 +189,7 @@ func (sm *BlockProcessor) RetryProcess(block *types.Block) (logs state.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
// on top of the block's parent state (given it exists) and will return wether it was // on top of the block's parent state (given it exists) and will return wether it was
// successful or not. // successful or not.
func (sm *BlockProcessor) Process(block *types.Block) (logs state.Logs, receipts types.Receipts, err error) { func (sm *BlockProcessor) Process(block *types.Block) (logs vm.Logs, receipts types.Receipts, err error) {
// Processing a blocks may never happen simultaneously // Processing a blocks may never happen simultaneously
sm.mutex.Lock() sm.mutex.Lock()
defer sm.mutex.Unlock() defer sm.mutex.Unlock()
@ -204,7 +205,7 @@ func (sm *BlockProcessor) Process(block *types.Block) (logs state.Logs, receipts
return sm.processWithParent(block, parent) return sm.processWithParent(block, parent)
} }
func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (logs state.Logs, receipts types.Receipts, err error) { func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (logs vm.Logs, receipts types.Receipts, err error) {
// Create a new state based on the parent's root (e.g., create copy) // Create a new state based on the parent's root (e.g., create copy)
state := state.New(parent.Root(), sm.chainDb) state := state.New(parent.Root(), sm.chainDb)
header := block.Header() header := block.Header()
@ -356,7 +357,7 @@ func (sm *BlockProcessor) GetBlockReceipts(bhash common.Hash) types.Receipts {
// GetLogs returns the logs of the given block. This method is using a two step approach // GetLogs returns the logs of the given block. This method is using a two step approach
// where it tries to get it from the (updated) method which gets them from the receipts or // where it tries to get it from the (updated) method which gets them from the receipts or
// the depricated way by re-processing the block. // the depricated way by re-processing the block.
func (sm *BlockProcessor) GetLogs(block *types.Block) (logs state.Logs, err error) { func (sm *BlockProcessor) GetLogs(block *types.Block) (logs vm.Logs, err error) {
receipts := GetBlockReceipts(sm.chainDb, block.Hash()) receipts := GetBlockReceipts(sm.chainDb, block.Hash())
// coalesce logs // coalesce logs
for _, receipt := range receipts { for _, receipt := range receipts {

View File

@ -24,6 +24,7 @@ import (
"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"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/pow/ezp" "github.com/ethereum/go-ethereum/pow/ezp"
@ -69,7 +70,7 @@ func TestPutReceipt(t *testing.T) {
hash[0] = 2 hash[0] = 2
receipt := new(types.Receipt) receipt := new(types.Receipt)
receipt.SetLogs(state.Logs{&state.Log{ receipt.SetLogs(vm.Logs{&vm.Log{
Address: addr, Address: addr,
Topics: []common.Hash{hash}, Topics: []common.Hash{hash},
Data: []byte("hi"), Data: []byte("hi"),

View File

@ -28,8 +28,8 @@ import (
"github.com/ethereum/ethash" "github.com/ethereum/ethash"
"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/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
@ -361,7 +361,7 @@ func TestChainMultipleInsertions(t *testing.T) {
type bproc struct{} type bproc struct{}
func (bproc) Process(*types.Block) (state.Logs, types.Receipts, error) { return nil, nil, nil } func (bproc) Process(*types.Block) (vm.Logs, types.Receipts, error) { return nil, nil, nil }
func makeChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.Block { func makeChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.Block {
var chain []*types.Block var chain []*types.Block

View File

@ -20,8 +20,8 @@ import (
"math/big" "math/big"
"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/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
) )
// TxPreEvent is posted when a transaction enters the transaction pool. // TxPreEvent is posted when a transaction enters the transaction pool.
@ -42,23 +42,23 @@ type RemovedTransactionEvent struct{ Txs types.Transactions }
// ChainSplit is posted when a new head is detected // ChainSplit is posted when a new head is detected
type ChainSplitEvent struct { type ChainSplitEvent struct {
Block *types.Block Block *types.Block
Logs state.Logs Logs vm.Logs
} }
type ChainEvent struct { type ChainEvent struct {
Block *types.Block Block *types.Block
Hash common.Hash Hash common.Hash
Logs state.Logs Logs vm.Logs
} }
type ChainSideEvent struct { type ChainSideEvent struct {
Block *types.Block Block *types.Block
Logs state.Logs Logs vm.Logs
} }
type PendingBlockEvent struct { type PendingBlockEvent struct {
Block *types.Block Block *types.Block
Logs state.Logs Logs vm.Logs
} }
type ChainUncleEvent struct { type ChainUncleEvent struct {

View File

@ -17,108 +17,104 @@
package core package core
import ( import (
"errors"
"math/big" "math/big"
"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/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
) )
// Execution is the execution environment for the given call or create action. // Call executes within the given contract
type Execution struct { func Call(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice, value *big.Int) (ret []byte, err error) {
env vm.Environment ret, _, err = exec(env, caller, &addr, &addr, input, env.Db().GetCode(addr), gas, gasPrice, value)
address *common.Address return ret, err
input []byte
evm vm.VirtualMachine
Gas, price, value *big.Int
} }
// NewExecution returns a new execution environment that handles all calling // CallCode executes the given address' code as the given contract address
// and creation logic defined by the YP. func CallCode(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice, value *big.Int) (ret []byte, err error) {
func NewExecution(env vm.Environment, address *common.Address, input []byte, gas, gasPrice, value *big.Int) *Execution { prev := caller.Address()
exe := &Execution{env: env, address: address, input: input, Gas: gas, price: gasPrice, value: value} ret, _, err = exec(env, caller, &prev, &addr, input, env.Db().GetCode(addr), gas, gasPrice, value)
exe.evm = vm.NewVm(env) return ret, err
return exe
} }
// Call executes within the given context // Create creates a new contract with the given code
func (self *Execution) Call(codeAddr common.Address, caller vm.ContextRef) ([]byte, error) { func Create(env vm.Environment, caller vm.ContractRef, code []byte, gas, gasPrice, value *big.Int) (ret []byte, address common.Address, err error) {
// Retrieve the executing code ret, address, err = exec(env, caller, nil, nil, nil, code, gas, gasPrice, value)
code := self.env.State().GetCode(codeAddr)
return self.exec(&codeAddr, code, caller)
}
// Create creates a new contract and runs the initialisation procedure of the
// contract. This returns the returned code for the contract and is stored
// elsewhere.
func (self *Execution) Create(caller vm.ContextRef) (ret []byte, err error, account *state.StateObject) {
// Input must be nil for create
code := self.input
self.input = nil
ret, err = self.exec(nil, code, caller)
// Here we get an error if we run into maximum stack depth, // Here we get an error if we run into maximum stack depth,
// See: https://github.com/ethereum/yellowpaper/pull/131 // See: https://github.com/ethereum/yellowpaper/pull/131
// and YP definitions for CREATE instruction // and YP definitions for CREATE instruction
if err != nil { if err != nil {
return nil, err, nil return nil, address, err
} }
account = self.env.State().GetStateObject(*self.address) return ret, address, err
return
} }
// exec executes the given code and executes within the contextAddr context. func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.Address, input, code []byte, gas, gasPrice, value *big.Int) (ret []byte, addr common.Address, err error) {
func (self *Execution) exec(contextAddr *common.Address, code []byte, caller vm.ContextRef) (ret []byte, err error) { evm := vm.NewVm(env)
env := self.env
evm := self.evm
// Depth check execution. Fail if we're trying to execute above the // Depth check execution. Fail if we're trying to execute above the
// limit. // limit.
if env.Depth() > int(params.CallCreateDepth.Int64()) { if env.Depth() > int(params.CallCreateDepth.Int64()) {
caller.ReturnGas(self.Gas, self.price) caller.ReturnGas(gas, gasPrice)
return nil, vm.DepthError return nil, common.Address{}, vm.DepthError
} }
if !env.CanTransfer(env.State().GetStateObject(caller.Address()), self.value) { if !env.CanTransfer(caller.Address(), value) {
caller.ReturnGas(self.Gas, self.price) caller.ReturnGas(gas, gasPrice)
return nil, ValueTransferErr("insufficient funds to transfer value. Req %v, has %v", self.value, env.State().GetBalance(caller.Address())) return nil, common.Address{}, ValueTransferErr("insufficient funds to transfer value. Req %v, has %v", value, env.Db().GetBalance(caller.Address()))
} }
var createAccount bool var createAccount bool
if self.address == nil { if address == nil {
// Generate a new address // Generate a new address
nonce := env.State().GetNonce(caller.Address()) nonce := env.Db().GetNonce(caller.Address())
env.State().SetNonce(caller.Address(), nonce+1) env.Db().SetNonce(caller.Address(), nonce+1)
addr := crypto.CreateAddress(caller.Address(), nonce) addr = crypto.CreateAddress(caller.Address(), nonce)
self.address = &addr address = &addr
createAccount = true createAccount = true
} }
snapshot := env.State().Copy() snapshot := env.MakeSnapshot()
var ( var (
from = env.State().GetStateObject(caller.Address()) from = env.Db().GetAccount(caller.Address())
to *state.StateObject to vm.Account
) )
if createAccount { if createAccount {
to = env.State().CreateAccount(*self.address) to = env.Db().CreateAccount(*address)
} else { } else {
to = env.State().GetOrNewStateObject(*self.address) if !env.Db().Exist(*address) {
to = env.Db().CreateAccount(*address)
} else {
to = env.Db().GetAccount(*address)
}
} }
vm.Transfer(from, to, self.value) env.Transfer(from, to, value)
context := vm.NewContext(caller, to, self.value, self.Gas, self.price) contract := vm.NewContract(caller, to, value, gas, gasPrice)
context.SetCallCode(contextAddr, code) contract.SetCallCode(codeAddr, code)
ret, err = evm.Run(context, self.input) ret, err = evm.Run(contract, input)
if err != nil { if err != nil {
env.State().Set(snapshot) env.SetSnapshot(snapshot) //env.Db().Set(snapshot)
} }
return return ret, addr, err
}
// generic transfer method
func Transfer(from, to vm.Account, amount *big.Int) error {
if from.Balance().Cmp(amount) < 0 {
return errors.New("Insufficient balance in account")
}
from.SubBalance(amount)
to.AddBalance(amount)
return nil
} }

View File

@ -21,6 +21,7 @@ import (
"math/big" "math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/logger/glog"
@ -42,7 +43,7 @@ type StateDB struct {
thash, bhash common.Hash thash, bhash common.Hash
txIndex int txIndex int
logs map[common.Hash]Logs logs map[common.Hash]vm.Logs
logSize uint logSize uint
} }
@ -59,7 +60,7 @@ func New(root common.Hash, db ethdb.Database) *StateDB {
trie: tr, trie: tr,
stateObjects: make(map[string]*StateObject), stateObjects: make(map[string]*StateObject),
refund: new(big.Int), refund: new(big.Int),
logs: make(map[common.Hash]Logs), logs: make(map[common.Hash]vm.Logs),
} }
} }
@ -69,7 +70,7 @@ func (self *StateDB) StartRecord(thash, bhash common.Hash, ti int) {
self.txIndex = ti self.txIndex = ti
} }
func (self *StateDB) AddLog(log *Log) { func (self *StateDB) AddLog(log *vm.Log) {
log.TxHash = self.thash log.TxHash = self.thash
log.BlockHash = self.bhash log.BlockHash = self.bhash
log.TxIndex = uint(self.txIndex) log.TxIndex = uint(self.txIndex)
@ -78,30 +79,34 @@ func (self *StateDB) AddLog(log *Log) {
self.logSize++ self.logSize++
} }
func (self *StateDB) GetLogs(hash common.Hash) Logs { func (self *StateDB) GetLogs(hash common.Hash) vm.Logs {
return self.logs[hash] return self.logs[hash]
} }
func (self *StateDB) Logs() Logs { func (self *StateDB) Logs() vm.Logs {
var logs Logs var logs vm.Logs
for _, lgs := range self.logs { for _, lgs := range self.logs {
logs = append(logs, lgs...) logs = append(logs, lgs...)
} }
return logs return logs
} }
func (self *StateDB) Refund(gas *big.Int) { func (self *StateDB) AddRefund(gas *big.Int) {
self.refund.Add(self.refund, gas) self.refund.Add(self.refund, gas)
} }
/*
* GETTERS
*/
func (self *StateDB) HasAccount(addr common.Address) bool { func (self *StateDB) HasAccount(addr common.Address) bool {
return self.GetStateObject(addr) != nil return self.GetStateObject(addr) != nil
} }
func (self *StateDB) Exist(addr common.Address) bool {
return self.GetStateObject(addr) != nil
}
func (self *StateDB) GetAccount(addr common.Address) vm.Account {
return self.GetStateObject(addr)
}
// Retrieve the balance from the given address or 0 if object not found // Retrieve the balance from the given address or 0 if object not found
func (self *StateDB) GetBalance(addr common.Address) *big.Int { func (self *StateDB) GetBalance(addr common.Address) *big.Int {
stateObject := self.GetStateObject(addr) stateObject := self.GetStateObject(addr)
@ -245,7 +250,7 @@ func (self *StateDB) SetStateObject(object *StateObject) {
func (self *StateDB) GetOrNewStateObject(addr common.Address) *StateObject { func (self *StateDB) GetOrNewStateObject(addr common.Address) *StateObject {
stateObject := self.GetStateObject(addr) stateObject := self.GetStateObject(addr)
if stateObject == nil || stateObject.deleted { if stateObject == nil || stateObject.deleted {
stateObject = self.CreateAccount(addr) stateObject = self.CreateStateObject(addr)
} }
return stateObject return stateObject
@ -264,7 +269,7 @@ func (self *StateDB) newStateObject(addr common.Address) *StateObject {
} }
// Creates creates a new state object and takes ownership. This is different from "NewStateObject" // Creates creates a new state object and takes ownership. This is different from "NewStateObject"
func (self *StateDB) CreateAccount(addr common.Address) *StateObject { func (self *StateDB) CreateStateObject(addr common.Address) *StateObject {
// Get previous (if any) // Get previous (if any)
so := self.GetStateObject(addr) so := self.GetStateObject(addr)
// Create a new one // Create a new one
@ -278,6 +283,10 @@ func (self *StateDB) CreateAccount(addr common.Address) *StateObject {
return newSo return newSo
} }
func (self *StateDB) CreateAccount(addr common.Address) vm.Account {
return self.CreateStateObject(addr)
}
// //
// Setting, copying of the state methods // Setting, copying of the state methods
// //
@ -292,7 +301,7 @@ func (self *StateDB) Copy() *StateDB {
state.refund.Set(self.refund) state.refund.Set(self.refund)
for hash, logs := range self.logs { for hash, logs := range self.logs {
state.logs[hash] = make(Logs, len(logs)) state.logs[hash] = make(vm.Logs, len(logs))
copy(state.logs[hash], logs) copy(state.logs[hash], logs)
} }
state.logSize = self.logSize state.logSize = self.logSize
@ -309,6 +318,10 @@ func (self *StateDB) Set(state *StateDB) {
self.logSize = state.logSize self.logSize = state.logSize
} }
func (self *StateDB) GetRefund() *big.Int {
return self.refund
}
// IntermediateRoot computes the current root hash of the state trie. // IntermediateRoot computes the current root hash of the state trie.
// It is called in between transactions to get the root hash that // It is called in between transactions to get the root hash that
// goes into transaction receipts. // goes into transaction receipts.

View File

@ -51,7 +51,7 @@ type StateTransition struct {
initialGas *big.Int initialGas *big.Int
value *big.Int value *big.Int
data []byte data []byte
state *state.StateDB state vm.Database
env vm.Environment env vm.Environment
} }
@ -95,11 +95,7 @@ func IntrinsicGas(data []byte) *big.Int {
} }
func ApplyMessage(env vm.Environment, msg Message, gp GasPool) ([]byte, *big.Int, error) { func ApplyMessage(env vm.Environment, msg Message, gp GasPool) ([]byte, *big.Int, error) {
return NewStateTransition(env, msg, gp).transitionState() var st = StateTransition{
}
func NewStateTransition(env vm.Environment, msg Message, gp GasPool) *StateTransition {
return &StateTransition{
gp: gp, gp: gp,
env: env, env: env,
msg: msg, msg: msg,
@ -108,18 +104,22 @@ func NewStateTransition(env vm.Environment, msg Message, gp GasPool) *StateTrans
initialGas: new(big.Int), initialGas: new(big.Int),
value: msg.Value(), value: msg.Value(),
data: msg.Data(), data: msg.Data(),
state: env.State(), state: env.Db(),
} }
return st.transitionDb()
} }
func (self *StateTransition) From() (*state.StateObject, error) { func (self *StateTransition) from() (vm.Account, error) {
f, err := self.msg.From() f, err := self.msg.From()
if err != nil { if err != nil {
return nil, err return nil, err
} }
return self.state.GetOrNewStateObject(f), nil if !self.state.Exist(f) {
return self.state.CreateAccount(f), nil
}
return self.state.GetAccount(f), nil
} }
func (self *StateTransition) To() *state.StateObject { func (self *StateTransition) to() vm.Account {
if self.msg == nil { if self.msg == nil {
return nil return nil
} }
@ -127,10 +127,14 @@ func (self *StateTransition) To() *state.StateObject {
if to == nil { if to == nil {
return nil // contract creation return nil // contract creation
} }
return self.state.GetOrNewStateObject(*to)
if !self.state.Exist(*to) {
return self.state.CreateAccount(*to)
}
return self.state.GetAccount(*to)
} }
func (self *StateTransition) UseGas(amount *big.Int) error { func (self *StateTransition) useGas(amount *big.Int) error {
if self.gas.Cmp(amount) < 0 { if self.gas.Cmp(amount) < 0 {
return vm.OutOfGasError return vm.OutOfGasError
} }
@ -139,15 +143,15 @@ func (self *StateTransition) UseGas(amount *big.Int) error {
return nil return nil
} }
func (self *StateTransition) AddGas(amount *big.Int) { func (self *StateTransition) addGas(amount *big.Int) {
self.gas.Add(self.gas, amount) self.gas.Add(self.gas, amount)
} }
func (self *StateTransition) BuyGas() error { func (self *StateTransition) buyGas() error {
mgas := self.msg.Gas() mgas := self.msg.Gas()
mgval := new(big.Int).Mul(mgas, self.gasPrice) mgval := new(big.Int).Mul(mgas, self.gasPrice)
sender, err := self.From() sender, err := self.from()
if err != nil { if err != nil {
return err return err
} }
@ -157,7 +161,7 @@ func (self *StateTransition) BuyGas() error {
if err = self.gp.SubGas(mgas, self.gasPrice); err != nil { if err = self.gp.SubGas(mgas, self.gasPrice); err != nil {
return err return err
} }
self.AddGas(mgas) self.addGas(mgas)
self.initialGas.Set(mgas) self.initialGas.Set(mgas)
sender.SubBalance(mgval) sender.SubBalance(mgval)
return nil return nil
@ -165,18 +169,19 @@ func (self *StateTransition) BuyGas() error {
func (self *StateTransition) preCheck() (err error) { func (self *StateTransition) preCheck() (err error) {
msg := self.msg msg := self.msg
sender, err := self.From() sender, err := self.from()
if err != nil { if err != nil {
return err return err
} }
// Make sure this transaction's nonce is correct // Make sure this transaction's nonce is correct
if sender.Nonce() != msg.Nonce() { //if sender.Nonce() != msg.Nonce() {
return NonceError(msg.Nonce(), sender.Nonce()) if n := self.state.GetNonce(sender.Address()); n != msg.Nonce() {
return NonceError(msg.Nonce(), n)
} }
// Pre-pay gas / Buy gas of the coinbase account // Pre-pay gas / Buy gas of the coinbase account
if err = self.BuyGas(); err != nil { if err = self.buyGas(); err != nil {
if state.IsGasLimitErr(err) { if state.IsGasLimitErr(err) {
return err return err
} }
@ -186,28 +191,28 @@ func (self *StateTransition) preCheck() (err error) {
return nil return nil
} }
func (self *StateTransition) transitionState() (ret []byte, usedGas *big.Int, err error) { func (self *StateTransition) transitionDb() (ret []byte, usedGas *big.Int, err error) {
if err = self.preCheck(); err != nil { if err = self.preCheck(); err != nil {
return return
} }
msg := self.msg msg := self.msg
sender, _ := self.From() // err checked in preCheck sender, _ := self.from() // err checked in preCheck
// Pay intrinsic gas // Pay intrinsic gas
if err = self.UseGas(IntrinsicGas(self.data)); err != nil { if err = self.useGas(IntrinsicGas(self.data)); err != nil {
return nil, nil, InvalidTxError(err) return nil, nil, InvalidTxError(err)
} }
vmenv := self.env vmenv := self.env
var ref vm.ContextRef var addr common.Address
if MessageCreatesContract(msg) { if MessageCreatesContract(msg) {
ret, err, ref = vmenv.Create(sender, self.data, self.gas, self.gasPrice, self.value) ret, addr, err = vmenv.Create(sender, self.data, self.gas, self.gasPrice, self.value)
if err == nil { if err == nil {
dataGas := big.NewInt(int64(len(ret))) dataGas := big.NewInt(int64(len(ret)))
dataGas.Mul(dataGas, params.CreateDataGas) dataGas.Mul(dataGas, params.CreateDataGas)
if err := self.UseGas(dataGas); err == nil { if err := self.useGas(dataGas); err == nil {
ref.SetCode(ret) self.state.SetCode(addr, ret)
} else { } else {
ret = nil // does not affect consensus but useful for StateTests validations ret = nil // does not affect consensus but useful for StateTests validations
glog.V(logger.Core).Infoln("Insufficient gas for creating code. Require", dataGas, "and have", self.gas) glog.V(logger.Core).Infoln("Insufficient gas for creating code. Require", dataGas, "and have", self.gas)
@ -216,8 +221,8 @@ func (self *StateTransition) transitionState() (ret []byte, usedGas *big.Int, er
glog.V(logger.Core).Infoln("VM create err:", err) glog.V(logger.Core).Infoln("VM create err:", err)
} else { } else {
// Increment the nonce for the next transaction // Increment the nonce for the next transaction
self.state.SetNonce(sender.Address(), sender.Nonce()+1) self.state.SetNonce(sender.Address(), self.state.GetNonce(sender.Address())+1)
ret, err = vmenv.Call(sender, self.To().Address(), self.data, self.gas, self.gasPrice, self.value) ret, err = vmenv.Call(sender, self.to().Address(), self.data, self.gas, self.gasPrice, self.value)
glog.V(logger.Core).Infoln("VM call err:", err) glog.V(logger.Core).Infoln("VM call err:", err)
} }
@ -241,13 +246,13 @@ func (self *StateTransition) transitionState() (ret []byte, usedGas *big.Int, er
} }
func (self *StateTransition) refundGas() { func (self *StateTransition) refundGas() {
sender, _ := self.From() // err already checked sender, _ := self.from() // err already checked
// Return remaining gas // Return remaining gas
remaining := new(big.Int).Mul(self.gas, self.gasPrice) remaining := new(big.Int).Mul(self.gas, self.gasPrice)
sender.AddBalance(remaining) sender.AddBalance(remaining)
uhalf := remaining.Div(self.gasUsed(), common.Big2) uhalf := remaining.Div(self.gasUsed(), common.Big2)
refund := common.BigMin(uhalf, self.state.Refunds()) refund := common.BigMin(uhalf, self.state.GetRefund())
self.gas.Add(self.gas, refund) self.gas.Add(self.gas, refund)
self.state.AddBalance(sender.Address(), refund.Mul(refund, self.gasPrice)) self.state.AddBalance(sender.Address(), refund.Mul(refund, self.gasPrice))

View File

@ -20,7 +20,7 @@ import (
"math/big" "math/big"
"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/vm"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
) )
@ -37,7 +37,7 @@ func CreateBloom(receipts Receipts) Bloom {
return BytesToBloom(bin.Bytes()) return BytesToBloom(bin.Bytes())
} }
func LogsBloom(logs state.Logs) *big.Int { func LogsBloom(logs vm.Logs) *big.Int {
bin := new(big.Int) bin := new(big.Int)
for _, log := range logs { for _, log := range logs {
data := make([]common.Hash, len(log.Topics)) data := make([]common.Hash, len(log.Topics))

View File

@ -19,14 +19,14 @@ package types
import ( import (
"math/big" "math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"fmt" "fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/vm"
) )
type BlockProcessor interface { type BlockProcessor interface {
Process(*Block) (state.Logs, Receipts, error) Process(*Block) (vm.Logs, Receipts, error)
} }
const bloomLength = 256 const bloomLength = 256

View File

@ -23,7 +23,7 @@ import (
"math/big" "math/big"
"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/vm"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
) )
@ -33,7 +33,7 @@ type Receipt struct {
Bloom Bloom Bloom Bloom
TxHash common.Hash TxHash common.Hash
ContractAddress common.Address ContractAddress common.Address
logs state.Logs logs vm.Logs
GasUsed *big.Int GasUsed *big.Int
} }
@ -41,11 +41,11 @@ func NewReceipt(root []byte, cumalativeGasUsed *big.Int) *Receipt {
return &Receipt{PostState: common.CopyBytes(root), CumulativeGasUsed: new(big.Int).Set(cumalativeGasUsed)} return &Receipt{PostState: common.CopyBytes(root), CumulativeGasUsed: new(big.Int).Set(cumalativeGasUsed)}
} }
func (self *Receipt) SetLogs(logs state.Logs) { func (self *Receipt) SetLogs(logs vm.Logs) {
self.logs = logs self.logs = logs
} }
func (self *Receipt) Logs() state.Logs { func (self *Receipt) Logs() vm.Logs {
return self.logs return self.logs
} }
@ -60,7 +60,7 @@ func (self *Receipt) DecodeRLP(s *rlp.Stream) error {
Bloom Bloom Bloom Bloom
TxHash common.Hash TxHash common.Hash
ContractAddress common.Address ContractAddress common.Address
Logs state.Logs Logs vm.Logs
GasUsed *big.Int GasUsed *big.Int
} }
if err := s.Decode(&r); err != nil { if err := s.Decode(&r); err != nil {
@ -74,9 +74,9 @@ func (self *Receipt) DecodeRLP(s *rlp.Stream) error {
type ReceiptForStorage Receipt type ReceiptForStorage Receipt
func (self *ReceiptForStorage) EncodeRLP(w io.Writer) error { func (self *ReceiptForStorage) EncodeRLP(w io.Writer) error {
storageLogs := make([]*state.LogForStorage, len(self.logs)) storageLogs := make([]*vm.LogForStorage, len(self.logs))
for i, log := range self.logs { for i, log := range self.logs {
storageLogs[i] = (*state.LogForStorage)(log) storageLogs[i] = (*vm.LogForStorage)(log)
} }
return rlp.Encode(w, []interface{}{self.PostState, self.CumulativeGasUsed, self.Bloom, self.TxHash, self.ContractAddress, storageLogs, self.GasUsed}) return rlp.Encode(w, []interface{}{self.PostState, self.CumulativeGasUsed, self.Bloom, self.TxHash, self.ContractAddress, storageLogs, self.GasUsed})
} }

View File

@ -23,6 +23,8 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
) )
// Dissassemble dissassembles the byte code and returns the string
// representation (human readable opcodes).
func Disassemble(script []byte) (asm []string) { func Disassemble(script []byte) (asm []string) {
pc := new(big.Int) pc := new(big.Int)
for { for {

View File

@ -22,34 +22,34 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/params"
) )
// Global Debug flag indicating Debug VM (full logging) // Global Debug flag indicating Debug VM (full logging)
var Debug bool var Debug bool
// Type is the VM type accepted by **NewVm**
type Type byte type Type byte
const ( const (
StdVmTy Type = iota StdVmTy Type = iota // Default standard VM
JitVmTy JitVmTy // LLVM JIT VM
MaxVmTy MaxVmTy
LogTyPretty byte = 0x1
LogTyDiff byte = 0x2
) )
var ( var (
Pow256 = common.BigPow(2, 256) Pow256 = common.BigPow(2, 256) // Pew256 is 2**256
U256 = common.U256 U256 = common.U256 // Shortcut to common.U256
S256 = common.S256 S256 = common.S256 // Shortcut to common.S256
Zero = common.Big0 Zero = common.Big0 // Shortcut to common.Big0
One = common.Big1 One = common.Big1 // Shortcut to common.Big1
max = big.NewInt(math.MaxInt64) max = big.NewInt(math.MaxInt64) // Maximum 256 bit integer
) )
// NewVm returns a new VM based on the Environment
func NewVm(env Environment) VirtualMachine { func NewVm(env Environment) VirtualMachine {
switch env.VmType() { switch env.VmType() {
case JitVmTy: case JitVmTy:
@ -62,6 +62,7 @@ func NewVm(env Environment) VirtualMachine {
} }
} }
// calculates the memory size required for a step
func calcMemSize(off, l *big.Int) *big.Int { func calcMemSize(off, l *big.Int) *big.Int {
if l.Cmp(common.Big0) == 0 { if l.Cmp(common.Big0) == 0 {
return common.Big0 return common.Big0
@ -70,6 +71,32 @@ func calcMemSize(off, l *big.Int) *big.Int {
return new(big.Int).Add(off, l) return new(big.Int).Add(off, l)
} }
// calculates the quadratic gas
func quadMemGas(mem *Memory, newMemSize, gas *big.Int) {
if newMemSize.Cmp(common.Big0) > 0 {
newMemSizeWords := toWordSize(newMemSize)
newMemSize.Mul(newMemSizeWords, u256(32))
if newMemSize.Cmp(u256(int64(mem.Len()))) > 0 {
// be careful reusing variables here when changing.
// The order has been optimised to reduce allocation
oldSize := toWordSize(big.NewInt(int64(mem.Len())))
pow := new(big.Int).Exp(oldSize, common.Big2, Zero)
linCoef := oldSize.Mul(oldSize, params.MemoryGas)
quadCoef := new(big.Int).Div(pow, params.QuadCoeffDiv)
oldTotalFee := new(big.Int).Add(linCoef, quadCoef)
pow.Exp(newMemSizeWords, common.Big2, Zero)
linCoef = linCoef.Mul(newMemSizeWords, params.MemoryGas)
quadCoef = quadCoef.Div(pow, params.QuadCoeffDiv)
newTotalFee := linCoef.Add(linCoef, quadCoef)
fee := newTotalFee.Sub(newTotalFee, oldTotalFee)
gas.Add(gas, fee)
}
}
}
// Simple helper // Simple helper
func u256(n int64) *big.Int { func u256(n int64) *big.Int {
return big.NewInt(n) return big.NewInt(n)
@ -86,6 +113,8 @@ func toValue(val *big.Int) interface{} {
return val return val
} }
// getData returns a slice from the data based on the start and size and pads
// up to size with zero's. This function is overflow safe.
func getData(data []byte, start, size *big.Int) []byte { func getData(data []byte, start, size *big.Int) []byte {
dlen := big.NewInt(int64(len(data))) dlen := big.NewInt(int64(len(data)))
@ -94,7 +123,9 @@ func getData(data []byte, start, size *big.Int) []byte {
return common.RightPadBytes(data[s.Uint64():e.Uint64()], int(size.Uint64())) return common.RightPadBytes(data[s.Uint64():e.Uint64()], int(size.Uint64()))
} }
func UseGas(gas, amount *big.Int) bool { // useGas attempts to subtract the amount of gas and returns whether it was
// successful
func useGas(gas, amount *big.Int) bool {
if gas.Cmp(amount) < 0 { if gas.Cmp(amount) < 0 {
return false return false
} }

View File

@ -22,15 +22,18 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
) )
type ContextRef interface { // ContractRef is a reference to the contract's backing object
type ContractRef interface {
ReturnGas(*big.Int, *big.Int) ReturnGas(*big.Int, *big.Int)
Address() common.Address Address() common.Address
SetCode([]byte) SetCode([]byte)
} }
type Context struct { // Contract represents an ethereum contract in the state database. It contains
caller ContextRef // the the contract code, calling arguments. Contract implements ContractReg
self ContextRef type Contract struct {
caller ContractRef
self ContractRef
jumpdests destinations // result of JUMPDEST analysis. jumpdests destinations // result of JUMPDEST analysis.
@ -44,10 +47,10 @@ type Context struct {
} }
// Create a new context for the given data items. // Create a new context for the given data items.
func NewContext(caller ContextRef, object ContextRef, value, gas, price *big.Int) *Context { func NewContract(caller ContractRef, object ContractRef, value, gas, price *big.Int) *Contract {
c := &Context{caller: caller, self: object, Args: nil} c := &Contract{caller: caller, self: object, Args: nil}
if parent, ok := caller.(*Context); ok { if parent, ok := caller.(*Contract); ok {
// Reuse JUMPDEST analysis from parent context if available. // Reuse JUMPDEST analysis from parent context if available.
c.jumpdests = parent.jumpdests c.jumpdests = parent.jumpdests
} else { } else {
@ -66,11 +69,13 @@ func NewContext(caller ContextRef, object ContextRef, value, gas, price *big.Int
return c return c
} }
func (c *Context) GetOp(n uint64) OpCode { // GetOp returns the n'th element in the contract's byte array
func (c *Contract) GetOp(n uint64) OpCode {
return OpCode(c.GetByte(n)) return OpCode(c.GetByte(n))
} }
func (c *Context) GetByte(n uint64) byte { // GetByte returns the n'th byte in the contract's byte array
func (c *Contract) GetByte(n uint64) byte {
if n < uint64(len(c.Code)) { if n < uint64(len(c.Code)) {
return c.Code[n] return c.Code[n]
} }
@ -78,43 +83,44 @@ func (c *Context) GetByte(n uint64) byte {
return 0 return 0
} }
func (c *Context) Return(ret []byte) []byte { // Return returns the given ret argument and returns any remaining gas to the
// caller
func (c *Contract) Return(ret []byte) []byte {
// Return the remaining gas to the caller // Return the remaining gas to the caller
c.caller.ReturnGas(c.Gas, c.Price) c.caller.ReturnGas(c.Gas, c.Price)
return ret return ret
} }
/* // UseGas attempts the use gas and subtracts it and returns true on success
* Gas functions func (c *Contract) UseGas(gas *big.Int) (ok bool) {
*/ ok = useGas(c.Gas, gas)
func (c *Context) UseGas(gas *big.Int) (ok bool) {
ok = UseGas(c.Gas, gas)
if ok { if ok {
c.UsedGas.Add(c.UsedGas, gas) c.UsedGas.Add(c.UsedGas, gas)
} }
return return
} }
// Implement the caller interface // ReturnGas adds the given gas back to itself.
func (c *Context) ReturnGas(gas, price *big.Int) { func (c *Contract) ReturnGas(gas, price *big.Int) {
// Return the gas to the context // Return the gas to the context
c.Gas.Add(c.Gas, gas) c.Gas.Add(c.Gas, gas)
c.UsedGas.Sub(c.UsedGas, gas) c.UsedGas.Sub(c.UsedGas, gas)
} }
/* // Address returns the contracts address
* Set / Get func (c *Contract) Address() common.Address {
*/
func (c *Context) Address() common.Address {
return c.self.Address() return c.self.Address()
} }
func (self *Context) SetCode(code []byte) { // SetCode sets the code to the contract
func (self *Contract) SetCode(code []byte) {
self.Code = code self.Code = code
} }
func (self *Context) SetCallCode(addr *common.Address, code []byte) { // SetCallCode sets the address of the code address and sets the code
// of the contract according to the backing database.
func (self *Contract) SetCallCode(addr *common.Address, code []byte) {
self.Code = code self.Code = code
self.CodeAddr = addr self.CodeAddr = addr
} }

View File

@ -26,22 +26,22 @@ import (
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
) )
type Address interface { // PrecompiledAccount represents a native ethereum contract
Call(in []byte) []byte
}
type PrecompiledAccount struct { type PrecompiledAccount struct {
Gas func(l int) *big.Int Gas func(l int) *big.Int
fn func(in []byte) []byte fn func(in []byte) []byte
} }
// Call calls the native function
func (self PrecompiledAccount) Call(in []byte) []byte { func (self PrecompiledAccount) Call(in []byte) []byte {
return self.fn(in) return self.fn(in)
} }
// Precompiled contains the default set of ethereum contracts
var Precompiled = PrecompiledContracts() var Precompiled = PrecompiledContracts()
// XXX Could set directly. Testing requires resetting and setting of pre compiled contracts. // PrecompiledContracts returns the default set of precompiled ethereum
// contracts defined by the ethereum yellow paper.
func PrecompiledContracts() map[string]*PrecompiledAccount { func PrecompiledContracts() map[string]*PrecompiledAccount {
return map[string]*PrecompiledAccount{ return map[string]*PrecompiledAccount{
// ECRECOVER // ECRECOVER

View File

@ -14,12 +14,19 @@
// 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 vm implements the Ethereum Virtual Machine.
The vm package implements two EVMs, a byte code VM and a JIT VM. The BC
(Byte Code) VM loops over a set of bytes and executes them according to a set
of rules defined in the Ethereum yellow paper. When the BC VM is invokes it
invokes the JIT VM in a seperate goroutine and compiles the byte code in JIT
instructions.
The JIT VM, when invoked, loops around a set of pre-defined instructions until
it either runs of gas, causes an internel error, returns or stops. At a later
stage the JIT VM will see some additional features that will cause sets of
instructions to be compiled down to segments. Segments are sets of instructions
that can be ran in one go saving precious time during execution.
*/
package vm package vm
var (
EnableJit bool // Enables the JIT VM
ForceJit bool // Force the JIT, skip byte VM
MaxProgSize int // Max cache size for JIT Programs
)
const defaultJitMaxCache int = 64

View File

@ -17,39 +17,86 @@
package vm package vm
import ( import (
"errors"
"math/big" "math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
) )
// Environment is is required by the virtual machine to get information from // Environment is is required by the virtual machine to get information from
// it's own isolated environment. For an example see `core.VMEnv` // it's own isolated environment.
type Environment interface {
State() *state.StateDB
// Environment is an EVM requirement and helper which allows access to outside
// information such like states.
type Environment interface {
// The state database
Db() Database
// Creates a restorable snapshot
MakeSnapshot() Database
// Set database to previous snapshot
SetSnapshot(Database)
// Address of the original invoker (first occurance of the VM invoker)
Origin() common.Address Origin() common.Address
// The block number this VM is invoken on
BlockNumber() *big.Int BlockNumber() *big.Int
// The n'th hash ago from this block number
GetHash(n uint64) common.Hash GetHash(n uint64) common.Hash
// The handler's address
Coinbase() common.Address Coinbase() common.Address
// The current time (block time)
Time() *big.Int Time() *big.Int
// Difficulty set on the current block
Difficulty() *big.Int Difficulty() *big.Int
// The gas limit of the block
GasLimit() *big.Int GasLimit() *big.Int
CanTransfer(from Account, balance *big.Int) bool // Determines whether it's possible to transact
CanTransfer(from common.Address, balance *big.Int) bool
// Transfer from to to with amount set
Transfer(from, to Account, amount *big.Int) error Transfer(from, to Account, amount *big.Int) error
AddLog(*state.Log) // Adds a LOG to the state
AddLog(*Log)
// Adds a structured log to the env
AddStructLog(StructLog) AddStructLog(StructLog)
// Returns all coalesced structured logs
StructLogs() []StructLog StructLogs() []StructLog
// Type of the VM
VmType() Type VmType() Type
// Current calling depth
Depth() int Depth() int
SetDepth(i int) SetDepth(i int)
Call(me ContextRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) // Call another contract
CallCode(me ContextRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) Call(me ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error)
Create(me ContextRef, data []byte, gas, price, value *big.Int) ([]byte, error, ContextRef) // Take another's contract code and execute within our own context
CallCode(me ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error)
// Create a new contract
Create(me ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error)
}
// Database is a EVM database for full state querying
type Database interface {
GetAccount(common.Address) Account
CreateAccount(common.Address) Account
AddBalance(common.Address, *big.Int)
GetBalance(common.Address) *big.Int
GetNonce(common.Address) uint64
SetNonce(common.Address, uint64)
GetCode(common.Address) []byte
SetCode(common.Address, []byte)
AddRefund(*big.Int)
GetRefund() *big.Int
GetState(common.Address, common.Hash) common.Hash
SetState(common.Address, common.Hash, common.Hash)
Delete(common.Address) bool
Exist(common.Address) bool
IsDeleted(common.Address) bool
} }
// StructLog is emited to the Environment each cycle and lists information about the curent internal state // StructLog is emited to the Environment each cycle and lists information about the curent internal state
@ -68,18 +115,10 @@ type StructLog struct {
type Account interface { type Account interface {
SubBalance(amount *big.Int) SubBalance(amount *big.Int)
AddBalance(amount *big.Int) AddBalance(amount *big.Int)
SetBalance(*big.Int)
SetNonce(uint64)
Balance() *big.Int Balance() *big.Int
Address() common.Address Address() common.Address
} ReturnGas(*big.Int, *big.Int)
SetCode([]byte)
// generic transfer method
func Transfer(from, to Account, amount *big.Int) error {
if from.Balance().Cmp(amount) < 0 {
return errors.New("Insufficient balance in account")
}
from.SubBalance(amount)
to.AddBalance(amount)
return nil
} }

View File

@ -37,6 +37,7 @@ var (
GasContractByte = big.NewInt(200) GasContractByte = big.NewInt(200)
) )
// baseCheck checks for any stack error underflows
func baseCheck(op OpCode, stack *stack, gas *big.Int) error { func baseCheck(op OpCode, stack *stack, gas *big.Int) error {
// PUSH and DUP are a bit special. They all cost the same but we do want to have checking on stack push limit // PUSH and DUP are a bit special. They all cost the same but we do want to have checking on stack push limit
// PUSH is also allowed to calculate the same price for all PUSHes // PUSH is also allowed to calculate the same price for all PUSHes
@ -63,6 +64,7 @@ func baseCheck(op OpCode, stack *stack, gas *big.Int) error {
return nil return nil
} }
// casts a arbitrary number to the amount of words (sets of 32 bytes)
func toWordSize(size *big.Int) *big.Int { func toWordSize(size *big.Int) *big.Int {
tmp := new(big.Int) tmp := new(big.Int)
tmp.Add(size, u256(31)) tmp.Add(size, u256(31))

View File

@ -20,46 +20,52 @@ import (
"math/big" "math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
) )
type instrFn func(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) type programInstruction interface {
type instrExFn func(instr instruction, ret *big.Int, env Environment, context *Context, memory *Memory, stack *stack) Do(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack)
}
type instrFn func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack)
// Do executes the function. This implements programInstruction
func (fn instrFn) Do(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
fn(instr, pc, env, contract, memory, stack)
}
type instruction struct { type instruction struct {
op OpCode op OpCode
pc uint64 pc uint64
fn instrFn fn instrFn
specFn instrExFn data *big.Int
data *big.Int
gas *big.Int gas *big.Int
spop int spop int
spush int spush int
} }
func opStaticJump(instr instruction, ret *big.Int, env Environment, context *Context, memory *Memory, stack *stack) { func opStaticJump(instr instruction, pc *uint64, ret *big.Int, env Environment, contract *Contract, memory *Memory, stack *stack) {
ret.Set(instr.data) ret.Set(instr.data)
} }
func opAdd(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opAdd(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
x, y := stack.pop(), stack.pop() x, y := stack.pop(), stack.pop()
stack.push(U256(x.Add(x, y))) stack.push(U256(x.Add(x, y)))
} }
func opSub(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opSub(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
x, y := stack.pop(), stack.pop() x, y := stack.pop(), stack.pop()
stack.push(U256(x.Sub(x, y))) stack.push(U256(x.Sub(x, y)))
} }
func opMul(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opMul(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
x, y := stack.pop(), stack.pop() x, y := stack.pop(), stack.pop()
stack.push(U256(x.Mul(x, y))) stack.push(U256(x.Mul(x, y)))
} }
func opDiv(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opDiv(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
x, y := stack.pop(), stack.pop() x, y := stack.pop(), stack.pop()
if y.Cmp(common.Big0) != 0 { if y.Cmp(common.Big0) != 0 {
stack.push(U256(x.Div(x, y))) stack.push(U256(x.Div(x, y)))
@ -68,7 +74,7 @@ func opDiv(instr instruction, env Environment, context *Context, memory *Memory,
} }
} }
func opSdiv(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opSdiv(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
x, y := S256(stack.pop()), S256(stack.pop()) x, y := S256(stack.pop()), S256(stack.pop())
if y.Cmp(common.Big0) == 0 { if y.Cmp(common.Big0) == 0 {
stack.push(new(big.Int)) stack.push(new(big.Int))
@ -88,7 +94,7 @@ func opSdiv(instr instruction, env Environment, context *Context, memory *Memory
} }
} }
func opMod(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opMod(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
x, y := stack.pop(), stack.pop() x, y := stack.pop(), stack.pop()
if y.Cmp(common.Big0) == 0 { if y.Cmp(common.Big0) == 0 {
stack.push(new(big.Int)) stack.push(new(big.Int))
@ -97,7 +103,7 @@ func opMod(instr instruction, env Environment, context *Context, memory *Memory,
} }
} }
func opSmod(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opSmod(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
x, y := S256(stack.pop()), S256(stack.pop()) x, y := S256(stack.pop()), S256(stack.pop())
if y.Cmp(common.Big0) == 0 { if y.Cmp(common.Big0) == 0 {
@ -117,12 +123,12 @@ func opSmod(instr instruction, env Environment, context *Context, memory *Memory
} }
} }
func opExp(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opExp(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
x, y := stack.pop(), stack.pop() x, y := stack.pop(), stack.pop()
stack.push(U256(x.Exp(x, y, Pow256))) stack.push(U256(x.Exp(x, y, Pow256)))
} }
func opSignExtend(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opSignExtend(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
back := stack.pop() back := stack.pop()
if back.Cmp(big.NewInt(31)) < 0 { if back.Cmp(big.NewInt(31)) < 0 {
bit := uint(back.Uint64()*8 + 7) bit := uint(back.Uint64()*8 + 7)
@ -139,12 +145,12 @@ func opSignExtend(instr instruction, env Environment, context *Context, memory *
} }
} }
func opNot(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opNot(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
x := stack.pop() x := stack.pop()
stack.push(U256(x.Not(x))) stack.push(U256(x.Not(x)))
} }
func opLt(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opLt(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
x, y := stack.pop(), stack.pop() x, y := stack.pop(), stack.pop()
if x.Cmp(y) < 0 { if x.Cmp(y) < 0 {
stack.push(big.NewInt(1)) stack.push(big.NewInt(1))
@ -153,7 +159,7 @@ func opLt(instr instruction, env Environment, context *Context, memory *Memory,
} }
} }
func opGt(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opGt(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
x, y := stack.pop(), stack.pop() x, y := stack.pop(), stack.pop()
if x.Cmp(y) > 0 { if x.Cmp(y) > 0 {
stack.push(big.NewInt(1)) stack.push(big.NewInt(1))
@ -162,7 +168,7 @@ func opGt(instr instruction, env Environment, context *Context, memory *Memory,
} }
} }
func opSlt(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opSlt(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
x, y := S256(stack.pop()), S256(stack.pop()) x, y := S256(stack.pop()), S256(stack.pop())
if x.Cmp(S256(y)) < 0 { if x.Cmp(S256(y)) < 0 {
stack.push(big.NewInt(1)) stack.push(big.NewInt(1))
@ -171,7 +177,7 @@ func opSlt(instr instruction, env Environment, context *Context, memory *Memory,
} }
} }
func opSgt(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opSgt(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
x, y := S256(stack.pop()), S256(stack.pop()) x, y := S256(stack.pop()), S256(stack.pop())
if x.Cmp(y) > 0 { if x.Cmp(y) > 0 {
stack.push(big.NewInt(1)) stack.push(big.NewInt(1))
@ -180,7 +186,7 @@ func opSgt(instr instruction, env Environment, context *Context, memory *Memory,
} }
} }
func opEq(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opEq(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
x, y := stack.pop(), stack.pop() x, y := stack.pop(), stack.pop()
if x.Cmp(y) == 0 { if x.Cmp(y) == 0 {
stack.push(big.NewInt(1)) stack.push(big.NewInt(1))
@ -189,7 +195,7 @@ func opEq(instr instruction, env Environment, context *Context, memory *Memory,
} }
} }
func opIszero(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opIszero(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
x := stack.pop() x := stack.pop()
if x.Cmp(common.Big0) > 0 { if x.Cmp(common.Big0) > 0 {
stack.push(new(big.Int)) stack.push(new(big.Int))
@ -198,19 +204,19 @@ func opIszero(instr instruction, env Environment, context *Context, memory *Memo
} }
} }
func opAnd(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opAnd(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
x, y := stack.pop(), stack.pop() x, y := stack.pop(), stack.pop()
stack.push(x.And(x, y)) stack.push(x.And(x, y))
} }
func opOr(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opOr(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
x, y := stack.pop(), stack.pop() x, y := stack.pop(), stack.pop()
stack.push(x.Or(x, y)) stack.push(x.Or(x, y))
} }
func opXor(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opXor(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
x, y := stack.pop(), stack.pop() x, y := stack.pop(), stack.pop()
stack.push(x.Xor(x, y)) stack.push(x.Xor(x, y))
} }
func opByte(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opByte(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
th, val := stack.pop(), stack.pop() th, val := stack.pop(), stack.pop()
if th.Cmp(big.NewInt(32)) < 0 { if th.Cmp(big.NewInt(32)) < 0 {
byte := big.NewInt(int64(common.LeftPadBytes(val.Bytes(), 32)[th.Int64()])) byte := big.NewInt(int64(common.LeftPadBytes(val.Bytes(), 32)[th.Int64()]))
@ -219,7 +225,7 @@ func opByte(instr instruction, env Environment, context *Context, memory *Memory
stack.push(new(big.Int)) stack.push(new(big.Int))
} }
} }
func opAddmod(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opAddmod(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
x, y, z := stack.pop(), stack.pop(), stack.pop() x, y, z := stack.pop(), stack.pop(), stack.pop()
if z.Cmp(Zero) > 0 { if z.Cmp(Zero) > 0 {
add := x.Add(x, y) add := x.Add(x, y)
@ -229,7 +235,7 @@ func opAddmod(instr instruction, env Environment, context *Context, memory *Memo
stack.push(new(big.Int)) stack.push(new(big.Int))
} }
} }
func opMulmod(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opMulmod(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
x, y, z := stack.pop(), stack.pop(), stack.pop() x, y, z := stack.pop(), stack.pop(), stack.pop()
if z.Cmp(Zero) > 0 { if z.Cmp(Zero) > 0 {
mul := x.Mul(x, y) mul := x.Mul(x, y)
@ -240,92 +246,92 @@ func opMulmod(instr instruction, env Environment, context *Context, memory *Memo
} }
} }
func opSha3(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opSha3(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
offset, size := stack.pop(), stack.pop() offset, size := stack.pop(), stack.pop()
hash := crypto.Sha3(memory.Get(offset.Int64(), size.Int64())) hash := crypto.Sha3(memory.Get(offset.Int64(), size.Int64()))
stack.push(common.BytesToBig(hash)) stack.push(common.BytesToBig(hash))
} }
func opAddress(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opAddress(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
stack.push(common.Bytes2Big(context.Address().Bytes())) stack.push(common.Bytes2Big(contract.Address().Bytes()))
} }
func opBalance(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opBalance(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
addr := common.BigToAddress(stack.pop()) addr := common.BigToAddress(stack.pop())
balance := env.State().GetBalance(addr) balance := env.Db().GetBalance(addr)
stack.push(new(big.Int).Set(balance)) stack.push(new(big.Int).Set(balance))
} }
func opOrigin(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opOrigin(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
stack.push(env.Origin().Big()) stack.push(env.Origin().Big())
} }
func opCaller(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opCaller(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
stack.push(common.Bytes2Big(context.caller.Address().Bytes())) stack.push(common.Bytes2Big(contract.caller.Address().Bytes()))
} }
func opCallValue(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opCallValue(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
stack.push(new(big.Int).Set(context.value)) stack.push(new(big.Int).Set(contract.value))
} }
func opCalldataLoad(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opCalldataLoad(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
stack.push(common.Bytes2Big(getData(context.Input, stack.pop(), common.Big32))) stack.push(common.Bytes2Big(getData(contract.Input, stack.pop(), common.Big32)))
} }
func opCalldataSize(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opCalldataSize(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
stack.push(big.NewInt(int64(len(context.Input)))) stack.push(big.NewInt(int64(len(contract.Input))))
} }
func opCalldataCopy(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opCalldataCopy(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
var ( var (
mOff = stack.pop() mOff = stack.pop()
cOff = stack.pop() cOff = stack.pop()
l = stack.pop() l = stack.pop()
) )
memory.Set(mOff.Uint64(), l.Uint64(), getData(context.Input, cOff, l)) memory.Set(mOff.Uint64(), l.Uint64(), getData(contract.Input, cOff, l))
} }
func opExtCodeSize(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opExtCodeSize(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
addr := common.BigToAddress(stack.pop()) addr := common.BigToAddress(stack.pop())
l := big.NewInt(int64(len(env.State().GetCode(addr)))) l := big.NewInt(int64(len(env.Db().GetCode(addr))))
stack.push(l) stack.push(l)
} }
func opCodeSize(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opCodeSize(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
l := big.NewInt(int64(len(context.Code))) l := big.NewInt(int64(len(contract.Code)))
stack.push(l) stack.push(l)
} }
func opCodeCopy(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opCodeCopy(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
var ( var (
mOff = stack.pop() mOff = stack.pop()
cOff = stack.pop() cOff = stack.pop()
l = stack.pop() l = stack.pop()
) )
codeCopy := getData(context.Code, cOff, l) codeCopy := getData(contract.Code, cOff, l)
memory.Set(mOff.Uint64(), l.Uint64(), codeCopy) memory.Set(mOff.Uint64(), l.Uint64(), codeCopy)
} }
func opExtCodeCopy(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opExtCodeCopy(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
var ( var (
addr = common.BigToAddress(stack.pop()) addr = common.BigToAddress(stack.pop())
mOff = stack.pop() mOff = stack.pop()
cOff = stack.pop() cOff = stack.pop()
l = stack.pop() l = stack.pop()
) )
codeCopy := getData(env.State().GetCode(addr), cOff, l) codeCopy := getData(env.Db().GetCode(addr), cOff, l)
memory.Set(mOff.Uint64(), l.Uint64(), codeCopy) memory.Set(mOff.Uint64(), l.Uint64(), codeCopy)
} }
func opGasprice(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opGasprice(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
stack.push(new(big.Int).Set(context.Price)) stack.push(new(big.Int).Set(contract.Price))
} }
func opBlockhash(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opBlockhash(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
num := stack.pop() num := stack.pop()
n := new(big.Int).Sub(env.BlockNumber(), common.Big257) n := new(big.Int).Sub(env.BlockNumber(), common.Big257)
@ -336,43 +342,43 @@ func opBlockhash(instr instruction, env Environment, context *Context, memory *M
} }
} }
func opCoinbase(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opCoinbase(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
stack.push(env.Coinbase().Big()) stack.push(env.Coinbase().Big())
} }
func opTimestamp(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opTimestamp(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
stack.push(U256(new(big.Int).Set(env.Time()))) stack.push(U256(new(big.Int).Set(env.Time())))
} }
func opNumber(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opNumber(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
stack.push(U256(new(big.Int).Set(env.BlockNumber()))) stack.push(U256(new(big.Int).Set(env.BlockNumber())))
} }
func opDifficulty(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opDifficulty(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
stack.push(U256(new(big.Int).Set(env.Difficulty()))) stack.push(U256(new(big.Int).Set(env.Difficulty())))
} }
func opGasLimit(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opGasLimit(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
stack.push(U256(new(big.Int).Set(env.GasLimit()))) stack.push(U256(new(big.Int).Set(env.GasLimit())))
} }
func opPop(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opPop(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
stack.pop() stack.pop()
} }
func opPush(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opPush(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
stack.push(new(big.Int).Set(instr.data)) stack.push(new(big.Int).Set(instr.data))
} }
func opDup(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opDup(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
stack.dup(int(instr.data.Int64())) stack.dup(int(instr.data.Int64()))
} }
func opSwap(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opSwap(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
stack.swap(int(instr.data.Int64())) stack.swap(int(instr.data.Int64()))
} }
func opLog(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opLog(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
n := int(instr.data.Int64()) n := int(instr.data.Int64())
topics := make([]common.Hash, n) topics := make([]common.Hash, n)
mStart, mSize := stack.pop(), stack.pop() mStart, mSize := stack.pop(), stack.pop()
@ -381,85 +387,88 @@ func opLog(instr instruction, env Environment, context *Context, memory *Memory,
} }
d := memory.Get(mStart.Int64(), mSize.Int64()) d := memory.Get(mStart.Int64(), mSize.Int64())
log := state.NewLog(context.Address(), topics, d, env.BlockNumber().Uint64()) log := NewLog(contract.Address(), topics, d, env.BlockNumber().Uint64())
env.AddLog(log) env.AddLog(log)
} }
func opMload(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opMload(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
offset := stack.pop() offset := stack.pop()
val := common.BigD(memory.Get(offset.Int64(), 32)) val := common.BigD(memory.Get(offset.Int64(), 32))
stack.push(val) stack.push(val)
} }
func opMstore(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opMstore(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
// pop value of the stack // pop value of the stack
mStart, val := stack.pop(), stack.pop() mStart, val := stack.pop(), stack.pop()
memory.Set(mStart.Uint64(), 32, common.BigToBytes(val, 256)) memory.Set(mStart.Uint64(), 32, common.BigToBytes(val, 256))
} }
func opMstore8(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opMstore8(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
off, val := stack.pop().Int64(), stack.pop().Int64() off, val := stack.pop().Int64(), stack.pop().Int64()
memory.store[off] = byte(val & 0xff) memory.store[off] = byte(val & 0xff)
} }
func opSload(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opSload(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
loc := common.BigToHash(stack.pop()) loc := common.BigToHash(stack.pop())
val := env.State().GetState(context.Address(), loc).Big() val := env.Db().GetState(contract.Address(), loc).Big()
stack.push(val) stack.push(val)
} }
func opSstore(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opSstore(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
loc := common.BigToHash(stack.pop()) loc := common.BigToHash(stack.pop())
val := stack.pop() val := stack.pop()
env.State().SetState(context.Address(), loc, common.BigToHash(val)) env.Db().SetState(contract.Address(), loc, common.BigToHash(val))
} }
func opJump(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {} func opJump(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
func opJumpi(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {} }
func opJumpdest(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {} func opJumpi(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
}
func opJumpdest(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
}
func opPc(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opPc(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
stack.push(new(big.Int).Set(instr.data)) stack.push(new(big.Int).Set(instr.data))
} }
func opMsize(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opMsize(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
stack.push(big.NewInt(int64(memory.Len()))) stack.push(big.NewInt(int64(memory.Len())))
} }
func opGas(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opGas(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
stack.push(new(big.Int).Set(context.Gas)) stack.push(new(big.Int).Set(contract.Gas))
} }
func opCreate(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opCreate(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
var ( var (
value = stack.pop() value = stack.pop()
offset, size = stack.pop(), stack.pop() offset, size = stack.pop(), stack.pop()
input = memory.Get(offset.Int64(), size.Int64()) input = memory.Get(offset.Int64(), size.Int64())
gas = new(big.Int).Set(context.Gas) gas = new(big.Int).Set(contract.Gas)
addr common.Address addr common.Address
ret []byte
suberr error
) )
context.UseGas(context.Gas) contract.UseGas(contract.Gas)
ret, suberr, ref := env.Create(context, input, gas, context.Price, value) ret, addr, suberr = env.Create(contract, input, gas, contract.Price, value)
if suberr != nil { if suberr != nil {
stack.push(new(big.Int)) stack.push(new(big.Int))
} else { } else {
// gas < len(ret) * Createinstr.dataGas == NO_CODE // gas < len(ret) * Createinstr.dataGas == NO_CODE
dataGas := big.NewInt(int64(len(ret))) dataGas := big.NewInt(int64(len(ret)))
dataGas.Mul(dataGas, params.CreateDataGas) dataGas.Mul(dataGas, params.CreateDataGas)
if context.UseGas(dataGas) { if contract.UseGas(dataGas) {
ref.SetCode(ret) env.Db().SetCode(addr, ret)
} }
addr = ref.Address()
stack.push(addr.Big()) stack.push(addr.Big())
} }
} }
func opCall(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opCall(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
gas := stack.pop() gas := stack.pop()
// pop gas and value of the stack. // pop gas and value of the stack.
addr, value := stack.pop(), stack.pop() addr, value := stack.pop(), stack.pop()
@ -478,7 +487,7 @@ func opCall(instr instruction, env Environment, context *Context, memory *Memory
gas.Add(gas, params.CallStipend) gas.Add(gas, params.CallStipend)
} }
ret, err := env.Call(context, address, args, gas, context.Price, value) ret, err := env.Call(contract, address, args, gas, contract.Price, value)
if err != nil { if err != nil {
stack.push(new(big.Int)) stack.push(new(big.Int))
@ -490,7 +499,7 @@ func opCall(instr instruction, env Environment, context *Context, memory *Memory
} }
} }
func opCallCode(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { func opCallCode(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
gas := stack.pop() gas := stack.pop()
// pop gas and value of the stack. // pop gas and value of the stack.
addr, value := stack.pop(), stack.pop() addr, value := stack.pop(), stack.pop()
@ -509,7 +518,7 @@ func opCallCode(instr instruction, env Environment, context *Context, memory *Me
gas.Add(gas, params.CallStipend) gas.Add(gas, params.CallStipend)
} }
ret, err := env.CallCode(context, address, args, gas, context.Price, value) ret, err := env.CallCode(contract, address, args, gas, contract.Price, value)
if err != nil { if err != nil {
stack.push(new(big.Int)) stack.push(new(big.Int))
@ -521,14 +530,58 @@ func opCallCode(instr instruction, env Environment, context *Context, memory *Me
} }
} }
func opReturn(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {} func opReturn(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
func opStop(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) {} }
func opStop(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
func opSuicide(instr instruction, env Environment, context *Context, memory *Memory, stack *stack) { }
receiver := env.State().GetOrNewStateObject(common.BigToAddress(stack.pop()))
balance := env.State().GetBalance(context.Address()) func opSuicide(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
//receiver := env.Db().GetOrNewStateObject(common.BigToAddress(stack.pop()))
receiver.AddBalance(balance) //receiver.AddBalance(balance)
balance := env.Db().GetBalance(contract.Address())
env.State().Delete(context.Address()) env.Db().AddBalance(common.BigToAddress(stack.pop()), balance)
env.Db().Delete(contract.Address())
}
// following functions are used by the instruction jump table
// make log instruction function
func makeLog(size int) instrFn {
return func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
topics := make([]common.Hash, size)
mStart, mSize := stack.pop(), stack.pop()
for i := 0; i < size; i++ {
topics[i] = common.BigToHash(stack.pop())
}
d := memory.Get(mStart.Int64(), mSize.Int64())
log := NewLog(contract.Address(), topics, d, env.BlockNumber().Uint64())
env.AddLog(log)
}
}
// make push instruction function
func makePush(size uint64, bsize *big.Int) instrFn {
return func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
byts := getData(contract.Code, new(big.Int).SetUint64(*pc+1), bsize)
stack.push(common.Bytes2Big(byts))
*pc += size
}
}
// make push instruction function
func makeDup(size int64) instrFn {
return func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
stack.dup(int(size))
}
}
// make swap instruction function
func makeSwap(size int64) instrFn {
// switch n + 1 otherwise n would be swapped with n
size += 1
return func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
stack.swap(int(size))
}
} }

View File

@ -20,10 +20,12 @@ import (
"fmt" "fmt"
"math/big" "math/big"
"sync/atomic" "sync/atomic"
"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/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/hashicorp/golang-lru" "github.com/hashicorp/golang-lru"
) )
@ -35,6 +37,14 @@ const (
progCompile progCompile
progReady progReady
progError progError
defaultJitMaxCache int = 64
)
var (
EnableJit bool // Enables the JIT VM
ForceJit bool // Force the JIT, skip byte VM
MaxProgSize int // Max cache size for JIT Programs
) )
var programs *lru.Cache var programs *lru.Cache
@ -74,7 +84,7 @@ type Program struct {
Id common.Hash // Id of the program Id common.Hash // Id of the program
status int32 // status should be accessed atomically status int32 // status should be accessed atomically
context *Context contract *Contract
instructions []instruction // instruction set instructions []instruction // instruction set
mapping map[uint64]int // real PC mapping to array indices mapping map[uint64]int // real PC mapping to array indices
@ -108,7 +118,7 @@ func (p *Program) addInstr(op OpCode, pc uint64, fn instrFn, data *big.Int) {
baseOp = DUP1 baseOp = DUP1
} }
base := _baseCheck[baseOp] base := _baseCheck[baseOp]
instr := instruction{op, pc, fn, nil, data, base.gas, base.stackPop, base.stackPush} instr := instruction{op, pc, fn, data, base.gas, base.stackPop, base.stackPush}
p.instructions = append(p.instructions, instr) p.instructions = append(p.instructions, instr)
p.mapping[pc] = len(p.instructions) - 1 p.mapping[pc] = len(p.instructions) - 1
@ -127,6 +137,13 @@ func CompileProgram(program *Program) (err error) {
atomic.StoreInt32(&program.status, int32(progReady)) atomic.StoreInt32(&program.status, int32(progReady))
} }
}() }()
if glog.V(logger.Debug) {
glog.Infof("compiling %x\n", program.Id[:4])
tstart := time.Now()
defer func() {
glog.Infof("compiled %x instrc: %d time: %v\n", program.Id[:4], len(program.instructions), time.Since(tstart))
}()
}
// loop thru the opcodes and "compile" in to instructions // loop thru the opcodes and "compile" in to instructions
for pc := uint64(0); pc < uint64(len(program.code)); pc++ { for pc := uint64(0); pc < uint64(len(program.code)); pc++ {
@ -264,7 +281,7 @@ func CompileProgram(program *Program) (err error) {
program.addInstr(op, pc, opReturn, nil) program.addInstr(op, pc, opReturn, nil)
case SUICIDE: case SUICIDE:
program.addInstr(op, pc, opSuicide, nil) program.addInstr(op, pc, opSuicide, nil)
case STOP: // Stop the context case STOP: // Stop the contract
program.addInstr(op, pc, opStop, nil) program.addInstr(op, pc, opStop, nil)
default: default:
program.addInstr(op, pc, nil, nil) program.addInstr(op, pc, nil, nil)
@ -274,23 +291,24 @@ func CompileProgram(program *Program) (err error) {
return nil return nil
} }
// RunProgram runs the program given the enviroment and context and returns an // RunProgram runs the program given the enviroment and contract and returns an
// error if the execution failed (non-consensus) // error if the execution failed (non-consensus)
func RunProgram(program *Program, env Environment, context *Context, input []byte) ([]byte, error) { func RunProgram(program *Program, env Environment, contract *Contract, input []byte) ([]byte, error) {
return runProgram(program, 0, NewMemory(), newstack(), env, context, input) return runProgram(program, 0, NewMemory(), newstack(), env, contract, input)
} }
func runProgram(program *Program, pcstart uint64, mem *Memory, stack *stack, env Environment, context *Context, input []byte) ([]byte, error) { func runProgram(program *Program, pcstart uint64, mem *Memory, stack *stack, env Environment, contract *Contract, input []byte) ([]byte, error) {
context.Input = input contract.Input = input
var ( var (
caller = context.caller caller = contract.caller
statedb = env.State() statedb = env.Db()
pc int = program.mapping[pcstart] pc int = program.mapping[pcstart]
instrCount = 0
jump = func(to *big.Int) error { jump = func(to *big.Int) error {
if !validDest(program.destinations, to) { if !validDest(program.destinations, to) {
nop := context.GetOp(to.Uint64()) nop := contract.GetOp(to.Uint64())
return fmt.Errorf("invalid jump destination (%v) %v", nop, to) return fmt.Errorf("invalid jump destination (%v) %v", nop, to)
} }
@ -300,18 +318,28 @@ func runProgram(program *Program, pcstart uint64, mem *Memory, stack *stack, env
} }
) )
if glog.V(logger.Debug) {
glog.Infof("running JIT program %x\n", program.Id[:4])
tstart := time.Now()
defer func() {
glog.Infof("JIT program %x done. time: %v instrc: %v\n", program.Id[:4], time.Since(tstart), instrCount)
}()
}
for pc < len(program.instructions) { for pc < len(program.instructions) {
instrCount++
instr := program.instructions[pc] instr := program.instructions[pc]
// calculate the new memory size and gas price for the current executing opcode // calculate the new memory size and gas price for the current executing opcode
newMemSize, cost, err := jitCalculateGasAndSize(env, context, caller, instr, statedb, mem, stack) newMemSize, cost, err := jitCalculateGasAndSize(env, contract, caller, instr, statedb, mem, stack)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Use the calculated gas. When insufficient gas is present, use all gas and return an // Use the calculated gas. When insufficient gas is present, use all gas and return an
// Out Of Gas error // Out Of Gas error
if !context.UseGas(cost) { if !contract.UseGas(cost) {
return nil, OutOfGasError return nil, OutOfGasError
} }
// Resize the memory calculated previously // Resize the memory calculated previously
@ -338,27 +366,27 @@ func runProgram(program *Program, pcstart uint64, mem *Memory, stack *stack, env
offset, size := stack.pop(), stack.pop() offset, size := stack.pop(), stack.pop()
ret := mem.GetPtr(offset.Int64(), size.Int64()) ret := mem.GetPtr(offset.Int64(), size.Int64())
return context.Return(ret), nil return contract.Return(ret), nil
case SUICIDE: case SUICIDE:
instr.fn(instr, env, context, mem, stack) instr.fn(instr, nil, env, contract, mem, stack)
return context.Return(nil), nil return contract.Return(nil), nil
case STOP: case STOP:
return context.Return(nil), nil return contract.Return(nil), nil
default: default:
if instr.fn == nil { if instr.fn == nil {
return nil, fmt.Errorf("Invalid opcode %x", instr.op) return nil, fmt.Errorf("Invalid opcode %x", instr.op)
} }
instr.fn(instr, env, context, mem, stack) instr.fn(instr, nil, env, contract, mem, stack)
} }
pc++ pc++
} }
context.Input = nil contract.Input = nil
return context.Return(nil), nil return contract.Return(nil), nil
} }
// validDest checks if the given distination is a valid one given the // validDest checks if the given distination is a valid one given the
@ -375,7 +403,7 @@ func validDest(dests map[uint64]struct{}, dest *big.Int) bool {
// jitCalculateGasAndSize calculates the required given the opcode and stack items calculates the new memorysize for // jitCalculateGasAndSize calculates the required given the opcode and stack items calculates the new memorysize for
// the operation. This does not reduce gas or resizes the memory. // the operation. This does not reduce gas or resizes the memory.
func jitCalculateGasAndSize(env Environment, context *Context, caller ContextRef, instr instruction, statedb *state.StateDB, mem *Memory, stack *stack) (*big.Int, *big.Int, error) { func jitCalculateGasAndSize(env Environment, contract *Contract, caller ContractRef, instr instruction, statedb Database, mem *Memory, stack *stack) (*big.Int, *big.Int, error) {
var ( var (
gas = new(big.Int) gas = new(big.Int)
newMemSize *big.Int = new(big.Int) newMemSize *big.Int = new(big.Int)
@ -426,27 +454,25 @@ func jitCalculateGasAndSize(env Environment, context *Context, caller ContextRef
var g *big.Int var g *big.Int
y, x := stack.data[stack.len()-2], stack.data[stack.len()-1] y, x := stack.data[stack.len()-2], stack.data[stack.len()-1]
val := statedb.GetState(context.Address(), common.BigToHash(x)) val := statedb.GetState(contract.Address(), common.BigToHash(x))
// This checks for 3 scenario's and calculates gas accordingly // This checks for 3 scenario's and calculates gas accordingly
// 1. From a zero-value address to a non-zero value (NEW VALUE) // 1. From a zero-value address to a non-zero value (NEW VALUE)
// 2. From a non-zero value address to a zero-value address (DELETE) // 2. From a non-zero value address to a zero-value address (DELETE)
// 3. From a nen-zero to a non-zero (CHANGE) // 3. From a nen-zero to a non-zero (CHANGE)
if common.EmptyHash(val) && !common.EmptyHash(common.BigToHash(y)) { if common.EmptyHash(val) && !common.EmptyHash(common.BigToHash(y)) {
// 0 => non 0
g = params.SstoreSetGas g = params.SstoreSetGas
} else if !common.EmptyHash(val) && common.EmptyHash(common.BigToHash(y)) { } else if !common.EmptyHash(val) && common.EmptyHash(common.BigToHash(y)) {
statedb.Refund(params.SstoreRefundGas) statedb.AddRefund(params.SstoreRefundGas)
g = params.SstoreClearGas g = params.SstoreClearGas
} else { } else {
// non 0 => non 0 (or 0 => 0)
g = params.SstoreClearGas g = params.SstoreClearGas
} }
gas.Set(g) gas.Set(g)
case SUICIDE: case SUICIDE:
if !statedb.IsDeleted(context.Address()) { if !statedb.IsDeleted(contract.Address()) {
statedb.Refund(params.SuicideRefundGas) statedb.AddRefund(params.SuicideRefundGas)
} }
case MLOAD: case MLOAD:
newMemSize = calcMemSize(stack.peek(), u256(32)) newMemSize = calcMemSize(stack.peek(), u256(32))
@ -483,7 +509,8 @@ func jitCalculateGasAndSize(env Environment, context *Context, caller ContextRef
gas.Add(gas, stack.data[stack.len()-1]) gas.Add(gas, stack.data[stack.len()-1])
if op == CALL { if op == CALL {
if env.State().GetStateObject(common.BigToAddress(stack.data[stack.len()-2])) == nil { //if env.Db().GetStateObject(common.BigToAddress(stack.data[stack.len()-2])) == nil {
if !env.Db().Exist(common.BigToAddress(stack.data[stack.len()-2])) {
gas.Add(gas, params.CallNewAccountGas) gas.Add(gas, params.CallNewAccountGas)
} }
} }
@ -497,29 +524,7 @@ func jitCalculateGasAndSize(env Environment, context *Context, caller ContextRef
newMemSize = common.BigMax(x, y) newMemSize = common.BigMax(x, y)
} }
quadMemGas(mem, newMemSize, gas)
if newMemSize.Cmp(common.Big0) > 0 {
newMemSizeWords := toWordSize(newMemSize)
newMemSize.Mul(newMemSizeWords, u256(32))
if newMemSize.Cmp(u256(int64(mem.Len()))) > 0 {
// be careful reusing variables here when changing.
// The order has been optimised to reduce allocation
oldSize := toWordSize(big.NewInt(int64(mem.Len())))
pow := new(big.Int).Exp(oldSize, common.Big2, Zero)
linCoef := oldSize.Mul(oldSize, params.MemoryGas)
quadCoef := new(big.Int).Div(pow, params.QuadCoeffDiv)
oldTotalFee := new(big.Int).Add(linCoef, quadCoef)
pow.Exp(newMemSizeWords, common.Big2, Zero)
linCoef = linCoef.Mul(newMemSizeWords, params.MemoryGas)
quadCoef = quadCoef.Div(pow, params.QuadCoeffDiv)
newTotalFee := linCoef.Add(linCoef, quadCoef)
fee := newTotalFee.Sub(newTotalFee, oldTotalFee)
gas.Add(gas, fee)
}
}
return newMemSize, gas, nil return newMemSize, gas, nil
} }

View File

@ -21,45 +21,42 @@ import (
"time" "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/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
) )
const maxRun = 1000 const maxRun = 1000
type vmBench struct { func TestCompiling(t *testing.T) {
precompile bool // compile prior to executing prog := NewProgram([]byte{0x60, 0x10})
nojit bool // ignore jit (sets DisbaleJit = true err := CompileProgram(prog)
forcejit bool // forces the jit, precompile is ignored if err != nil {
t.Error("didn't expect compile error")
}
code []byte if len(prog.instructions) != 1 {
input []byte t.Error("exected 1 compiled instruction, got", len(prog.instructions))
}
} }
func runVmBench(test vmBench, b *testing.B) { func TestResetInput(t *testing.T) {
db, _ := ethdb.NewMemDatabase() var sender account
sender := state.NewStateObject(common.Address{}, db)
if test.precompile && !test.forcejit {
NewProgram(test.code)
}
env := NewEnv() env := NewEnv()
contract := NewContract(sender, sender, big.NewInt(100), big.NewInt(10000), big.NewInt(0))
contract.CodeAddr = &common.Address{}
EnableJit = !test.nojit program := NewProgram([]byte{})
ForceJit = test.forcejit RunProgram(program, env, contract, []byte{0xbe, 0xef})
if contract.Input != nil {
t.Errorf("expected input to be nil, got %x", contract.Input)
}
}
b.ResetTimer() func TestPcMappingToInstruction(t *testing.T) {
program := NewProgram([]byte{byte(PUSH2), 0xbe, 0xef, byte(ADD)})
for i := 0; i < b.N; i++ { CompileProgram(program)
context := NewContext(sender, sender, big.NewInt(100), big.NewInt(10000), big.NewInt(0)) if program.mapping[3] != 1 {
context.Code = test.code t.Error("expected mapping PC 4 to me instr no. 2, got", program.mapping[4])
context.CodeAddr = &common.Address{}
_, err := New(env).Run(context, test.input)
if err != nil {
b.Error(err)
b.FailNow()
}
} }
} }
@ -74,6 +71,51 @@ func BenchmarkPushes(b *testing.B) {
runVmBench(benchmarks["pushes"], b) runVmBench(benchmarks["pushes"], b)
} }
type vmBench struct {
precompile bool // compile prior to executing
nojit bool // ignore jit (sets DisbaleJit = true
forcejit bool // forces the jit, precompile is ignored
code []byte
input []byte
}
type account struct{}
func (account) SubBalance(amount *big.Int) {}
func (account) AddBalance(amount *big.Int) {}
func (account) SetBalance(*big.Int) {}
func (account) SetNonce(uint64) {}
func (account) Balance() *big.Int { return nil }
func (account) Address() common.Address { return common.Address{} }
func (account) ReturnGas(*big.Int, *big.Int) {}
func (account) SetCode([]byte) {}
func runVmBench(test vmBench, b *testing.B) {
var sender account
if test.precompile && !test.forcejit {
NewProgram(test.code)
}
env := NewEnv()
EnableJit = !test.nojit
ForceJit = test.forcejit
b.ResetTimer()
for i := 0; i < b.N; i++ {
context := NewContract(sender, sender, big.NewInt(100), big.NewInt(10000), big.NewInt(0))
context.Code = test.code
context.CodeAddr = &common.Address{}
_, err := New(env).Run(context, test.input)
if err != nil {
b.Error(err)
b.FailNow()
}
}
}
type Env struct { type Env struct {
gasLimit *big.Int gasLimit *big.Int
depth int depth int
@ -93,30 +135,32 @@ func (self *Env) StructLogs() []StructLog {
//func (self *Env) PrevHash() []byte { return self.parent } //func (self *Env) PrevHash() []byte { return self.parent }
func (self *Env) Coinbase() common.Address { return common.Address{} } func (self *Env) Coinbase() common.Address { return common.Address{} }
func (self *Env) MakeSnapshot() Database { return nil }
func (self *Env) SetSnapshot(Database) {}
func (self *Env) Time() *big.Int { return big.NewInt(time.Now().Unix()) } func (self *Env) Time() *big.Int { return big.NewInt(time.Now().Unix()) }
func (self *Env) Difficulty() *big.Int { return big.NewInt(0) } func (self *Env) Difficulty() *big.Int { return big.NewInt(0) }
func (self *Env) State() *state.StateDB { return nil } func (self *Env) Db() Database { return nil }
func (self *Env) GasLimit() *big.Int { return self.gasLimit } func (self *Env) GasLimit() *big.Int { return self.gasLimit }
func (self *Env) VmType() Type { return StdVmTy } func (self *Env) VmType() Type { return StdVmTy }
func (self *Env) GetHash(n uint64) common.Hash { func (self *Env) GetHash(n uint64) common.Hash {
return common.BytesToHash(crypto.Sha3([]byte(big.NewInt(int64(n)).String()))) return common.BytesToHash(crypto.Sha3([]byte(big.NewInt(int64(n)).String())))
} }
func (self *Env) AddLog(log *state.Log) { func (self *Env) AddLog(log *Log) {
} }
func (self *Env) Depth() int { return self.depth } func (self *Env) Depth() int { return self.depth }
func (self *Env) SetDepth(i int) { self.depth = i } func (self *Env) SetDepth(i int) { self.depth = i }
func (self *Env) CanTransfer(from Account, balance *big.Int) bool { func (self *Env) CanTransfer(from common.Address, balance *big.Int) bool {
return from.Balance().Cmp(balance) >= 0 return true
} }
func (self *Env) Transfer(from, to Account, amount *big.Int) error { func (self *Env) Transfer(from, to Account, amount *big.Int) error {
return nil return nil
} }
func (self *Env) Call(caller ContextRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) { func (self *Env) Call(caller ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
return nil, nil return nil, nil
} }
func (self *Env) CallCode(caller ContextRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) { func (self *Env) CallCode(caller ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
return nil, nil return nil, nil
} }
func (self *Env) Create(caller ContextRef, data []byte, gas, price, value *big.Int) ([]byte, error, ContextRef) { func (self *Env) Create(caller ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) {
return nil, nil, nil return nil, common.Address{}, nil
} }

143
core/vm/jump_table.go Normal file
View File

@ -0,0 +1,143 @@
package vm
import "math/big"
type jumpPtr struct {
fn instrFn
valid bool
}
var jumpTable [256]jumpPtr
func init() {
jumpTable[ADD] = jumpPtr{opAdd, true}
jumpTable[SUB] = jumpPtr{opSub, true}
jumpTable[MUL] = jumpPtr{opMul, true}
jumpTable[DIV] = jumpPtr{opDiv, true}
jumpTable[SDIV] = jumpPtr{opSdiv, true}
jumpTable[MOD] = jumpPtr{opMod, true}
jumpTable[SMOD] = jumpPtr{opSmod, true}
jumpTable[EXP] = jumpPtr{opExp, true}
jumpTable[SIGNEXTEND] = jumpPtr{opSignExtend, true}
jumpTable[NOT] = jumpPtr{opNot, true}
jumpTable[LT] = jumpPtr{opLt, true}
jumpTable[GT] = jumpPtr{opGt, true}
jumpTable[SLT] = jumpPtr{opSlt, true}
jumpTable[SGT] = jumpPtr{opSgt, true}
jumpTable[EQ] = jumpPtr{opEq, true}
jumpTable[ISZERO] = jumpPtr{opIszero, true}
jumpTable[AND] = jumpPtr{opAnd, true}
jumpTable[OR] = jumpPtr{opOr, true}
jumpTable[XOR] = jumpPtr{opXor, true}
jumpTable[BYTE] = jumpPtr{opByte, true}
jumpTable[ADDMOD] = jumpPtr{opAddmod, true}
jumpTable[MULMOD] = jumpPtr{opMulmod, true}
jumpTable[SHA3] = jumpPtr{opSha3, true}
jumpTable[ADDRESS] = jumpPtr{opAddress, true}
jumpTable[BALANCE] = jumpPtr{opBalance, true}
jumpTable[ORIGIN] = jumpPtr{opOrigin, true}
jumpTable[CALLER] = jumpPtr{opCaller, true}
jumpTable[CALLVALUE] = jumpPtr{opCallValue, true}
jumpTable[CALLDATALOAD] = jumpPtr{opCalldataLoad, true}
jumpTable[CALLDATASIZE] = jumpPtr{opCalldataSize, true}
jumpTable[CALLDATACOPY] = jumpPtr{opCalldataCopy, true}
jumpTable[CODESIZE] = jumpPtr{opCodeSize, true}
jumpTable[EXTCODESIZE] = jumpPtr{opExtCodeSize, true}
jumpTable[CODECOPY] = jumpPtr{opCodeCopy, true}
jumpTable[EXTCODECOPY] = jumpPtr{opExtCodeCopy, true}
jumpTable[GASPRICE] = jumpPtr{opGasprice, true}
jumpTable[BLOCKHASH] = jumpPtr{opBlockhash, true}
jumpTable[COINBASE] = jumpPtr{opCoinbase, true}
jumpTable[TIMESTAMP] = jumpPtr{opTimestamp, true}
jumpTable[NUMBER] = jumpPtr{opNumber, true}
jumpTable[DIFFICULTY] = jumpPtr{opDifficulty, true}
jumpTable[GASLIMIT] = jumpPtr{opGasLimit, true}
jumpTable[POP] = jumpPtr{opPop, true}
jumpTable[MLOAD] = jumpPtr{opMload, true}
jumpTable[MSTORE] = jumpPtr{opMstore, true}
jumpTable[MSTORE8] = jumpPtr{opMstore8, true}
jumpTable[SLOAD] = jumpPtr{opSload, true}
jumpTable[SSTORE] = jumpPtr{opSstore, true}
jumpTable[JUMPDEST] = jumpPtr{opJumpdest, true}
jumpTable[PC] = jumpPtr{nil, true}
jumpTable[MSIZE] = jumpPtr{opMsize, true}
jumpTable[GAS] = jumpPtr{opGas, true}
jumpTable[CREATE] = jumpPtr{opCreate, true}
jumpTable[CALL] = jumpPtr{opCall, true}
jumpTable[CALLCODE] = jumpPtr{opCallCode, true}
jumpTable[LOG0] = jumpPtr{makeLog(0), true}
jumpTable[LOG1] = jumpPtr{makeLog(1), true}
jumpTable[LOG2] = jumpPtr{makeLog(2), true}
jumpTable[LOG3] = jumpPtr{makeLog(3), true}
jumpTable[LOG4] = jumpPtr{makeLog(4), true}
jumpTable[SWAP1] = jumpPtr{makeSwap(1), true}
jumpTable[SWAP2] = jumpPtr{makeSwap(2), true}
jumpTable[SWAP3] = jumpPtr{makeSwap(3), true}
jumpTable[SWAP4] = jumpPtr{makeSwap(4), true}
jumpTable[SWAP5] = jumpPtr{makeSwap(5), true}
jumpTable[SWAP6] = jumpPtr{makeSwap(6), true}
jumpTable[SWAP7] = jumpPtr{makeSwap(7), true}
jumpTable[SWAP8] = jumpPtr{makeSwap(8), true}
jumpTable[SWAP9] = jumpPtr{makeSwap(9), true}
jumpTable[SWAP10] = jumpPtr{makeSwap(10), true}
jumpTable[SWAP11] = jumpPtr{makeSwap(11), true}
jumpTable[SWAP12] = jumpPtr{makeSwap(12), true}
jumpTable[SWAP13] = jumpPtr{makeSwap(13), true}
jumpTable[SWAP14] = jumpPtr{makeSwap(14), true}
jumpTable[SWAP15] = jumpPtr{makeSwap(15), true}
jumpTable[SWAP16] = jumpPtr{makeSwap(16), true}
jumpTable[PUSH1] = jumpPtr{makePush(1, big.NewInt(1)), true}
jumpTable[PUSH2] = jumpPtr{makePush(2, big.NewInt(2)), true}
jumpTable[PUSH3] = jumpPtr{makePush(3, big.NewInt(3)), true}
jumpTable[PUSH4] = jumpPtr{makePush(4, big.NewInt(4)), true}
jumpTable[PUSH5] = jumpPtr{makePush(5, big.NewInt(5)), true}
jumpTable[PUSH6] = jumpPtr{makePush(6, big.NewInt(6)), true}
jumpTable[PUSH7] = jumpPtr{makePush(7, big.NewInt(7)), true}
jumpTable[PUSH8] = jumpPtr{makePush(8, big.NewInt(8)), true}
jumpTable[PUSH9] = jumpPtr{makePush(9, big.NewInt(9)), true}
jumpTable[PUSH10] = jumpPtr{makePush(10, big.NewInt(10)), true}
jumpTable[PUSH11] = jumpPtr{makePush(11, big.NewInt(11)), true}
jumpTable[PUSH12] = jumpPtr{makePush(12, big.NewInt(12)), true}
jumpTable[PUSH13] = jumpPtr{makePush(13, big.NewInt(13)), true}
jumpTable[PUSH14] = jumpPtr{makePush(14, big.NewInt(14)), true}
jumpTable[PUSH15] = jumpPtr{makePush(15, big.NewInt(15)), true}
jumpTable[PUSH16] = jumpPtr{makePush(16, big.NewInt(16)), true}
jumpTable[PUSH17] = jumpPtr{makePush(17, big.NewInt(17)), true}
jumpTable[PUSH18] = jumpPtr{makePush(18, big.NewInt(18)), true}
jumpTable[PUSH19] = jumpPtr{makePush(19, big.NewInt(19)), true}
jumpTable[PUSH20] = jumpPtr{makePush(20, big.NewInt(20)), true}
jumpTable[PUSH21] = jumpPtr{makePush(21, big.NewInt(21)), true}
jumpTable[PUSH22] = jumpPtr{makePush(22, big.NewInt(22)), true}
jumpTable[PUSH23] = jumpPtr{makePush(23, big.NewInt(23)), true}
jumpTable[PUSH24] = jumpPtr{makePush(24, big.NewInt(24)), true}
jumpTable[PUSH25] = jumpPtr{makePush(25, big.NewInt(25)), true}
jumpTable[PUSH26] = jumpPtr{makePush(26, big.NewInt(26)), true}
jumpTable[PUSH27] = jumpPtr{makePush(27, big.NewInt(27)), true}
jumpTable[PUSH28] = jumpPtr{makePush(28, big.NewInt(28)), true}
jumpTable[PUSH29] = jumpPtr{makePush(29, big.NewInt(29)), true}
jumpTable[PUSH30] = jumpPtr{makePush(30, big.NewInt(30)), true}
jumpTable[PUSH31] = jumpPtr{makePush(31, big.NewInt(31)), true}
jumpTable[PUSH32] = jumpPtr{makePush(32, big.NewInt(32)), true}
jumpTable[DUP1] = jumpPtr{makeDup(1), true}
jumpTable[DUP2] = jumpPtr{makeDup(2), true}
jumpTable[DUP3] = jumpPtr{makeDup(3), true}
jumpTable[DUP4] = jumpPtr{makeDup(4), true}
jumpTable[DUP5] = jumpPtr{makeDup(5), true}
jumpTable[DUP6] = jumpPtr{makeDup(6), true}
jumpTable[DUP7] = jumpPtr{makeDup(7), true}
jumpTable[DUP8] = jumpPtr{makeDup(8), true}
jumpTable[DUP9] = jumpPtr{makeDup(9), true}
jumpTable[DUP10] = jumpPtr{makeDup(10), true}
jumpTable[DUP11] = jumpPtr{makeDup(11), true}
jumpTable[DUP12] = jumpPtr{makeDup(12), true}
jumpTable[DUP13] = jumpPtr{makeDup(13), true}
jumpTable[DUP14] = jumpPtr{makeDup(14), true}
jumpTable[DUP15] = jumpPtr{makeDup(15), true}
jumpTable[DUP16] = jumpPtr{makeDup(16), true}
jumpTable[RETURN] = jumpPtr{nil, true}
jumpTable[SUICIDE] = jumpPtr{nil, true}
jumpTable[JUMP] = jumpPtr{nil, true}
jumpTable[JUMPI] = jumpPtr{nil, true}
jumpTable[STOP] = jumpPtr{nil, true}
}

View File

@ -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 state package vm
import ( import (
"fmt" "fmt"

View File

@ -24,6 +24,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
) )
// StdErrFormat formats a slice of StructLogs to human readable format
func StdErrFormat(logs []StructLog) { func StdErrFormat(logs []StructLog) {
fmt.Fprintf(os.Stderr, "VM STAT %d OPs\n", len(logs)) fmt.Fprintf(os.Stderr, "VM STAT %d OPs\n", len(logs))
for _, log := range logs { for _, log := range logs {

View File

@ -18,6 +18,7 @@ package vm
import "fmt" import "fmt"
// Memory implements ethereum RAM backed by a simple byte slice
type Memory struct { type Memory struct {
store []byte store []byte
} }
@ -26,6 +27,7 @@ func NewMemory() *Memory {
return &Memory{nil} return &Memory{nil}
} }
// Set sets offset + size to value
func (m *Memory) Set(offset, size uint64, value []byte) { func (m *Memory) Set(offset, size uint64, value []byte) {
// length of store may never be less than offset + size. // length of store may never be less than offset + size.
// The store should be resized PRIOR to setting the memory // The store should be resized PRIOR to setting the memory
@ -40,12 +42,14 @@ func (m *Memory) Set(offset, size uint64, value []byte) {
} }
} }
// Resize resizes the memory to size
func (m *Memory) Resize(size uint64) { func (m *Memory) Resize(size uint64) {
if uint64(m.Len()) < size { if uint64(m.Len()) < size {
m.store = append(m.store, make([]byte, size-uint64(m.Len()))...) m.store = append(m.store, make([]byte, size-uint64(m.Len()))...)
} }
} }
// Get returns offset + size as a new slice
func (self *Memory) Get(offset, size int64) (cpy []byte) { func (self *Memory) Get(offset, size int64) (cpy []byte) {
if size == 0 { if size == 0 {
return nil return nil
@ -61,6 +65,7 @@ func (self *Memory) Get(offset, size int64) (cpy []byte) {
return return
} }
// GetPtr returns the offset + size
func (self *Memory) GetPtr(offset, size int64) []byte { func (self *Memory) GetPtr(offset, size int64) []byte {
if size == 0 { if size == 0 {
return nil return nil
@ -73,10 +78,12 @@ func (self *Memory) GetPtr(offset, size int64) []byte {
return nil return nil
} }
// Len returns the length of the backing slice
func (m *Memory) Len() int { func (m *Memory) Len() int {
return len(m.store) return len(m.store)
} }
// Data returns the backing slice
func (m *Memory) Data() []byte { func (m *Memory) Data() []byte {
return m.store return m.store
} }

View File

@ -20,9 +20,9 @@ import (
"fmt" "fmt"
) )
// OpCode is an EVM opcode
type OpCode byte type OpCode byte
// Op codes
const ( const (
// 0x0 range - arithmetic ops // 0x0 range - arithmetic ops
STOP OpCode = iota STOP OpCode = iota

View File

@ -16,7 +16,8 @@
package vm package vm
// VirtualMachine is an EVM interface
type VirtualMachine interface { type VirtualMachine interface {
Env() Environment Env() Environment
Run(context *Context, data []byte) ([]byte, error) Run(*Contract, []byte) ([]byte, error)
} }

View File

@ -14,33 +14,32 @@
// 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 vm implements the Ethereum Virtual Machine.
package vm package vm
import ( import (
"fmt" "fmt"
"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/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
) )
// Vm implements VirtualMachine // Vm is an EVM and implements VirtualMachine
type Vm struct { type Vm struct {
env Environment env Environment
} }
// New returns a new Virtual Machine // New returns a new Vm
func New(env Environment) *Vm { func New(env Environment) *Vm {
return &Vm{env: env} return &Vm{env: env}
} }
// Run loops and evaluates the contract's code with the given input data // Run loops and evaluates the contract's code with the given input data
func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) { func (self *Vm) Run(contract *Contract, input []byte) (ret []byte, err error) {
self.env.SetDepth(self.env.Depth() + 1) self.env.SetDepth(self.env.Depth() + 1)
defer self.env.SetDepth(self.env.Depth() - 1) defer self.env.SetDepth(self.env.Depth() - 1)
@ -48,42 +47,48 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
defer func() { defer func() {
if err != nil { if err != nil {
// In case of a VM exception (known exceptions) all gas consumed (panics NOT included). // In case of a VM exception (known exceptions) all gas consumed (panics NOT included).
context.UseGas(context.Gas) contract.UseGas(contract.Gas)
ret = context.Return(nil) ret = contract.Return(nil)
} }
}() }()
if context.CodeAddr != nil { if contract.CodeAddr != nil {
if p := Precompiled[context.CodeAddr.Str()]; p != nil { if p := Precompiled[contract.CodeAddr.Str()]; p != nil {
return self.RunPrecompiled(p, input, context) return self.RunPrecompiled(p, input, contract)
} }
} }
// Don't bother with the execution if there's no code.
if len(contract.Code) == 0 {
return contract.Return(nil), nil
}
var ( var (
codehash = crypto.Sha3Hash(context.Code) // codehash is used when doing jump dest caching codehash = crypto.Sha3Hash(contract.Code) // codehash is used when doing jump dest caching
program *Program program *Program
) )
if EnableJit { if EnableJit {
// Fetch program status. // If the JIT is enabled check the status of the JIT program,
// * If ready run using JIT // if it doesn't exist compile a new program in a seperate
// * If unknown, compile in a seperate goroutine // goroutine or wait for compilation to finish if the JIT is
// * If forced wait for compilation and run once done // forced.
if status := GetProgramStatus(codehash); status == progReady { switch GetProgramStatus(codehash) {
return RunProgram(GetProgram(codehash), self.env, context, input) case progReady:
} else if status == progUnknown { return RunProgram(GetProgram(codehash), self.env, contract, input)
case progUnknown:
if ForceJit { if ForceJit {
// Create and compile program // Create and compile program
program = NewProgram(context.Code) program = NewProgram(contract.Code)
perr := CompileProgram(program) perr := CompileProgram(program)
if perr == nil { if perr == nil {
return RunProgram(program, self.env, context, input) return RunProgram(program, self.env, contract, input)
} }
glog.V(logger.Info).Infoln("error compiling program", err) glog.V(logger.Info).Infoln("error compiling program", err)
} else { } else {
// create and compile the program. Compilation // create and compile the program. Compilation
// is done in a seperate goroutine // is done in a seperate goroutine
program = NewProgram(context.Code) program = NewProgram(contract.Code)
go func() { go func() {
err := CompileProgram(program) err := CompileProgram(program)
if err != nil { if err != nil {
@ -96,15 +101,14 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
} }
var ( var (
caller = context.caller caller = contract.caller
code = context.Code code = contract.Code
value = context.value instrCount = 0
price = context.Price
op OpCode // current opcode op OpCode // current opcode
mem = NewMemory() // bound memory mem = NewMemory() // bound memory
stack = newstack() // local stack stack = newstack() // local stack
statedb = self.env.State() // current state statedb = self.env.Db() // current state
// For optimisation reason we're using uint64 as the program counter. // For optimisation reason we're using uint64 as the program counter.
// It's theoretically possible to go above 2^64. The YP defines the PC to be uint256. Pratically much less so feasible. // It's theoretically possible to go above 2^64. The YP defines the PC to be uint256. Pratically much less so feasible.
pc = uint64(0) // program counter pc = uint64(0) // program counter
@ -112,8 +116,8 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
// jump evaluates and checks whether the given jump destination is a valid one // jump evaluates and checks whether the given jump destination is a valid one
// if valid move the `pc` otherwise return an error. // if valid move the `pc` otherwise return an error.
jump = func(from uint64, to *big.Int) error { jump = func(from uint64, to *big.Int) error {
if !context.jumpdests.has(codehash, code, to) { if !contract.jumpdests.has(codehash, code, to) {
nop := context.GetOp(to.Uint64()) nop := contract.GetOp(to.Uint64())
return fmt.Errorf("invalid jump destination (%v) %v", nop, to) return fmt.Errorf("invalid jump destination (%v) %v", nop, to)
} }
@ -125,552 +129,92 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
newMemSize *big.Int newMemSize *big.Int
cost *big.Int cost *big.Int
) )
contract.Input = input
// User defer pattern to check for an error and, based on the error being nil or not, use all gas and return. // User defer pattern to check for an error and, based on the error being nil or not, use all gas and return.
defer func() { defer func() {
if err != nil { if err != nil {
self.log(pc, op, context.Gas, cost, mem, stack, context, err) self.log(pc, op, contract.Gas, cost, mem, stack, contract, err)
} }
}() }()
// Don't bother with the execution if there's no code. if glog.V(logger.Debug) {
if len(code) == 0 { glog.Infof("running byte VM %x\n", codehash[:4])
return context.Return(nil), nil tstart := time.Now()
defer func() {
glog.Infof("byte VM %x done. time: %v instrc: %v\n", codehash[:4], time.Since(tstart), instrCount)
}()
} }
for { for ; ; instrCount++ {
// Overhead of the atomic read might not be worth it /*
/* TODO this still causes a few issues in the tests if EnableJit && it%100 == 0 {
if program != nil && progStatus(atomic.LoadInt32(&program.status)) == progReady { if program != nil && progStatus(atomic.LoadInt32(&program.status)) == progReady {
// move execution // move execution
glog.V(logger.Info).Infoln("Moved execution to JIT") fmt.Println("moved", it)
return runProgram(program, pc, mem, stack, self.env, context, input) glog.V(logger.Info).Infoln("Moved execution to JIT")
} return runProgram(program, pc, mem, stack, self.env, contract, input)
}
}
*/ */
// The base for all big integer arithmetic
base := new(big.Int)
// Get the memory location of pc // Get the memory location of pc
op = context.GetOp(pc) op = contract.GetOp(pc)
// calculate the new memory size and gas price for the current executing opcode // calculate the new memory size and gas price for the current executing opcode
newMemSize, cost, err = calculateGasAndSize(self.env, context, caller, op, statedb, mem, stack) newMemSize, cost, err = calculateGasAndSize(self.env, contract, caller, op, statedb, mem, stack)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Use the calculated gas. When insufficient gas is present, use all gas and return an // Use the calculated gas. When insufficient gas is present, use all gas and return an
// Out Of Gas error // Out Of Gas error
if !context.UseGas(cost) { if !contract.UseGas(cost) {
return nil, OutOfGasError return nil, OutOfGasError
} }
// Resize the memory calculated previously // Resize the memory calculated previously
mem.Resize(newMemSize.Uint64()) mem.Resize(newMemSize.Uint64())
// Add a log message // Add a log message
self.log(pc, op, context.Gas, cost, mem, stack, context, nil) self.log(pc, op, contract.Gas, cost, mem, stack, contract, nil)
switch op { if opPtr := jumpTable[op]; opPtr.valid {
case ADD: if opPtr.fn != nil {
x, y := stack.pop(), stack.pop() opPtr.fn(instruction{}, &pc, self.env, contract, mem, stack)
base.Add(x, y)
U256(base)
// pop result back on the stack
stack.push(base)
case SUB:
x, y := stack.pop(), stack.pop()
base.Sub(x, y)
U256(base)
// pop result back on the stack
stack.push(base)
case MUL:
x, y := stack.pop(), stack.pop()
base.Mul(x, y)
U256(base)
// pop result back on the stack
stack.push(base)
case DIV:
x, y := stack.pop(), stack.pop()
if y.Cmp(common.Big0) != 0 {
base.Div(x, y)
}
U256(base)
// pop result back on the stack
stack.push(base)
case SDIV:
x, y := S256(stack.pop()), S256(stack.pop())
if y.Cmp(common.Big0) == 0 {
base.Set(common.Big0)
} else { } else {
n := new(big.Int) switch op {
if new(big.Int).Mul(x, y).Cmp(common.Big0) < 0 { case PC:
n.SetInt64(-1) opPc(instruction{data: new(big.Int).SetUint64(pc)}, &pc, self.env, contract, mem, stack)
} else { case JUMP:
n.SetInt64(1) if err := jump(pc, stack.pop()); err != nil {
return nil, err
}
continue
case JUMPI:
pos, cond := stack.pop(), stack.pop()
if cond.Cmp(common.BigTrue) >= 0 {
if err := jump(pc, pos); err != nil {
return nil, err
}
continue
}
case RETURN:
offset, size := stack.pop(), stack.pop()
ret := mem.GetPtr(offset.Int64(), size.Int64())
return contract.Return(ret), nil
case SUICIDE:
opSuicide(instruction{}, nil, self.env, contract, mem, stack)
fallthrough
case STOP: // Stop the contract
return contract.Return(nil), nil
} }
base.Div(x.Abs(x), y.Abs(y)).Mul(base, n)
U256(base)
} }
} else {
stack.push(base)
case MOD:
x, y := stack.pop(), stack.pop()
if y.Cmp(common.Big0) == 0 {
base.Set(common.Big0)
} else {
base.Mod(x, y)
}
U256(base)
stack.push(base)
case SMOD:
x, y := S256(stack.pop()), S256(stack.pop())
if y.Cmp(common.Big0) == 0 {
base.Set(common.Big0)
} else {
n := new(big.Int)
if x.Cmp(common.Big0) < 0 {
n.SetInt64(-1)
} else {
n.SetInt64(1)
}
base.Mod(x.Abs(x), y.Abs(y)).Mul(base, n)
U256(base)
}
stack.push(base)
case EXP:
x, y := stack.pop(), stack.pop()
base.Exp(x, y, Pow256)
U256(base)
stack.push(base)
case SIGNEXTEND:
back := stack.pop()
if back.Cmp(big.NewInt(31)) < 0 {
bit := uint(back.Uint64()*8 + 7)
num := stack.pop()
mask := new(big.Int).Lsh(common.Big1, bit)
mask.Sub(mask, common.Big1)
if common.BitTest(num, int(bit)) {
num.Or(num, mask.Not(mask))
} else {
num.And(num, mask)
}
num = U256(num)
stack.push(num)
}
case NOT:
stack.push(U256(new(big.Int).Not(stack.pop())))
case LT:
x, y := stack.pop(), stack.pop()
// x < y
if x.Cmp(y) < 0 {
stack.push(common.BigTrue)
} else {
stack.push(common.BigFalse)
}
case GT:
x, y := stack.pop(), stack.pop()
// x > y
if x.Cmp(y) > 0 {
stack.push(common.BigTrue)
} else {
stack.push(common.BigFalse)
}
case SLT:
x, y := S256(stack.pop()), S256(stack.pop())
// x < y
if x.Cmp(S256(y)) < 0 {
stack.push(common.BigTrue)
} else {
stack.push(common.BigFalse)
}
case SGT:
x, y := S256(stack.pop()), S256(stack.pop())
// x > y
if x.Cmp(y) > 0 {
stack.push(common.BigTrue)
} else {
stack.push(common.BigFalse)
}
case EQ:
x, y := stack.pop(), stack.pop()
// x == y
if x.Cmp(y) == 0 {
stack.push(common.BigTrue)
} else {
stack.push(common.BigFalse)
}
case ISZERO:
x := stack.pop()
if x.Cmp(common.BigFalse) > 0 {
stack.push(common.BigFalse)
} else {
stack.push(common.BigTrue)
}
case AND:
x, y := stack.pop(), stack.pop()
stack.push(base.And(x, y))
case OR:
x, y := stack.pop(), stack.pop()
stack.push(base.Or(x, y))
case XOR:
x, y := stack.pop(), stack.pop()
stack.push(base.Xor(x, y))
case BYTE:
th, val := stack.pop(), stack.pop()
if th.Cmp(big.NewInt(32)) < 0 {
byt := big.NewInt(int64(common.LeftPadBytes(val.Bytes(), 32)[th.Int64()]))
base.Set(byt)
} else {
base.Set(common.BigFalse)
}
stack.push(base)
case ADDMOD:
x := stack.pop()
y := stack.pop()
z := stack.pop()
if z.Cmp(Zero) > 0 {
add := new(big.Int).Add(x, y)
base.Mod(add, z)
base = U256(base)
}
stack.push(base)
case MULMOD:
x := stack.pop()
y := stack.pop()
z := stack.pop()
if z.Cmp(Zero) > 0 {
mul := new(big.Int).Mul(x, y)
base.Mod(mul, z)
U256(base)
}
stack.push(base)
case SHA3:
offset, size := stack.pop(), stack.pop()
data := crypto.Sha3(mem.Get(offset.Int64(), size.Int64()))
stack.push(common.BigD(data))
case ADDRESS:
stack.push(common.Bytes2Big(context.Address().Bytes()))
case BALANCE:
addr := common.BigToAddress(stack.pop())
balance := statedb.GetBalance(addr)
stack.push(new(big.Int).Set(balance))
case ORIGIN:
origin := self.env.Origin()
stack.push(origin.Big())
case CALLER:
caller := context.caller.Address()
stack.push(common.Bytes2Big(caller.Bytes()))
case CALLVALUE:
stack.push(new(big.Int).Set(value))
case CALLDATALOAD:
data := getData(input, stack.pop(), common.Big32)
stack.push(common.Bytes2Big(data))
case CALLDATASIZE:
l := int64(len(input))
stack.push(big.NewInt(l))
case CALLDATACOPY:
var (
mOff = stack.pop()
cOff = stack.pop()
l = stack.pop()
)
data := getData(input, cOff, l)
mem.Set(mOff.Uint64(), l.Uint64(), data)
case CODESIZE, EXTCODESIZE:
var code []byte
if op == EXTCODESIZE {
addr := common.BigToAddress(stack.pop())
code = statedb.GetCode(addr)
} else {
code = context.Code
}
l := big.NewInt(int64(len(code)))
stack.push(l)
case CODECOPY, EXTCODECOPY:
var code []byte
if op == EXTCODECOPY {
addr := common.BigToAddress(stack.pop())
code = statedb.GetCode(addr)
} else {
code = context.Code
}
var (
mOff = stack.pop()
cOff = stack.pop()
l = stack.pop()
)
codeCopy := getData(code, cOff, l)
mem.Set(mOff.Uint64(), l.Uint64(), codeCopy)
case GASPRICE:
stack.push(new(big.Int).Set(context.Price))
case BLOCKHASH:
num := stack.pop()
n := new(big.Int).Sub(self.env.BlockNumber(), common.Big257)
if num.Cmp(n) > 0 && num.Cmp(self.env.BlockNumber()) < 0 {
stack.push(self.env.GetHash(num.Uint64()).Big())
} else {
stack.push(common.Big0)
}
case COINBASE:
coinbase := self.env.Coinbase()
stack.push(coinbase.Big())
case TIMESTAMP:
time := self.env.Time()
stack.push(new(big.Int).Set(time))
case NUMBER:
number := self.env.BlockNumber()
stack.push(U256(number))
case DIFFICULTY:
difficulty := self.env.Difficulty()
stack.push(new(big.Int).Set(difficulty))
case GASLIMIT:
stack.push(new(big.Int).Set(self.env.GasLimit()))
case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32:
size := uint64(op - PUSH1 + 1)
byts := getData(code, new(big.Int).SetUint64(pc+1), new(big.Int).SetUint64(size))
// push value to stack
stack.push(common.Bytes2Big(byts))
pc += size
case POP:
stack.pop()
case DUP1, DUP2, DUP3, DUP4, DUP5, DUP6, DUP7, DUP8, DUP9, DUP10, DUP11, DUP12, DUP13, DUP14, DUP15, DUP16:
n := int(op - DUP1 + 1)
stack.dup(n)
case SWAP1, SWAP2, SWAP3, SWAP4, SWAP5, SWAP6, SWAP7, SWAP8, SWAP9, SWAP10, SWAP11, SWAP12, SWAP13, SWAP14, SWAP15, SWAP16:
n := int(op - SWAP1 + 2)
stack.swap(n)
case LOG0, LOG1, LOG2, LOG3, LOG4:
n := int(op - LOG0)
topics := make([]common.Hash, n)
mStart, mSize := stack.pop(), stack.pop()
for i := 0; i < n; i++ {
topics[i] = common.BigToHash(stack.pop())
}
data := mem.Get(mStart.Int64(), mSize.Int64())
log := state.NewLog(context.Address(), topics, data, self.env.BlockNumber().Uint64())
self.env.AddLog(log)
case MLOAD:
offset := stack.pop()
val := common.BigD(mem.Get(offset.Int64(), 32))
stack.push(val)
case MSTORE:
// pop value of the stack
mStart, val := stack.pop(), stack.pop()
mem.Set(mStart.Uint64(), 32, common.BigToBytes(val, 256))
case MSTORE8:
off, val := stack.pop().Int64(), stack.pop().Int64()
mem.store[off] = byte(val & 0xff)
case SLOAD:
loc := common.BigToHash(stack.pop())
val := statedb.GetState(context.Address(), loc).Big()
stack.push(val)
case SSTORE:
loc := common.BigToHash(stack.pop())
val := stack.pop()
statedb.SetState(context.Address(), loc, common.BigToHash(val))
case JUMP:
if err := jump(pc, stack.pop()); err != nil {
return nil, err
}
continue
case JUMPI:
pos, cond := stack.pop(), stack.pop()
if cond.Cmp(common.BigTrue) >= 0 {
if err := jump(pc, pos); err != nil {
return nil, err
}
continue
}
case JUMPDEST:
case PC:
stack.push(new(big.Int).SetUint64(pc))
case MSIZE:
stack.push(big.NewInt(int64(mem.Len())))
case GAS:
stack.push(new(big.Int).Set(context.Gas))
case CREATE:
var (
value = stack.pop()
offset, size = stack.pop(), stack.pop()
input = mem.Get(offset.Int64(), size.Int64())
gas = new(big.Int).Set(context.Gas)
addr common.Address
)
context.UseGas(context.Gas)
ret, suberr, ref := self.env.Create(context, input, gas, price, value)
if suberr != nil {
stack.push(common.BigFalse)
} else {
// gas < len(ret) * CreateDataGas == NO_CODE
dataGas := big.NewInt(int64(len(ret)))
dataGas.Mul(dataGas, params.CreateDataGas)
if context.UseGas(dataGas) {
ref.SetCode(ret)
}
addr = ref.Address()
stack.push(addr.Big())
}
case CALL, CALLCODE:
gas := stack.pop()
// pop gas and value of the stack.
addr, value := stack.pop(), stack.pop()
value = U256(value)
// pop input size and offset
inOffset, inSize := stack.pop(), stack.pop()
// pop return size and offset
retOffset, retSize := stack.pop(), stack.pop()
address := common.BigToAddress(addr)
// Get the arguments from the memory
args := mem.Get(inOffset.Int64(), inSize.Int64())
if len(value.Bytes()) > 0 {
gas.Add(gas, params.CallStipend)
}
var (
ret []byte
err error
)
if op == CALLCODE {
ret, err = self.env.CallCode(context, address, args, gas, price, value)
} else {
ret, err = self.env.Call(context, address, args, gas, price, value)
}
if err != nil {
stack.push(common.BigFalse)
} else {
stack.push(common.BigTrue)
mem.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
case RETURN:
offset, size := stack.pop(), stack.pop()
ret := mem.GetPtr(offset.Int64(), size.Int64())
return context.Return(ret), nil
case SUICIDE:
receiver := statedb.GetOrNewStateObject(common.BigToAddress(stack.pop()))
balance := statedb.GetBalance(context.Address())
receiver.AddBalance(balance)
statedb.Delete(context.Address())
fallthrough
case STOP: // Stop the context
return context.Return(nil), nil
default:
return nil, fmt.Errorf("Invalid opcode %x", op) return nil, fmt.Errorf("Invalid opcode %x", op)
} }
@ -681,7 +225,7 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
// calculateGasAndSize calculates the required given the opcode and stack items calculates the new memorysize for // calculateGasAndSize calculates the required given the opcode and stack items calculates the new memorysize for
// the operation. This does not reduce gas or resizes the memory. // the operation. This does not reduce gas or resizes the memory.
func calculateGasAndSize(env Environment, context *Context, caller ContextRef, op OpCode, statedb *state.StateDB, mem *Memory, stack *stack) (*big.Int, *big.Int, error) { func calculateGasAndSize(env Environment, contract *Contract, caller ContractRef, op OpCode, statedb Database, mem *Memory, stack *stack) (*big.Int, *big.Int, error) {
var ( var (
gas = new(big.Int) gas = new(big.Int)
newMemSize *big.Int = new(big.Int) newMemSize *big.Int = new(big.Int)
@ -731,7 +275,7 @@ func calculateGasAndSize(env Environment, context *Context, caller ContextRef, o
var g *big.Int var g *big.Int
y, x := stack.data[stack.len()-2], stack.data[stack.len()-1] y, x := stack.data[stack.len()-2], stack.data[stack.len()-1]
val := statedb.GetState(context.Address(), common.BigToHash(x)) val := statedb.GetState(contract.Address(), common.BigToHash(x))
// This checks for 3 scenario's and calculates gas accordingly // This checks for 3 scenario's and calculates gas accordingly
// 1. From a zero-value address to a non-zero value (NEW VALUE) // 1. From a zero-value address to a non-zero value (NEW VALUE)
@ -741,7 +285,7 @@ func calculateGasAndSize(env Environment, context *Context, caller ContextRef, o
// 0 => non 0 // 0 => non 0
g = params.SstoreSetGas g = params.SstoreSetGas
} else if !common.EmptyHash(val) && common.EmptyHash(common.BigToHash(y)) { } else if !common.EmptyHash(val) && common.EmptyHash(common.BigToHash(y)) {
statedb.Refund(params.SstoreRefundGas) statedb.AddRefund(params.SstoreRefundGas)
g = params.SstoreClearGas g = params.SstoreClearGas
} else { } else {
@ -750,8 +294,8 @@ func calculateGasAndSize(env Environment, context *Context, caller ContextRef, o
} }
gas.Set(g) gas.Set(g)
case SUICIDE: case SUICIDE:
if !statedb.IsDeleted(context.Address()) { if !statedb.IsDeleted(contract.Address()) {
statedb.Refund(params.SuicideRefundGas) statedb.AddRefund(params.SuicideRefundGas)
} }
case MLOAD: case MLOAD:
newMemSize = calcMemSize(stack.peek(), u256(32)) newMemSize = calcMemSize(stack.peek(), u256(32))
@ -788,7 +332,8 @@ func calculateGasAndSize(env Environment, context *Context, caller ContextRef, o
gas.Add(gas, stack.data[stack.len()-1]) gas.Add(gas, stack.data[stack.len()-1])
if op == CALL { if op == CALL {
if env.State().GetStateObject(common.BigToAddress(stack.data[stack.len()-2])) == nil { //if env.Db().GetStateObject(common.BigToAddress(stack.data[stack.len()-2])) == nil {
if !env.Db().Exist(common.BigToAddress(stack.data[stack.len()-2])) {
gas.Add(gas, params.CallNewAccountGas) gas.Add(gas, params.CallNewAccountGas)
} }
} }
@ -802,38 +347,18 @@ func calculateGasAndSize(env Environment, context *Context, caller ContextRef, o
newMemSize = common.BigMax(x, y) newMemSize = common.BigMax(x, y)
} }
quadMemGas(mem, newMemSize, gas)
if newMemSize.Cmp(common.Big0) > 0 {
newMemSizeWords := toWordSize(newMemSize)
newMemSize.Mul(newMemSizeWords, u256(32))
if newMemSize.Cmp(u256(int64(mem.Len()))) > 0 {
oldSize := toWordSize(big.NewInt(int64(mem.Len())))
pow := new(big.Int).Exp(oldSize, common.Big2, Zero)
linCoef := new(big.Int).Mul(oldSize, params.MemoryGas)
quadCoef := new(big.Int).Div(pow, params.QuadCoeffDiv)
oldTotalFee := new(big.Int).Add(linCoef, quadCoef)
pow.Exp(newMemSizeWords, common.Big2, Zero)
linCoef = new(big.Int).Mul(newMemSizeWords, params.MemoryGas)
quadCoef = new(big.Int).Div(pow, params.QuadCoeffDiv)
newTotalFee := new(big.Int).Add(linCoef, quadCoef)
fee := new(big.Int).Sub(newTotalFee, oldTotalFee)
gas.Add(gas, fee)
}
}
return newMemSize, gas, nil return newMemSize, gas, nil
} }
// RunPrecompile runs and evaluate the output of a precompiled contract defined in contracts.go // RunPrecompile runs and evaluate the output of a precompiled contract defined in contracts.go
func (self *Vm) RunPrecompiled(p *PrecompiledAccount, input []byte, context *Context) (ret []byte, err error) { func (self *Vm) RunPrecompiled(p *PrecompiledAccount, input []byte, contract *Contract) (ret []byte, err error) {
gas := p.Gas(len(input)) gas := p.Gas(len(input))
if context.UseGas(gas) { if contract.UseGas(gas) {
ret = p.Call(input) ret = p.Call(input)
return context.Return(ret), nil return contract.Return(ret), nil
} else { } else {
return nil, OutOfGasError return nil, OutOfGasError
} }
@ -841,18 +366,20 @@ func (self *Vm) RunPrecompiled(p *PrecompiledAccount, input []byte, context *Con
// log emits a log event to the environment for each opcode encountered. This is not to be confused with the // log emits a log event to the environment for each opcode encountered. This is not to be confused with the
// LOG* opcode. // LOG* opcode.
func (self *Vm) log(pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *stack, context *Context, err error) { func (self *Vm) log(pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *stack, contract *Contract, err error) {
if Debug { if Debug {
mem := make([]byte, len(memory.Data())) mem := make([]byte, len(memory.Data()))
copy(mem, memory.Data()) copy(mem, memory.Data())
stck := make([]*big.Int, len(stack.Data())) stck := make([]*big.Int, len(stack.Data()))
copy(stck, stack.Data()) copy(stck, stack.Data())
object := context.self.(*state.StateObject)
storage := make(map[common.Hash][]byte) storage := make(map[common.Hash][]byte)
object.EachStorage(func(k, v []byte) { /*
storage[common.BytesToHash(k)] = v object := contract.self.(*state.StateObject)
}) object.EachStorage(func(k, v []byte) {
storage[common.BytesToHash(k)] = v
})
*/
self.env.AddStructLog(StructLog{pc, op, new(big.Int).Set(gas), cost, mem, stck, storage, err}) self.env.AddStructLog(StructLog{pc, op, new(big.Int).Set(gas), cost, mem, stck, storage, err})
} }

View File

@ -30,6 +30,7 @@ void evmjit_destroy(void* _jit);
*/ */
import "C" import "C"
/*
import ( import (
"bytes" "bytes"
"errors" "errors"
@ -385,4 +386,4 @@ func env_extcode(_vm unsafe.Pointer, _addr unsafe.Pointer, o_size *uint64) *byte
code := vm.Env().State().GetCode(addr) code := vm.Env().State().GetCode(addr)
*o_size = uint64(len(code)) *o_size = uint64(len(code))
return getDataPtr(code) return getDataPtr(code)
} }*/

View File

@ -53,7 +53,7 @@ func (self *VMEnv) Time() *big.Int { return self.header.Time }
func (self *VMEnv) Difficulty() *big.Int { return self.header.Difficulty } func (self *VMEnv) Difficulty() *big.Int { return self.header.Difficulty }
func (self *VMEnv) GasLimit() *big.Int { return self.header.GasLimit } func (self *VMEnv) GasLimit() *big.Int { return self.header.GasLimit }
func (self *VMEnv) Value() *big.Int { return self.msg.Value() } func (self *VMEnv) Value() *big.Int { return self.msg.Value() }
func (self *VMEnv) State() *state.StateDB { return self.state } func (self *VMEnv) Db() vm.Database { return self.state }
func (self *VMEnv) Depth() int { return self.depth } func (self *VMEnv) Depth() int { return self.depth }
func (self *VMEnv) SetDepth(i int) { self.depth = i } func (self *VMEnv) SetDepth(i int) { self.depth = i }
func (self *VMEnv) VmType() vm.Type { return self.typ } func (self *VMEnv) VmType() vm.Type { return self.typ }
@ -66,30 +66,34 @@ 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 *vm.Log) {
self.state.AddLog(log) self.state.AddLog(log)
} }
func (self *VMEnv) CanTransfer(from vm.Account, balance *big.Int) bool { func (self *VMEnv) CanTransfer(from common.Address, balance *big.Int) bool {
return from.Balance().Cmp(balance) >= 0 return self.state.GetBalance(from).Cmp(balance) >= 0
}
func (self *VMEnv) MakeSnapshot() vm.Database {
return self.state.Copy()
}
func (self *VMEnv) SetSnapshot(copy vm.Database) {
self.state.Set(copy.(*state.StateDB))
} }
func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) error { func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) error {
return vm.Transfer(from, to, amount) return Transfer(from, to, amount)
} }
func (self *VMEnv) Call(me vm.ContextRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) { func (self *VMEnv) Call(me vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
exe := NewExecution(self, &addr, data, gas, price, value) return Call(self, me, addr, data, gas, price, value)
return exe.Call(addr, me)
} }
func (self *VMEnv) CallCode(me vm.ContextRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) { func (self *VMEnv) CallCode(me vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
maddr := me.Address() return CallCode(self, me, addr, data, gas, price, value)
exe := NewExecution(self, &maddr, data, gas, price, value)
return exe.Call(addr, me)
} }
func (self *VMEnv) Create(me vm.ContextRef, data []byte, gas, price, value *big.Int) ([]byte, error, vm.ContextRef) { func (self *VMEnv) Create(me vm.ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) {
exe := NewExecution(self, nil, data, gas, price, value) return Create(self, me, data, gas, price, value)
return exe.Create(me)
} }
func (self *VMEnv) StructLogs() []vm.StructLog { func (self *VMEnv) StructLogs() []vm.StructLog {

View File

@ -21,8 +21,8 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
) )
type AccountChange struct { type AccountChange struct {
@ -39,9 +39,9 @@ type Filter struct {
max int max int
topics [][]common.Hash topics [][]common.Hash
BlockCallback func(*types.Block, state.Logs) BlockCallback func(*types.Block, vm.Logs)
TransactionCallback func(*types.Transaction) TransactionCallback func(*types.Transaction)
LogsCallback func(state.Logs) LogsCallback func(vm.Logs)
} }
// Create a new filter which uses a bloom filter on blocks to figure out whether a particular block // Create a new filter which uses a bloom filter on blocks to figure out whether a particular block
@ -78,7 +78,7 @@ func (self *Filter) SetSkip(skip int) {
} }
// Run filters logs with the current parameters set // Run filters logs with the current parameters set
func (self *Filter) Find() state.Logs { func (self *Filter) Find() vm.Logs {
earliestBlock := core.GetCurrentBlock(self.db) earliestBlock := core.GetCurrentBlock(self.db)
var earliestBlockNo uint64 = uint64(self.earliest) var earliestBlockNo uint64 = uint64(self.earliest)
if self.earliest == -1 { if self.earliest == -1 {
@ -90,7 +90,7 @@ func (self *Filter) Find() state.Logs {
} }
var ( var (
logs state.Logs logs vm.Logs
block = core.GetBlockByNumber(self.db, latestBlockNo) block = core.GetBlockByNumber(self.db, latestBlockNo)
) )
@ -112,7 +112,7 @@ done:
// Get the logs of the block // Get the logs of the block
var ( var (
receipts = core.GetBlockReceipts(self.db, block.Hash()) receipts = core.GetBlockReceipts(self.db, block.Hash())
unfiltered state.Logs unfiltered vm.Logs
) )
for _, receipt := range receipts { for _, receipt := range receipts {
unfiltered = append(unfiltered, receipt.Logs()...) unfiltered = append(unfiltered, receipt.Logs()...)
@ -138,8 +138,8 @@ func includes(addresses []common.Address, a common.Address) bool {
return false return false
} }
func (self *Filter) FilterLogs(logs state.Logs) state.Logs { func (self *Filter) FilterLogs(logs vm.Logs) vm.Logs {
var ret state.Logs var ret vm.Logs
// Filter the logs for interesting stuff // Filter the logs for interesting stuff
Logs: Logs:

View File

@ -22,7 +22,7 @@ import (
"sync" "sync"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
) )
@ -89,7 +89,7 @@ func (fs *FilterSystem) filterLoop() {
//core.PendingBlockEvent{}, //core.PendingBlockEvent{},
core.ChainEvent{}, core.ChainEvent{},
core.TxPreEvent{}, core.TxPreEvent{},
state.Logs(nil)) vm.Logs(nil))
out: out:
for { for {
@ -116,7 +116,7 @@ out:
} }
fs.filterMu.RUnlock() fs.filterMu.RUnlock()
case state.Logs: case vm.Logs:
fs.filterMu.RLock() fs.filterMu.RLock()
for _, filter := range fs.filters { for _, filter := range fs.filters {
if filter.LogsCallback != nil { if filter.LogsCallback != nil {

View File

@ -29,6 +29,7 @@ import (
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
@ -298,7 +299,7 @@ func (self *worker) wait() {
} }
// broadcast before waiting for validation // broadcast before waiting for validation
go func(block *types.Block, logs state.Logs, receipts []*types.Receipt) { go func(block *types.Block, logs vm.Logs, receipts []*types.Receipt) {
self.mux.Post(core.NewMinedBlockEvent{block}) self.mux.Post(core.NewMinedBlockEvent{block})
self.mux.Post(core.ChainEvent{block, block.Hash(), logs}) self.mux.Post(core.ChainEvent{block, block.Hash(), logs})
if stat == core.CanonStatTy { if stat == core.CanonStatTy {

View File

@ -24,8 +24,8 @@ import (
"strings" "strings"
"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/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/rpc/shared" "github.com/ethereum/go-ethereum/rpc/shared"
) )
@ -830,7 +830,7 @@ type LogRes struct {
TransactionIndex *hexnum `json:"transactionIndex"` TransactionIndex *hexnum `json:"transactionIndex"`
} }
func NewLogRes(log *state.Log) LogRes { func NewLogRes(log *vm.Log) LogRes {
var l LogRes var l LogRes
l.Topics = make([]*hexdata, len(log.Topics)) l.Topics = make([]*hexdata, len(log.Topics))
for j, topic := range log.Topics { for j, topic := range log.Topics {
@ -847,7 +847,7 @@ func NewLogRes(log *state.Log) LogRes {
return l return l
} }
func NewLogsRes(logs state.Logs) (ls []LogRes) { func NewLogsRes(logs vm.Logs) (ls []LogRes) {
ls = make([]LogRes, len(logs)) ls = make([]LogRes, len(logs))
for i, log := range logs { for i, log := range logs {

View File

@ -168,7 +168,7 @@ func runStateTest(test VmTest) error {
ret []byte ret []byte
// gas *big.Int // gas *big.Int
// err error // err error
logs state.Logs logs vm.Logs
) )
ret, logs, _, _ = RunState(statedb, env, test.Transaction) ret, logs, _, _ = RunState(statedb, env, test.Transaction)
@ -216,7 +216,7 @@ func runStateTest(test VmTest) error {
return nil return nil
} }
func RunState(statedb *state.StateDB, env, tx map[string]string) ([]byte, state.Logs, *big.Int, error) { func RunState(statedb *state.StateDB, env, tx map[string]string) ([]byte, vm.Logs, *big.Int, error) {
var ( var (
data = common.FromHex(tx["data"]) data = common.FromHex(tx["data"])
gas = common.Big(tx["gasLimit"]) gas = common.Big(tx["gasLimit"])

View File

@ -30,7 +30,7 @@ import (
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
) )
func checkLogs(tlog []Log, logs state.Logs) error { func checkLogs(tlog []Log, logs vm.Logs) error {
if len(tlog) != len(logs) { if len(tlog) != len(logs) {
return fmt.Errorf("log length mismatch. Expected %d, got %d", len(tlog), len(logs)) return fmt.Errorf("log length mismatch. Expected %d, got %d", len(tlog), len(logs))
@ -53,7 +53,7 @@ func checkLogs(tlog []Log, logs state.Logs) error {
} }
} }
} }
genBloom := common.LeftPadBytes(types.LogsBloom(state.Logs{logs[i]}).Bytes(), 256) genBloom := common.LeftPadBytes(types.LogsBloom(vm.Logs{logs[i]}).Bytes(), 256)
if !bytes.Equal(genBloom, common.Hex2Bytes(log.BloomF)) { if !bytes.Equal(genBloom, common.Hex2Bytes(log.BloomF)) {
return fmt.Errorf("bloom mismatch") return fmt.Errorf("bloom mismatch")
@ -181,18 +181,18 @@ func (self *Env) BlockNumber() *big.Int { return self.number }
func (self *Env) Coinbase() common.Address { return self.coinbase } func (self *Env) Coinbase() common.Address { return self.coinbase }
func (self *Env) Time() *big.Int { return self.time } func (self *Env) Time() *big.Int { return self.time }
func (self *Env) Difficulty() *big.Int { return self.difficulty } func (self *Env) Difficulty() *big.Int { return self.difficulty }
func (self *Env) State() *state.StateDB { return self.state } func (self *Env) Db() vm.Database { return self.state }
func (self *Env) GasLimit() *big.Int { return self.gasLimit } func (self *Env) GasLimit() *big.Int { return self.gasLimit }
func (self *Env) VmType() vm.Type { return vm.StdVmTy } func (self *Env) VmType() vm.Type { return vm.StdVmTy }
func (self *Env) GetHash(n uint64) common.Hash { func (self *Env) GetHash(n uint64) common.Hash {
return common.BytesToHash(crypto.Sha3([]byte(big.NewInt(int64(n)).String()))) return common.BytesToHash(crypto.Sha3([]byte(big.NewInt(int64(n)).String())))
} }
func (self *Env) AddLog(log *state.Log) { func (self *Env) AddLog(log *vm.Log) {
self.state.AddLog(log) self.state.AddLog(log)
} }
func (self *Env) Depth() int { return self.depth } func (self *Env) Depth() int { return self.depth }
func (self *Env) SetDepth(i int) { self.depth = i } func (self *Env) SetDepth(i int) { self.depth = i }
func (self *Env) CanTransfer(from vm.Account, balance *big.Int) bool { func (self *Env) CanTransfer(from common.Address, balance *big.Int) bool {
if self.skipTransfer { if self.skipTransfer {
if self.initial { if self.initial {
self.initial = false self.initial = false
@ -200,58 +200,53 @@ func (self *Env) CanTransfer(from vm.Account, balance *big.Int) bool {
} }
} }
return from.Balance().Cmp(balance) >= 0 return self.state.GetBalance(from).Cmp(balance) >= 0
}
func (self *Env) MakeSnapshot() vm.Database {
return self.state.Copy()
}
func (self *Env) SetSnapshot(copy vm.Database) {
self.state.Set(copy.(*state.StateDB))
} }
func (self *Env) Transfer(from, to vm.Account, amount *big.Int) error { func (self *Env) Transfer(from, to vm.Account, amount *big.Int) error {
if self.skipTransfer { if self.skipTransfer {
return nil return nil
} }
return vm.Transfer(from, to, amount) return core.Transfer(from, to, amount)
} }
func (self *Env) vm(addr *common.Address, data []byte, gas, price, value *big.Int) *core.Execution { func (self *Env) Call(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
exec := core.NewExecution(self, addr, data, gas, price, value)
return exec
}
func (self *Env) Call(caller vm.ContextRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
if self.vmTest && self.depth > 0 { if self.vmTest && self.depth > 0 {
caller.ReturnGas(gas, price) caller.ReturnGas(gas, price)
return nil, nil return nil, nil
} }
exe := self.vm(&addr, data, gas, price, value) ret, err := core.Call(self, caller, addr, data, gas, price, value)
ret, err := exe.Call(addr, caller) self.Gas = gas
self.Gas = exe.Gas
return ret, err return ret, err
} }
func (self *Env) CallCode(caller vm.ContextRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) { func (self *Env) CallCode(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
if self.vmTest && self.depth > 0 { if self.vmTest && self.depth > 0 {
caller.ReturnGas(gas, price) caller.ReturnGas(gas, price)
return nil, nil return nil, nil
} }
return core.CallCode(self, caller, addr, data, gas, price, value)
caddr := caller.Address()
exe := self.vm(&caddr, data, gas, price, value)
return exe.Call(addr, caller)
} }
func (self *Env) Create(caller vm.ContextRef, data []byte, gas, price, value *big.Int) ([]byte, error, vm.ContextRef) { func (self *Env) Create(caller vm.ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) {
exe := self.vm(nil, data, gas, price, value)
if self.vmTest { if self.vmTest {
caller.ReturnGas(gas, price) caller.ReturnGas(gas, price)
nonce := self.state.GetNonce(caller.Address()) nonce := self.state.GetNonce(caller.Address())
obj := self.state.GetOrNewStateObject(crypto.CreateAddress(caller.Address(), nonce)) obj := self.state.GetOrNewStateObject(crypto.CreateAddress(caller.Address(), nonce))
return nil, nil, obj return nil, obj.Address(), nil
} else { } else {
return exe.Create(caller) return core.Create(self, caller, data, gas, price, value)
} }
} }

View File

@ -24,14 +24,14 @@ import (
func BenchmarkVmAckermann32Tests(b *testing.B) { func BenchmarkVmAckermann32Tests(b *testing.B) {
fn := filepath.Join(vmTestDir, "vmPerformanceTest.json") fn := filepath.Join(vmTestDir, "vmPerformanceTest.json")
if err := BenchVmTest(fn, bconf{"ackermann32", true, os.Getenv("JITVM") == "true"}, b); err != nil { if err := BenchVmTest(fn, bconf{"ackermann32", os.Getenv("JITFORCE") == "true", os.Getenv("JITVM") == "true"}, b); err != nil {
b.Error(err) b.Error(err)
} }
} }
func BenchmarkVmFibonacci16Tests(b *testing.B) { func BenchmarkVmFibonacci16Tests(b *testing.B) {
fn := filepath.Join(vmTestDir, "vmPerformanceTest.json") fn := filepath.Join(vmTestDir, "vmPerformanceTest.json")
if err := BenchVmTest(fn, bconf{"fibonacci16", true, os.Getenv("JITVM") == "true"}, b); err != nil { if err := BenchVmTest(fn, bconf{"fibonacci16", os.Getenv("JITFORCE") == "true", os.Getenv("JITVM") == "true"}, b); err != nil {
b.Error(err) b.Error(err)
} }
} }

View File

@ -185,7 +185,7 @@ func runVmTest(test VmTest) error {
ret []byte ret []byte
gas *big.Int gas *big.Int
err error err error
logs state.Logs logs vm.Logs
) )
ret, logs, gas, err = RunVm(statedb, env, test.Exec) ret, logs, gas, err = RunVm(statedb, env, test.Exec)
@ -234,7 +234,7 @@ func runVmTest(test VmTest) error {
return nil return nil
} }
func RunVm(state *state.StateDB, env, exec map[string]string) ([]byte, state.Logs, *big.Int, error) { func RunVm(state *state.StateDB, env, exec map[string]string) ([]byte, vm.Logs, *big.Int, error) {
var ( var (
to = common.HexToAddress(exec["address"]) to = common.HexToAddress(exec["address"])
from = common.HexToAddress(exec["caller"]) from = common.HexToAddress(exec["caller"])

View File

@ -33,6 +33,7 @@ import (
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/eth/filters" "github.com/ethereum/go-ethereum/eth/filters"
@ -541,7 +542,7 @@ func (self *XEth) NewLogFilter(earliest, latest int64, skip, max int, address []
filter.SetMax(max) filter.SetMax(max)
filter.SetAddress(cAddress(address)) filter.SetAddress(cAddress(address))
filter.SetTopics(cTopics(topics)) filter.SetTopics(cTopics(topics))
filter.LogsCallback = func(logs state.Logs) { filter.LogsCallback = func(logs vm.Logs) {
self.logMu.Lock() self.logMu.Lock()
defer self.logMu.Unlock() defer self.logMu.Unlock()
@ -580,7 +581,7 @@ func (self *XEth) NewBlockFilter() int {
id := self.filterManager.Add(filter) id := self.filterManager.Add(filter)
self.blockQueue[id] = &hashQueue{timeout: time.Now()} self.blockQueue[id] = &hashQueue{timeout: time.Now()}
filter.BlockCallback = func(block *types.Block, logs state.Logs) { filter.BlockCallback = func(block *types.Block, logs vm.Logs) {
self.blockMu.Lock() self.blockMu.Lock()
defer self.blockMu.Unlock() defer self.blockMu.Unlock()
@ -603,7 +604,7 @@ func (self *XEth) GetFilterType(id int) byte {
return UnknownFilterTy return UnknownFilterTy
} }
func (self *XEth) LogFilterChanged(id int) state.Logs { func (self *XEth) LogFilterChanged(id int) vm.Logs {
self.logMu.Lock() self.logMu.Lock()
defer self.logMu.Unlock() defer self.logMu.Unlock()
@ -633,7 +634,7 @@ func (self *XEth) TransactionFilterChanged(id int) []common.Hash {
return nil return nil
} }
func (self *XEth) Logs(id int) state.Logs { func (self *XEth) Logs(id int) vm.Logs {
filter := self.filterManager.Get(id) filter := self.filterManager.Get(id)
if filter != nil { if filter != nil {
return filter.Find() return filter.Find()
@ -642,7 +643,7 @@ func (self *XEth) Logs(id int) state.Logs {
return nil return nil
} }
func (self *XEth) AllLogs(earliest, latest int64, skip, max int, address []string, topics [][]string) state.Logs { func (self *XEth) AllLogs(earliest, latest int64, skip, max int, address []string, topics [][]string) vm.Logs {
filter := filters.New(self.backend.ChainDb()) filter := filters.New(self.backend.ChainDb())
filter.SetEarliestBlock(earliest) filter.SetEarliestBlock(earliest)
filter.SetLatestBlock(latest) filter.SetLatestBlock(latest)
@ -1029,19 +1030,19 @@ func (m callmsg) Data() []byte { return m.data }
type logQueue struct { type logQueue struct {
mu sync.Mutex mu sync.Mutex
logs state.Logs logs vm.Logs
timeout time.Time timeout time.Time
id int id int
} }
func (l *logQueue) add(logs ...*state.Log) { func (l *logQueue) add(logs ...*vm.Log) {
l.mu.Lock() l.mu.Lock()
defer l.mu.Unlock() defer l.mu.Unlock()
l.logs = append(l.logs, logs...) l.logs = append(l.logs, logs...)
} }
func (l *logQueue) get() state.Logs { func (l *logQueue) get() vm.Logs {
l.mu.Lock() l.mu.Lock()
defer l.mu.Unlock() defer l.mu.Unlock()