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)
This commit is contained in:
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
//
|
||||
|
@ -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.
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
1
core/vm/contracts_test.go
Normal file
1
core/vm/contracts_test.go
Normal file
@ -0,0 +1 @@
|
||||
package vm
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 NewJumpTable() [256]operation {
|
||||
func NewHomesteadInstructionSet() [256]operation {
|
||||
instructionSet := NewBaseInstructionSet()
|
||||
instructionSet[DELEGATECALL] = operation{
|
||||
execute: opDelegateCall,
|
||||
gasCost: gasDelegateCall,
|
||||
validateStack: makeStackFunc(6, 1),
|
||||
memorySize: memoryDelegateCall,
|
||||
valid: true,
|
||||
}
|
||||
return instructionSet
|
||||
}
|
||||
|
||||
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,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ import "fmt"
|
||||
type Memory struct {
|
||||
store []byte
|
||||
lastGasCost uint64
|
||||
lastReturn []byte
|
||||
}
|
||||
|
||||
func NewMemory() *Memory {
|
||||
|
Reference in New Issue
Block a user