ewasm precompiles pass rinkeby sync

This commit is contained in:
Guillaume Ballet 2019-02-07 22:07:12 +01:00
parent b5c6111ad7
commit a098295149
9 changed files with 209 additions and 61 deletions

View file

@ -39,7 +39,7 @@ import (
// contract.
type PrecompiledContract interface {
RequiredGas(input []byte) uint64 // RequiredPrice calculates the contract gas use
Run(input []byte) ([]byte, error) // Run runs the precompiled contract
Run(input []byte, contract *Contract) ([]byte, error) // Run runs the precompiled contract
}
// PrecompiledContractsHomestead contains the default set of pre-compiled Ethereum
@ -67,21 +67,21 @@ var PrecompiledContractsByzantium = map[common.Address]PrecompiledContract{
// PrecompiledContractsEWASM contains the default set of pre-compiled Ethereum
// contracts used for Ethereum 1.x release.
var PrecompiledContractsEWASM = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{1}): newEWASMPrecompile(ewasmEcRecoverCode),
common.BytesToAddress([]byte{2}): newEWASMPrecompile(ewasmSha256HashCode),
common.BytesToAddress([]byte{3}): newEWASMPrecompile(ewasmRipemd160hashCode),
common.BytesToAddress([]byte{4}): newEWASMPrecompile(ewasmIdentityCode),
common.BytesToAddress([]byte{5}): newEWASMPrecompile(ewasmExpModCode),
common.BytesToAddress([]byte{6}): newEWASMPrecompile(ewasmEcAddCode),
common.BytesToAddress([]byte{7}): newEWASMPrecompile(ewasmEcMulCode),
common.BytesToAddress([]byte{8}): newEWASMPrecompile(ewasmEcPairingCode),
common.BytesToAddress([]byte{1}): newEWASMPrecompile(ewasmEcrecoverCode, 1),
common.BytesToAddress([]byte{2}): newEWASMPrecompile(ewasmSha256HashCode, 2),
common.BytesToAddress([]byte{3}): newEWASMPrecompile(ewasmRipemd160hashCode, 3),
common.BytesToAddress([]byte{4}): newEWASMPrecompile(ewasmIdentityCode, 4),
common.BytesToAddress([]byte{5}): newEWASMPrecompile(ewasmExpmodCode, 5),
common.BytesToAddress([]byte{6}): newEWASMPrecompile(ewasmEcaddCode, 6),
common.BytesToAddress([]byte{7}): newEWASMPrecompile(ewasmEcmulCode, 7),
common.BytesToAddress([]byte{8}): newEWASMPrecompile(ewasmEcpairingCode, 8),
}
// RunPrecompiledContract runs and evaluates the output of a precompiled contract.
func RunPrecompiledContract(p PrecompiledContract, input []byte, contract *Contract) (ret []byte, err error) {
gas := p.RequiredGas(input)
if contract.UseGas(gas) {
return p.Run(input)
return p.Run(input, contract)
}
return nil, ErrOutOfGas
}
@ -91,6 +91,8 @@ type ewasmPrecompile struct {
vm *exec.VM
contract *Contract
retData []byte
idx uint32
input []byte
}
// This is a subset of the functions available in the full EEI at
@ -142,17 +144,19 @@ func moduleResolver(name string, precompile *ewasmPrecompile) (*wasm.Module, err
{
Sig: &m.Types.Entries[2],
Host: reflect.ValueOf(func(p *exec.Process, r, d, l int32) {
if l > 0 {
// Unlike regular EEI functions, the gas is not charged at this
// time but I'm leaving that code here for future reference.
// in.gasAccounting(GasCostVeryLow + GasCostCopy*(uint64(l+31)>>5))
p.WriteAt(precompile.contract.Input[d:d+l], int64(r))
p.WriteAt(precompile.input[d:d+l], int64(r))
}
}),
Body: &wasm.FunctionBody{},
},
{
Sig: &m.Types.Entries[3],
Host: reflect.ValueOf(func(p *exec.Process) int32 {
return int32(len(precompile.contract.Input))
return int32(len(precompile.input))
}),
Body: &wasm.FunctionBody{},
},
@ -202,10 +206,17 @@ func newEWASMPrecompile(code []byte) *ewasmPrecompile {
panic(fmt.Sprintf("Could not read precompile module: %v", err))
}
for name, export := range module.Export.Entries {
if name == "main" && export.Kind == wasm.ExternalFunction {
ret.idx = export.Index
}
}
vm, err := exec.NewVM(module)
if err != nil {
panic("Could not create precompile VM")
}
vm.RecoverPanic = true
ret.vm = vm
return ret
@ -215,7 +226,8 @@ func (c *ewasmPrecompile) RequiredGas(input []byte) uint64 {
return 0
}
func (c *ewasmPrecompile) Run(input []byte) ([]byte, error) {
func (c *ewasmPrecompile) Run(input []byte, contract *Contract) ([]byte, error) {
c.vm.Restart()
mem := c.vm.Memory()
/* Copy input into memory */
@ -223,8 +235,15 @@ func (c *ewasmPrecompile) Run(input []byte) ([]byte, error) {
return nil, fmt.Errorf("input size (%d) is greater than available memory (%d)", len(input), len(mem))
}
c.input = input
c.contract = contract
defer func() {
c.input = nil
c.contract = nil
}()
/* Run the contract */
_, err := c.vm.ExecCode(0)
_, err := c.vm.ExecCode(int64(c.idx))
if err == nil {
return c.retData, nil
}
@ -238,7 +257,7 @@ func (c *ecrecover) RequiredGas(input []byte) uint64 {
return params.EcrecoverGas
}
func (c *ecrecover) Run(input []byte) ([]byte, error) {
func (c *ecrecover) Run(input []byte, contract *Contract) ([]byte, error) {
const ecRecoverInputLength = 128
input = common.RightPadBytes(input, ecRecoverInputLength)
@ -253,6 +272,7 @@ func (c *ecrecover) Run(input []byte) ([]byte, error) {
if !allZero(input[32:63]) || !crypto.ValidateSignatureValues(v, r, s, false) {
return nil, nil
}
// v needs to be at the end for libsecp256k1
pubKey, err := crypto.Ecrecover(input[:32], append(input[64:128], v))
// make sure the public key is a valid one
@ -274,7 +294,7 @@ type sha256hash struct{}
func (c *sha256hash) RequiredGas(input []byte) uint64 {
return uint64(len(input)+31)/32*params.Sha256PerWordGas + params.Sha256BaseGas
}
func (c *sha256hash) Run(input []byte) ([]byte, error) {
func (c *sha256hash) Run(input []byte, contract *Contract) ([]byte, error) {
h := sha256.Sum256(input)
return h[:], nil
}
@ -289,7 +309,7 @@ type ripemd160hash struct{}
func (c *ripemd160hash) RequiredGas(input []byte) uint64 {
return uint64(len(input)+31)/32*params.Ripemd160PerWordGas + params.Ripemd160BaseGas
}
func (c *ripemd160hash) Run(input []byte) ([]byte, error) {
func (c *ripemd160hash) Run(input []byte, contract *Contract) ([]byte, error) {
ripemd := ripemd160.New()
ripemd.Write(input)
return common.LeftPadBytes(ripemd.Sum(nil), 32), nil
@ -305,7 +325,7 @@ type dataCopy struct{}
func (c *dataCopy) RequiredGas(input []byte) uint64 {
return uint64(len(input)+31)/32*params.IdentityPerWordGas + params.IdentityBaseGas
}
func (c *dataCopy) Run(in []byte) ([]byte, error) {
func (c *dataCopy) Run(in []byte, contract *Contract) ([]byte, error) {
return in, nil
}
@ -386,7 +406,7 @@ func (c *bigModExp) RequiredGas(input []byte) uint64 {
return gas.Uint64()
}
func (c *bigModExp) Run(input []byte) ([]byte, error) {
func (c *bigModExp) Run(input []byte, contract *Contract) ([]byte, error) {
var (
baseLen = new(big.Int).SetBytes(getData(input, 0, 32)).Uint64()
expLen = new(big.Int).SetBytes(getData(input, 32, 32)).Uint64()
@ -442,7 +462,7 @@ func (c *bn256Add) RequiredGas(input []byte) uint64 {
return params.Bn256AddGas
}
func (c *bn256Add) Run(input []byte) ([]byte, error) {
func (c *bn256Add) Run(input []byte, contract *Contract) ([]byte, error) {
x, err := newCurvePoint(getData(input, 0, 64))
if err != nil {
return nil, err
@ -464,7 +484,7 @@ func (c *bn256ScalarMul) RequiredGas(input []byte) uint64 {
return params.Bn256ScalarMulGas
}
func (c *bn256ScalarMul) Run(input []byte) ([]byte, error) {
func (c *bn256ScalarMul) Run(input []byte, contract *Contract) ([]byte, error) {
p, err := newCurvePoint(getData(input, 0, 64))
if err != nil {
return nil, err
@ -493,7 +513,7 @@ func (c *bn256Pairing) RequiredGas(input []byte) uint64 {
return params.Bn256PairingBaseGas + uint64(len(input)/192)*params.Bn256PairingPerPointGas
}
func (c *bn256Pairing) Run(input []byte) ([]byte, error) {
func (c *bn256Pairing) Run(input []byte, contract *Contract) ([]byte, error) {
// Handle some corner cases cheaply
if len(input)%192 > 0 {
return nil, errBadPairingInput

View file

@ -17,6 +17,7 @@
package vm
import (
"bytes"
"fmt"
"math/big"
"testing"
@ -350,11 +351,138 @@ func testPrecompiled(addr string, test precompiledTest, t *testing.T) {
})
}
var (
ecRecoverAddr = common.HexToAddress("01")
ripeMdAddr = common.HexToAddress("03")
expModAddr = common.HexToAddress("05")
)
type ewasmPrecompileTest struct {
name string
input []byte
err error
addr common.Address
expected []byte
}
var ewasmPrecompileTests = []ewasmPrecompileTest{
{
name: "EmptyEcRecover",
input: []byte{},
addr: ecRecoverAddr,
expected: []byte{},
},
{
name: "SimpleEcRecover",
input: common.Hex2Bytes("38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e000000000000000000000000000000000000000000000000000000000000001b38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e789d1dd423d25f0772d2748d60f7e4b81bb14d086eba8e8e8efb6dcff8a4ae02"),
addr: ecRecoverAddr,
expected: common.Hex2Bytes("000000000000000000000000ceaccac640adf55b2028469bd36ba501f28b699d"),
},
{
name: "RipeMdPadding",
input: []byte{178, 121, 24, 45, 153, 230, 87, 3, 240, 7, 110, 72, 18, 101, 58, 171, 133, 252, 160, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 136, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 89, 155, 67, 62},
addr: ecRecoverAddr,
expected: []byte{},
},
{
name: "EmptyModExp",
input: []byte{},
addr: ecRecoverAddr,
expected: []byte{},
},
{
name: "SimpleModExp",
input: []byte{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 105, 244, 148, 17, 230, 162, 38, 226, 21, 123, 99, 173, 92, 154, 225, 98, 221, 178, 13, 54, 142, 147, 227, 219, 150, 131, 6, 199, 20, 249, 189, 163, 10, 56, 142, 74, 160, 10, 23, 22, 133, 219, 196, 237, 209, 230, 181, 148, 170, 136, 152, 109, 145, 62, 113, 35, 177, 70, 90, 254, 242, 241, 37, 193, 137, 34, 80, 63, 201, 219, 98, 68, 242, 229, 107, 41, 194, 61, 241, 15, 186, 12, 61, 121, 166, 99, 226, 51, 245, 19, 98, 0, 231, 66, 220, 25, 113, 116, 46, 94, 215, 214, 68, 212, 24, 96, 201, 47, 67, 161, 233, 119, 20, 115, 28, 119, 60, 46, 206, 8, 59, 243, 245, 178, 177, 123, 131, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 132, 63, 73, 203, 78, 27, 50, 102, 66, 138, 185, 34, 220, 161, 134, 52, 173, 16, 83, 166, 181, 82, 151, 245, 166, 158, 136, 110, 61, 191, 138, 213, 72, 170, 180, 26, 170, 159, 62, 89, 236, 155, 217, 242, 130, 160, 23, 193, 205, 210, 69, 52, 141, 166, 173, 203, 220, 93, 101, 86, 158, 107, 181, 184, 207, 171, 57, 103, 67, 243, 102, 12, 159, 44, 122, 105, 76, 152, 164, 7, 96, 175, 192, 144, 139, 76, 117, 90, 26, 188, 138, 149, 200, 175, 108, 201, 17, 211, 247, 177, 92, 214, 65, 243, 35, 10, 106, 79, 218, 176, 18, 106, 2, 71, 3, 174, 218, 178, 16, 244, 244, 89, 165, 195, 179, 247, 120, 47,
},
addr: expModAddr,
expected: []byte{0, 1, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 48, 49, 48, 13, 6, 9, 96, 134, 72, 1, 101, 3, 4, 2, 1, 5, 0, 4, 32, 152, 203, 93, 245, 45, 169, 18, 4, 171, 193, 131, 35, 108, 212, 190, 245, 181, 170, 134, 108, 120, 170, 237, 179, 241, 226, 128, 212, 149, 52, 51, 151},
},
{
name: "IncompleteInputModExp",
input: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
addr: expModAddr,
expected: []byte{},
},
{
name: "PaddingExpMod",
input: []byte{32},
addr: expModAddr,
expected: []byte{},
},
}
func testEwasmPrecompiled(test ewasmPrecompileTest, t *testing.T) {
ref := PrecompiledContractsByzantium[test.addr]
p := PrecompiledContractsEWASM[test.addr]
input := test.input
contract := NewContract(AccountRef(common.HexToAddress("1337")),
nil, new(big.Int), ref.RequiredGas(input))
t.Run(fmt.Sprintf("%s-Gas=%d", test.name, contract.Gas), func(t *testing.T) {
res, err := p.Run(input, contract)
refRes, refErr := ref.Run(input, contract)
if err != refErr {
t.Fatalf("Expected error %v, got %v", test.err, err)
} else {
if bytes.Compare(refRes, res) != 0 {
t.Fatalf("Expected result to be %v, got %v", refRes, res)
}
}
})
}
func TestEwasmPrecompiles(t *testing.T) {
for _, test := range ewasmPrecompileTests {
testEwasmPrecompiled(test, t)
}
}
func TestExpModEwasmPadding(t *testing.T) {
ecRecoverAddr := common.HexToAddress("05")
p := PrecompiledContractsEWASM[ecRecoverAddr]
input := []byte{32}
reqGas := PrecompiledContractsByzantium[ecRecoverAddr].RequiredGas(input)
contract := NewContract(AccountRef(common.HexToAddress("1337")),
nil, new(big.Int), reqGas)
fmt.Println(contract.Gas)
res, err := RunPrecompiledContract(p, input, contract)
if err != nil {
t.Errorf("%v", err)
}
expected, _ := PrecompiledContractsByzantium[ecRecoverAddr].Run(input, contract)
fmt.Println(expected, res)
if bytes.Compare(res, expected) != 0 {
t.Errorf("Expected %v, got %v", expected, res)
}
if bytes.Compare(res, []byte{}) == 0 {
t.Error("Result should not be empty")
}
}
func TestOutOfBoundsEwasm(t *testing.T) {
for i := 0; i < 4000; i++ {
p := PrecompiledContractsEWASM[common.HexToAddress("02")]
input := []byte{1, 4, 8, 156, 149, 204, 68, 56, 9, 182, 75, 13, 33, 153, 31, 97, 58, 45, 60, 242, 214, 14, 44, 150, 23, 112, 129, 74, 183, 21, 241, 39, 61, 172, 144, 133, 93, 5, 217, 174, 157, 25, 62, 127, 95, 122, 213, 55, 21, 235, 57, 238, 102, 222, 98, 244, 67, 189, 66, 161, 183, 59, 94, 234, 248, 227, 253, 148, 250, 113, 188, 11, 161, 13, 57, 212, 100, 208, 216, 244, 101, 239, 238, 240, 162, 118, 78, 56, 135, 252, 201, 223, 65, 222, 210, 15, 80, 92}
reqGas := PrecompiledContractsByzantium[common.HexToAddress("02")].RequiredGas(input)
contract := NewContract(AccountRef(common.HexToAddress("1337")),
nil, new(big.Int), reqGas)
fmt.Println("req gas:", reqGas)
res, err := RunPrecompiledContract(p, input, contract)
if err != nil {
t.Errorf("%v", err)
}
expected, _ := PrecompiledContractsByzantium[common.HexToAddress("02")].Run(input, contract)
if bytes.Compare(res, expected) != 0 {
t.Errorf("Expected %v, got %v", expected, res)
}
}
}
func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) {
if test.noBenchmark {
return
}
p := PrecompiledContractsByzantium[common.HexToAddress(addr)]
p := PrecompiledContractsEWASM[common.HexToAddress(addr)]
in := common.Hex2Bytes(test.input)
reqGas := p.RequiredGas(in)
contract := NewContract(AccountRef(common.HexToAddress("1337")),

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long