core/vm, tests: refactored native contracts

Refactored native contract such that native contract can be easily
extended using a struct that implements the vm.NativeContract interface.
This commit is contained in:
Jeffrey Wilcke 2016-07-29 12:41:39 +02:00
parent 9182d80946
commit b5d6206bdc
4 changed files with 59 additions and 57 deletions

View file

@ -26,25 +26,27 @@ 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{},
}
// RunPrecompile runs and evaluate the output of a precompiled contract defined in contracts.go // RunPrecompile runs and evaluate the output of a precompiled contract defined in contracts.go
func RunPrecompiled(p *PrecompiledAccount, input []byte, contract *Contract) (ret []byte, err error) { func RunPrecompiled(p PrecompiledContract, input []byte, contract *Contract) (ret []byte, err error) {
gas := p.Gas(len(input)) gas := p.RequiredGas(len(input))
if contract.UseGas(gas.Uint64()) { if contract.UseGas(gas.Uint64()) {
ret = p.Call(input) ret = p.Run(input)
return ret, nil return ret, nil
} else { } else {
@ -52,50 +54,17 @@ func RunPrecompiled(p *PrecompiledAccount, input []byte, contract *Contract) (re
} }
} }
// PrecompiledContracts returns the default set of precompiled ethereum // ECRECOVER implemented as a native contract
// contracts defined by the ethereum yellow paper. type ecrecover struct{}
func PrecompiledContracts() map[string]*PrecompiledAccount {
return map[string]*PrecompiledAccount{ func (c *ecrecover) RequiredGas(inputSize int) *big.Int {
// ECRECOVER
string(common.LeftPadBytes([]byte{1}, 20)): &PrecompiledAccount{func(l int) *big.Int {
return params.EcrecoverGas return params.EcrecoverGas
}, ecrecoverFunc},
// 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},
}
} }
func sha256Func(in []byte) []byte { func (c *ecrecover) Run(in []byte) []byte {
return crypto.Sha256(in) const ecRecoverInputLength = 128
}
func ripemd160Func(in []byte) []byte { in = common.RightPadBytes(in, ecRecoverInputLength)
return common.LeftPadBytes(crypto.Ripemd160(in), 32)
}
const ecRecoverInputLength = 128
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)
@ -126,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

@ -58,7 +58,7 @@ func (evm *EVM) Run(contract *Contract, input []byte) ([]byte, error) {
defer evm.env.SetDepth(evm.env.Depth() - 1) defer evm.env.SetDepth(evm.env.Depth() - 1)
if contract.CodeAddr != nil { if contract.CodeAddr != nil {
if p := Precompiled[contract.CodeAddr.Str()]; p != nil { if p, exist := PrecompiledContracts[*contract.CodeAddr]; exist {
return RunPrecompiled(p, input, contract) return RunPrecompiled(p, input, contract)
} }
} }

View file

@ -231,7 +231,7 @@ func RunState(ruleSet RuleSet, statedb *state.StateDB, env, tx map[string]string
to = &t to = &t
} }
// Set pre compiled contracts // Set pre compiled contracts
vm.Precompiled = vm.PrecompiledContracts() //vm.Precompiled = vm.PrecompiledContracts()
snapshot := statedb.Copy() snapshot := statedb.Copy()
gaspool := new(core.GasPool).AddGas(common.Big(env["currentGasLimit"])) gaspool := new(core.GasPool).AddGas(common.Big(env["currentGasLimit"]))

View file

@ -236,7 +236,7 @@ func RunVm(state *state.StateDB, env, exec map[string]string) ([]byte, vm.Logs,
value = common.Big(exec["value"]) value = common.Big(exec["value"])
) )
// Reset the pre-compiled contracts for VM tests. // Reset the pre-compiled contracts for VM tests.
vm.Precompiled = make(map[string]*vm.PrecompiledAccount) vm.PrecompiledContracts = make(map[common.Address]vm.PrecompiledContract)
caller := state.GetOrNewStateObject(from) caller := state.GetOrNewStateObject(from)