diff options
Diffstat (limited to 'core')
34 files changed, 811 insertions, 196 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..73c33d8dd 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,7 +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 { - return fmt.Errorf("unable to replicate block's bloom=%x", rbloom) + return fmt.Errorf("unable to replicate block's bloom=%x vs calculated bloom=%x", header.Bloom, rbloom) } // Tre receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, R1]])) receiptSha := types.DeriveSha(receipts) @@ -241,3 +247,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) + + // 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 + } + + // for the exponential factor + periodCount := new(big.Int).Add(parentNumber, common.Big1) + periodCount.Div(periodCount, ExpDiffPeriod) + + // the exponential factor, commonly refered to as "the bomb" + // diff = diff + 2^(periodCount - 2) + if periodCount.Cmp(common.Big1) > 0 { + 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/blockchain.go b/core/blockchain.go index 22dd617ad..2c6ff24f9 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -725,14 +725,18 @@ func (self *BlockChain) writeHeader(header *types.Header) error { if ptd == nil { return ParentError(header.ParentHash) } - td := new(big.Int).Add(header.Difficulty, ptd) + + localTd := self.GetTd(self.currentHeader.Hash()) + externTd := new(big.Int).Add(header.Difficulty, ptd) // Make sure no inconsistent state is leaked during insertion self.mu.Lock() defer self.mu.Unlock() // If the total difficulty is higher than our known, add it to the canonical chain - if td.Cmp(self.GetTd(self.currentHeader.Hash())) > 0 { + // Second clause in the if statement reduces the vulnerability to selfish mining. + // Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf + if externTd.Cmp(localTd) > 0 || (externTd.Cmp(localTd) == 0 && mrand.Float64() < 0.5) { // Delete any canonical number assignments above the new head for i := header.Number.Uint64() + 1; GetCanonicalHash(self.chainDb, i) != (common.Hash{}); i++ { DeleteCanonicalHash(self.chainDb, i) @@ -753,7 +757,7 @@ func (self *BlockChain) writeHeader(header *types.Header) error { self.currentHeader = types.CopyHeader(header) } // Irrelevant of the canonical status, write the header itself to the database - if err := WriteTd(self.chainDb, header.Hash(), td); err != nil { + if err := WriteTd(self.chainDb, header.Hash(), externTd); err != nil { glog.Fatalf("failed to write header total difficulty: %v", err) } if err := WriteHeader(self.chainDb, header); err != nil { @@ -1060,14 +1064,18 @@ func (self *BlockChain) WriteBlock(block *types.Block) (status writeStatus, err if ptd == nil { return NonStatTy, ParentError(block.ParentHash()) } - td := new(big.Int).Add(block.Difficulty(), ptd) + + localTd := self.GetTd(self.currentBlock.Hash()) + externTd := new(big.Int).Add(block.Difficulty(), ptd) // Make sure no inconsistent state is leaked during insertion self.mu.Lock() defer self.mu.Unlock() // If the total difficulty is higher than our known, add it to the canonical chain - if td.Cmp(self.GetTd(self.currentBlock.Hash())) > 0 { + // Second clause in the if statement reduces the vulnerability to selfish mining. + // Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf + if externTd.Cmp(localTd) > 0 || (externTd.Cmp(localTd) == 0 && mrand.Float64() < 0.5) { // Reorganize the chain if the parent is not the head block if block.ParentHash() != self.currentBlock.Hash() { if err := self.reorg(self.currentBlock, block); err != nil { @@ -1081,7 +1089,7 @@ func (self *BlockChain) WriteBlock(block *types.Block) (status writeStatus, err status = SideStatTy } // Irrelevant of the canonical status, write the block itself to the database - if err := WriteTd(self.chainDb, block.Hash(), td); err != nil { + if err := WriteTd(self.chainDb, block.Hash(), externTd); err != nil { glog.Fatalf("failed to write block total difficulty: %v", err) } if err := WriteBlock(self.chainDb, block); err != nil { diff --git a/core/database_util.go b/core/database_util.go index 2dc113e29..fd2b4c312 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()...)) diff --git a/core/database_util_test.go b/core/database_util_test.go index 059f1ae9f..6b3793635 100644 --- a/core/database_util_test.go +++ b/core/database_util_test.go @@ -62,7 +62,7 @@ func (d *diffTest) UnmarshalJSON(b []byte) (err error) { return nil } -func TestDifficulty(t *testing.T) { +func TestDifficultyFrontier(t *testing.T) { file, err := os.Open("../tests/files/BasicTests/difficulty.json") if err != nil { t.Fatal(err) @@ -77,7 +77,7 @@ func TestDifficulty(t *testing.T) { for name, test := range tests { number := new(big.Int).Sub(test.CurrentBlocknumber, big.NewInt(1)) - diff := CalcDifficulty(test.CurrentTimestamp, test.ParentTimestamp, number, test.ParentDifficulty) + diff := calcDifficultyFrontier(test.CurrentTimestamp, test.ParentTimestamp, number, test.ParentDifficulty) if diff.Cmp(test.CurrentDifficulty) != 0 { t.Error(name, "failed. Expected", test.CurrentDifficulty, "and calculated", diff) } diff --git a/core/execution.go b/core/execution.go index fd8464f6e..24c0c93ae 100644 --- a/core/execution.go +++ b/core/execution.go @@ -33,8 +33,17 @@ 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) return ret, err } @@ -52,7 +61,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()) { @@ -69,17 +77,15 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A var createAccount bool if address == nil { - // Generate a new address + // Create a new account on the state nonce := env.Db().GetNonce(caller.Address()) env.Db().SetNonce(caller.Address(), nonce+1) - addr = crypto.CreateAddress(caller.Address(), nonce) - address = &addr createAccount = true } - snapshot := env.MakeSnapshot() + snapshotPreTransfer := env.MakeSnapshot() var ( from = env.Db().GetAccount(caller.Address()) to vm.Account @@ -95,12 +101,68 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A } env.Transfer(from, to, value) + // initialise a new contract and set the code that is to be used by the + // EVM. The contract is a scoped environment for this execution context + // only. contract := vm.NewContract(caller, to, value, gas, gasPrice) contract.SetCallCode(codeAddr, code) + defer contract.Finalise() + + ret, err = evm.Run(contract, input) + // if the contract creation ran successfully and no errors were returned + // calculate the gas required to store the code. If the code could not + // be stored due to not enough gas set an error and let it be handled + // by the error checking condition below. + if err == nil && createAccount { + dataGas := big.NewInt(int64(len(ret))) + dataGas.Mul(dataGas, params.CreateDataGas) + if contract.UseGas(dataGas) { + env.Db().SetCode(*address, ret) + } else { + err = vm.CodeStoreOutOfGasError + } + } + + // When an error was returned by the EVM or when setting the creation code + // above we revert to the snapshot and consume any gas remaining. Additionally + // when we're in homestead this also counts for code storage gas errors. + if err != nil && (params.IsHomestead(env.BlockNumber()) || err != vm.CodeStoreOutOfGasError) { + contract.UseGas(contract.Gas) + + env.SetSnapshot(snapshotPreTransfer) + } + + return ret, addr, err +} + +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 + } + + snapshot := env.MakeSnapshot() + + var to vm.Account + if !env.Db().Exist(*toAddr) { + to = env.Db().CreateAccount(*toAddr) + } else { + to = env.Db().GetAccount(*toAddr) + } + + // Iinitialise a new contract and make initialise the delegate values + contract := vm.NewContract(caller, to, value, gas, gasPrice).AsDelegate() + contract.SetCallCode(codeAddr, code) + defer contract.Finalise() ret, err = evm.Run(contract, input) if err != nil { - env.SetSnapshot(snapshot) //env.Db().Set(snapshot) + contract.UseGas(contract.Gas) + + env.SetSnapshot(snapshot) } return ret, addr, err diff --git a/core/state/dump.go b/core/state/dump.go index cff9c50aa..8eb03e8e4 100644 --- a/core/state/dump.go +++ b/core/state/dump.go @@ -28,6 +28,7 @@ type Account struct { Nonce uint64 `json:"nonce"` Root string `json:"root"` CodeHash string `json:"codeHash"` + Code string `json:"code"` Storage map[string]string `json:"storage"` } @@ -47,7 +48,7 @@ func (self *StateDB) RawDump() World { addr := self.trie.GetKey(it.Key) stateObject, _ := DecodeObject(common.BytesToAddress(addr), self.db, it.Value) - account := Account{Balance: stateObject.balance.String(), Nonce: stateObject.nonce, Root: common.Bytes2Hex(stateObject.Root()), CodeHash: common.Bytes2Hex(stateObject.codeHash)} + account := Account{Balance: stateObject.balance.String(), Nonce: stateObject.nonce, Root: common.Bytes2Hex(stateObject.Root()), CodeHash: common.Bytes2Hex(stateObject.codeHash), Code: common.Bytes2Hex(stateObject.Code())} account.Storage = make(map[string]string) storageIt := stateObject.trie.Iterator() diff --git a/core/state/iterator.go b/core/state/iterator.go new file mode 100644 index 000000000..9d8a69b7c --- /dev/null +++ b/core/state/iterator.go @@ -0,0 +1,160 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package state + +import ( + "bytes" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/trie" +) + +// NodeIterator is an iterator to traverse the entire state trie post-order, +// including all of the contract code and contract state tries. +type NodeIterator struct { + state *StateDB // State being iterated + + stateIt *trie.NodeIterator // Primary iterator for the global state trie + dataIt *trie.NodeIterator // Secondary iterator for the data trie of a contract + + accountHash common.Hash // Hash of the node containing the account + codeHash common.Hash // Hash of the contract source code + code []byte // Source code associated with a contract + + Hash common.Hash // Hash of the current entry being iterated (nil if not standalone) + Entry interface{} // Current state entry being iterated (internal representation) + Parent common.Hash // Hash of the first full ancestor node (nil if current is the root) + + Error error // Failure set in case of an internal error in the iterator +} + +// NewNodeIterator creates an post-order state node iterator. +func NewNodeIterator(state *StateDB) *NodeIterator { + return &NodeIterator{ + state: state, + } +} + +// Next moves the iterator to the next node, returning whether there are any +// further nodes. In case of an internal error this method returns false and +// sets the Error field to the encountered failure. +func (it *NodeIterator) Next() bool { + // If the iterator failed previously, don't do anything + if it.Error != nil { + return false + } + // Otherwise step forward with the iterator and report any errors + if err := it.step(); err != nil { + it.Error = err + return false + } + return it.retrieve() +} + +// step moves the iterator to the next entry of the state trie. +func (it *NodeIterator) step() error { + // Abort if we reached the end of the iteration + if it.state == nil { + return nil + } + // Initialize the iterator if we've just started + if it.stateIt == nil { + it.stateIt = trie.NewNodeIterator(it.state.trie.Trie) + } + // If we had data nodes previously, we surely have at least state nodes + if it.dataIt != nil { + if cont := it.dataIt.Next(); !cont { + if it.dataIt.Error != nil { + return it.dataIt.Error + } + it.dataIt = nil + } + return nil + } + // If we had source code previously, discard that + if it.code != nil { + it.code = nil + return nil + } + // Step to the next state trie node, terminating if we're out of nodes + if cont := it.stateIt.Next(); !cont { + if it.stateIt.Error != nil { + return it.stateIt.Error + } + it.state, it.stateIt = nil, nil + return nil + } + // If the state trie node is an internal entry, leave as is + if !it.stateIt.Leaf { + return nil + } + // Otherwise we've reached an account node, initiate data iteration + var account struct { + Nonce uint64 + Balance *big.Int + Root common.Hash + CodeHash []byte + } + if err := rlp.Decode(bytes.NewReader(it.stateIt.LeafBlob), &account); err != nil { + return err + } + dataTrie, err := trie.New(account.Root, it.state.db) + if err != nil { + return err + } + it.dataIt = trie.NewNodeIterator(dataTrie) + if !it.dataIt.Next() { + it.dataIt = nil + } + if bytes.Compare(account.CodeHash, emptyCodeHash) != 0 { + it.codeHash = common.BytesToHash(account.CodeHash) + it.code, err = it.state.db.Get(account.CodeHash) + if err != nil { + return fmt.Errorf("code %x: %v", account.CodeHash, err) + } + } + it.accountHash = it.stateIt.Parent + return nil +} + +// retrieve pulls and caches the current state entry the iterator is traversing. +// The method returns whether there are any more data left for inspection. +func (it *NodeIterator) retrieve() bool { + // Clear out any previously set values + it.Hash, it.Entry = common.Hash{}, nil + + // If the iteration's done, return no available data + if it.state == nil { + return false + } + // Otherwise retrieve the current entry + switch { + case it.dataIt != nil: + it.Hash, it.Entry, it.Parent = it.dataIt.Hash, it.dataIt.Node, it.dataIt.Parent + if it.Parent == (common.Hash{}) { + it.Parent = it.accountHash + } + case it.code != nil: + it.Hash, it.Entry, it.Parent = it.codeHash, it.code, it.accountHash + case it.stateIt != nil: + it.Hash, it.Entry, it.Parent = it.stateIt.Hash, it.stateIt.Node, it.stateIt.Parent + } + return true +} diff --git a/core/state/iterator_test.go b/core/state/iterator_test.go new file mode 100644 index 000000000..8b68870c6 --- /dev/null +++ b/core/state/iterator_test.go @@ -0,0 +1,57 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package state + +import ( + "bytes" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethdb" +) + +// Tests that the node iterator indeed walks over the entire database contents. +func TestNodeIteratorCoverage(t *testing.T) { + // Create some arbitrary test state to iterate + db, root, _ := makeTestState() + + state, err := New(root, db) + if err != nil { + t.Fatalf("failed to create state trie at %x: %v", root, err) + } + // Gather all the node hashes found by the iterator + hashes := make(map[common.Hash]struct{}) + for it := NewNodeIterator(state); it.Next(); { + if it.Hash != (common.Hash{}) { + hashes[it.Hash] = struct{}{} + } + } + // Cross check the hashes and the database itself + for hash, _ := range hashes { + if _, err := db.Get(hash.Bytes()); err != nil { + t.Errorf("failed to retrieve reported node %x: %v", hash, err) + } + } + for _, key := range db.(*ethdb.MemDatabase).Keys() { + if bytes.HasPrefix(key, []byte("secure-key-")) { + continue + } + if _, ok := hashes[common.BytesToHash(key)]; !ok { + t.Errorf("state entry not reported %x", key) + } + } +} diff --git a/core/state/state_object.go b/core/state/state_object.go index 47546112f..0f86325c6 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -30,7 +30,7 @@ import ( "github.com/ethereum/go-ethereum/trie" ) -var emptyCodeHash = crypto.Sha3(nil) +var emptyCodeHash = crypto.Keccak256(nil) type Code []byte @@ -225,7 +225,7 @@ func (self *StateObject) Code() []byte { func (self *StateObject) SetCode(code []byte) { self.code = code - self.codeHash = crypto.Sha3(code) + self.codeHash = crypto.Keccak256(code) self.dirty = true } @@ -238,6 +238,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 { + panic("Value on StateObject should never be called") +} + 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/sync_test.go b/core/state/sync_test.go index 0dab372ba..a2a1edbdb 100644 --- a/core/state/sync_test.go +++ b/core/state/sync_test.go @@ -22,6 +22,7 @@ import ( "testing" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/trie" ) @@ -42,7 +43,7 @@ func makeTestState() (ethdb.Database, common.Hash, []*testAccount) { // Fill it with some arbitrary data accounts := []*testAccount{} - for i := byte(0); i < 255; i++ { + for i := byte(0); i < 96; i++ { obj := state.GetOrNewStateObject(common.BytesToAddress([]byte{i})) acc := &testAccount{address: common.BytesToAddress([]byte{i})} @@ -61,6 +62,9 @@ func makeTestState() (ethdb.Database, common.Hash, []*testAccount) { } root, _ := state.Commit() + // Remove any potentially cached data from the test state creation + trie.ClearGlobalCache() + // Return the generated state return db, root, accounts } @@ -68,9 +72,18 @@ func makeTestState() (ethdb.Database, common.Hash, []*testAccount) { // checkStateAccounts cross references a reconstructed state with an expected // account array. func checkStateAccounts(t *testing.T, db ethdb.Database, root common.Hash, accounts []*testAccount) { - state, _ := New(root, db) - for i, acc := range accounts { + // Remove any potentially cached data from the state synchronisation + trie.ClearGlobalCache() + // Check root availability and state contents + state, err := New(root, db) + if err != nil { + t.Fatalf("failed to create state trie at %x: %v", root, err) + } + if err := checkStateConsistency(db, root); err != nil { + t.Fatalf("inconsistent state trie at %x: %v", root, err) + } + for i, acc := range accounts { if balance := state.GetBalance(acc.address); balance.Cmp(acc.balance) != 0 { t.Errorf("account %d: balance mismatch: have %v, want %v", i, balance, acc.balance) } @@ -83,6 +96,25 @@ func checkStateAccounts(t *testing.T, db ethdb.Database, root common.Hash, accou } } +// checkStateConsistency checks that all nodes in a state trie are indeed present. +func checkStateConsistency(db ethdb.Database, root common.Hash) error { + // Remove any potentially cached data from the test state creation or previous checks + trie.ClearGlobalCache() + + // Create and iterate a state trie rooted in a sub-node + if _, err := db.Get(root.Bytes()); err != nil { + return nil // Consider a non existent state consistent + } + state, err := New(root, db) + if err != nil { + return err + } + it := NewNodeIterator(state) + for it.Next() { + } + return it.Error +} + // Tests that an empty state is not scheduled for syncing. func TestEmptyStateSync(t *testing.T) { empty := common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") @@ -236,3 +268,65 @@ func TestIterativeRandomDelayedStateSync(t *testing.T) { // Cross check that the two states are in sync checkStateAccounts(t, dstDb, srcRoot, srcAccounts) } + +// Tests that at any point in time during a sync, only complete sub-tries are in +// the database. +func TestIncompleteStateSync(t *testing.T) { + // Create a random state to copy + srcDb, srcRoot, srcAccounts := makeTestState() + + // Create a destination state and sync with the scheduler + dstDb, _ := ethdb.NewMemDatabase() + sched := NewStateSync(srcRoot, dstDb) + + added := []common.Hash{} + queue := append([]common.Hash{}, sched.Missing(1)...) + for len(queue) > 0 { + // Fetch a batch of state nodes + results := make([]trie.SyncResult, len(queue)) + for i, hash := range queue { + data, err := srcDb.Get(hash.Bytes()) + if err != nil { + t.Fatalf("failed to retrieve node data for %x: %v", hash, err) + } + results[i] = trie.SyncResult{hash, data} + } + // Process each of the state nodes + if index, err := sched.Process(results); err != nil { + t.Fatalf("failed to process result #%d: %v", index, err) + } + for _, result := range results { + added = append(added, result.Hash) + } + // Check that all known sub-tries in the synced state is complete + for _, root := range added { + // Skim through the accounts and make sure the root hash is not a code node + codeHash := false + for _, acc := range srcAccounts { + if bytes.Compare(root.Bytes(), crypto.Sha3(acc.code)) == 0 { + codeHash = true + break + } + } + // If the root is a real trie node, check consistency + if !codeHash { + if err := checkStateConsistency(dstDb, root); err != nil { + t.Fatalf("state inconsistent: %v", err) + } + } + } + // Fetch the next batch to retrieve + queue = append(queue[:0], sched.Missing(1)...) + } + // Sanity check that removing any node from the database is detected + for _, node := range added[1:] { + key := node.Bytes() + value, _ := dstDb.Get(key) + + dstDb.Delete(key) + if err := checkStateConsistency(dstDb, added[0]); err == nil { + t.Fatalf("trie inconsistency not caught, missing: %x", key) + } + dstDb.Put(key, value) + } +} 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 12c3ab3a1..2887f6228 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -27,6 +27,10 @@ import ( "github.com/ethereum/go-ethereum/params" ) +var ( + Big0 = big.NewInt(0) +) + /* The State Transitioning Model @@ -59,6 +63,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 +80,13 @@ func MessageCreatesContract(msg Message) bool { // IntrinsicGas computes the 'intrinsic 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 +120,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,35 +213,35 @@ 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 - var addr common.Address - if MessageCreatesContract(msg) { - ret, addr, err = vmenv.Create(sender, self.data, self.gas, self.gasPrice, self.value) - if err == nil { - dataGas := big.NewInt(int64(len(ret))) - dataGas.Mul(dataGas, params.CreateDataGas) - if err := self.useGas(dataGas); err == nil { - self.state.SetCode(addr, ret) - } else { - 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) - } + //var addr common.Address + if contractCreation { + ret, _, err = vmenv.Create(sender, self.data, self.gas, self.gasPrice, self.value) + if homestead && err == vm.CodeStoreOutOfGasError { + self.gas = Big0 + } + + if err != nil { + ret = nil + glog.V(logger.Core).Infoln("VM create err:", err) } - glog.V(logger.Core).Infoln("VM create err:", err) } else { // Increment the nonce for the next transaction self.state.SetNonce(sender.Address(), self.state.GetNonce(sender.Address())+1) ret, err = vmenv.Call(sender, self.to().Address(), self.data, self.gas, self.gasPrice, self.value) - glog.V(logger.Core).Infoln("VM call err:", err) + if err != nil { + glog.V(logger.Core).Infoln("VM call err:", err) + } } if err != nil && IsValueTransferErr(err) { diff --git a/core/transaction_pool.go b/core/tx_pool.go index 456de3cb6..b8fb4cd35 100644 --- a/core/transaction_pool.go +++ b/core/tx_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 ( @@ -70,6 +71,8 @@ type TxPool struct { mu sync.RWMutex pending map[common.Hash]*types.Transaction // processable transactions queue map[common.Address]map[common.Hash]*types.Transaction + + homestead bool } func NewTxPool(eventMux *event.TypeMux, currentStateFn stateFn, gasLimitFn func() *big.Int) *TxPool { @@ -85,6 +88,7 @@ func NewTxPool(eventMux *event.TypeMux, currentStateFn stateFn, gasLimitFn func( localTx: newTxSet(), events: eventMux.Subscribe(ChainHeadEvent{}, GasPriceChanged{}, RemovedTransactionEvent{}), } + go pool.eventLoop() return pool @@ -98,6 +102,10 @@ func (pool *TxPool) eventLoop() { switch ev := ev.Data.(type) { case ChainHeadEvent: pool.mu.Lock() + if ev.Block != nil && params.IsHomestead(ev.Block.Number()) { + pool.homestead = true + } + pool.resetState() pool.mu.Unlock() case GasPriceChanged: @@ -210,30 +218,24 @@ func (pool *TxPool) SetLocal(tx *types.Transaction) { // validateTx checks whether a transaction is valid according // to the consensus rules. func (pool *TxPool) validateTx(tx *types.Transaction) error { - // Validate sender - var ( - from common.Address - err error - ) - local := pool.localTx.contains(tx.Hash()) // Drop transactions under our own minimal accepted gas price if !local && pool.minGasPrice.Cmp(tx.GasPrice()) > 0 { return ErrCheap } - // Validate the transaction sender and it's sig. Throw - // if the from fields is invalid. - if from, err = tx.From(); err != nil { + currentState, err := pool.currentState() + if err != nil { + return err + } + + from, err := tx.From() + 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 } @@ -262,8 +264,8 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error { return ErrInsufficientFunds } - // Should supply enough intrinsic gas - if tx.Gas().Cmp(IntrinsicGas(tx.Data())) < 0 { + intrGas := IntrinsicGas(tx.Data(), MessageCreatesContract(tx), pool.homestead) + if tx.Gas().Cmp(intrGas) < 0 { return ErrIntrinsicGas } diff --git a/core/transaction_pool_test.go b/core/tx_pool_test.go index 811e40111..811e40111 100644 --- a/core/transaction_pool_test.go +++ b/core/tx_pool_test.go diff --git a/core/types/bloom9.go b/core/types/bloom9.go index 372045ab2..ecf2bffc2 100644 --- a/core/types/bloom9.go +++ b/core/types/bloom9.go @@ -101,7 +101,7 @@ func LogsBloom(logs vm.Logs) *big.Int { } func bloom9(b []byte) *big.Int { - b = crypto.Sha3(b[:]) + b = crypto.Keccak256(b[:]) r := new(big.Int) diff --git a/core/types/bloom9_test.go b/core/types/bloom9_test.go index 5744bec6c..58e8f7073 100644 --- a/core/types/bloom9_test.go +++ b/core/types/bloom9_test.go @@ -73,7 +73,7 @@ func TestBloom9(t *testing.T) { func TestAddress(t *testing.T) { block := &Block{} block.Coinbase = common.Hex2Bytes("22341ae42d6dd7384bc8584e50419ea3ac75b83f") - fmt.Printf("%x\n", crypto.Sha3(block.Coinbase)) + fmt.Printf("%x\n", crypto.Keccak256(block.Coinbase)) bin := CreateBloom(block) fmt.Printf("bin = %x\n", common.LeftPadBytes(bin, 64)) diff --git a/core/types/transaction.go b/core/types/transaction.go index 4049ae888..37715ee53 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -157,16 +157,52 @@ func (tx *Transaction) Size() common.StorageSize { return common.StorageSize(c) } +// From returns the address derived from the signature (V, R, S) using secp256k1 +// eliptic curve and an error if it failed deriving or upon an incorrect +// signature. +// +// From Uses the homestead consensus rules to determine whether the signature is +// valid. +// +// From caches the address, allowing it to be used regardless of +// Frontier / Homestead. however, the first time called it runs +// signature validations, so we need two versions. This makes it +// easier to ensure backwards compatibility of things like package rpc +// 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) +} + +// FromFrontier returns the address derived from the signature (V, R, S) using +// secp256k1 eliptic curve and an error if it failed deriving or upon an +// incorrect signature. +// +// FromFrantier uses the frontier consensus rules to determine whether the +// signature is valid. +// +// FromFrontier caches the address, allowing it to be used regardless of +// Frontier / Homestead. however, the first time called it runs +// signature validations, so we need two versions. This makes it +// easier to ensure backwards compatibility of things like package rpc +// where eth_getblockbynumber uses tx.From() and needs to work for +// both txs before and after the first homestead block. Signatures +// valid in homestead are a subset of valid ones in Frontier) +func (tx *Transaction) FromFrontier() (common.Address, error) { + return doFrom(tx, false) +} + +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 } var addr common.Address - copy(addr[:], crypto.Sha3(pubkey[1:])[12:]) + copy(addr[:], crypto.Keccak256(pubkey[1:])[12:]) tx.from.Store(addr) return addr, nil } @@ -182,8 +218,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..d23995218 100644 --- a/core/vm/contract.go +++ b/core/vm/contract.go @@ -26,15 +26,20 @@ import ( type ContractRef interface { ReturnGas(*big.Int, *big.Int) Address() 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 + // CallerAddress is the result of the caller which initialised this + // contract. However when the "call method" is delegated this value + // needs to be initialised to that of the caller's caller. + CallerAddress common.Address + caller ContractRef + self ContractRef jumpdests destinations // result of JUMPDEST analysis. @@ -45,11 +50,13 @@ type Contract struct { value, Gas, UsedGas, Price *big.Int Args []byte + + DelegateCall bool } -// Create a new context for the given data items. +// NewContract returns a new contract environment for the execution of EVM. func NewContract(caller ContractRef, object ContractRef, value, gas, price *big.Int) *Contract { - c := &Contract{caller: caller, self: object, Args: nil} + c := &Contract{CallerAddress: caller.Address(), caller: caller, self: object, Args: nil} if parent, ok := caller.(*Contract); ok { // Reuse JUMPDEST analysis from parent context if available. @@ -70,6 +77,16 @@ func NewContract(caller ContractRef, object ContractRef, value, gas, price *big. return c } +// AsDelegate sets the contract to be a delegate call and returns the current +// contract (for chaining calls) +func (c *Contract) AsDelegate() *Contract { + c.DelegateCall = true + // NOTE: caller must, at all times be a contract. It should never happen + // that caller is something other than a Contract. + c.CallerAddress = c.caller.(*Contract).CallerAddress + return c +} + // GetOp returns the n'th element in the contract's byte array func (c *Contract) GetOp(n uint64) OpCode { return OpCode(c.GetByte(n)) @@ -84,13 +101,19 @@ func (c *Contract) GetByte(n uint64) byte { return 0 } -// Return returns the given ret argument and returns any remaining gas to the -// caller -func (c *Contract) Return(ret []byte) []byte { +// Caller returns the caller of the contract. +// +// Caller will recursively call caller when the contract is a delegate +// call, including that of caller's caller. +func (c *Contract) Caller() common.Address { + return c.CallerAddress +} + +// Finalise finalises the contract and returning any remaining gas to the original +// caller. +func (c *Contract) Finalise() { // Return the remaining gas to the caller c.caller.ReturnGas(c.Gas, c.Price) - - return ret } // UseGas attempts the use gas and subtracts it and returns true on success @@ -114,6 +137,11 @@ func (c *Contract) Address() common.Address { return c.self.Address() } +// 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..5cc9f903b 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 } @@ -110,7 +111,7 @@ func ecrecoverFunc(in []byte) []byte { } // the first byte of pubkey is bitcoin heritage - return common.LeftPadBytes(crypto.Sha3(pubKey[1:])[12:], 32) + return common.LeftPadBytes(crypto.Keccak256(pubKey[1:])[12:], 32) } func memCpy(in []byte) []byte { diff --git a/core/vm/environment.go b/core/vm/environment.go index 4fee583bf..a58e3ba2b 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) } @@ -122,4 +124,5 @@ type Account interface { 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..1e1086b13 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -316,7 +316,7 @@ func opMulmod(instr instruction, pc *uint64, env Environment, contract *Contract func opSha3(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { offset, size := stack.pop(), stack.pop() - hash := crypto.Sha3(memory.Get(offset.Int64(), size.Int64())) + hash := crypto.Keccak256(memory.Get(offset.Int64(), size.Int64())) stack.push(common.BytesToBig(hash)) } @@ -337,7 +337,7 @@ func opOrigin(instr instruction, pc *uint64, env Environment, contract *Contract } func opCaller(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { - stack.push(common.Bytes2Big(contract.caller.Address().Bytes())) + stack.push(contract.Caller().Big()) } func opCallValue(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) { @@ -485,7 +485,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)) } @@ -514,25 +513,19 @@ func opCreate(instr instruction, pc *uint64, env Environment, contract *Contract 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 { + _, addr, suberr := env.Create(contract, input, gas, contract.Price, value) + // Push item on the stack based on the returned error. If the ruleset is + // homestead we must check for CodeStoreOutOfGasError (homestead only + // rule) and treat as an error, if the ruleset is frontier we must + // ignore this error and pretend the operation was successful. + if params.IsHomestead(env.BlockNumber()) && suberr == CodeStoreOutOfGasError { + stack.push(new(big.Int)) + } else if suberr != nil && suberr != CodeStoreOutOfGasError { stack.push(new(big.Int)) } else { - // 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()) - } } @@ -598,6 +591,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/jit.go b/core/vm/jit.go index 1aa7d7ef2..5404730c1 100644 --- a/core/vm/jit.go +++ b/core/vm/jit.go @@ -96,7 +96,7 @@ type Program struct { // NewProgram returns a new JIT program func NewProgram(code []byte) *Program { program := &Program{ - Id: crypto.Sha3Hash(code), + Id: crypto.Keccak256Hash(code), mapping: make(map[uint64]uint64), destinations: make(map[uint64]struct{}), code: code, @@ -275,6 +275,11 @@ func CompileProgram(program *Program) (err error) { program.addInstr(op, pc, opGas, nil) case CREATE: program.addInstr(op, pc, opCreate, nil) + case DELEGATECALL: + // Instruction added regardless of homestead phase. + // Homestead (and execution of the opcode) is checked during + // runtime. + program.addInstr(op, pc, opDelegateCall, nil) case CALL: program.addInstr(op, pc, opCall, nil) case CALLCODE: @@ -317,10 +322,14 @@ func runProgram(program *Program, pcstart uint64, mem *Memory, stack *stack, env }() } + homestead := params.IsHomestead(env.BlockNumber()) for pc < uint64(len(program.instructions)) { instrCount++ instr := program.instructions[pc] + if instr.Op() == DELEGATECALL && !homestead { + return nil, fmt.Errorf("Invalid opcode 0x%x", instr.Op()) + } ret, err := instr.do(program, &pc, env, contract, mem, stack) if err != nil { @@ -328,13 +337,13 @@ func runProgram(program *Program, pcstart uint64, mem *Memory, stack *stack, env } if instr.halts() { - return contract.Return(ret), nil + return ret, nil } } contract.Input = nil - return contract.Return(nil), nil + return nil, nil } // validDest checks if the given distination is a valid one given the @@ -457,7 +466,6 @@ func jitCalculateGasAndSize(env Environment, contract *Contract, instr instructi gas.Add(gas, stack.data[stack.len()-1]) if op == CALL { - //if env.Db().GetStateObject(common.BigToAddress(stack.data[stack.len()-2])) == nil { if !env.Db().Exist(common.BigToAddress(stack.data[stack.len()-2])) { gas.Add(gas, params.CallNewAccountGas) } @@ -471,6 +479,13 @@ func jitCalculateGasAndSize(env Environment, contract *Contract, instr instructi y := calcMemSize(stack.data[stack.len()-4], stack.data[stack.len()-5]) newMemSize = common.BigMax(x, y) + case DELEGATECALL: + gas.Add(gas, stack.data[stack.len()-1]) + + x := calcMemSize(stack.data[stack.len()-5], stack.data[stack.len()-6]) + y := calcMemSize(stack.data[stack.len()-3], stack.data[stack.len()-4]) + + newMemSize = common.BigMax(x, y) } quadMemGas(mem, newMemSize, gas) diff --git a/core/vm/jit_test.go b/core/vm/jit_test.go index 8c50ed0f5..4174c666f 100644 --- a/core/vm/jit_test.go +++ b/core/vm/jit_test.go @@ -127,6 +127,8 @@ type account struct{} func (account) SubBalance(amount *big.Int) {} func (account) AddBalance(amount *big.Int) {} +func (account) SetAddress(common.Address) {} +func (account) Value() *big.Int { return nil } func (account) SetBalance(*big.Int) {} func (account) SetNonce(uint64) {} func (account) Balance() *big.Int { return nil } @@ -187,7 +189,7 @@ func (self *Env) Db() Database { return nil } func (self *Env) GasLimit() *big.Int { return self.gasLimit } func (self *Env) VmType() Type { return StdVmTy } func (self *Env) GetHash(n uint64) common.Hash { - return common.BytesToHash(crypto.Sha3([]byte(big.NewInt(int64(n)).String()))) + return common.BytesToHash(crypto.Keccak256([]byte(big.NewInt(int64(n)).String()))) } func (self *Env) AddLog(log *Log) { } @@ -206,3 +208,6 @@ func (self *Env) CallCode(caller ContractRef, addr common.Address, data []byte, func (self *Env) Create(caller ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) { return nil, common.Address{}, nil } +func (self *Env) DelegateCall(me ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) { + return nil, nil +} diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index ab899647f..37d7bb160 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -1,13 +1,29 @@ package vm -import "math/big" +import ( + "math/big" + + "github.com/ethereum/go-ethereum/params" +) type jumpPtr struct { fn instrFn valid bool } -var jumpTable [256]jumpPtr +type vmJumpTable [256]jumpPtr + +func (jt vmJumpTable) init(blockNumber *big.Int) { + // when initialising a new VM execution we must first check the homestead + // changes. + if params.IsHomestead(blockNumber) { + jumpTable[DELEGATECALL] = jumpPtr{opDelegateCall, true} + } else { + jumpTable[DELEGATECALL] = jumpPtr{nil, false} + } +} + +var jumpTable vmJumpTable func init() { jumpTable[ADD] = jumpPtr{opAdd, true} diff --git a/core/vm/jump_table_test.go b/core/vm/jump_table_test.go new file mode 100644 index 000000000..98d34bef2 --- /dev/null +++ b/core/vm/jump_table_test.go @@ -0,0 +1,24 @@ +package vm + +import ( + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/params" +) + +func TestInit(t *testing.T) { + params.HomesteadBlock = big.NewInt(1) + + jumpTable.init(big.NewInt(0)) + if jumpTable[DELEGATECALL].valid { + t.Error("Expected DELEGATECALL not to be present") + } + + for _, n := range []int64{1, 2, 100} { + jumpTable.init(big.NewInt(n)) + if !jumpTable[DELEGATECALL].valid { + t.Error("Expected DELEGATECALL to be present for block", n) + } + } +} diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go index dc4139092..7d861f1de 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", @@ -402,6 +404,7 @@ var stringToOp = map[string]OpCode{ "CALLDATALOAD": CALLDATALOAD, "CALLDATASIZE": CALLDATASIZE, "CALLDATACOPY": CALLDATACOPY, + "DELEGATECALL": DELEGATECALL, "CODESIZE": CODESIZE, "CODECOPY": CODECOPY, "GASPRICE": GASPRICE, diff --git a/core/vm/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/runtime/runtime.go b/core/vm/runtime/runtime.go index 1fa06e980..565ce7b73 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -67,7 +67,7 @@ func setDefaults(cfg *Config) { } if cfg.GetHashFn == nil { cfg.GetHashFn = func(n uint64) common.Hash { - return common.BytesToHash(crypto.Sha3([]byte(new(big.Int).SetUint64(n).String()))) + return common.BytesToHash(crypto.Keccak256([]byte(new(big.Int).SetUint64(n).String()))) } } } diff --git a/core/vm/vm.go b/core/vm/vm.go index 0c6bbcd42..d45d136b5 100644 --- a/core/vm/vm.go +++ b/core/vm/vm.go @@ -35,6 +35,9 @@ type Vm struct { // New returns a new Vm func New(env Environment) *Vm { + // init the jump table. Also prepares the homestead changes + jumpTable.init(env.BlockNumber()) + return &Vm{env: env} } @@ -43,16 +46,6 @@ func (self *Vm) Run(contract *Contract, input []byte) (ret []byte, err error) { self.env.SetDepth(self.env.Depth() + 1) defer self.env.SetDepth(self.env.Depth() - 1) - // User defer pattern to check for an error and, based on the error being nil or not, use all gas and return. - defer func() { - if err != nil { - // In case of a VM exception (known exceptions) all gas consumed (panics NOT included). - contract.UseGas(contract.Gas) - - ret = contract.Return(nil) - } - }() - if contract.CodeAddr != nil { if p := Precompiled[contract.CodeAddr.Str()]; p != nil { return self.RunPrecompiled(p, input, contract) @@ -61,11 +54,11 @@ func (self *Vm) Run(contract *Contract, input []byte) (ret []byte, err error) { // Don't bother with the execution if there's no code. if len(contract.Code) == 0 { - return contract.Return(nil), nil + return nil, nil } var ( - codehash = crypto.Sha3Hash(contract.Code) // codehash is used when doing jump dest caching + codehash = crypto.Keccak256Hash(contract.Code) // codehash is used when doing jump dest caching program *Program ) if EnableJit { @@ -160,7 +153,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 +169,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) @@ -205,13 +196,13 @@ func (self *Vm) Run(contract *Contract, input []byte) (ret []byte, err error) { offset, size := stack.pop(), stack.pop() ret := mem.GetPtr(offset.Int64(), size.Int64()) - return contract.Return(ret), nil + return ret, nil case SUICIDE: opSuicide(instruction{}, nil, self.env, contract, mem, stack) fallthrough case STOP: // Stop the contract - return contract.Return(nil), nil + return nil, nil } } } else { @@ -332,7 +323,6 @@ func calculateGasAndSize(env Environment, contract *Contract, caller ContractRef gas.Add(gas, stack.data[stack.len()-1]) if op == CALL { - //if env.Db().GetStateObject(common.BigToAddress(stack.data[stack.len()-2])) == nil { if !env.Db().Exist(common.BigToAddress(stack.data[stack.len()-2])) { gas.Add(gas, params.CallNewAccountGas) } @@ -346,6 +336,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) @@ -358,7 +355,7 @@ func (self *Vm) RunPrecompiled(p *PrecompiledAccount, input []byte, contract *Co if contract.UseGas(gas) { ret = p.Call(input) - return contract.Return(ret), nil + return ret, nil } else { return nil, OutOfGasError } diff --git a/core/vm/vm_jit.go b/core/vm/vm_jit.go index 07cb52d4a..589c30fa8 100644 --- a/core/vm/vm_jit.go +++ b/core/vm/vm_jit.go @@ -200,7 +200,7 @@ func (self *JitVm) Run(me, caller ContextRef, code []byte, value, gas, price *bi self.data.timestamp = self.env.Time() self.data.code = getDataPtr(code) self.data.codeSize = uint64(len(code)) - self.data.codeHash = hash2llvm(crypto.Sha3(code)) // TODO: Get already computed hash? + self.data.codeHash = hash2llvm(crypto.Keccak256(code)) // TODO: Get already computed hash? jit := C.evmjit_create() retCode := C.evmjit_run(jit, unsafe.Pointer(&self.data), unsafe.Pointer(self)) @@ -242,7 +242,7 @@ func (self *JitVm) Env() Environment { //export env_sha3 func env_sha3(dataPtr *byte, length uint64, resultPtr unsafe.Pointer) { data := llvm2bytesRef(dataPtr, length) - hash := crypto.Sha3(data) + hash := crypto.Keccak256(data) result := (*i256)(resultPtr) *result = hash2llvm(hash) } 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) } |