Support for both wasm precompile types

This commit is contained in:
Guillaume Ballet 2020-02-05 11:48:49 +01:00
parent 4f2eb8bf6f
commit a8d615cd5c

View file

@ -17,11 +17,14 @@
package vm package vm
import ( import (
"bytes"
"crypto/sha256" "crypto/sha256"
"encoding/binary" "encoding/binary"
"errors" "errors"
"fmt" "fmt"
"math/big" "math/big"
"os"
"time"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/common/math"
@ -48,11 +51,68 @@ type PrecompiledContract interface {
type wasmPrecompile struct { type wasmPrecompile struct {
vm *life_exec.VirtualMachine vm *life_exec.VirtualMachine
entrypoint int entrypoint int
original PrecompiledContract
num int
totalGas uint64
input []byte
retData []byte
} }
type wasmPrecompileResolver struct{} type wasmPrecompileResolver struct {
wp *wasmPrecompile
}
func (wpr *wasmPrecompileResolver) ResolveFunc(module, field string) life_exec.FunctionImport { func (wpr *wasmPrecompileResolver) ResolveFunc(module, field string) life_exec.FunctionImport {
if module != "ethereum" {
panic(fmt.Sprintf("unknown module %s", module))
}
switch field {
case "useGas":
return func(vm *life_exec.VirtualMachine) int64 {
frame := vm.GetCurrentFrame()
gas := uint64(frame.Locals[0])
if gas > wpr.wp.totalGas {
vm.Exited = true
return 0
}
wpr.wp.totalGas -= gas
return 0
}
case "callDataCopy":
return func(vm *life_exec.VirtualMachine) int64 {
frame := vm.GetCurrentFrame()
offset := frame.Locals[0]
start := frame.Locals[1]
end := start + frame.Locals[2]
copy(vm.Memory[offset:], wpr.wp.input[start:end])
return 0
}
case "getCallDataSize":
return func(vm *life_exec.VirtualMachine) int64 {
frame := vm.GetCurrentFrame()
frame.Regs[frame.ReturnReg] = int64(len(wpr.wp.input))
return int64(len(wpr.wp.input))
}
case "finish", "revert":
return func(vm *life_exec.VirtualMachine) int64 {
frame := vm.GetCurrentFrame()
vm.Exited = true
if field == "finish" {
vm.ExitError = 0
} else {
vm.ExitError = 2
}
offset := frame.Locals[0]
length := frame.Locals[1]
wpr.wp.retData = make([]byte, int64(length))
copy(wpr.wp.retData, vm.Memory[offset:offset+length])
return 0
}
default:
panic(fmt.Sprintf("Unknown field %s", field))
}
return func(vm *life_exec.VirtualMachine) int64 { return func(vm *life_exec.VirtualMachine) int64 {
return 0 return 0
} }
@ -63,7 +123,11 @@ func (wpr *wasmPrecompileResolver) ResolveGlobal(module, field string) int64 {
} }
func newWasmPrecompile(code []byte, orig PrecompiledContract, num int) *wasmPrecompile { func newWasmPrecompile(code []byte, orig PrecompiledContract, num int) *wasmPrecompile {
vm, err := life_exec.NewVirtualMachine(code, life_exec.VMConfig{DisableFloatingPoint: true}, &wasmPrecompileResolver{}, nil) wp := &wasmPrecompile{
original: orig,
num: num,
}
vm, err := life_exec.NewVirtualMachine(code, life_exec.VMConfig{DisableFloatingPoint: true}, &wasmPrecompileResolver{wp: wp}, nil)
if err != nil { if err != nil {
panic(fmt.Sprintf("error loading a precompile: %v", err)) panic(fmt.Sprintf("error loading a precompile: %v", err))
} }
@ -72,13 +136,10 @@ func newWasmPrecompile(code []byte, orig PrecompiledContract, num int) *wasmPrec
if !ok { if !ok {
panic("Wasm doesn't have a main function") panic("Wasm doesn't have a main function")
} }
wp.entrypoint = entrypoint
wp.vm = vm
return &wasmPrecompile{ return wp
entrypoint: entrypoint,
vm: vm,
original: orig,
num: num,
}
} }
func (wp *wasmPrecompile) RequiredGas(input []byte) uint64 { func (wp *wasmPrecompile) RequiredGas(input []byte) uint64 {
@ -86,12 +147,23 @@ func (wp *wasmPrecompile) RequiredGas(input []byte) uint64 {
} }
func (wp *wasmPrecompile) Run(input []byte) ([]byte, error) { func (wp *wasmPrecompile) Run(input []byte) ([]byte, error) {
retOrig, err := wp.original.Run(input)
if err != nil {
return nil, err
}
}
}
// Set the maximum gas so that the contract won't die when calling useGas
wp.totalGas = math.MaxUint64
var ret int64
if wp.num >= 10 {
// The expected format of the input is: // The expected format of the input is:
// - account list (rlp) // - account list (rlp)
// - proof (rlp) // - proof (rlp)
// this uses rlp.Split to separate the two items // this uses rlp.Split to separate the two items
_, addrList, serializedProof, err := rlp.Split(input) _, addrList, serializedProof, err := rlp.Split(input)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -116,13 +188,28 @@ func (wp *wasmPrecompile) Run(input []byte) ([]byte, error) {
} }
spPtr := binary.LittleEndian.Uint32(wp.vm.Memory[wp.vm.Globals[proof]:]) spPtr := binary.LittleEndian.Uint32(wp.vm.Memory[wp.vm.Globals[proof]:])
copy(wp.vm.Memory[spPtr:], serializedProof) copy(wp.vm.Memory[spPtr:], serializedProof)
startNew := time.Now().Unix()
ret, err := wp.vm.Run(wp.entrypoint) ret, err = wp.vm.Run(wp.entrypoint)
endNew := time.Now().Unix()
if err != nil || ret < 0 { if err != nil || ret < 0 {
wp.vm.PrintStackTrace() wp.vm.PrintStackTrace()
frame := wp.vm.GetCurrentFrame() frame := wp.vm.GetCurrentFrame()
} }
validPtr := binary.LittleEndian.Uint32(wp.vm.Memory[wp.vm.Globals[valid]:]) validPtr := binary.LittleEndian.Uint32(wp.vm.Memory[wp.vm.Globals[valid]:])
} else {
wp.input = input
startNew := time.Now().Unix()
ret, err = wp.vm.Run(wp.entrypoint)
endNew := time.Now().Unix()
if err != nil && err.Error() != "0" {
panic(err)
}
}
if !bytes.Equal(wp.retData, retOrig) {
panic(fmt.Sprintf("difference in output: %v != %v", wp.retData, retOrig))
}
return nil, err return nil, err
} }