aboutsummaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorJeffrey Wilcke <geffobscura@gmail.com>2016-01-20 06:50:00 +0800
committerJeffrey Wilcke <geffobscura@gmail.com>2016-02-18 17:11:48 +0800
commitb6d88a0e9f9aaeb47d585d79c768d457b545af90 (patch)
tree49e667cd929ce6b561961ac741d9ff0f82e61261 /core
parent4f4d2b647488eaa056613fa6f026229ac91f066a (diff)
downloaddexon-b6d88a0e9f9aaeb47d585d79c768d457b545af90.tar
dexon-b6d88a0e9f9aaeb47d585d79c768d457b545af90.tar.gz
dexon-b6d88a0e9f9aaeb47d585d79c768d457b545af90.tar.bz2
dexon-b6d88a0e9f9aaeb47d585d79c768d457b545af90.tar.lz
dexon-b6d88a0e9f9aaeb47d585d79c768d457b545af90.tar.xz
dexon-b6d88a0e9f9aaeb47d585d79c768d457b545af90.tar.zst
dexon-b6d88a0e9f9aaeb47d585d79c768d457b545af90.zip
core, core/vm, crypto: fixes for homestead
* Removed some strange code that didn't apply state reverting properly * Refactored code setting from vm & state transition to the executioner * Updated tests
Diffstat (limited to 'core')
-rw-r--r--core/block_validator.go11
-rw-r--r--core/database_util.go14
-rw-r--r--core/database_util_test.go4
-rw-r--r--core/execution.go60
-rw-r--r--core/state/dump.go3
-rw-r--r--core/state/state_object.go7
-rw-r--r--core/state/statedb.go17
-rw-r--r--core/state_transition.go39
-rw-r--r--core/tx_pool.go (renamed from core/transaction_pool.go)26
-rw-r--r--core/tx_pool_test.go (renamed from core/transaction_pool_test.go)0
-rw-r--r--core/types/transaction.go23
-rw-r--r--core/vm/contract.go44
-rw-r--r--core/vm/environment.go1
-rw-r--r--core/vm/instructions.go27
-rw-r--r--core/vm/jit.go21
-rw-r--r--core/vm/jit_test.go5
-rw-r--r--core/vm/jump_table.go23
-rw-r--r--core/vm/jump_table_test.go24
-rw-r--r--core/vm/opcodes.go1
-rw-r--r--core/vm/vm.go51
20 files changed, 210 insertions, 191 deletions
diff --git a/core/block_validator.go b/core/block_validator.go
index 901744c61..73c33d8dd 100644
--- a/core/block_validator.go
+++ b/core/block_validator.go
@@ -117,8 +117,7 @@ func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *stat
// For valid blocks this should always validate to true.
rbloom := types.CreateBloom(receipts)
if rbloom != header.Bloom {
- //fmt.Printf("FUNKY: ValidateState: block number: %v\n", block.Number())
- return fmt.Errorf("unable to replicate block's bloom=%x", rbloom)
+ return fmt.Errorf("unable to replicate block's bloom=%x vs calculated bloom=%x", header.Bloom, rbloom)
}
// Tre receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, R1]]))
receiptSha := types.DeriveSha(receipts)
@@ -270,10 +269,6 @@ func calcDifficultyHomestead(time, parentTime uint64, parentNumber, parentDiff *
bigTime := new(big.Int).SetUint64(time)
bigParentTime := new(big.Int).SetUint64(parentTime)
- // for the exponential factor
- periodCount := new(big.Int).Add(parentNumber, common.Big1)
- periodCount.Div(periodCount, ExpDiffPeriod)
-
// holds intermediate values to make the algo easier to read & audit
x := new(big.Int)
y := new(big.Int)
@@ -298,6 +293,10 @@ func calcDifficultyHomestead(time, parentTime uint64, parentNumber, parentDiff *
x = params.MinimumDifficulty
}
+ // for the exponential factor
+ periodCount := new(big.Int).Add(parentNumber, common.Big1)
+ periodCount.Div(periodCount, ExpDiffPeriod)
+
// the exponential factor, commonly refered to as "the bomb"
// diff = diff + 2^(periodCount - 2)
if periodCount.Cmp(common.Big1) > 0 {
diff --git a/core/database_util.go b/core/database_util.go
index 18ca1f44c..fd2b4c312 100644
--- a/core/database_util.go
+++ b/core/database_util.go
@@ -95,20 +95,6 @@ func GetHeadFastBlockHash(db ethdb.Database) common.Hash {
return common.BytesToHash(data)
}
-// GetHeadBlockNum retrieves the block number of the current canonical head block.
-func GetHeadBlockNum(db ethdb.Database) *big.Int {
- data, _ := db.Get(headBlockKey)
- if len(data) == 0 {
- return nil
- }
- header := new(types.Header)
- if err := rlp.Decode(bytes.NewReader(data), header); err != nil {
- glog.V(logger.Error).Infof("invalid block header RLP for head block: %v", err)
- return nil
- }
- return header.Number
-}
-
// GetHeaderRLP retrieves a block header in its raw RLP database encoding, or nil
// if the header's not found.
func GetHeaderRLP(db ethdb.Database, hash common.Hash) rlp.RawValue {
diff --git a/core/database_util_test.go b/core/database_util_test.go
index 059f1ae9f..6b3793635 100644
--- a/core/database_util_test.go
+++ b/core/database_util_test.go
@@ -62,7 +62,7 @@ func (d *diffTest) UnmarshalJSON(b []byte) (err error) {
return nil
}
-func TestDifficulty(t *testing.T) {
+func TestDifficultyFrontier(t *testing.T) {
file, err := os.Open("../tests/files/BasicTests/difficulty.json")
if err != nil {
t.Fatal(err)
@@ -77,7 +77,7 @@ func TestDifficulty(t *testing.T) {
for name, test := range tests {
number := new(big.Int).Sub(test.CurrentBlocknumber, big.NewInt(1))
- diff := CalcDifficulty(test.CurrentTimestamp, test.ParentTimestamp, number, test.ParentDifficulty)
+ diff := calcDifficultyFrontier(test.CurrentTimestamp, test.ParentTimestamp, number, test.ParentDifficulty)
if diff.Cmp(test.CurrentDifficulty) != 0 {
t.Error(name, "failed. Expected", test.CurrentDifficulty, "and calculated", diff)
}
diff --git a/core/execution.go b/core/execution.go
index 0c7e8cc9f..24c0c93ae 100644
--- a/core/execution.go
+++ b/core/execution.go
@@ -44,7 +44,6 @@ func DelegateCall(env vm.Environment, caller vm.ContractRef, addr common.Address
originAddr := env.Origin()
callerValue := caller.Value()
ret, _, err = execDelegateCall(env, caller, &originAddr, &callerAddr, &addr, input, env.Db().GetCode(addr), gas, gasPrice, callerValue)
- caller.SetAddress(callerAddr)
return ret, err
}
@@ -78,15 +77,15 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A
var createAccount bool
if address == nil {
- // Generate a new address
+ // Create a new account on the state
nonce := env.Db().GetNonce(caller.Address())
env.Db().SetNonce(caller.Address(), nonce+1)
addr = crypto.CreateAddress(caller.Address(), nonce)
address = &addr
createAccount = true
}
- snapshotPreTransfer := env.MakeSnapshot()
+ snapshotPreTransfer := env.MakeSnapshot()
var (
from = env.Db().GetAccount(caller.Address())
to vm.Account
@@ -101,24 +100,38 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A
}
}
env.Transfer(from, to, value)
- snapshotPostTransfer := env.MakeSnapshot()
+ // initialise a new contract and set the code that is to be used by the
+ // EVM. The contract is a scoped environment for this execution context
+ // only.
contract := vm.NewContract(caller, to, value, gas, gasPrice)
contract.SetCallCode(codeAddr, code)
+ defer contract.Finalise()
ret, err = evm.Run(contract, input)
-
- if err != nil {
- if err == vm.CodeStoreOutOfGasError {
- // TODO: this is rather hacky, restore all state changes
- // except sender's account nonce increment
- toNonce := env.Db().GetNonce(to.Address())
- env.SetSnapshot(snapshotPostTransfer)
- env.Db().SetNonce(to.Address(), toNonce)
+ // if the contract creation ran successfully and no errors were returned
+ // calculate the gas required to store the code. If the code could not
+ // be stored due to not enough gas set an error and let it be handled
+ // by the error checking condition below.
+ if err == nil && createAccount {
+ dataGas := big.NewInt(int64(len(ret)))
+ dataGas.Mul(dataGas, params.CreateDataGas)
+ if contract.UseGas(dataGas) {
+ env.Db().SetCode(*address, ret)
} else {
- env.SetSnapshot(snapshotPreTransfer) //env.Db().Set(snapshot)
+ err = vm.CodeStoreOutOfGasError
}
}
+
+ // When an error was returned by the EVM or when setting the creation code
+ // above we revert to the snapshot and consume any gas remaining. Additionally
+ // when we're in homestead this also counts for code storage gas errors.
+ if err != nil && (params.IsHomestead(env.BlockNumber()) || err != vm.CodeStoreOutOfGasError) {
+ contract.UseGas(contract.Gas)
+
+ env.SetSnapshot(snapshotPreTransfer)
+ }
+
return ret, addr, err
}
@@ -131,32 +144,27 @@ func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toA
return nil, common.Address{}, vm.DepthError
}
- if !env.CanTransfer(*originAddr, value) {
- caller.ReturnGas(gas, gasPrice)
- return nil, common.Address{}, ValueTransferErr("insufficient funds to transfer value. Req %v, has %v", value, env.Db().GetBalance(caller.Address()))
- }
-
snapshot := env.MakeSnapshot()
- var (
- //from = env.Db().GetAccount(*originAddr)
- to vm.Account
- )
+ var to vm.Account
if !env.Db().Exist(*toAddr) {
to = env.Db().CreateAccount(*toAddr)
} else {
to = env.Db().GetAccount(*toAddr)
}
- contract := vm.NewContract(caller, to, value, gas, gasPrice)
+ // Iinitialise a new contract and make initialise the delegate values
+ contract := vm.NewContract(caller, to, value, gas, gasPrice).AsDelegate()
contract.SetCallCode(codeAddr, code)
- contract.DelegateCall = true
+ defer contract.Finalise()
ret, err = evm.Run(contract, input)
-
if err != nil {
- env.SetSnapshot(snapshot) //env.Db().Set(snapshot)
+ contract.UseGas(contract.Gas)
+
+ env.SetSnapshot(snapshot)
}
+
return ret, addr, err
}
diff --git a/core/state/dump.go b/core/state/dump.go
index cff9c50aa..8eb03e8e4 100644
--- a/core/state/dump.go
+++ b/core/state/dump.go
@@ -28,6 +28,7 @@ type Account struct {
Nonce uint64 `json:"nonce"`
Root string `json:"root"`
CodeHash string `json:"codeHash"`
+ Code string `json:"code"`
Storage map[string]string `json:"storage"`
}
@@ -47,7 +48,7 @@ func (self *StateDB) RawDump() World {
addr := self.trie.GetKey(it.Key)
stateObject, _ := DecodeObject(common.BytesToAddress(addr), self.db, it.Value)
- account := Account{Balance: stateObject.balance.String(), Nonce: stateObject.nonce, Root: common.Bytes2Hex(stateObject.Root()), CodeHash: common.Bytes2Hex(stateObject.codeHash)}
+ account := Account{Balance: stateObject.balance.String(), Nonce: stateObject.nonce, Root: common.Bytes2Hex(stateObject.Root()), CodeHash: common.Bytes2Hex(stateObject.codeHash), Code: common.Bytes2Hex(stateObject.Code())}
account.Storage = make(map[string]string)
storageIt := stateObject.trie.Iterator()
diff --git a/core/state/state_object.go b/core/state/state_object.go
index ebc9f8358..6095fc96a 100644
--- a/core/state/state_object.go
+++ b/core/state/state_object.go
@@ -211,11 +211,6 @@ func (c *StateObject) Address() common.Address {
return c.address
}
-// Sets the address of the contract/account
-func (c *StateObject) SetAddress(addr common.Address) {
- c.address = addr
-}
-
func (self *StateObject) Trie() *trie.SecureTrie {
return self.trie
}
@@ -247,7 +242,7 @@ func (self *StateObject) Nonce() uint64 {
// as a vm.Account interface that also satisfies the vm.ContractRef
// interface. Interfaces are awesome.
func (self *StateObject) Value() *big.Int {
- return nil
+ panic("Value on StateObject should never be called")
}
func (self *StateObject) EachStorage(cb func(key, value []byte)) {
diff --git a/core/state/statedb.go b/core/state/statedb.go
index e1dde84d1..22ffa36a0 100644
--- a/core/state/statedb.go
+++ b/core/state/statedb.go
@@ -87,18 +87,6 @@ func (self *StateDB) GetLogs(hash common.Hash) vm.Logs {
return self.logs[hash]
}
-func (self *StateDB) GetAllLogs() *map[common.Hash]vm.Logs {
- copy := make(map[common.Hash]vm.Logs, len(self.logs))
- for k, v := range self.logs {
- copy[k] = v
- }
- return &copy
-}
-
-func (self *StateDB) SetAllLogs(logs *map[common.Hash]vm.Logs) {
- self.logs = *logs
-}
-
func (self *StateDB) Logs() vm.Logs {
var logs vm.Logs
for _, lgs := range self.logs {
@@ -107,11 +95,6 @@ func (self *StateDB) Logs() vm.Logs {
return logs
}
-// TODO: this may not be the most proper thing
-func (self *StateDB) GetDB() ethdb.Database {
- return self.db
-}
-
func (self *StateDB) AddRefund(gas *big.Int) {
self.refund.Add(self.refund, gas)
}
diff --git a/core/state_transition.go b/core/state_transition.go
index 0cd226262..52a46c63d 100644
--- a/core/state_transition.go
+++ b/core/state_transition.go
@@ -21,7 +21,6 @@ import (
"math/big"
"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/logger"
"github.com/ethereum/go-ethereum/logger/glog"
@@ -225,38 +224,24 @@ func (self *StateTransition) transitionDb() (ret []byte, usedGas *big.Int, err e
}
vmenv := self.env
- snapshot := vmenv.MakeSnapshot()
- var addr common.Address
+ //var addr common.Address
if contractCreation {
- ret, addr, err = vmenv.Create(sender, self.data, self.gas, self.gasPrice, self.value)
- if err == nil {
- dataGas := big.NewInt(int64(len(ret)))
- dataGas.Mul(dataGas, params.CreateDataGas)
- if err := self.useGas(dataGas); err == nil {
- self.state.SetCode(addr, ret)
- } else {
- if homestead {
- // rollback all contract creation changes except for
- // sender's incremented account nonce and gas usage
- // TODO: fucking gas hack... verify potential DoS vuln
- accNonce := vmenv.Db().GetNonce(sender.Address())
- logs := vmenv.Db().(*state.StateDB).GetAllLogs()
- vmenv.SetSnapshot(snapshot)
- vmenv.Db().SetNonce(sender.Address(), accNonce)
- vmenv.Db().(*state.StateDB).SetAllLogs(logs)
- self.gas = Big0
-
- }
- 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)
- }
+ ret, _, err = vmenv.Create(sender, self.data, self.gas, self.gasPrice, self.value)
+ if homestead && err == vm.CodeStoreOutOfGasError {
+ self.gas = Big0
+ }
+
+ if err != nil {
+ ret = nil
+ glog.V(logger.Core).Infoln("VM create err:", err)
}
- glog.V(logger.Core).Infoln("VM create err:", err)
} else {
// Increment the nonce for the next transaction
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)
- glog.V(logger.Core).Infoln("VM call err:", err)
+ if err != nil {
+ glog.V(logger.Core).Infoln("VM call err:", err)
+ }
}
if err != nil && IsValueTransferErr(err) {
diff --git a/core/transaction_pool.go b/core/tx_pool.go
index a815c9ef0..b8fb4cd35 100644
--- a/core/transaction_pool.go
+++ b/core/tx_pool.go
@@ -71,6 +71,8 @@ type TxPool struct {
mu sync.RWMutex
pending map[common.Hash]*types.Transaction // processable transactions
queue map[common.Address]map[common.Hash]*types.Transaction
+
+ homestead bool
}
func NewTxPool(eventMux *event.TypeMux, currentStateFn stateFn, gasLimitFn func() *big.Int) *TxPool {
@@ -86,6 +88,7 @@ func NewTxPool(eventMux *event.TypeMux, currentStateFn stateFn, gasLimitFn func(
localTx: newTxSet(),
events: eventMux.Subscribe(ChainHeadEvent{}, GasPriceChanged{}, RemovedTransactionEvent{}),
}
+
go pool.eventLoop()
return pool
@@ -99,6 +102,10 @@ func (pool *TxPool) eventLoop() {
switch ev := ev.Data.(type) {
case ChainHeadEvent:
pool.mu.Lock()
+ if ev.Block != nil && params.IsHomestead(ev.Block.Number()) {
+ pool.homestead = true
+ }
+
pool.resetState()
pool.mu.Unlock()
case GasPriceChanged:
@@ -211,12 +218,6 @@ func (pool *TxPool) SetLocal(tx *types.Transaction) {
// validateTx checks whether a transaction is valid according
// to the consensus rules.
func (pool *TxPool) validateTx(tx *types.Transaction) error {
- // Validate sender
- var (
- from common.Address
- err error
- )
-
local := pool.localTx.contains(tx.Hash())
// Drop transactions under our own minimal accepted gas price
if !local && pool.minGasPrice.Cmp(tx.GasPrice()) > 0 {
@@ -228,15 +229,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error {
return err
}
- homestead := params.IsHomestead(GetHeadBlockNum(currentState.GetDB()))
-
- // Validate the transaction sender and it's sig. Throw
- // if the from fields is invalid.
- if homestead {
- from, err = tx.From()
- } else {
- from, err = tx.FromFrontier()
- }
+ from, err := tx.From()
if err != nil {
return ErrInvalidSender
}
@@ -271,8 +264,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error {
return ErrInsufficientFunds
}
- // Should supply enough intrinsic gas
- intrGas := IntrinsicGas(tx.Data(), MessageCreatesContract(tx), homestead)
+ intrGas := IntrinsicGas(tx.Data(), MessageCreatesContract(tx), pool.homestead)
if tx.Gas().Cmp(intrGas) < 0 {
return ErrIntrinsicGas
}
diff --git a/core/transaction_pool_test.go b/core/tx_pool_test.go
index 811e40111..811e40111 100644
--- a/core/transaction_pool_test.go
+++ b/core/tx_pool_test.go
diff --git a/core/types/transaction.go b/core/types/transaction.go
index af952e450..0c9c1ce18 100644
--- a/core/types/transaction.go
+++ b/core/types/transaction.go
@@ -157,7 +157,14 @@ func (tx *Transaction) Size() common.StorageSize {
return common.StorageSize(c)
}
-// From() caches the address, allowing it to be used regardless of
+// From returns the address derived from the signature (V, R, S) using secp256k1
+// eliptic curve and an error if it failed deriving or upon an incorrect
+// signature.
+//
+// From Uses the homestead consensus rules to determine whether the signature is
+// valid.
+//
+// From caches the address, allowing it to be used regardless of
// Frontier / Homestead. however, the first time called it runs
// signature validations, so we need two versions. This makes it
// easier to ensure backwards compatibility of things like package rpc
@@ -168,6 +175,20 @@ func (tx *Transaction) From() (common.Address, error) {
return doFrom(tx, true)
}
+// FromFrontier returns the address derived from the signature (V, R, S) using
+// secp256k1 eliptic curve and an error if it failed deriving or upon an
+// incorrect signature.
+//
+// FromFrantier uses the frontier consensus rules to determine whether the
+// signature is valid.
+//
+// FromFrontier caches the address, allowing it to be used regardless of
+// Frontier / Homestead. however, the first time called it runs
+// signature validations, so we need two versions. This makes it
+// easier to ensure backwards compatibility of things like package rpc
+// where eth_getblockbynumber uses tx.From() and needs to work for
+// both txs before and after the first homestead block. Signatures
+// valid in homestead are a subset of valid ones in Frontier)
func (tx *Transaction) FromFrontier() (common.Address, error) {
return doFrom(tx, false)
}
diff --git a/core/vm/contract.go b/core/vm/contract.go
index dac81a529..d23995218 100644
--- a/core/vm/contract.go
+++ b/core/vm/contract.go
@@ -26,7 +26,6 @@ import (
type ContractRef interface {
ReturnGas(*big.Int, *big.Int)
Address() common.Address
- SetAddress(common.Address)
Value() *big.Int
SetCode([]byte)
EachStorage(cb func(key, value []byte))
@@ -35,8 +34,12 @@ type ContractRef interface {
// Contract represents an ethereum contract in the state database. It contains
// the the contract code, calling arguments. Contract implements ContractRef
type Contract struct {
- caller ContractRef
- self ContractRef
+ // CallerAddress is the result of the caller which initialised this
+ // contract. However when the "call method" is delegated this value
+ // needs to be initialised to that of the caller's caller.
+ CallerAddress common.Address
+ caller ContractRef
+ self ContractRef
jumpdests destinations // result of JUMPDEST analysis.
@@ -51,9 +54,9 @@ type Contract struct {
DelegateCall bool
}
-// Create a new context for the given data items.
+// NewContract returns a new contract environment for the execution of EVM.
func NewContract(caller ContractRef, object ContractRef, value, gas, price *big.Int) *Contract {
- c := &Contract{caller: caller, self: object, Args: nil}
+ c := &Contract{CallerAddress: caller.Address(), caller: caller, self: object, Args: nil}
if parent, ok := caller.(*Contract); ok {
// Reuse JUMPDEST analysis from parent context if available.
@@ -74,6 +77,16 @@ func NewContract(caller ContractRef, object ContractRef, value, gas, price *big.
return c
}
+// AsDelegate sets the contract to be a delegate call and returns the current
+// contract (for chaining calls)
+func (c *Contract) AsDelegate() *Contract {
+ c.DelegateCall = true
+ // NOTE: caller must, at all times be a contract. It should never happen
+ // that caller is something other than a Contract.
+ c.CallerAddress = c.caller.(*Contract).CallerAddress
+ return c
+}
+
// GetOp returns the n'th element in the contract's byte array
func (c *Contract) GetOp(n uint64) OpCode {
return OpCode(c.GetByte(n))
@@ -88,13 +101,19 @@ func (c *Contract) GetByte(n uint64) byte {
return 0
}
-// Return returns the given ret argument and returns any remaining gas to the
-// caller
-func (c *Contract) Return(ret []byte) []byte {
+// Caller returns the caller of the contract.
+//
+// Caller will recursively call caller when the contract is a delegate
+// call, including that of caller's caller.
+func (c *Contract) Caller() common.Address {
+ return c.CallerAddress
+}
+
+// Finalise finalises the contract and returning any remaining gas to the original
+// caller.
+func (c *Contract) Finalise() {
// Return the remaining gas to the caller
c.caller.ReturnGas(c.Gas, c.Price)
-
- return ret
}
// UseGas attempts the use gas and subtracts it and returns true on success
@@ -118,11 +137,6 @@ func (c *Contract) Address() common.Address {
return c.self.Address()
}
-// SetAddress sets the contracts address
-func (c *Contract) SetAddress(addr common.Address) {
- c.self.SetAddress(addr)
-}
-
// Value returns the contracts value (sent to it from it's caller)
func (c *Contract) Value() *big.Int {
return c.value
diff --git a/core/vm/environment.go b/core/vm/environment.go
index dc60af2ca..a58e3ba2b 100644
--- a/core/vm/environment.go
+++ b/core/vm/environment.go
@@ -121,7 +121,6 @@ type Account interface {
SetNonce(uint64)
Balance() *big.Int
Address() common.Address
- SetAddress(common.Address)
ReturnGas(*big.Int, *big.Int)
SetCode([]byte)
EachStorage(cb func(key, value []byte))
diff --git a/core/vm/instructions.go b/core/vm/instructions.go
index 9d3d4e6fe..26f7671ff 100644
--- a/core/vm/instructions.go
+++ b/core/vm/instructions.go
@@ -337,13 +337,7 @@ func opOrigin(instr instruction, pc *uint64, env Environment, contract *Contract
}
func opCaller(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
- var bigAddr *big.Int
- if contract.DelegateCall {
- bigAddr = env.Origin().Big()
- } else {
- bigAddr = contract.caller.Address().Big()
- }
- stack.push(bigAddr)
+ stack.push(contract.Caller().Big())
}
func opCallValue(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
@@ -514,6 +508,25 @@ func opGas(instr instruction, pc *uint64, env Environment, contract *Contract, m
}
func opCreate(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
+ var (
+ value = stack.pop()
+ offset, size = stack.pop(), stack.pop()
+ input = memory.Get(offset.Int64(), size.Int64())
+ gas = new(big.Int).Set(contract.Gas)
+ )
+ contract.UseGas(contract.Gas)
+ _, addr, suberr := env.Create(contract, input, gas, contract.Price, value)
+ // Push item on the stack based on the returned error. If the ruleset is
+ // homestead we must check for CodeStoreOutOfGasError (homestead only
+ // rule) and treat as an error, if the ruleset is frontier we must
+ // ignore this error and pretend the operation was successful.
+ if params.IsHomestead(env.BlockNumber()) && suberr == CodeStoreOutOfGasError {
+ stack.push(new(big.Int))
+ } else if suberr != nil && suberr != CodeStoreOutOfGasError {
+ stack.push(new(big.Int))
+ } else {
+ stack.push(addr.Big())
+ }
}
func opCall(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
diff --git a/core/vm/jit.go b/core/vm/jit.go
index 1aa7d7ef2..504aab523 100644
--- a/core/vm/jit.go
+++ b/core/vm/jit.go
@@ -275,6 +275,11 @@ func CompileProgram(program *Program) (err error) {
program.addInstr(op, pc, opGas, nil)
case CREATE:
program.addInstr(op, pc, opCreate, nil)
+ case DELEGATECALL:
+ // Instruction added regardless of homestead phase.
+ // Homestead (and execution of the opcode) is checked during
+ // runtime.
+ program.addInstr(op, pc, opDelegateCall, nil)
case CALL:
program.addInstr(op, pc, opCall, nil)
case CALLCODE:
@@ -317,10 +322,14 @@ func runProgram(program *Program, pcstart uint64, mem *Memory, stack *stack, env
}()
}
+ homestead := params.IsHomestead(env.BlockNumber())
for pc < uint64(len(program.instructions)) {
instrCount++
instr := program.instructions[pc]
+ if instr.Op() == DELEGATECALL && !homestead {
+ return nil, fmt.Errorf("Invalid opcode 0x%x", instr.Op())
+ }
ret, err := instr.do(program, &pc, env, contract, mem, stack)
if err != nil {
@@ -328,13 +337,13 @@ func runProgram(program *Program, pcstart uint64, mem *Memory, stack *stack, env
}
if instr.halts() {
- return contract.Return(ret), nil
+ return ret, nil
}
}
contract.Input = nil
- return contract.Return(nil), nil
+ return nil, nil
}
// validDest checks if the given distination is a valid one given the
@@ -457,7 +466,6 @@ func jitCalculateGasAndSize(env Environment, contract *Contract, instr instructi
gas.Add(gas, stack.data[stack.len()-1])
if op == CALL {
- //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)
}
@@ -471,6 +479,13 @@ func jitCalculateGasAndSize(env Environment, contract *Contract, instr instructi
y := calcMemSize(stack.data[stack.len()-4], stack.data[stack.len()-5])
newMemSize = common.BigMax(x, y)
+ case DELEGATECALL:
+ gas.Add(gas, stack.data[stack.len()-1])
+
+ x := calcMemSize(stack.data[stack.len()-5], stack.data[stack.len()-6])
+ y := calcMemSize(stack.data[stack.len()-3], stack.data[stack.len()-4])
+
+ newMemSize = common.BigMax(x, y)
}
quadMemGas(mem, newMemSize, gas)
diff --git a/core/vm/jit_test.go b/core/vm/jit_test.go
index 8c50ed0f5..e8e078a46 100644
--- a/core/vm/jit_test.go
+++ b/core/vm/jit_test.go
@@ -127,6 +127,8 @@ type account struct{}
func (account) SubBalance(amount *big.Int) {}
func (account) AddBalance(amount *big.Int) {}
+func (account) SetAddress(common.Address) {}
+func (account) Value() *big.Int { return nil }
func (account) SetBalance(*big.Int) {}
func (account) SetNonce(uint64) {}
func (account) Balance() *big.Int { return nil }
@@ -206,3 +208,6 @@ func (self *Env) CallCode(caller ContractRef, addr common.Address, data []byte,
func (self *Env) Create(caller ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) {
return nil, common.Address{}, nil
}
+func (self *Env) DelegateCall(me ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) {
+ return nil, nil
+}
diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go
index 222c93854..37d7bb160 100644
--- a/core/vm/jump_table.go
+++ b/core/vm/jump_table.go
@@ -1,13 +1,29 @@
package vm
-import "math/big"
+import (
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/params"
+)
type jumpPtr struct {
fn instrFn
valid bool
}
-var jumpTable [256]jumpPtr
+type vmJumpTable [256]jumpPtr
+
+func (jt vmJumpTable) init(blockNumber *big.Int) {
+ // when initialising a new VM execution we must first check the homestead
+ // changes.
+ if params.IsHomestead(blockNumber) {
+ jumpTable[DELEGATECALL] = jumpPtr{opDelegateCall, true}
+ } else {
+ jumpTable[DELEGATECALL] = jumpPtr{nil, false}
+ }
+}
+
+var jumpTable vmJumpTable
func init() {
jumpTable[ADD] = jumpPtr{opAdd, true}
@@ -62,10 +78,9 @@ func init() {
jumpTable[PC] = jumpPtr{nil, true}
jumpTable[MSIZE] = jumpPtr{opMsize, true}
jumpTable[GAS] = jumpPtr{opGas, true}
- jumpTable[CREATE] = jumpPtr{nil, true}
+ jumpTable[CREATE] = jumpPtr{opCreate, true}
jumpTable[CALL] = jumpPtr{opCall, true}
jumpTable[CALLCODE] = jumpPtr{opCallCode, true}
- jumpTable[DELEGATECALL] = jumpPtr{opDelegateCall, true}
jumpTable[LOG0] = jumpPtr{makeLog(0), true}
jumpTable[LOG1] = jumpPtr{makeLog(1), true}
jumpTable[LOG2] = jumpPtr{makeLog(2), true}
diff --git a/core/vm/jump_table_test.go b/core/vm/jump_table_test.go
new file mode 100644
index 000000000..98d34bef2
--- /dev/null
+++ b/core/vm/jump_table_test.go
@@ -0,0 +1,24 @@
+package vm
+
+import (
+ "math/big"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/params"
+)
+
+func TestInit(t *testing.T) {
+ params.HomesteadBlock = big.NewInt(1)
+
+ jumpTable.init(big.NewInt(0))
+ if jumpTable[DELEGATECALL].valid {
+ t.Error("Expected DELEGATECALL not to be present")
+ }
+
+ for _, n := range []int64{1, 2, 100} {
+ jumpTable.init(big.NewInt(n))
+ if !jumpTable[DELEGATECALL].valid {
+ t.Error("Expected DELEGATECALL to be present for block", n)
+ }
+ }
+}
diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go
index 00593ae95..7d861f1de 100644
--- a/core/vm/opcodes.go
+++ b/core/vm/opcodes.go
@@ -404,6 +404,7 @@ var stringToOp = map[string]OpCode{
"CALLDATALOAD": CALLDATALOAD,
"CALLDATASIZE": CALLDATASIZE,
"CALLDATACOPY": CALLDATACOPY,
+ "DELEGATECALL": DELEGATECALL,
"CODESIZE": CODESIZE,
"CODECOPY": CODECOPY,
"GASPRICE": GASPRICE,
diff --git a/core/vm/vm.go b/core/vm/vm.go
index 863b2cc0d..320135ff2 100644
--- a/core/vm/vm.go
+++ b/core/vm/vm.go
@@ -35,6 +35,9 @@ type Vm struct {
// New returns a new Vm
func New(env Environment) *Vm {
+ // init the jump table. Also prepares the homestead changes
+ jumpTable.init(env.BlockNumber())
+
return &Vm{env: env}
}
@@ -43,16 +46,6 @@ func (self *Vm) Run(contract *Contract, input []byte) (ret []byte, err error) {
self.env.SetDepth(self.env.Depth() + 1)
defer self.env.SetDepth(self.env.Depth() - 1)
- // User defer pattern to check for an error and, based on the error being nil or not, use all gas and return.
- defer func() {
- if err != nil {
- // In case of a VM exception (known exceptions) all gas consumed (panics NOT included).
- contract.UseGas(contract.Gas)
-
- ret = contract.Return(nil)
- }
- }()
-
if contract.CodeAddr != nil {
if p := Precompiled[contract.CodeAddr.Str()]; p != nil {
return self.RunPrecompiled(p, input, contract)
@@ -61,7 +54,7 @@ func (self *Vm) Run(contract *Contract, input []byte) (ret []byte, err error) {
// Don't bother with the execution if there's no code.
if len(contract.Code) == 0 {
- return contract.Return(nil), nil
+ return nil, nil
}
var (
@@ -199,46 +192,17 @@ func (self *Vm) Run(contract *Contract, input []byte) (ret []byte, err error) {
continue
}
- case CREATE:
- var (
- value = stack.pop()
- offset, size = stack.pop(), stack.pop()
- input = mem.Get(offset.Int64(), size.Int64())
- gas = new(big.Int).Set(contract.Gas)
- addr common.Address
- ret []byte
- suberr error
- )
- contract.UseGas(contract.Gas)
- ret, addr, suberr = self.env.Create(contract, input, gas, contract.Price, value)
- if suberr != nil {
- stack.push(new(big.Int))
- } else {
- // gas < len(ret) * Createinstr.dataGas == NO_CODE
- dataGas := big.NewInt(int64(len(ret)))
- dataGas.Mul(dataGas, params.CreateDataGas)
- if contract.UseGas(dataGas) {
- self.env.Db().SetCode(addr, ret)
- } else {
- if params.IsHomestead(self.env.BlockNumber()) {
- stack.push(new(big.Int))
- return nil, CodeStoreOutOfGasError
- }
- }
- stack.push(addr.Big())
- }
-
case RETURN:
offset, size := stack.pop(), stack.pop()
ret := mem.GetPtr(offset.Int64(), size.Int64())
- return contract.Return(ret), nil
+ return ret, nil
case SUICIDE:
opSuicide(instruction{}, nil, self.env, contract, mem, stack)
fallthrough
case STOP: // Stop the contract
- return contract.Return(nil), nil
+ return nil, nil
}
}
} else {
@@ -359,7 +323,6 @@ func calculateGasAndSize(env Environment, contract *Contract, caller ContractRef
gas.Add(gas, stack.data[stack.len()-1])
if op == CALL {
- //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)
}
@@ -392,7 +355,7 @@ func (self *Vm) RunPrecompiled(p *PrecompiledAccount, input []byte, contract *Co
if contract.UseGas(gas) {
ret = p.Call(input)
- return contract.Return(ret), nil
+ return ret, nil
} else {
return nil, OutOfGasError
}