mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
Support for both wasm precompile types
This commit is contained in:
parent
4f2eb8bf6f
commit
a8d615cd5c
1 changed files with 123 additions and 36 deletions
|
|
@ -17,11 +17,14 @@
|
|||
package vm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
|
|
@ -48,11 +51,68 @@ type PrecompiledContract interface {
|
|||
type wasmPrecompile struct {
|
||||
vm *life_exec.VirtualMachine
|
||||
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 {
|
||||
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 0
|
||||
}
|
||||
|
|
@ -63,7 +123,11 @@ func (wpr *wasmPrecompileResolver) ResolveGlobal(module, field string) int64 {
|
|||
}
|
||||
|
||||
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 {
|
||||
panic(fmt.Sprintf("error loading a precompile: %v", err))
|
||||
}
|
||||
|
|
@ -72,13 +136,10 @@ func newWasmPrecompile(code []byte, orig PrecompiledContract, num int) *wasmPrec
|
|||
if !ok {
|
||||
panic("Wasm doesn't have a main function")
|
||||
}
|
||||
wp.entrypoint = entrypoint
|
||||
wp.vm = vm
|
||||
|
||||
return &wasmPrecompile{
|
||||
entrypoint: entrypoint,
|
||||
vm: vm,
|
||||
original: orig,
|
||||
num: num,
|
||||
}
|
||||
return wp
|
||||
}
|
||||
|
||||
func (wp *wasmPrecompile) RequiredGas(input []byte) uint64 {
|
||||
|
|
@ -86,43 +147,69 @@ func (wp *wasmPrecompile) RequiredGas(input []byte) uint64 {
|
|||
}
|
||||
|
||||
func (wp *wasmPrecompile) Run(input []byte) ([]byte, error) {
|
||||
// The expected format of the input is:
|
||||
// - account list (rlp)
|
||||
// - proof (rlp)
|
||||
// this uses rlp.Split to separate the two items
|
||||
_, addrList, serializedProof, err := rlp.Split(input)
|
||||
|
||||
retOrig, err := wp.original.Run(input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
valid, ok := wp.vm.GetGlobalExport("valid")
|
||||
if !ok || valid > len(wp.vm.Globals) || valid < 0 {
|
||||
return nil, fmt.Errorf("Could not find `valid` in Wasm blob")
|
||||
}
|
||||
|
||||
addrlist, ok := wp.vm.GetGlobalExport("address_list")
|
||||
if !ok || addrlist > len(wp.vm.Globals) || addrlist < 0 {
|
||||
return nil, fmt.Errorf("Could not find `address_list` in Wasm blob")
|
||||
}
|
||||
spAL := binary.LittleEndian.Uint32(wp.vm.Memory[wp.vm.Globals[addrlist]:])
|
||||
// Split removes the first byte, which is required by the contract. Get it
|
||||
// from the input.
|
||||
copy(wp.vm.Memory[spAL:], input[:len(addrList)])
|
||||
// Set the maximum gas so that the contract won't die when calling useGas
|
||||
wp.totalGas = math.MaxUint64
|
||||
|
||||
proof, ok := wp.vm.GetGlobalExport("serialized_proof")
|
||||
if !ok || proof > len(wp.vm.Globals) || proof < 0 {
|
||||
return nil, fmt.Errorf("Could not find `serialized_proof` in Wasm blob")
|
||||
}
|
||||
spPtr := binary.LittleEndian.Uint32(wp.vm.Memory[wp.vm.Globals[proof]:])
|
||||
copy(wp.vm.Memory[spPtr:], serializedProof)
|
||||
var ret int64
|
||||
if wp.num >= 10 {
|
||||
// The expected format of the input is:
|
||||
// - account list (rlp)
|
||||
// - proof (rlp)
|
||||
// this uses rlp.Split to separate the two items
|
||||
_, addrList, serializedProof, err := rlp.Split(input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret, err := wp.vm.Run(wp.entrypoint)
|
||||
if err != nil || ret < 0 {
|
||||
wp.vm.PrintStackTrace()
|
||||
frame := wp.vm.GetCurrentFrame()
|
||||
valid, ok := wp.vm.GetGlobalExport("valid")
|
||||
if !ok || valid > len(wp.vm.Globals) || valid < 0 {
|
||||
return nil, fmt.Errorf("Could not find `valid` in Wasm blob")
|
||||
}
|
||||
|
||||
addrlist, ok := wp.vm.GetGlobalExport("address_list")
|
||||
if !ok || addrlist > len(wp.vm.Globals) || addrlist < 0 {
|
||||
return nil, fmt.Errorf("Could not find `address_list` in Wasm blob")
|
||||
}
|
||||
spAL := binary.LittleEndian.Uint32(wp.vm.Memory[wp.vm.Globals[addrlist]:])
|
||||
// Split removes the first byte, which is required by the contract. Get it
|
||||
// from the input.
|
||||
copy(wp.vm.Memory[spAL:], input[:len(addrList)])
|
||||
|
||||
proof, ok := wp.vm.GetGlobalExport("serialized_proof")
|
||||
if !ok || proof > len(wp.vm.Globals) || proof < 0 {
|
||||
return nil, fmt.Errorf("Could not find `serialized_proof` in Wasm blob")
|
||||
}
|
||||
spPtr := binary.LittleEndian.Uint32(wp.vm.Memory[wp.vm.Globals[proof]:])
|
||||
copy(wp.vm.Memory[spPtr:], serializedProof)
|
||||
startNew := time.Now().Unix()
|
||||
ret, err = wp.vm.Run(wp.entrypoint)
|
||||
endNew := time.Now().Unix()
|
||||
if err != nil || ret < 0 {
|
||||
wp.vm.PrintStackTrace()
|
||||
frame := wp.vm.GetCurrentFrame()
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
validPtr := binary.LittleEndian.Uint32(wp.vm.Memory[wp.vm.Globals[valid]:])
|
||||
|
||||
if !bytes.Equal(wp.retData, retOrig) {
|
||||
panic(fmt.Sprintf("difference in output: %v != %v", wp.retData, retOrig))
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue