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:
parent
f7a71996fb
commit
361082ec4b
|
@ -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,7 +185,9 @@ 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) MakeSnapshot() vm.Database { return self.state.Copy() }
|
||||||
|
func (self *VMEnv) SetSnapshot(db vm.Database) { self.state.Set(db.(*state.StateDB)) }
|
||||||
func (self *VMEnv) Origin() common.Address { return *self.transactor }
|
func (self *VMEnv) Origin() common.Address { return *self.transactor }
|
||||||
func (self *VMEnv) BlockNumber() *big.Int { return common.Big0 }
|
func (self *VMEnv) BlockNumber() *big.Int { return common.Big0 }
|
||||||
func (self *VMEnv) Coinbase() common.Address { return *self.transactor }
|
func (self *VMEnv) Coinbase() common.Address { return *self.transactor }
|
||||||
|
@ -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)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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"),
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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))
|
||||||
|
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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})
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
|
@ -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
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -20,19 +20,25 @@ 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
|
||||||
|
@ -40,26 +46,26 @@ type instruction struct {
|
||||||
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))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
109
core/vm/jit.go
109
core/vm/jit.go
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
b.ResetTimer()
|
t.Errorf("expected input to be nil, got %x", contract.Input)
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
context := NewContext(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()
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPcMappingToInstruction(t *testing.T) {
|
||||||
|
program := NewProgram([]byte{byte(PUSH2), 0xbe, 0xef, byte(ADD)})
|
||||||
|
CompileProgram(program)
|
||||||
|
if program.mapping[3] != 1 {
|
||||||
|
t.Error("expected mapping PC 4 to me instr no. 2, got", program.mapping[4])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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}
|
||||||
|
}
|
|
@ -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"
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
629
core/vm/vm.go
629
core/vm/vm.go
|
@ -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,443 +129,62 @@ 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
|
||||||
|
fmt.Println("moved", it)
|
||||||
glog.V(logger.Info).Infoln("Moved execution to JIT")
|
glog.V(logger.Info).Infoln("Moved execution to JIT")
|
||||||
return runProgram(program, pc, mem, stack, self.env, context, input)
|
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)
|
||||||
|
|
||||||
|
if opPtr := jumpTable[op]; opPtr.valid {
|
||||||
|
if opPtr.fn != nil {
|
||||||
|
opPtr.fn(instruction{}, &pc, self.env, contract, mem, stack)
|
||||||
|
} else {
|
||||||
switch op {
|
switch op {
|
||||||
case ADD:
|
case PC:
|
||||||
x, y := stack.pop(), stack.pop()
|
opPc(instruction{data: new(big.Int).SetUint64(pc)}, &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 {
|
|
||||||
n := new(big.Int)
|
|
||||||
if new(big.Int).Mul(x, y).Cmp(common.Big0) < 0 {
|
|
||||||
n.SetInt64(-1)
|
|
||||||
} else {
|
|
||||||
n.SetInt64(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
base.Div(x.Abs(x), y.Abs(y)).Mul(base, n)
|
|
||||||
|
|
||||||
U256(base)
|
|
||||||
}
|
|
||||||
|
|
||||||
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:
|
case JUMP:
|
||||||
if err := jump(pc, stack.pop()); err != nil {
|
if err := jump(pc, stack.pop()); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -578,99 +201,20 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
|
||||||
|
|
||||||
continue
|
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:
|
case RETURN:
|
||||||
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:
|
||||||
receiver := statedb.GetOrNewStateObject(common.BigToAddress(stack.pop()))
|
opSuicide(instruction{}, nil, self.env, contract, mem, stack)
|
||||||
balance := statedb.GetBalance(context.Address())
|
|
||||||
|
|
||||||
receiver.AddBalance(balance)
|
|
||||||
|
|
||||||
statedb.Delete(context.Address())
|
|
||||||
|
|
||||||
fallthrough
|
fallthrough
|
||||||
case STOP: // Stop the context
|
case STOP: // Stop the contract
|
||||||
|
return contract.Return(nil), nil
|
||||||
return context.Return(nil), nil
|
}
|
||||||
default:
|
}
|
||||||
|
} else {
|
||||||
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 := contract.self.(*state.StateObject)
|
||||||
object.EachStorage(func(k, v []byte) {
|
object.EachStorage(func(k, v []byte) {
|
||||||
storage[common.BytesToHash(k)] = v
|
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})
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}*/
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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"])
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"])
|
||||||
|
|
17
xeth/xeth.go
17
xeth/xeth.go
|
@ -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()
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue