core/vm: improved EVM run loop & instruction calling (#3378)

The run loop, which previously contained custom opcode executes have been
removed and has been simplified to a few checks.

Each operation consists of 4 elements: execution function, gas cost function,
stack validation function and memory size function. The execution function
implements the operation's runtime behaviour, the gas cost function implements
the operation gas costs function and greatly depends on the memory and stack,
the stack validation function validates the stack and makes sure that enough
items can be popped off and pushed on and the memory size function calculates
the memory required for the operation and returns it.

This commit also allows the EVM to go unmetered. This is helpful for offline
operations such as contract calls.
This commit is contained in:
Jeffrey Wilcke
2017-01-05 11:52:10 +01:00
committed by Felix Lange
parent 2126d81488
commit bbc4ea4ae8
44 changed files with 1659 additions and 2002 deletions

View File

@ -26,64 +26,45 @@ import (
"github.com/ethereum/go-ethereum/params"
)
// PrecompiledAccount represents a native ethereum contract
type PrecompiledAccount struct {
Gas func(l int) *big.Int
fn func(in []byte) []byte
}
// Call calls the native function
func (self PrecompiledAccount) Call(in []byte) []byte {
return self.fn(in)
// 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) *big.Int // RequiredPrice calculates the contract gas use
Run(input []byte) []byte // Run runs the precompiled contract
}
// Precompiled contains the default set of ethereum contracts
var Precompiled = PrecompiledContracts()
var PrecompiledContracts = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{1}): &ecrecover{},
common.BytesToAddress([]byte{2}): &sha256{},
common.BytesToAddress([]byte{3}): &ripemd160{},
common.BytesToAddress([]byte{4}): &dataCopy{},
}
// PrecompiledContracts returns the default set of precompiled ethereum
// contracts defined by the ethereum yellow paper.
func PrecompiledContracts() map[string]*PrecompiledAccount {
return map[string]*PrecompiledAccount{
// ECRECOVER
string(common.LeftPadBytes([]byte{1}, 20)): &PrecompiledAccount{func(l int) *big.Int {
return params.EcrecoverGas
}, ecrecoverFunc},
// 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))
if contract.UseGas(gas) {
ret = p.Run(input)
// SHA256
string(common.LeftPadBytes([]byte{2}, 20)): &PrecompiledAccount{func(l int) *big.Int {
n := big.NewInt(int64(l+31) / 32)
n.Mul(n, params.Sha256WordGas)
return n.Add(n, params.Sha256Gas)
}, sha256Func},
// RIPEMD160
string(common.LeftPadBytes([]byte{3}, 20)): &PrecompiledAccount{func(l int) *big.Int {
n := big.NewInt(int64(l+31) / 32)
n.Mul(n, params.Ripemd160WordGas)
return n.Add(n, params.Ripemd160Gas)
}, ripemd160Func},
string(common.LeftPadBytes([]byte{4}, 20)): &PrecompiledAccount{func(l int) *big.Int {
n := big.NewInt(int64(l+31) / 32)
n.Mul(n, params.IdentityWordGas)
return n.Add(n, params.IdentityGas)
}, memCpy},
return ret, nil
} else {
return nil, ErrOutOfGas
}
}
func sha256Func(in []byte) []byte {
return crypto.Sha256(in)
// ECRECOVER implemented as a native contract
type ecrecover struct{}
func (c *ecrecover) RequiredGas(inputSize int) *big.Int {
return params.EcrecoverGas
}
func ripemd160Func(in []byte) []byte {
return common.LeftPadBytes(crypto.Ripemd160(in), 32)
}
func (c *ecrecover) Run(in []byte) []byte {
const ecRecoverInputLength = 128
const ecRecoverInputLength = 128
func ecrecoverFunc(in []byte) []byte {
in = common.RightPadBytes(in, 128)
in = common.RightPadBytes(in, ecRecoverInputLength)
// "in" is (hash, v, r, s), each 32 bytes
// but for ecrecover we want (r, s, v)
@ -108,6 +89,39 @@ func ecrecoverFunc(in []byte) []byte {
return common.LeftPadBytes(crypto.Keccak256(pubKey[1:])[12:], 32)
}
func memCpy(in []byte) []byte {
// SHA256 implemented as a native contract
type sha256 struct{}
func (c *sha256) RequiredGas(inputSize int) *big.Int {
n := big.NewInt(int64(inputSize+31) / 32)
n.Mul(n, params.Sha256WordGas)
return n.Add(n, params.Sha256Gas)
}
func (c *sha256) Run(in []byte) []byte {
return crypto.Sha256(in)
}
// RIPMED160 implemented as a native contract
type ripemd160 struct{}
func (c *ripemd160) RequiredGas(inputSize int) *big.Int {
n := big.NewInt(int64(inputSize+31) / 32)
n.Mul(n, params.Ripemd160WordGas)
return n.Add(n, params.Ripemd160Gas)
}
func (c *ripemd160) Run(in []byte) []byte {
return common.LeftPadBytes(crypto.Ripemd160(in), 32)
}
// data copy implemented as a native contract
type dataCopy struct{}
func (c *dataCopy) RequiredGas(inputSize int) *big.Int {
n := big.NewInt(int64(inputSize+31) / 32)
n.Mul(n, params.IdentityWordGas)
return n.Add(n, params.IdentityGas)
}
func (c *dataCopy) Run(in []byte) []byte {
return in
}