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
This commit is contained in:
committed by
Jeffrey Wilcke
parent
aa36a6ae4f
commit
371871d685
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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 ©
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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},
|
||||
|
@ -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) {
|
||||
|
@ -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}
|
||||
|
@ -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",
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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())
|
||||
@ -345,6 +372,13 @@ func calculateGasAndSize(env Environment, contract *Contract, caller ContractRef
|
||||
x := calcMemSize(stack.data[stack.len()-6], stack.data[stack.len()-7])
|
||||
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)
|
||||
|
@ -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)
|
||||
}
|
||||
|
Reference in New Issue
Block a user