aboutsummaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorFelix Lange <fjl@users.noreply.github.com>2017-05-25 04:28:22 +0800
committerGitHub <noreply@github.com>2017-05-25 04:28:22 +0800
commit261b3e235160d30cc7176e02fd0a43f2b60409c6 (patch)
tree9d3eb6eec9fc2d30badba7bc6824560bcb317132 /core
parent344f25fb3ec26818c673a5b68b21b527759d7499 (diff)
parent11cf5b7eadb7fcfa56a0cb98ec4ebbddce00f4c0 (diff)
downloadgo-tangerine-261b3e235160d30cc7176e02fd0a43f2b60409c6.tar
go-tangerine-261b3e235160d30cc7176e02fd0a43f2b60409c6.tar.gz
go-tangerine-261b3e235160d30cc7176e02fd0a43f2b60409c6.tar.bz2
go-tangerine-261b3e235160d30cc7176e02fd0a43f2b60409c6.tar.lz
go-tangerine-261b3e235160d30cc7176e02fd0a43f2b60409c6.tar.xz
go-tangerine-261b3e235160d30cc7176e02fd0a43f2b60409c6.tar.zst
go-tangerine-261b3e235160d30cc7176e02fd0a43f2b60409c6.zip
Merge pull request #14336 from obscuren/metropolis-preparation
consensus, core/*, params: metropolis preparation refactor
Diffstat (limited to 'core')
-rw-r--r--core/chain_makers.go19
-rw-r--r--core/state/journal.go9
-rw-r--r--core/state/statedb.go93
-rw-r--r--core/state_processor.go5
-rw-r--r--core/types/transaction_signing.go7
-rw-r--r--core/vm/contracts.go49
-rw-r--r--core/vm/contracts_test.go1
-rw-r--r--core/vm/evm.go49
-rw-r--r--core/vm/instructions.go38
-rw-r--r--core/vm/interpreter.go82
-rw-r--r--core/vm/intpool.go6
-rw-r--r--core/vm/jump_table.go101
-rw-r--r--core/vm/memory.go1
-rw-r--r--core/vm/stack.go4
14 files changed, 290 insertions, 174 deletions
diff --git a/core/chain_makers.go b/core/chain_makers.go
index f34279ba0..c81239607 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,20 @@ 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
}
+
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/journal.go b/core/state/journal.go
index 73218dd28..b5c8ca9a2 100644
--- a/core/state/journal.go
+++ b/core/state/journal.go
@@ -71,8 +71,8 @@ type (
hash common.Hash
}
touchChange struct {
- account *common.Address
- prev bool
+ account *common.Address
+ prev bool
prevDirty bool
}
)
@@ -91,6 +91,11 @@ func (ch suicideChange) undo(s *StateDB) {
if obj != nil {
obj.suicided = ch.prev
obj.setBalance(ch.prevbalance)
+ // if the object wasn't suicided before, remove
+ // it from the list of destructed objects as well.
+ if !obj.suicided {
+ delete(s.stateObjectsDestructed, *ch.account)
+ }
}
}
diff --git a/core/state/statedb.go b/core/state/statedb.go
index 3b753a2e6..05869a0c8 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})
@@ -380,6 +378,8 @@ func (self *StateDB) Suicide(addr common.Address) bool {
})
stateObject.markSuicided()
stateObject.data.Balance = new(big.Int)
+ self.stateObjectsDestructed[addr] = struct{}{}
+
return true
}
@@ -510,21 +510,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 +594,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..b0f3275b2 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("ffffffffffffffffffffffffffffffffffffffff")
+)
// sigCache is used to cache the derived sender and contains
// the signer used to derive it.
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..42f1781d8 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
}
@@ -709,10 +706,23 @@ func makeLog(size int) executionFunc {
}
// make push instruction function
-func makePush(size uint64, bsize *big.Int) executionFunc {
+func makePush(size uint64, pushByteSize int) executionFunc {
return func(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- byts := getData(contract.Code, evm.interpreter.intPool.get().SetUint64(*pc+1), bsize)
- stack.push(new(big.Int).SetBytes(byts))
+ codeLen := len(contract.Code)
+
+ startMin := codeLen
+ if int(*pc+1) < startMin {
+ startMin = int(*pc + 1)
+ }
+
+ endMin := codeLen
+ if startMin+pushByteSize < endMin {
+ endMin = startMin + pushByteSize
+ }
+
+ integer := evm.interpreter.intPool.get()
+ stack.push(integer.SetBytes(common.RightPadBytes(contract.Code[startMin:endMin], pushByteSize)))
+
*pc += size
return nil, nil
}
@@ -721,7 +731,7 @@ func makePush(size uint64, bsize *big.Int) executionFunc {
// make push instruction function
func makeDup(size int64) executionFunc {
return func(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.dup(int(size))
+ stack.dup(evm.interpreter.intPool, int(size))
return nil, nil
}
}
diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go
index 8ee9d3ca7..17edc9e33 100644
--- a/core/vm/interpreter.go
+++ b/core/vm/interpreter.go
@@ -52,43 +52,53 @@ type Config struct {
}
// 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 = frontierInstructionSet
+ }
}
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,31 +124,34 @@ 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("interpreter running contract", "hash", codehash[:])
tstart := time.Now()
- defer log.Debug("EVM finished running contract", "hash", codehash[:], "elapsed", time.Since(tstart))
+ defer log.Debug("interpreter 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 parent context.Context.
- for atomic.LoadInt32(&evm.env.abort) == 0 {
+ // the execution of one of the operations or until the done flag is set by the
+ // parent context.
+ 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 {
- return nil, fmt.Errorf("invalid opcode %x", op)
+ return nil, fmt.Errorf("invalid opcode 0x%x", int(op))
}
// validate the stack and make sure there enough stack items available
@@ -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/intpool.go b/core/vm/intpool.go
index 4f1228e14..384f5df59 100644
--- a/core/vm/intpool.go
+++ b/core/vm/intpool.go
@@ -20,6 +20,8 @@ import "math/big"
var checkVal = big.NewInt(-42)
+const poolLimit = 256
+
// intPool is a pool of big integers that
// can be reused for all big.Int operations.
type intPool struct {
@@ -37,6 +39,10 @@ func (p *intPool) get() *big.Int {
return new(big.Int)
}
func (p *intPool) put(is ...*big.Int) {
+ if len(p.pool.data) > poolLimit {
+ return
+ }
+
for _, i := range is {
// verifyPool is a build flag. Pool verification makes sure the integrity
// of the integer pool by comparing values to a default value.
diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go
index ed30100ac..0034eacb7 100644
--- a/core/vm/jump_table.go
+++ b/core/vm/jump_table.go
@@ -47,13 +47,36 @@ 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 (
+ frontierInstructionSet = NewFrontierInstructionSet()
+ homesteadInstructionSet = NewHomesteadInstructionSet()
+)
+
+// NewHomesteadInstructionSet returns the frontier and homestead
+// instructions that can be executed during the homestead phase.
+func NewHomesteadInstructionSet() [256]operation {
+ instructionSet := NewFrontierInstructionSet()
+ instructionSet[DELEGATECALL] = operation{
+ execute: opDelegateCall,
+ gasCost: gasDelegateCall,
+ validateStack: makeStackFunc(6, 1),
+ memorySize: memoryDelegateCall,
+ valid: true,
+ }
+ return instructionSet
+}
-func NewJumpTable() [256]operation {
+// NewFrontierInstructionSet returns the frontier instructions
+// that can be executed during the frontier phase.
+func NewFrontierInstructionSet() [256]operation {
return [256]operation{
STOP: {
execute: opStop,
@@ -357,6 +380,7 @@ func NewJumpTable() [256]operation {
gasCost: gasSStore,
validateStack: makeStackFunc(2, 0),
valid: true,
+ writes: true,
},
JUMP: {
execute: opJump,
@@ -397,193 +421,193 @@ func NewJumpTable() [256]operation {
valid: true,
},
PUSH1: {
- execute: makePush(1, big.NewInt(1)),
+ execute: makePush(1, 1),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH2: {
- execute: makePush(2, big.NewInt(2)),
+ execute: makePush(2, 2),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH3: {
- execute: makePush(3, big.NewInt(3)),
+ execute: makePush(3, 3),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH4: {
- execute: makePush(4, big.NewInt(4)),
+ execute: makePush(4, 4),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH5: {
- execute: makePush(5, big.NewInt(5)),
+ execute: makePush(5, 5),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH6: {
- execute: makePush(6, big.NewInt(6)),
+ execute: makePush(6, 6),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH7: {
- execute: makePush(7, big.NewInt(7)),
+ execute: makePush(7, 7),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH8: {
- execute: makePush(8, big.NewInt(8)),
+ execute: makePush(8, 8),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH9: {
- execute: makePush(9, big.NewInt(9)),
+ execute: makePush(9, 9),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH10: {
- execute: makePush(10, big.NewInt(10)),
+ execute: makePush(10, 10),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH11: {
- execute: makePush(11, big.NewInt(11)),
+ execute: makePush(11, 11),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH12: {
- execute: makePush(12, big.NewInt(12)),
+ execute: makePush(12, 12),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH13: {
- execute: makePush(13, big.NewInt(13)),
+ execute: makePush(13, 13),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH14: {
- execute: makePush(14, big.NewInt(14)),
+ execute: makePush(14, 14),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH15: {
- execute: makePush(15, big.NewInt(15)),
+ execute: makePush(15, 15),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH16: {
- execute: makePush(16, big.NewInt(16)),
+ execute: makePush(16, 16),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH17: {
- execute: makePush(17, big.NewInt(17)),
+ execute: makePush(17, 17),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH18: {
- execute: makePush(18, big.NewInt(18)),
+ execute: makePush(18, 18),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH19: {
- execute: makePush(19, big.NewInt(19)),
+ execute: makePush(19, 19),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH20: {
- execute: makePush(20, big.NewInt(20)),
+ execute: makePush(20, 20),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH21: {
- execute: makePush(21, big.NewInt(21)),
+ execute: makePush(21, 21),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH22: {
- execute: makePush(22, big.NewInt(22)),
+ execute: makePush(22, 22),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH23: {
- execute: makePush(23, big.NewInt(23)),
+ execute: makePush(23, 23),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH24: {
- execute: makePush(24, big.NewInt(24)),
+ execute: makePush(24, 24),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH25: {
- execute: makePush(25, big.NewInt(25)),
+ execute: makePush(25, 25),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH26: {
- execute: makePush(26, big.NewInt(26)),
+ execute: makePush(26, 26),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH27: {
- execute: makePush(27, big.NewInt(27)),
+ execute: makePush(27, 27),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH28: {
- execute: makePush(28, big.NewInt(28)),
+ execute: makePush(28, 28),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH29: {
- execute: makePush(29, big.NewInt(29)),
+ execute: makePush(29, 29),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH30: {
- execute: makePush(30, big.NewInt(30)),
+ execute: makePush(30, 30),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH31: {
- execute: makePush(31, big.NewInt(31)),
+ execute: makePush(31, 31),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
},
PUSH32: {
- execute: makePush(32, big.NewInt(32)),
+ execute: makePush(32, 32),
gasCost: gasPush,
validateStack: makeStackFunc(0, 1),
valid: true,
@@ -821,6 +845,7 @@ func NewJumpTable() [256]operation {
validateStack: makeStackFunc(3, 1),
memorySize: memoryCreate,
valid: true,
+ writes: true,
},
CALL: {
execute: opCall,
@@ -844,19 +869,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 {
diff --git a/core/vm/stack.go b/core/vm/stack.go
index 2d1b7bb82..f4777c5b3 100644
--- a/core/vm/stack.go
+++ b/core/vm/stack.go
@@ -60,8 +60,8 @@ func (st *Stack) swap(n int) {
st.data[st.len()-n], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-n]
}
-func (st *Stack) dup(n int) {
- st.push(new(big.Int).Set(st.data[st.len()-n]))
+func (st *Stack) dup(pool *intPool, n int) {
+ st.push(pool.get().Set(st.data[st.len()-n]))
}
func (st *Stack) peek() *big.Int {