aboutsummaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorGustav Simonsson <gustav.simonsson@gmail.com>2015-11-27 22:40:29 +0800
committerJeffrey Wilcke <geffobscura@gmail.com>2016-02-18 17:08:11 +0800
commit371871d685d54b916aef28de689d6f0af7822083 (patch)
treee704b02ba2ffd2d1164001885fba15106b0f7d94 /core
parentaa36a6ae4f24f07e2c470a21c93ff37ad5861982 (diff)
downloadgo-tangerine-371871d685d54b916aef28de689d6f0af7822083.tar
go-tangerine-371871d685d54b916aef28de689d6f0af7822083.tar.gz
go-tangerine-371871d685d54b916aef28de689d6f0af7822083.tar.bz2
go-tangerine-371871d685d54b916aef28de689d6f0af7822083.tar.lz
go-tangerine-371871d685d54b916aef28de689d6f0af7822083.tar.xz
go-tangerine-371871d685d54b916aef28de689d6f0af7822083.tar.zst
go-tangerine-371871d685d54b916aef28de689d6f0af7822083.zip
parmas, crypto, core, core/vm: homestead consensus protocol changes
* change gas cost for contract creating txs * invalidate signature with s value greater than secp256k1 N / 2 * OOG contract creation if not enough gas to store code * new difficulty adjustment algorithm * new DELEGATECALL op code
Diffstat (limited to 'core')
-rw-r--r--core/bench_test.go2
-rw-r--r--core/block_validator.go131
-rw-r--r--core/database_util.go83
-rw-r--r--core/execution.go68
-rw-r--r--core/state/state_object.go12
-rw-r--r--core/state/statedb.go17
-rw-r--r--core/state_processor.go1
-rw-r--r--core/state_transition.go45
-rw-r--r--core/transaction_pool.go22
-rw-r--r--core/types/transaction.go21
-rw-r--r--core/vm/contract.go16
-rw-r--r--core/vm/contracts.go3
-rw-r--r--core/vm/environment.go4
-rw-r--r--core/vm/errors.go1
-rw-r--r--core/vm/gas.go1
-rw-r--r--core/vm/instructions.go49
-rw-r--r--core/vm/jump_table.go3
-rw-r--r--core/vm/opcodes.go12
-rw-r--r--core/vm/runtime/env.go4
-rw-r--r--core/vm/vm.go38
-rw-r--r--core/vm_env.go4
21 files changed, 407 insertions, 130 deletions
diff --git a/core/bench_test.go b/core/bench_test.go
index 6fa7659b9..f991e5e7f 100644
--- a/core/bench_test.go
+++ b/core/bench_test.go
@@ -82,7 +82,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) {
return func(i int, gen *BlockGen) {
toaddr := common.Address{}
data := make([]byte, nbytes)
- gas := IntrinsicGas(data)
+ gas := IntrinsicGas(data, false, false)
tx, _ := types.NewTransaction(gen.TxNonce(benchRootAddr), toaddr, big.NewInt(1), gas, nil, data).SignECDSA(benchRootKey)
gen.AddTx(tx)
}
diff --git a/core/block_validator.go b/core/block_validator.go
index 62d096d02..901744c61 100644
--- a/core/block_validator.go
+++ b/core/block_validator.go
@@ -30,6 +30,12 @@ import (
"gopkg.in/fatih/set.v0"
)
+var (
+ ExpDiffPeriod = big.NewInt(100000)
+ big10 = big.NewInt(10)
+ bigMinus99 = big.NewInt(-99)
+)
+
// BlockValidator is responsible for validating block headers, uncles and
// processed state.
//
@@ -111,6 +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)
}
// Tre receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, R1]]))
@@ -241,3 +248,127 @@ func ValidateHeader(pow pow.PoW, header *types.Header, parent *types.Header, che
}
return nil
}
+
+// CalcDifficulty is the difficulty adjustment algorithm. It returns
+// the difficulty that a new block should have when created at time
+// given the parent block's time and difficulty.
+func CalcDifficulty(time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int {
+ if params.IsHomestead(new(big.Int).Add(parentNumber, common.Big1)) {
+ return calcDifficultyHomestead(time, parentTime, parentNumber, parentDiff)
+ } else {
+ return calcDifficultyFrontier(time, parentTime, parentNumber, parentDiff)
+ }
+}
+
+func calcDifficultyHomestead(time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int {
+ // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2.mediawiki
+ // algorithm:
+ // diff = (parent_diff +
+ // (parent_diff / 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99))
+ // ) + 2^(periodCount - 2)
+
+ 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)
+
+ // 1 - (block_timestamp -parent_timestamp) // 10
+ x.Sub(bigTime, bigParentTime)
+ x.Div(x, big10)
+ x.Sub(common.Big1, x)
+
+ // max(1 - (block_timestamp - parent_timestamp) // 10, -99)))
+ if x.Cmp(bigMinus99) < 0 {
+ x.Set(bigMinus99)
+ }
+
+ // (parent_diff + parent_diff // 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99))
+ y.Div(parentDiff, params.DifficultyBoundDivisor)
+ x.Mul(y, x)
+ x.Add(parentDiff, x)
+
+ // minimum difficulty can ever be (before exponential factor)
+ if x.Cmp(params.MinimumDifficulty) < 0 {
+ x = params.MinimumDifficulty
+ }
+
+ // the exponential factor, commonly refered to as "the bomb"
+ // diff = diff + 2^(periodCount - 2)
+ if periodCount.Cmp(common.Big1) > 0 {
+ y.Sub(periodCount, common.Big2)
+ y.Exp(common.Big2, y, nil)
+ x.Add(x, y)
+ }
+
+ return x
+}
+
+func calcDifficultyFrontier(time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int {
+ diff := new(big.Int)
+ adjust := new(big.Int).Div(parentDiff, params.DifficultyBoundDivisor)
+ bigTime := new(big.Int)
+ bigParentTime := new(big.Int)
+
+ bigTime.SetUint64(time)
+ bigParentTime.SetUint64(parentTime)
+
+ if bigTime.Sub(bigTime, bigParentTime).Cmp(params.DurationLimit) < 0 {
+ diff.Add(parentDiff, adjust)
+ } else {
+ diff.Sub(parentDiff, adjust)
+ }
+ if diff.Cmp(params.MinimumDifficulty) < 0 {
+ diff = params.MinimumDifficulty
+ }
+
+ periodCount := new(big.Int).Add(parentNumber, common.Big1)
+ periodCount.Div(periodCount, ExpDiffPeriod)
+ if periodCount.Cmp(common.Big1) > 0 {
+ // diff = diff + 2^(periodCount - 2)
+ expDiff := periodCount.Sub(periodCount, common.Big2)
+ expDiff.Exp(common.Big2, expDiff, nil)
+ diff.Add(diff, expDiff)
+ diff = common.BigMax(diff, params.MinimumDifficulty)
+ }
+
+ return diff
+}
+
+// CalcGasLimit computes the gas limit of the next block after parent.
+// The result may be modified by the caller.
+// This is miner strategy, not consensus protocol.
+func CalcGasLimit(parent *types.Block) *big.Int {
+ // contrib = (parentGasUsed * 3 / 2) / 1024
+ contrib := new(big.Int).Mul(parent.GasUsed(), big.NewInt(3))
+ contrib = contrib.Div(contrib, big.NewInt(2))
+ contrib = contrib.Div(contrib, params.GasLimitBoundDivisor)
+
+ // decay = parentGasLimit / 1024 -1
+ decay := new(big.Int).Div(parent.GasLimit(), params.GasLimitBoundDivisor)
+ decay.Sub(decay, big.NewInt(1))
+
+ /*
+ strategy: gasLimit of block-to-mine is set based on parent's
+ gasUsed value. if parentGasUsed > parentGasLimit * (2/3) then we
+ increase it, otherwise lower it (or leave it unchanged if it's right
+ at that usage) the amount increased/decreased depends on how far away
+ from parentGasLimit * (2/3) parentGasUsed is.
+ */
+ gl := new(big.Int).Sub(parent.GasLimit(), decay)
+ gl = gl.Add(gl, contrib)
+ gl.Set(common.BigMax(gl, params.MinGasLimit))
+
+ // however, if we're now below the target (GenesisGasLimit) we increase the
+ // limit as much as we can (parentGasLimit / 1024 -1)
+ if gl.Cmp(params.GenesisGasLimit) < 0 {
+ gl.Add(parent.GasLimit(), decay)
+ gl.Set(common.BigMin(gl, params.GenesisGasLimit))
+ }
+ return gl
+}
diff --git a/core/database_util.go b/core/database_util.go
index 2dc113e29..18ca1f44c 100644
--- a/core/database_util.go
+++ b/core/database_util.go
@@ -27,7 +27,6 @@ import (
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
- "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
)
@@ -50,77 +49,9 @@ var (
mipmapPre = []byte("mipmap-log-bloom-")
MIPMapLevels = []uint64{1000000, 500000, 100000, 50000, 1000}
- ExpDiffPeriod = big.NewInt(100000)
blockHashPrefix = []byte("block-hash-") // [deprecated by the header/block split, remove eventually]
)
-// CalcDifficulty is the difficulty adjustment algorithm. It returns
-// the difficulty that a new block b should have when created at time
-// given the parent block's time and difficulty.
-func CalcDifficulty(time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int {
- diff := new(big.Int)
- adjust := new(big.Int).Div(parentDiff, params.DifficultyBoundDivisor)
- bigTime := new(big.Int)
- bigParentTime := new(big.Int)
-
- bigTime.SetUint64(time)
- bigParentTime.SetUint64(parentTime)
-
- if bigTime.Sub(bigTime, bigParentTime).Cmp(params.DurationLimit) < 0 {
- diff.Add(parentDiff, adjust)
- } else {
- diff.Sub(parentDiff, adjust)
- }
- if diff.Cmp(params.MinimumDifficulty) < 0 {
- diff = params.MinimumDifficulty
- }
-
- periodCount := new(big.Int).Add(parentNumber, common.Big1)
- periodCount.Div(periodCount, ExpDiffPeriod)
- if periodCount.Cmp(common.Big1) > 0 {
- // diff = diff + 2^(periodCount - 2)
- expDiff := periodCount.Sub(periodCount, common.Big2)
- expDiff.Exp(common.Big2, expDiff, nil)
- diff.Add(diff, expDiff)
- diff = common.BigMax(diff, params.MinimumDifficulty)
- }
-
- return diff
-}
-
-// CalcGasLimit computes the gas limit of the next block after parent.
-// The result may be modified by the caller.
-// This is miner strategy, not consensus protocol.
-func CalcGasLimit(parent *types.Block) *big.Int {
- // contrib = (parentGasUsed * 3 / 2) / 1024
- contrib := new(big.Int).Mul(parent.GasUsed(), big.NewInt(3))
- contrib = contrib.Div(contrib, big.NewInt(2))
- contrib = contrib.Div(contrib, params.GasLimitBoundDivisor)
-
- // decay = parentGasLimit / 1024 -1
- decay := new(big.Int).Div(parent.GasLimit(), params.GasLimitBoundDivisor)
- decay.Sub(decay, big.NewInt(1))
-
- /*
- strategy: gasLimit of block-to-mine is set based on parent's
- gasUsed value. if parentGasUsed > parentGasLimit * (2/3) then we
- increase it, otherwise lower it (or leave it unchanged if it's right
- at that usage) the amount increased/decreased depends on how far away
- from parentGasLimit * (2/3) parentGasUsed is.
- */
- gl := new(big.Int).Sub(parent.GasLimit(), decay)
- gl = gl.Add(gl, contrib)
- gl.Set(common.BigMax(gl, params.MinGasLimit))
-
- // however, if we're now below the target (GenesisGasLimit) we increase the
- // limit as much as we can (parentGasLimit / 1024 -1)
- if gl.Cmp(params.GenesisGasLimit) < 0 {
- gl.Add(parent.GasLimit(), decay)
- gl.Set(common.BigMin(gl, params.GenesisGasLimit))
- }
- return gl
-}
-
// GetCanonicalHash retrieves a hash assigned to a canonical block number.
func GetCanonicalHash(db ethdb.Database, number uint64) common.Hash {
data, _ := db.Get(append(blockNumPrefix, big.NewInt(int64(number)).Bytes()...))
@@ -164,6 +95,20 @@ 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/execution.go b/core/execution.go
index fd8464f6e..0c7e8cc9f 100644
--- a/core/execution.go
+++ b/core/execution.go
@@ -33,8 +33,18 @@ func Call(env vm.Environment, caller vm.ContractRef, addr common.Address, input
// CallCode executes the given address' code as the given contract address
func CallCode(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice, value *big.Int) (ret []byte, err error) {
- prev := caller.Address()
- ret, _, err = exec(env, caller, &prev, &addr, input, env.Db().GetCode(addr), gas, gasPrice, value)
+ callerAddr := caller.Address()
+ ret, _, err = exec(env, caller, &callerAddr, &addr, input, env.Db().GetCode(addr), gas, gasPrice, value)
+ return ret, err
+}
+
+// DelegateCall is equivalent to CallCode except that sender and value propagates from parent scope to child scope
+func DelegateCall(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice *big.Int) (ret []byte, err error) {
+ callerAddr := caller.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
}
@@ -52,7 +62,6 @@ func Create(env vm.Environment, caller vm.ContractRef, code []byte, gas, gasPric
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) {
evm := vm.NewVm(env)
-
// Depth check execution. Fail if we're trying to execute above the
// limit.
if env.Depth() > int(params.CallCreateDepth.Int64()) {
@@ -72,13 +81,11 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A
// Generate a new address
nonce := env.Db().GetNonce(caller.Address())
env.Db().SetNonce(caller.Address(), nonce+1)
-
addr = crypto.CreateAddress(caller.Address(), nonce)
-
address = &addr
createAccount = true
}
- snapshot := env.MakeSnapshot()
+ snapshotPreTransfer := env.MakeSnapshot()
var (
from = env.Db().GetAccount(caller.Address())
@@ -94,15 +101,62 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A
}
}
env.Transfer(from, to, value)
+ snapshotPostTransfer := env.MakeSnapshot()
contract := vm.NewContract(caller, to, value, gas, gasPrice)
contract.SetCallCode(codeAddr, code)
ret, err = evm.Run(contract, input)
+
if err != nil {
- env.SetSnapshot(snapshot) //env.Db().Set(snapshot)
+ 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)
+ } else {
+ env.SetSnapshot(snapshotPreTransfer) //env.Db().Set(snapshot)
+ }
+ }
+ return ret, addr, err
+}
+
+func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toAddr, codeAddr *common.Address, input, code []byte, gas, gasPrice, value *big.Int) (ret []byte, addr common.Address, err error) {
+ evm := vm.NewVm(env)
+ // Depth check execution. Fail if we're trying to execute above the
+ // limit.
+ if env.Depth() > int(params.CallCreateDepth.Int64()) {
+ caller.ReturnGas(gas, gasPrice)
+ 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
+ )
+ if !env.Db().Exist(*toAddr) {
+ to = env.Db().CreateAccount(*toAddr)
+ } else {
+ to = env.Db().GetAccount(*toAddr)
}
+ contract := vm.NewContract(caller, to, value, gas, gasPrice)
+ contract.SetCallCode(codeAddr, code)
+ contract.DelegateCall = true
+
+ ret, err = evm.Run(contract, input)
+
+ if err != nil {
+ env.SetSnapshot(snapshot) //env.Db().Set(snapshot)
+ }
return ret, addr, err
}
diff --git a/core/state/state_object.go b/core/state/state_object.go
index 47546112f..ebc9f8358 100644
--- a/core/state/state_object.go
+++ b/core/state/state_object.go
@@ -211,6 +211,11 @@ 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
}
@@ -238,6 +243,13 @@ func (self *StateObject) Nonce() uint64 {
return self.nonce
}
+// Never called, but must be present to allow StateObject to be used
+// as a vm.Account interface that also satisfies the vm.ContractRef
+// interface. Interfaces are awesome.
+func (self *StateObject) Value() *big.Int {
+ return nil
+}
+
func (self *StateObject) EachStorage(cb func(key, value []byte)) {
// When iterating over the storage check the cache first
for h, v := range self.storage {
diff --git a/core/state/statedb.go b/core/state/statedb.go
index 22ffa36a0..e1dde84d1 100644
--- a/core/state/statedb.go
+++ b/core/state/statedb.go
@@ -87,6 +87,18 @@ 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 {
@@ -95,6 +107,11 @@ 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_processor.go b/core/state_processor.go
index d9c24935d..b9793b157 100644
--- a/core/state_processor.go
+++ b/core/state_processor.go
@@ -43,7 +43,6 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB) (ty
for i, tx := range block.Transactions() {
statedb.StartRecord(tx.Hash(), block.Hash(), i)
-
receipt, logs, _, err := ApplyTransaction(p.bc, gp, statedb, header, tx, totalUsedGas)
if err != nil {
return nil, nil, totalUsedGas, err
diff --git a/core/state_transition.go b/core/state_transition.go
index 7ecc01d4c..0cd226262 100644
--- a/core/state_transition.go
+++ b/core/state_transition.go
@@ -21,12 +21,17 @@ 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"
"github.com/ethereum/go-ethereum/params"
)
+var (
+ Big0 = big.NewInt(0)
+)
+
/*
The State Transitioning Model
@@ -59,6 +64,7 @@ type StateTransition struct {
// Message represents a message sent to a contract.
type Message interface {
From() (common.Address, error)
+ FromFrontier() (common.Address, error)
To() *common.Address
GasPrice() *big.Int
@@ -75,8 +81,13 @@ func MessageCreatesContract(msg Message) bool {
// IntrinsicGas computes the 'intrisic gas' for a message
// with the given data.
-func IntrinsicGas(data []byte) *big.Int {
- igas := new(big.Int).Set(params.TxGas)
+func IntrinsicGas(data []byte, contractCreation, homestead bool) *big.Int {
+ igas := new(big.Int)
+ if contractCreation && homestead {
+ igas.Set(params.TxGasContractCreation)
+ } else {
+ igas.Set(params.TxGas)
+ }
if len(data) > 0 {
var nz int64
for _, byt := range data {
@@ -110,7 +121,15 @@ func ApplyMessage(env vm.Environment, msg Message, gp *GasPool) ([]byte, *big.In
}
func (self *StateTransition) from() (vm.Account, error) {
- f, err := self.msg.From()
+ var (
+ f common.Address
+ err error
+ )
+ if params.IsHomestead(self.env.BlockNumber()) {
+ f, err = self.msg.From()
+ } else {
+ f, err = self.msg.FromFrontier()
+ }
if err != nil {
return nil, err
}
@@ -195,18 +214,20 @@ func (self *StateTransition) transitionDb() (ret []byte, usedGas *big.Int, err e
if err = self.preCheck(); err != nil {
return
}
-
msg := self.msg
sender, _ := self.from() // err checked in preCheck
+ homestead := params.IsHomestead(self.env.BlockNumber())
+ contractCreation := MessageCreatesContract(msg)
// Pay intrinsic gas
- if err = self.useGas(IntrinsicGas(self.data)); err != nil {
+ if err = self.useGas(IntrinsicGas(self.data, contractCreation, homestead)); err != nil {
return nil, nil, InvalidTxError(err)
}
vmenv := self.env
+ snapshot := vmenv.MakeSnapshot()
var addr common.Address
- if MessageCreatesContract(msg) {
+ 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)))
@@ -214,6 +235,18 @@ func (self *StateTransition) transitionDb() (ret []byte, usedGas *big.Int, err e
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)
}
diff --git a/core/transaction_pool.go b/core/transaction_pool.go
index 456de3cb6..a815c9ef0 100644
--- a/core/transaction_pool.go
+++ b/core/transaction_pool.go
@@ -30,6 +30,7 @@ import (
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
+ "github.com/ethereum/go-ethereum/params"
)
var (
@@ -222,18 +223,26 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error {
return ErrCheap
}
+ currentState, err := pool.currentState()
+ if err != nil {
+ return err
+ }
+
+ homestead := params.IsHomestead(GetHeadBlockNum(currentState.GetDB()))
+
// Validate the transaction sender and it's sig. Throw
// if the from fields is invalid.
- if from, err = tx.From(); err != nil {
+ if homestead {
+ from, err = tx.From()
+ } else {
+ from, err = tx.FromFrontier()
+ }
+ if err != nil {
return ErrInvalidSender
}
// Make sure the account exist. Non existent accounts
// haven't got funds and well therefor never pass.
- currentState, err := pool.currentState()
- if err != nil {
- return err
- }
if !currentState.HasAccount(from) {
return ErrNonExistentAccount
}
@@ -263,7 +272,8 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error {
}
// Should supply enough intrinsic gas
- if tx.Gas().Cmp(IntrinsicGas(tx.Data())) < 0 {
+ intrGas := IntrinsicGas(tx.Data(), MessageCreatesContract(tx), homestead)
+ if tx.Gas().Cmp(intrGas) < 0 {
return ErrIntrinsicGas
}
diff --git a/core/types/transaction.go b/core/types/transaction.go
index 4049ae888..af952e450 100644
--- a/core/types/transaction.go
+++ b/core/types/transaction.go
@@ -157,11 +157,26 @@ func (tx *Transaction) Size() common.StorageSize {
return common.StorageSize(c)
}
+// 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
+// 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) From() (common.Address, error) {
+ return doFrom(tx, true)
+}
+
+func (tx *Transaction) FromFrontier() (common.Address, error) {
+ return doFrom(tx, false)
+}
+
+func doFrom(tx *Transaction, homestead bool) (common.Address, error) {
if from := tx.from.Load(); from != nil {
return from.(common.Address), nil
}
- pubkey, err := tx.publicKey()
+ pubkey, err := tx.publicKey(homestead)
if err != nil {
return common.Address{}, err
}
@@ -182,8 +197,8 @@ func (tx *Transaction) SignatureValues() (v byte, r *big.Int, s *big.Int) {
return tx.data.V, new(big.Int).Set(tx.data.R), new(big.Int).Set(tx.data.S)
}
-func (tx *Transaction) publicKey() ([]byte, error) {
- if !crypto.ValidateSignatureValues(tx.data.V, tx.data.R, tx.data.S) {
+func (tx *Transaction) publicKey(homestead bool) ([]byte, error) {
+ if !crypto.ValidateSignatureValues(tx.data.V, tx.data.R, tx.data.S, homestead) {
return nil, ErrInvalidSig
}
diff --git a/core/vm/contract.go b/core/vm/contract.go
index 5981dcca0..dac81a529 100644
--- a/core/vm/contract.go
+++ b/core/vm/contract.go
@@ -26,12 +26,14 @@ 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))
}
// Contract represents an ethereum contract in the state database. It contains
-// the the contract code, calling arguments. Contract implements ContractReg
+// the the contract code, calling arguments. Contract implements ContractRef
type Contract struct {
caller ContractRef
self ContractRef
@@ -45,6 +47,8 @@ type Contract struct {
value, Gas, UsedGas, Price *big.Int
Args []byte
+
+ DelegateCall bool
}
// Create a new context for the given data items.
@@ -114,6 +118,16 @@ 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
+}
+
// SetCode sets the code to the contract
func (self *Contract) SetCode(code []byte) {
self.Code = code
diff --git a/core/vm/contracts.go b/core/vm/contracts.go
index 22cb9eab2..f204432a2 100644
--- a/core/vm/contracts.go
+++ b/core/vm/contracts.go
@@ -93,7 +93,8 @@ func ecrecoverFunc(in []byte) []byte {
vbig := common.Bytes2Big(in[32:64])
v := byte(vbig.Uint64())
- if !crypto.ValidateSignatureValues(v, r, s) {
+ // tighter sig s values in homestead only apply to tx sigs
+ if !crypto.ValidateSignatureValues(v, r, s, false) {
glog.V(logger.Debug).Infof("EC RECOVER FAIL: v, r or s value invalid")
return nil
}
diff --git a/core/vm/environment.go b/core/vm/environment.go
index 4fee583bf..dc60af2ca 100644
--- a/core/vm/environment.go
+++ b/core/vm/environment.go
@@ -70,6 +70,8 @@ type Environment interface {
Call(me ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error)
// 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)
+ // Same as CallCode except sender and value is propagated from parent to child scope
+ DelegateCall(me ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error)
// Create a new contract
Create(me ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error)
}
@@ -119,7 +121,9 @@ 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))
+ Value() *big.Int
}
diff --git a/core/vm/errors.go b/core/vm/errors.go
index e2fc84065..116fbe456 100644
--- a/core/vm/errors.go
+++ b/core/vm/errors.go
@@ -24,4 +24,5 @@ import (
)
var OutOfGasError = errors.New("Out of gas")
+var CodeStoreOutOfGasError = errors.New("Contract creation code storage out of gas")
var DepthError = fmt.Errorf("Max call depth exceeded (%d)", params.CallCreateDepth)
diff --git a/core/vm/gas.go b/core/vm/gas.go
index bff0ac91b..09feddd7d 100644
--- a/core/vm/gas.go
+++ b/core/vm/gas.go
@@ -136,6 +136,7 @@ var _baseCheck = map[OpCode]req{
CREATE: {3, params.CreateGas, 1},
CALL: {7, params.CallGas, 1},
CALLCODE: {7, params.CallGas, 1},
+ DELEGATECALL: {6, params.CallGas, 1},
JUMPDEST: {0, params.JumpdestGas, 0},
SUICIDE: {1, Zero, 0},
RETURN: {2, Zero, 0},
diff --git a/core/vm/instructions.go b/core/vm/instructions.go
index 2e868521e..9d3d4e6fe 100644
--- a/core/vm/instructions.go
+++ b/core/vm/instructions.go
@@ -337,7 +337,13 @@ func opOrigin(instr instruction, pc *uint64, env Environment, contract *Contract
}
func opCaller(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
- stack.push(common.Bytes2Big(contract.caller.Address().Bytes()))
+ var bigAddr *big.Int
+ if contract.DelegateCall {
+ bigAddr = env.Origin().Big()
+ } else {
+ bigAddr = contract.caller.Address().Big()
+ }
+ stack.push(bigAddr)
}
func opCallValue(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
@@ -485,7 +491,6 @@ func opSload(instr instruction, pc *uint64, env Environment, contract *Contract,
func opSstore(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
loc := common.BigToHash(stack.pop())
val := stack.pop()
-
env.Db().SetState(contract.Address(), loc, common.BigToHash(val))
}
@@ -509,31 +514,6 @@ 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)
- addr common.Address
- ret []byte
- suberr error
- )
-
- contract.UseGas(contract.Gas)
- ret, addr, suberr = 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) {
- env.Db().SetCode(addr, ret)
- }
-
- stack.push(addr.Big())
-
- }
}
func opCall(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
@@ -598,6 +578,21 @@ func opCallCode(instr instruction, pc *uint64, env Environment, contract *Contra
}
}
+func opDelegateCall(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
+ gas, to, inOffset, inSize, outOffset, outSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
+
+ toAddr := common.BigToAddress(to)
+ args := memory.Get(inOffset.Int64(), inSize.Int64())
+ ret, err := env.DelegateCall(contract, toAddr, args, gas, contract.Price)
+
+ if err != nil {
+ stack.push(new(big.Int))
+ } else {
+ stack.push(big.NewInt(1))
+ memory.Set(outOffset.Uint64(), outSize.Uint64(), ret)
+ }
+}
+
func opReturn(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
}
func opStop(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go
index ab899647f..222c93854 100644
--- a/core/vm/jump_table.go
+++ b/core/vm/jump_table.go
@@ -62,9 +62,10 @@ func init() {
jumpTable[PC] = jumpPtr{nil, true}
jumpTable[MSIZE] = jumpPtr{opMsize, true}
jumpTable[GAS] = jumpPtr{opGas, true}
- jumpTable[CREATE] = jumpPtr{opCreate, true}
+ jumpTable[CREATE] = jumpPtr{nil, 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/opcodes.go b/core/vm/opcodes.go
index dc4139092..00593ae95 100644
--- a/core/vm/opcodes.go
+++ b/core/vm/opcodes.go
@@ -200,6 +200,7 @@ const (
CALL
CALLCODE
RETURN
+ DELEGATECALL
SUICIDE = 0xff
)
@@ -349,11 +350,12 @@ var opCodeToString = map[OpCode]string{
LOG4: "LOG4",
// 0xf0 range
- CREATE: "CREATE",
- CALL: "CALL",
- RETURN: "RETURN",
- CALLCODE: "CALLCODE",
- SUICIDE: "SUICIDE",
+ CREATE: "CREATE",
+ CALL: "CALL",
+ RETURN: "RETURN",
+ CALLCODE: "CALLCODE",
+ DELEGATECALL: "DELEGATECALL",
+ SUICIDE: "SUICIDE",
PUSH: "PUSH",
DUP: "DUP",
diff --git a/core/vm/runtime/env.go b/core/vm/runtime/env.go
index 22f9ea14d..77519df81 100644
--- a/core/vm/runtime/env.go
+++ b/core/vm/runtime/env.go
@@ -101,6 +101,10 @@ func (self *Env) CallCode(caller vm.ContractRef, addr common.Address, data []byt
return core.CallCode(self, caller, addr, data, gas, price, value)
}
+func (self *Env) DelegateCall(me vm.ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) {
+ return core.DelegateCall(self, me, addr, data, gas, price)
+}
+
func (self *Env) Create(caller vm.ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) {
return core.Create(self, caller, data, gas, price, value)
}
diff --git a/core/vm/vm.go b/core/vm/vm.go
index 0c6bbcd42..863b2cc0d 100644
--- a/core/vm/vm.go
+++ b/core/vm/vm.go
@@ -160,7 +160,6 @@ func (self *Vm) Run(contract *Contract, input []byte) (ret []byte, err error) {
// Get the memory location of pc
op = contract.GetOp(pc)
-
// calculate the new memory size and gas price for the current executing opcode
newMemSize, cost, err = calculateGasAndSize(self.env, contract, caller, op, statedb, mem, stack)
if err != nil {
@@ -177,7 +176,6 @@ func (self *Vm) Run(contract *Contract, input []byte) (ret []byte, err error) {
mem.Resize(newMemSize.Uint64())
// Add a log message
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)
@@ -201,6 +199,35 @@ 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())
@@ -346,6 +373,13 @@ func calculateGasAndSize(env Environment, contract *Contract, caller ContractRef
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_env.go b/core/vm_env.go
index 1c787e982..db29cc32c 100644
--- a/core/vm_env.go
+++ b/core/vm_env.go
@@ -106,6 +106,10 @@ func (self *VMEnv) CallCode(me vm.ContractRef, addr common.Address, data []byte,
return CallCode(self, me, addr, data, gas, price, value)
}
+func (self *VMEnv) DelegateCall(me vm.ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) {
+ return DelegateCall(self, me, addr, data, gas, price)
+}
+
func (self *VMEnv) Create(me vm.ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) {
return Create(self, me, data, gas, price, value)
}