From 10a57fc3d45cbc59d6c8eeb0f7f2b93a71e8f4c9 Mon Sep 17 00:00:00 2001 From: Jeffrey Wilcke Date: Wed, 1 Feb 2017 22:36:51 +0100 Subject: consensus, core/*, params: metropolis preparation refactor This commit is a preparation for the upcoming metropolis hardfork. It prepares the state, core and vm packages such that integration with metropolis becomes less of a hassle. * Difficulty calculation requires header instead of individual parameters * statedb.StartRecord renamed to statedb.Prepare and added Finalise method required by metropolis, which removes unwanted accounts from the state (i.e. selfdestruct) * State keeps record of destructed objects (in addition to dirty objects) * core/vm pre-compiles may now return errors * core/vm pre-compiles gas check now take the full byte slice as argument instead of just the size * core/vm now keeps several hard-fork instruction tables instead of a single instruction table and removes the need for hard-fork checks in the instructions * core/vm contains a empty restruction function which is added in preparation of metropolis write-only mode operations * Adds the bn256 curve * Adds and sets the metropolis chain config block parameters (2^64-1) --- core/chain_makers.go | 22 +++++++--- core/state/statedb.go | 91 ++++++++++++++++++++++++--------------- core/state_processor.go | 5 ++- core/types/transaction_signing.go | 18 +++++++- core/vm/contracts.go | 49 ++++++++++----------- core/vm/contracts_test.go | 1 + core/vm/evm.go | 49 ++++++++++++++------- core/vm/instructions.go | 17 +++----- core/vm/interpreter.go | 80 +++++++++++++++++++++------------- core/vm/jump_table.go | 33 ++++++++++---- core/vm/memory.go | 1 + 11 files changed, 233 insertions(+), 133 deletions(-) create mode 100644 core/vm/contracts_test.go (limited to 'core') diff --git a/core/chain_makers.go b/core/chain_makers.go index f34279ba0..967744282 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -84,7 +84,7 @@ func (b *BlockGen) AddTx(tx *types.Transaction) { if b.gasPool == nil { b.SetCoinbase(common.Address{}) } - b.statedb.StartRecord(tx.Hash(), common.Hash{}, len(b.txs)) + b.statedb.Prepare(tx.Hash(), common.Hash{}, len(b.txs)) receipt, _, err := ApplyTransaction(b.config, nil, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, b.header.GasUsed, vm.Config{}) if err != nil { panic(err) @@ -142,7 +142,7 @@ func (b *BlockGen) OffsetTime(seconds int64) { if b.header.Time.Cmp(b.parent.Header().Time) <= 0 { panic("block time out of range") } - b.header.Difficulty = ethash.CalcDifficulty(b.config, b.header.Time.Uint64(), b.parent.Time().Uint64(), b.parent.Number(), b.parent.Difficulty()) + b.header.Difficulty = ethash.CalcDifficulty(b.config, b.header.Time.Uint64(), b.parent.Header()) } // GenerateChain creates a chain of n blocks. The first block's @@ -209,15 +209,23 @@ func makeHeader(config *params.ChainConfig, parent *types.Block, state *state.St } else { time = new(big.Int).Add(parent.Time(), big.NewInt(10)) // block time is fixed at 10 seconds } + parentHeader := parent.Header() + // adjust the parent time + parentHeader.Time = new(big.Int).Sub(time, big.NewInt(10)) + return &types.Header{ Root: state.IntermediateRoot(config.IsEIP158(parent.Number())), ParentHash: parent.Hash(), Coinbase: parent.Coinbase(), - Difficulty: ethash.CalcDifficulty(config, time.Uint64(), new(big.Int).Sub(time, big.NewInt(10)).Uint64(), parent.Number(), parent.Difficulty()), - GasLimit: CalcGasLimit(parent), - GasUsed: new(big.Int), - Number: new(big.Int).Add(parent.Number(), common.Big1), - Time: time, + Difficulty: ethash.CalcDifficulty(config, time.Uint64(), &types.Header{ + Number: parent.Number(), + Time: new(big.Int).Sub(time, big.NewInt(10)), + Difficulty: parent.Difficulty(), + }), + GasLimit: CalcGasLimit(parent), + GasUsed: new(big.Int), + Number: new(big.Int).Add(parent.Number(), common.Big1), + Time: time, } } diff --git a/core/state/statedb.go b/core/state/statedb.go index 3b753a2e6..8e4ba21c0 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -62,8 +62,9 @@ type StateDB struct { codeSizeCache *lru.Cache // This map holds 'live' objects, which will get modified while processing a state transition. - stateObjects map[common.Address]*stateObject - stateObjectsDirty map[common.Address]struct{} + stateObjects map[common.Address]*stateObject + stateObjectsDirty map[common.Address]struct{} + stateObjectsDestructed map[common.Address]struct{} // The refund counter, also used by state transitioning. refund *big.Int @@ -92,14 +93,15 @@ func New(root common.Hash, db ethdb.Database) (*StateDB, error) { } csc, _ := lru.New(codeSizeCacheSize) return &StateDB{ - db: db, - trie: tr, - codeSizeCache: csc, - stateObjects: make(map[common.Address]*stateObject), - stateObjectsDirty: make(map[common.Address]struct{}), - refund: new(big.Int), - logs: make(map[common.Hash][]*types.Log), - preimages: make(map[common.Hash][]byte), + db: db, + trie: tr, + codeSizeCache: csc, + stateObjects: make(map[common.Address]*stateObject), + stateObjectsDirty: make(map[common.Address]struct{}), + stateObjectsDestructed: make(map[common.Address]struct{}), + refund: new(big.Int), + logs: make(map[common.Hash][]*types.Log), + preimages: make(map[common.Hash][]byte), }, nil } @@ -114,14 +116,15 @@ func (self *StateDB) New(root common.Hash) (*StateDB, error) { return nil, err } return &StateDB{ - db: self.db, - trie: tr, - codeSizeCache: self.codeSizeCache, - stateObjects: make(map[common.Address]*stateObject), - stateObjectsDirty: make(map[common.Address]struct{}), - refund: new(big.Int), - logs: make(map[common.Hash][]*types.Log), - preimages: make(map[common.Hash][]byte), + db: self.db, + trie: tr, + codeSizeCache: self.codeSizeCache, + stateObjects: make(map[common.Address]*stateObject), + stateObjectsDirty: make(map[common.Address]struct{}), + stateObjectsDestructed: make(map[common.Address]struct{}), + refund: new(big.Int), + logs: make(map[common.Hash][]*types.Log), + preimages: make(map[common.Hash][]byte), }, nil } @@ -138,6 +141,7 @@ func (self *StateDB) Reset(root common.Hash) error { self.trie = tr self.stateObjects = make(map[common.Address]*stateObject) self.stateObjectsDirty = make(map[common.Address]struct{}) + self.stateObjectsDestructed = make(map[common.Address]struct{}) self.thash = common.Hash{} self.bhash = common.Hash{} self.txIndex = 0 @@ -173,12 +177,6 @@ func (self *StateDB) pushTrie(t *trie.SecureTrie) { } } -func (self *StateDB) StartRecord(thash, bhash common.Hash, ti int) { - self.thash = thash - self.bhash = bhash - self.txIndex = ti -} - func (self *StateDB) AddLog(log *types.Log) { self.journal = append(self.journal, addLogChange{txhash: self.thash}) @@ -510,21 +508,25 @@ func (self *StateDB) Copy() *StateDB { // Copy all the basic fields, initialize the memory ones state := &StateDB{ - db: self.db, - trie: self.trie, - pastTries: self.pastTries, - codeSizeCache: self.codeSizeCache, - stateObjects: make(map[common.Address]*stateObject, len(self.stateObjectsDirty)), - stateObjectsDirty: make(map[common.Address]struct{}, len(self.stateObjectsDirty)), - refund: new(big.Int).Set(self.refund), - logs: make(map[common.Hash][]*types.Log, len(self.logs)), - logSize: self.logSize, - preimages: make(map[common.Hash][]byte), + db: self.db, + trie: self.trie, + pastTries: self.pastTries, + codeSizeCache: self.codeSizeCache, + stateObjects: make(map[common.Address]*stateObject, len(self.stateObjectsDirty)), + stateObjectsDirty: make(map[common.Address]struct{}, len(self.stateObjectsDirty)), + stateObjectsDestructed: make(map[common.Address]struct{}, len(self.stateObjectsDestructed)), + refund: new(big.Int).Set(self.refund), + logs: make(map[common.Hash][]*types.Log, len(self.logs)), + logSize: self.logSize, + preimages: make(map[common.Hash][]byte), } // Copy the dirty states, logs, and preimages for addr := range self.stateObjectsDirty { state.stateObjects[addr] = self.stateObjects[addr].deepCopy(state, state.MarkStateObjectDirty) state.stateObjectsDirty[addr] = struct{}{} + if self.stateObjects[addr].suicided { + state.stateObjectsDestructed[addr] = struct{}{} + } } for hash, logs := range self.logs { state.logs[hash] = make([]*types.Log, len(logs)) @@ -590,6 +592,27 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { return s.trie.Hash() } +// Prepare sets the current transaction hash and index and block hash which is +// used when the EVM emits new state logs. +func (self *StateDB) Prepare(thash, bhash common.Hash, ti int) { + self.thash = thash + self.bhash = bhash + self.txIndex = ti +} + +// Finalise finalises the state by removing the self destructed objects +// in the current stateObjectsDestructed buffer and clears the journal +// as well as the refunds. +// +// Please note that Finalise is used by EIP#98 and is used instead of +// IntermediateRoot. +func (s *StateDB) Finalise() { + for addr := range s.stateObjectsDestructed { + s.deleteStateObject(s.stateObjects[addr]) + } + s.clearJournalAndRefund() +} + // DeleteSuicides flags the suicided objects for deletion so that it // won't be referenced again when called / queried up on. // diff --git a/core/state_processor.go b/core/state_processor.go index 4fc2f1eae..90f5a4f60 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -69,7 +69,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg } // Iterate over and process the individual transactions for i, tx := range block.Transactions() { - statedb.StartRecord(tx.Hash(), block.Hash(), i) + statedb.Prepare(tx.Hash(), block.Hash(), i) receipt, _, err := ApplyTransaction(p.config, p.bc, nil, gp, statedb, header, tx, totalUsedGas, cfg) if err != nil { return nil, nil, nil, err @@ -107,7 +107,8 @@ func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, author *common usedGas.Add(usedGas, gas) // Create a new receipt for the transaction, storing the intermediate root and gas used by the tx // based on the eip phase, we're passing wether the root touch-delete accounts. - receipt := types.NewReceipt(statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes(), usedGas) + root := statedb.IntermediateRoot(config.IsEIP158(header.Number)) + receipt := types.NewReceipt(root.Bytes(), usedGas) receipt.TxHash = tx.Hash() receipt.GasUsed = new(big.Int).Set(gas) // if the transaction created a contract, store the creation address in the receipt. diff --git a/core/types/transaction_signing.go b/core/types/transaction_signing.go index b4bab0aad..e50b656b3 100644 --- a/core/types/transaction_signing.go +++ b/core/types/transaction_signing.go @@ -27,7 +27,12 @@ import ( "github.com/ethereum/go-ethereum/params" ) -var ErrInvalidChainId = errors.New("invalid chaid id for signer") +var ( + ErrInvalidChainId = errors.New("invalid chaid id for signer") + + errAbstractSigner = errors.New("abstract signer") + abstractSignerAddress = common.HexToAddress("ffffffffffffffffffffffffffffffffffffff") +) // sigCache is used to cache the derived sender and contains // the signer used to derive it. @@ -103,6 +108,17 @@ type Signer interface { Equal(Signer) bool } +/* +// WithSignature returns a new transaction with the given signature. This signature +// needs to be in the [R || S || V] format where V is 0 or 1. +func (s EIP86Signer) WithSignature(tx *Transaction, sig []byte) (*Transaction, error) { +} + +// Hash returns the hash to be signed by the sender. +// It does not uniquely identify the transaction. +func (s EIP86Signer) Hash(tx *Transaction) common.Hash {} +*/ + // EIP155Transaction implements TransactionInterface using the // EIP155 rules type EIP155Signer struct { diff --git a/core/vm/contracts.go b/core/vm/contracts.go index e87640d02..90b2f913e 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -18,6 +18,7 @@ package vm import ( "crypto/sha256" + "errors" "math/big" "github.com/ethereum/go-ethereum/common" @@ -27,15 +28,17 @@ import ( "golang.org/x/crypto/ripemd160" ) +var errBadPrecompileInput = errors.New("bad pre compile input") + // Precompiled contract is the basic interface for native Go contracts. The implementation // requires a deterministic gas count based on the input size of the Run method of the // contract. type PrecompiledContract interface { - RequiredGas(inputSize int) uint64 // RequiredPrice calculates the contract gas use - Run(input []byte) []byte // Run runs the precompiled contract + RequiredGas(input []byte) uint64 // RequiredPrice calculates the contract gas use + Run(input []byte) ([]byte, error) // Run runs the precompiled contract } -// Precompiled contains the default set of ethereum contracts +// PrecompiledContracts contains the default set of ethereum contracts var PrecompiledContracts = map[common.Address]PrecompiledContract{ common.BytesToAddress([]byte{1}): &ecrecover{}, common.BytesToAddress([]byte{2}): &sha256hash{}, @@ -45,11 +48,9 @@ var PrecompiledContracts = map[common.Address]PrecompiledContract{ // RunPrecompile runs and evaluate the output of a precompiled contract defined in contracts.go func RunPrecompiledContract(p PrecompiledContract, input []byte, contract *Contract) (ret []byte, err error) { - gas := p.RequiredGas(len(input)) + gas := p.RequiredGas(input) if contract.UseGas(gas) { - ret = p.Run(input) - - return ret, nil + return p.Run(input) } else { return nil, ErrOutOfGas } @@ -58,11 +59,11 @@ func RunPrecompiledContract(p PrecompiledContract, input []byte, contract *Contr // ECRECOVER implemented as a native contract type ecrecover struct{} -func (c *ecrecover) RequiredGas(inputSize int) uint64 { +func (c *ecrecover) RequiredGas(input []byte) uint64 { return params.EcrecoverGas } -func (c *ecrecover) Run(in []byte) []byte { +func (c *ecrecover) Run(in []byte) ([]byte, error) { const ecRecoverInputLength = 128 in = common.RightPadBytes(in, ecRecoverInputLength) @@ -76,18 +77,18 @@ func (c *ecrecover) Run(in []byte) []byte { // tighter sig s values in homestead only apply to tx sigs if !allZero(in[32:63]) || !crypto.ValidateSignatureValues(v, r, s, false) { log.Trace("ECRECOVER error: v, r or s value invalid") - return nil + return nil, nil } // v needs to be at the end for libsecp256k1 pubKey, err := crypto.Ecrecover(in[:32], append(in[64:128], v)) // make sure the public key is a valid one if err != nil { log.Trace("ECRECOVER failed", "err", err) - return nil + return nil, nil } // the first byte of pubkey is bitcoin heritage - return common.LeftPadBytes(crypto.Keccak256(pubKey[1:])[12:], 32) + return common.LeftPadBytes(crypto.Keccak256(pubKey[1:])[12:], 32), nil } // SHA256 implemented as a native contract @@ -97,12 +98,12 @@ type sha256hash struct{} // // This method does not require any overflow checking as the input size gas costs // required for anything significant is so high it's impossible to pay for. -func (c *sha256hash) RequiredGas(inputSize int) uint64 { - return uint64(inputSize+31)/32*params.Sha256WordGas + params.Sha256Gas +func (c *sha256hash) RequiredGas(input []byte) uint64 { + return uint64(len(input)+31)/32*params.Sha256WordGas + params.Sha256Gas } -func (c *sha256hash) Run(in []byte) []byte { +func (c *sha256hash) Run(in []byte) ([]byte, error) { h := sha256.Sum256(in) - return h[:] + return h[:], nil } // RIPMED160 implemented as a native contract @@ -112,13 +113,13 @@ type ripemd160hash struct{} // // This method does not require any overflow checking as the input size gas costs // required for anything significant is so high it's impossible to pay for. -func (c *ripemd160hash) RequiredGas(inputSize int) uint64 { - return uint64(inputSize+31)/32*params.Ripemd160WordGas + params.Ripemd160Gas +func (c *ripemd160hash) RequiredGas(input []byte) uint64 { + return uint64(len(input)+31)/32*params.Ripemd160WordGas + params.Ripemd160Gas } -func (c *ripemd160hash) Run(in []byte) []byte { +func (c *ripemd160hash) Run(in []byte) ([]byte, error) { ripemd := ripemd160.New() ripemd.Write(in) - return common.LeftPadBytes(ripemd.Sum(nil), 32) + return common.LeftPadBytes(ripemd.Sum(nil), 32), nil } // data copy implemented as a native contract @@ -128,9 +129,9 @@ type dataCopy struct{} // // This method does not require any overflow checking as the input size gas costs // required for anything significant is so high it's impossible to pay for. -func (c *dataCopy) RequiredGas(inputSize int) uint64 { - return uint64(inputSize+31)/32*params.IdentityWordGas + params.IdentityGas +func (c *dataCopy) RequiredGas(input []byte) uint64 { + return uint64(len(input)+31)/32*params.IdentityWordGas + params.IdentityGas } -func (c *dataCopy) Run(in []byte) []byte { - return in +func (c *dataCopy) Run(in []byte) ([]byte, error) { + return in, nil } diff --git a/core/vm/contracts_test.go b/core/vm/contracts_test.go new file mode 100644 index 000000000..830a8f69d --- /dev/null +++ b/core/vm/contracts_test.go @@ -0,0 +1 @@ +package vm diff --git a/core/vm/evm.go b/core/vm/evm.go index 71efcfa45..9296cc7ca 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -33,7 +33,20 @@ type ( GetHashFunc func(uint64) common.Hash ) -// Context provides the EVM with auxiliary information. Once provided it shouldn't be modified. +// run runs the given contract and takes care of running precompiles with a fallback to the byte code interpreter. +func run(evm *EVM, snapshot int, contract *Contract, input []byte) ([]byte, error) { + if contract.CodeAddr != nil { + precompiledContracts := PrecompiledContracts + if p := precompiledContracts[*contract.CodeAddr]; p != nil { + return RunPrecompiledContract(p, input, contract) + } + } + + return evm.interpreter.Run(snapshot, contract, input) +} + +// Context provides the EVM with auxiliary information. Once provided +// it shouldn't be modified. type Context struct { // CanTransfer returns whether the account contains // sufficient ether to transfer the value @@ -55,7 +68,13 @@ type Context struct { Difficulty *big.Int // Provides information for DIFFICULTY } -// EVM provides information about external sources for the EVM +// EVM is the Ethereum Virtual Machine base object and provides +// the necessary tools to run a contract on the given state with +// the provided context. It should be noted that any error +// generated through any of the calls should be considered a +// revert-state-and-consume-all-gas operation, no checks on +// specific errors should ever be performed. The interpreter makes +// sure that any errors generated are to be considered faulty code. // // The EVM should never be reused and is not thread safe. type EVM struct { @@ -68,6 +87,8 @@ type EVM struct { // chainConfig contains information about the current chain chainConfig *params.ChainConfig + // chain rules contains the chain rules for the current epoch + chainRules params.Rules // virtual machine configuration options used to initialise the // evm. vmConfig Config @@ -79,21 +100,23 @@ type EVM struct { abort int32 } -// NewEVM retutrns a new EVM evmironment. +// NewEVM retutrns a new EVM evmironment. The returned EVM is not thread safe +// and should only ever be used *once*. func NewEVM(ctx Context, statedb StateDB, chainConfig *params.ChainConfig, vmConfig Config) *EVM { evm := &EVM{ Context: ctx, StateDB: statedb, vmConfig: vmConfig, chainConfig: chainConfig, + chainRules: chainConfig.Rules(ctx.BlockNumber), } evm.interpreter = NewInterpreter(evm, vmConfig) return evm } -// Cancel cancels any running EVM operation. This may be called concurrently and it's safe to be -// called multiple times. +// Cancel cancels any running EVM operation. This may be called concurrently and +// it's safe to be called multiple times. func (evm *EVM) Cancel() { atomic.StoreInt32(&evm.abort, 1) } @@ -134,13 +157,12 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas contract := NewContract(caller, to, value, gas) contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) - ret, err = evm.interpreter.Run(contract, input) + ret, err = run(evm, snapshot, contract, input) // 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 { contract.UseGas(contract.Gas) - evm.StateDB.RevertToSnapshot(snapshot) } return ret, contract.Gas, err @@ -175,10 +197,9 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, contract := NewContract(caller, to, value, gas) contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) - ret, err = evm.interpreter.Run(contract, input) + ret, err = run(evm, snapshot, contract, input) if err != nil { contract.UseGas(contract.Gas) - evm.StateDB.RevertToSnapshot(snapshot) } @@ -210,10 +231,9 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by contract := NewContract(caller, to, nil, gas).AsDelegate() contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) - ret, err = evm.interpreter.Run(contract, input) + ret, err = run(evm, snapshot, contract, input) if err != nil { contract.UseGas(contract.Gas) - evm.StateDB.RevertToSnapshot(snapshot) } @@ -253,8 +273,7 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I contract := NewContract(caller, AccountRef(contractAddr), value, gas) contract.SetCallCode(&contractAddr, crypto.Keccak256Hash(code), code) - ret, err = evm.interpreter.Run(contract, nil) - + ret, err = run(evm, snapshot, contract, nil) // check whether the max code size has been exceeded maxCodeSizeExceeded := len(ret) > params.MaxCodeSize // if the contract creation ran successfully and no errors were returned @@ -275,10 +294,8 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I // when we're in homestead this also counts for code storage gas errors. if maxCodeSizeExceeded || (err != nil && (evm.ChainConfig().IsHomestead(evm.BlockNumber) || err != ErrCodeStoreOutOfGas)) { + contract.UseGas(contract.Gas) evm.StateDB.RevertToSnapshot(snapshot) - - // Nothing should be returned when an error is thrown. - return nil, contractAddr, 0, err } // If the vm returned with an error the return value should be set to nil. // This isn't consensus critical but merely to for behaviour reasons such as diff --git a/core/vm/instructions.go b/core/vm/instructions.go index bfc0a668e..fa4dbe428 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -27,7 +27,9 @@ import ( "github.com/ethereum/go-ethereum/params" ) -var bigZero = new(big.Int) +var ( + bigZero = new(big.Int) +) func opAdd(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := stack.pop(), stack.pop() @@ -599,7 +601,7 @@ func opCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Sta contract.Gas += returnGas evm.interpreter.intPool.put(addr, value, inOffset, inSize, retOffset, retSize) - return nil, nil + return ret, nil } func opCallCode(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { @@ -633,16 +635,10 @@ func opCallCode(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack contract.Gas += returnGas evm.interpreter.intPool.put(addr, value, inOffset, inSize, retOffset, retSize) - return nil, nil + return ret, nil } func opDelegateCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - // if not homestead return an error. DELEGATECALL is not supported - // during pre-homestead. - if !evm.ChainConfig().IsHomestead(evm.BlockNumber) { - return nil, fmt.Errorf("invalid opcode %x", DELEGATECALL) - } - gas, to, inOffset, inSize, outOffset, outSize := stack.pop().Uint64(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() toAddr := common.BigToAddress(to) @@ -658,7 +654,7 @@ func opDelegateCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, st contract.Gas += returnGas evm.interpreter.intPool.put(to, inOffset, inSize, outOffset, outSize) - return nil, nil + return ret, nil } func opReturn(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { @@ -666,6 +662,7 @@ func opReturn(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *S ret := memory.GetPtr(offset.Int64(), size.Int64()) evm.interpreter.intPool.put(offset, size) + return ret, nil } diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 8ee9d3ca7..07971f876 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -45,50 +45,60 @@ type Config struct { DisableGasMetering bool // Enable recording of SHA3/keccak preimages EnablePreimageRecording bool - // JumpTable contains the EVM instruction table. This + // JumpTable contains the in instruction table. This // may me left uninitialised and will be set the default // table. JumpTable [256]operation } // Interpreter is used to run Ethereum based contracts and will utilise the -// passed environment to query external sources for state information. +// passed evmironment to query external sources for state information. // The Interpreter will run the byte code VM or JIT VM based on the passed // configuration. type Interpreter struct { - env *EVM + evm *EVM cfg Config gasTable params.GasTable intPool *intPool + + readonly bool } // NewInterpreter returns a new instance of the Interpreter. -func NewInterpreter(env *EVM, cfg Config) *Interpreter { +func NewInterpreter(evm *EVM, cfg Config) *Interpreter { // We use the STOP instruction whether to see // the jump table was initialised. If it was not // we'll set the default jump table. if !cfg.JumpTable[STOP].valid { - cfg.JumpTable = defaultJumpTable + switch { + case evm.ChainConfig().IsHomestead(evm.BlockNumber): + cfg.JumpTable = homesteadInstructionSet + default: + cfg.JumpTable = baseInstructionSet + } } return &Interpreter{ - env: env, + evm: evm, cfg: cfg, - gasTable: env.ChainConfig().GasTable(env.BlockNumber), + gasTable: evm.ChainConfig().GasTable(evm.BlockNumber), intPool: newIntPool(), } } -// Run loops and evaluates the contract's code with the given input data -func (evm *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err error) { - evm.env.depth++ - defer func() { evm.env.depth-- }() +func (in *Interpreter) enforceRestrictions(op OpCode, operation operation, stack *Stack) error { + return nil +} - if contract.CodeAddr != nil { - if p := PrecompiledContracts[*contract.CodeAddr]; p != nil { - return RunPrecompiledContract(p, input, contract) - } - } +// Run loops and evaluates the contract's code with the given input data and returns +// the return byte-slice and an error if one occured. +// +// It's important to note that any errors returned by the interpreter should be +// considered a revert-and-consume-all-gas operation. No error specific checks +// should be handled to reduce complexity and errors further down the in. +func (in *Interpreter) Run(snapshot int, contract *Contract, input []byte) (ret []byte, err error) { + in.evm.depth++ + defer func() { in.evm.depth-- }() // Don't bother with the execution if there's no code. if len(contract.Code) == 0 { @@ -105,7 +115,8 @@ func (evm *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err e mem = NewMemory() // bound memory stack = newstack() // local stack // For optimisation reason we're using uint64 as the program counter. - // It's theoretically possible to go above 2^64. The YP defines the PC to be uint256. Practically much less so feasible. + // It's theoretically possible to go above 2^64. The YP defines the PC + // to be uint256. Practically much less so feasible. pc = uint64(0) // program counter cost uint64 ) @@ -113,27 +124,30 @@ func (evm *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err e // 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 && evm.cfg.Debug { + if err != nil && in.cfg.Debug { // XXX For debugging //fmt.Printf("%04d: %8v cost = %-8d stack = %-8d ERR = %v\n", pc, op, cost, stack.len(), err) - evm.cfg.Tracer.CaptureState(evm.env, pc, op, contract.Gas, cost, mem, stack, contract, evm.env.depth, err) + in.cfg.Tracer.CaptureState(in.evm, pc, op, contract.Gas, cost, mem, stack, contract, in.evm.depth, err) } }() - log.Debug("EVM running contract", "hash", codehash[:]) + log.Debug("in running contract", "hash", codehash[:]) tstart := time.Now() - defer log.Debug("EVM finished running contract", "hash", codehash[:], "elapsed", time.Since(tstart)) + defer log.Debug("in finished running contract", "hash", codehash[:], "elapsed", time.Since(tstart)) // The Interpreter main run loop (contextual). This loop runs until either an // explicit STOP, RETURN or SELFDESTRUCT is executed, an error occurred during - // the execution of one of the operations or until the evm.done is set by + // the execution of one of the operations or until the in.done is set by // the parent context.Context. - for atomic.LoadInt32(&evm.env.abort) == 0 { + for atomic.LoadInt32(&in.evm.abort) == 0 { // Get the memory location of pc op = contract.GetOp(pc) // get the operation from the jump table matching the opcode - operation := evm.cfg.JumpTable[op] + operation := in.cfg.JumpTable[op] + if err := in.enforceRestrictions(op, operation, stack); err != nil { + return nil, err + } // if the op is invalid abort the process and return an error if !operation.valid { @@ -161,10 +175,10 @@ func (evm *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err e } } - if !evm.cfg.DisableGasMetering { + if !in.cfg.DisableGasMetering { // consume the gas and return an error if not enough gas is available. // cost is explicitly set so that the capture state defer method cas get the proper cost - cost, err = operation.gasCost(evm.gasTable, evm.env, contract, stack, mem, memorySize) + cost, err = operation.gasCost(in.gasTable, in.evm, contract, stack, mem, memorySize) if err != nil || !contract.UseGas(cost) { return nil, ErrOutOfGas } @@ -173,19 +187,20 @@ func (evm *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err e mem.Resize(memorySize) } - if evm.cfg.Debug { - evm.cfg.Tracer.CaptureState(evm.env, pc, op, contract.Gas, cost, mem, stack, contract, evm.env.depth, err) + if in.cfg.Debug { + in.cfg.Tracer.CaptureState(in.evm, pc, op, contract.Gas, cost, mem, stack, contract, in.evm.depth, err) } // XXX For debugging //fmt.Printf("%04d: %8v cost = %-8d stack = %-8d\n", pc, op, cost, stack.len()) // execute the operation - res, err := operation.execute(&pc, evm.env, contract, mem, stack) + res, err := operation.execute(&pc, in.evm, contract, mem, stack) // verifyPool is a build flag. Pool verification makes sure the integrity // of the integer pool by comparing values to a default value. if verifyPool { - verifyIntegerPool(evm.intPool) + verifyIntegerPool(in.intPool) } + switch { case err != nil: return nil, err @@ -194,6 +209,11 @@ func (evm *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err e case !operation.jumps: pc++ } + // if the operation returned a value make sure that is also set + // the last return data. + if res != nil { + mem.lastReturn = ret + } } return nil, nil } diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index ed30100ac..c4a1430b2 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -47,13 +47,32 @@ type operation struct { // jumps indicates whether operation made a jump. This prevents the program // counter from further incrementing. jumps bool + // writes determines whether this a state modifying operation + writes bool // valid is used to check whether the retrieved operation is valid and known valid bool + // reverts determined whether the operation reverts state + reverts bool } -var defaultJumpTable = NewJumpTable() +var ( + baseInstructionSet = NewBaseInstructionSet() + homesteadInstructionSet = NewHomesteadInstructionSet() +) + +func NewHomesteadInstructionSet() [256]operation { + instructionSet := NewBaseInstructionSet() + instructionSet[DELEGATECALL] = operation{ + execute: opDelegateCall, + gasCost: gasDelegateCall, + validateStack: makeStackFunc(6, 1), + memorySize: memoryDelegateCall, + valid: true, + } + return instructionSet +} -func NewJumpTable() [256]operation { +func NewBaseInstructionSet() [256]operation { return [256]operation{ STOP: { execute: opStop, @@ -357,6 +376,7 @@ func NewJumpTable() [256]operation { gasCost: gasSStore, validateStack: makeStackFunc(2, 0), valid: true, + writes: true, }, JUMP: { execute: opJump, @@ -821,6 +841,7 @@ func NewJumpTable() [256]operation { validateStack: makeStackFunc(3, 1), memorySize: memoryCreate, valid: true, + writes: true, }, CALL: { execute: opCall, @@ -844,19 +865,13 @@ func NewJumpTable() [256]operation { halts: true, valid: true, }, - DELEGATECALL: { - execute: opDelegateCall, - gasCost: gasDelegateCall, - validateStack: makeStackFunc(6, 1), - memorySize: memoryDelegateCall, - valid: true, - }, SELFDESTRUCT: { execute: opSuicide, gasCost: gasSuicide, validateStack: makeStackFunc(1, 0), halts: true, valid: true, + writes: true, }, } } diff --git a/core/vm/memory.go b/core/vm/memory.go index 99a84d227..6dbee94ef 100644 --- a/core/vm/memory.go +++ b/core/vm/memory.go @@ -22,6 +22,7 @@ import "fmt" type Memory struct { store []byte lastGasCost uint64 + lastReturn []byte } func NewMemory() *Memory { -- cgit v1.2.3