mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
ewasm precompiles pass rinkeby sync
This commit is contained in:
parent
b5c6111ad7
commit
a098295149
9 changed files with 209 additions and 61 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
19
core/vm/ewasm_precompile_ripemd160.go
Normal file
19
core/vm/ewasm_precompile_ripemd160.go
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue