diff options
author | Gustav Simonsson <gustav.simonsson@gmail.com> | 2015-11-27 22:40:29 +0800 |
---|---|---|
committer | Jeffrey Wilcke <geffobscura@gmail.com> | 2016-02-18 17:08:11 +0800 |
commit | 371871d685d54b916aef28de689d6f0af7822083 (patch) | |
tree | e704b02ba2ffd2d1164001885fba15106b0f7d94 /core | |
parent | aa36a6ae4f24f07e2c470a21c93ff37ad5861982 (diff) | |
download | go-tangerine-371871d685d54b916aef28de689d6f0af7822083.tar.gz 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.go | 2 | ||||
-rw-r--r-- | core/block_validator.go | 131 | ||||
-rw-r--r-- | core/database_util.go | 83 | ||||
-rw-r--r-- | core/execution.go | 68 | ||||
-rw-r--r-- | core/state/state_object.go | 12 | ||||
-rw-r--r-- | core/state/statedb.go | 17 | ||||
-rw-r--r-- | core/state_processor.go | 1 | ||||
-rw-r--r-- | core/state_transition.go | 45 | ||||
-rw-r--r-- | core/transaction_pool.go | 22 | ||||
-rw-r--r-- | core/types/transaction.go | 21 | ||||
-rw-r--r-- | core/vm/contract.go | 16 | ||||
-rw-r--r-- | core/vm/contracts.go | 3 | ||||
-rw-r--r-- | core/vm/environment.go | 4 | ||||
-rw-r--r-- | core/vm/errors.go | 1 | ||||
-rw-r--r-- | core/vm/gas.go | 1 | ||||
-rw-r--r-- | core/vm/instructions.go | 49 | ||||
-rw-r--r-- | core/vm/jump_table.go | 3 | ||||
-rw-r--r-- | core/vm/opcodes.go | 12 | ||||
-rw-r--r-- | core/vm/runtime/env.go | 4 | ||||
-rw-r--r-- | core/vm/vm.go | 38 | ||||
-rw-r--r-- | core/vm_env.go | 4 |
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 © +} + +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) } |