core/vm, tests: refactored precompiled contracts

This commit is contained in:
Jeffrey Wilcke 2016-12-03 11:15:54 +01:00
parent b4fff69f72
commit 0abf68b533
5 changed files with 66 additions and 66 deletions

View file

@ -26,64 +26,45 @@ import (
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
) )
// PrecompiledAccount represents a native ethereum contract // Precompiled contract is the basic interface for native Go contracts. The implementation
type PrecompiledAccount struct { // requires a deterministic gas count based on the input size of the Run method of the
Gas func(l int) *big.Int // contract.
fn func(in []byte) []byte type PrecompiledContract interface {
} RequiredGas(inputSize int) *big.Int // RequiredPrice calculates the contract gas use
Run(input []byte) []byte // Run runs the precompiled contract
// Call calls the native function
func (self PrecompiledAccount) Call(in []byte) []byte {
return self.fn(in)
} }
// Precompiled contains the default set of ethereum contracts // 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 // RunPrecompile runs and evaluate the output of a precompiled contract defined in contracts.go
// contracts defined by the ethereum yellow paper. func RunPrecompiledContract(p PrecompiledContract, input []byte, contract *Contract) (ret []byte, err error) {
func PrecompiledContracts() map[string]*PrecompiledAccount { gas := p.RequiredGas(len(input))
return map[string]*PrecompiledAccount{ if contract.UseGas(gas) {
// ECRECOVER ret = p.Run(input)
string(common.LeftPadBytes([]byte{1}, 20)): &PrecompiledAccount{func(l int) *big.Int {
return params.EcrecoverGas
}, ecrecoverFunc},
// SHA256 return ret, nil
string(common.LeftPadBytes([]byte{2}, 20)): &PrecompiledAccount{func(l int) *big.Int { } else {
n := big.NewInt(int64(l+31) / 32) return nil, OutOfGasError
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},
} }
} }
func sha256Func(in []byte) []byte { // ECRECOVER implemented as a native contract
return crypto.Sha256(in) type ecrecover struct{}
func (c *ecrecover) RequiredGas(inputSize int) *big.Int {
return params.EcrecoverGas
} }
func ripemd160Func(in []byte) []byte { func (c *ecrecover) Run(in []byte) []byte {
return common.LeftPadBytes(crypto.Ripemd160(in), 32) const ecRecoverInputLength = 128
}
const ecRecoverInputLength = 128 in = common.RightPadBytes(in, ecRecoverInputLength)
func ecrecoverFunc(in []byte) []byte {
in = common.RightPadBytes(in, 128)
// "in" is (hash, v, r, s), each 32 bytes // "in" is (hash, v, r, s), each 32 bytes
// but for ecrecover we want (r, s, v) // but for ecrecover we want (r, s, v)
@ -114,6 +95,39 @@ func ecrecoverFunc(in []byte) []byte {
return common.LeftPadBytes(crypto.Keccak256(pubKey[1:])[12:], 32) 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 return in
} }

View file

@ -113,7 +113,7 @@ func (env *Environment) Call(caller ContractRef, addr common.Address, input []by
snapshotPreTransfer = env.StateDB.Snapshot() snapshotPreTransfer = env.StateDB.Snapshot()
) )
if !env.StateDB.Exist(addr) { if !env.StateDB.Exist(addr) {
if Precompiled[addr.Str()] == nil && env.ChainConfig().IsEIP158(env.BlockNumber) && value.BitLen() == 0 { if PrecompiledContracts[addr] == nil && env.ChainConfig().IsEIP158(env.BlockNumber) && value.BitLen() == 0 {
caller.ReturnGas(gas) caller.ReturnGas(gas)
return nil, nil return nil, nil
} }

View file

@ -90,8 +90,8 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) {
defer func() { evm.env.Depth-- }() defer func() { evm.env.Depth-- }()
if contract.CodeAddr != nil { if contract.CodeAddr != nil {
if p := Precompiled[contract.CodeAddr.Str()]; p != nil { if p := PrecompiledContracts[*contract.CodeAddr]; p != nil {
return evm.RunPrecompiled(p, input, contract) return RunPrecompiledContract(p, input, contract)
} }
} }
@ -192,15 +192,3 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) {
} }
return nil, nil return nil, nil
} }
// RunPrecompile runs and evaluate the output of a precompiled contract defined in contracts.go
func (evm *EVM) RunPrecompiled(p *PrecompiledAccount, input []byte, contract *Contract) (ret []byte, err error) {
gas := p.Gas(len(input))
if contract.UseGas(gas) {
ret = p.Call(input)
return ret, nil
} else {
return nil, OutOfGasError
}
}

View file

@ -205,8 +205,6 @@ func runStateTest(chainConfig *params.ChainConfig, test VmTest) error {
func RunState(chainConfig *params.ChainConfig, statedb *state.StateDB, env, tx map[string]string) ([]byte, vm.Logs, *big.Int, error) { func RunState(chainConfig *params.ChainConfig, statedb *state.StateDB, env, tx map[string]string) ([]byte, vm.Logs, *big.Int, error) {
environment, msg := NewEVMEnvironment(false, chainConfig, statedb, env, tx) environment, msg := NewEVMEnvironment(false, chainConfig, statedb, env, tx)
// Set pre compiled contracts
vm.Precompiled = vm.PrecompiledContracts()
gaspool := new(core.GasPool).AddGas(common.Big(env["currentGasLimit"])) gaspool := new(core.GasPool).AddGas(common.Big(env["currentGasLimit"]))
root, _ := statedb.Commit(false) root, _ := statedb.Commit(false)

View file

@ -225,7 +225,7 @@ func RunVm(statedb *state.StateDB, env, exec map[string]string) ([]byte, vm.Logs
value = common.Big(exec["value"]) value = common.Big(exec["value"])
) )
caller := statedb.GetOrNewStateObject(from) caller := statedb.GetOrNewStateObject(from)
vm.Precompiled = make(map[string]*vm.PrecompiledAccount) vm.PrecompiledContracts = make(map[common.Address]vm.PrecompiledContract)
environment, _ := NewEVMEnvironment(true, chainConfig, statedb, env, exec) environment, _ := NewEVMEnvironment(true, chainConfig, statedb, env, exec)
ret, err := environment.Call(caller, to, data, gas, value) ret, err := environment.Call(caller, to, data, gas, value)