diff --git a/Makefile b/Makefile index edb9bbead..4a6180cdf 100644 --- a/Makefile +++ b/Makefile @@ -50,6 +50,12 @@ suavedevtools: ./suave/scripts/contracts.sh build go run ./suave/gen/main.go -write +wasm: + GOOS=wasip1 GOARCH=wasm go build -o core/vm/suave_wasm/store_retrieve.wasm core/vm/suave_wasm/store_retrieve/main.go + GOOS=wasip1 GOARCH=wasm go build -o core/vm/suave_wasm/store_put.wasm core/vm/suave_wasm/store_put/main.go + GOOS=wasip1 GOARCH=wasm go build -o core/vm/suave_wasm/extract_hint.wasm core/vm/suave_wasm/extract_hint/main.go + GOOS=wasip1 GOARCH=wasm go build -o core/vm/suave_wasm/suavexec.wasm core/vm/suave_wasm/suavexec/main.go + devnet-up: docker-compose -f ./suave/devenv/docker-compose.yml up -d --build diff --git a/core/types/suave.go b/core/types/suave.go new file mode 100644 index 000000000..c6a4b1c80 --- /dev/null +++ b/core/types/suave.go @@ -0,0 +1,25 @@ +package types + +import "github.com/ethereum/go-ethereum/common" + +type EngineBid struct { + Id BidId + Salt BidId + DecryptionCondition uint64 + AllowedPeekers []common.Address + AllowedStores []common.Address + Version string + CreationTx *Transaction + Signature []byte +} + +func (b *EngineBid) ToInnerBid() Bid { + return Bid{ + Id: b.Id, + Salt: b.Salt, + DecryptionCondition: b.DecryptionCondition, + AllowedPeekers: b.AllowedPeekers, + AllowedStores: b.AllowedStores, + Version: b.Version, + } +} diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 9f8d7a962..00a8e77a4 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -91,12 +91,15 @@ var PrecompiledContractsSuave = map[common.Address]SuavePrecompiledContract{ isConfidentialAddress: &isConfidentialPrecompile{}, confidentialInputsAddress: &confidentialInputsPrecompile{}, - confStoreStoreAddress: newConfStoreStore(), - confStoreRetrieveAddress: newConfStoreRetrieve(), + confStoreStoreAddress: newConfStoreStore(), - newBidAddress: newNewBid(), - fetchBidsAddress: newFetchBids(), - extractHintAddress: &extractHint{}, + // confStoreRetrieveAddress: newConfStoreRetrieve(), + confStoreRetrieveAddress: &WasiPrecompileWrapper{bytecode: wasmRetrieveBytecode}, + + newBidAddress: newNewBid(), + fetchBidsAddress: newFetchBids(), + //extractHintAddress: &extractHint{}, + extractHintAddress: &WasiPrecompileWrapper{bytecode: helloworld}, signEthTransactionAddress: &signEthTransaction{}, simulateBundleAddress: &simulateBundle{}, diff --git a/core/vm/contracts_suave_eth.go b/core/vm/contracts_suave_eth.go index efd4ea194..26220ae9c 100644 --- a/core/vm/contracts_suave_eth.go +++ b/core/vm/contracts_suave_eth.go @@ -3,6 +3,7 @@ package vm import ( "bytes" "context" + _ "embed" "encoding/json" "errors" "fmt" diff --git a/core/vm/suave.go b/core/vm/suave.go index 34c726e3c..37ac296c7 100644 --- a/core/vm/suave.go +++ b/core/vm/suave.go @@ -100,10 +100,10 @@ func (p *SuavePrecompiledContractWrapper) Run(input []byte) ([]byte, error) { ret, err = (&confidentialInputsPrecompile{}).RunConfidential(p.suaveContext, input) case confStoreStoreAddress: - ret, err = stub.confidentialStoreStore(input) + ret, err = (&WasiPrecompileWrapper{bytecode: wasmStorePutBytecode}).RunConfidential(p.suaveContext, input) case confStoreRetrieveAddress: - ret, err = stub.confidentialStoreRetrieve(input) + ret, err = (&WasiPrecompileWrapper{bytecode: wasmRetrieveBytecode}).RunConfidential(p.suaveContext, input) case newBidAddress: ret, err = stub.newBid(input) diff --git a/core/vm/suave_wasm/extract_hint.wasm b/core/vm/suave_wasm/extract_hint.wasm new file mode 100755 index 000000000..42edbe23b Binary files /dev/null and b/core/vm/suave_wasm/extract_hint.wasm differ diff --git a/core/vm/suave_wasm/extract_hint/main.go b/core/vm/suave_wasm/extract_hint/main.go new file mode 100644 index 000000000..0cd79154a --- /dev/null +++ b/core/vm/suave_wasm/extract_hint/main.go @@ -0,0 +1,52 @@ +package main + +import ( + "bytes" + "encoding/json" + "io" + "os" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/suave/artifacts" + + suave_lib "github.com/ethereum/go-ethereum/core/vm/suave_wasm/lib" +) + +func main() { + unpacked, err := suave_lib.UnpackInputs(artifacts.SuaveAbi.Methods["extractHint"].Inputs) + if err != nil { + suave_lib.Fail(err) + } + + bundleBytes := unpacked[0].([]byte) + + bundle := struct { + Txs types.Transactions `json:"txs"` + RevertingHashes []common.Hash `json:"revertingHashes"` + RefundPercent int `json:"percent"` + MatchId types.BidId `json:"MatchId"` + }{} + + err = json.Unmarshal(bundleBytes, &bundle) + if err != nil { + io.Copy(os.Stdout, bytes.NewBuffer([]byte(err.Error()))) + os.Exit(2) + } + + tx := bundle.Txs[0] + hint := struct { + To common.Address + Data []byte + }{ + To: *tx.To(), + Data: tx.Data(), + } + + hintBytes, err := json.Marshal(hint) + if err != nil { + suave_lib.Fail(err) + } + + suave_lib.ReturnBytes(hintBytes) +} diff --git a/core/vm/suave_wasm/lib/suave_lib.go b/core/vm/suave_wasm/lib/suave_lib.go new file mode 100644 index 000000000..5e7cffb68 --- /dev/null +++ b/core/vm/suave_wasm/lib/suave_lib.go @@ -0,0 +1,86 @@ +package lib + +import ( + "encoding/json" + "fmt" + "unsafe" + + "github.com/ethereum/go-ethereum/core/types" + suave_wasi "github.com/ethereum/go-ethereum/suave/wasi" +) + +type hostJsonFn = func(inputOffset, inputSize, outputOffset, outputSize, n uint32) uint32 + +func call_hostFn[In any, Out any](data In, fn hostJsonFn) (Out, error) { + var out Out + + jsonInputBytes, err := json.Marshal(data) + if err != nil { + return out, fmt.Errorf("failed to marshal: %w", err) + } + + inputBidOffset := bytesToPointer(jsonInputBytes) + inputBidSize := uint32(len(jsonInputBytes)) + + bufOffset := bytesToPointer(dataBuf) + bufSize := uint32(len(dataBuf)) + + n := uint32(0) + nOffset := uint32(uintptr(unsafe.Pointer(&n))) + + errno := fn(inputBidOffset, inputBidSize, bufOffset, bufSize, nOffset) + if errno != 0 { + return out, fmt.Errorf("host function failed: %s", string(dataBuf[:n])) + } + + err = json.Unmarshal(dataBuf[:n], &out) + if err != nil { + return out, fmt.Errorf("failed to unmarshal: %w", err) + } + + return out, nil +} + +func InitializeBid(rawBid types.Bid) (types.Bid, error) { + return call_hostFn[types.Bid, types.Bid](rawBid, initializeBid) +} + +func Store(bidId types.BidId, key string, value []byte) (types.EngineBid, error) { + return call_hostFn[suave_wasi.StoreHostFnArgs, types.EngineBid](suave_wasi.StoreHostFnArgs{bidId, key, value}, storePut) +} + +func StoreRetrieve(bidId types.BidId, key string) ([]byte, error) { + return call_hostFn[suave_wasi.RetrieveHostFnArgs, []byte](suave_wasi.RetrieveHostFnArgs{bidId, key}, storeRetrieve) +} + +func FetchBidById(bidId types.BidId) (types.EngineBid, error) { + return call_hostFn[types.BidId, types.EngineBid](bidId, fetchBidById) +} + +func FetchBidsByProtocolAndBlock(blockNumber uint64, namespace string) ([]types.EngineBid, error) { + return call_hostFn[suave_wasi.FetchBidByProtocolFnArgs, []types.EngineBid](suave_wasi.FetchBidByProtocolFnArgs{blockNumber, namespace}, fetchBidsByProtocolAndBlock) +} + +var ( + dataBuf = make([]byte, 1024*256) // max 256KB (TODO!) +) + +//go:wasmimport suavexec initializeBid +//go:noescape +func initializeBid(inputOffset, inputSize, outputOffset, outputSize, n uint32) uint32 + +//go:wasmimport suavexec storePut +//go:noescape +func storePut(inputOffset, inputSize, outputOffset, outputSize, n uint32) uint32 + +//go:wasmimport suavexec storeRetrieve +//go:noescape +func storeRetrieve(inputOffset, inputSize, outputOffset, outputSize, n uint32) uint32 + +//go:wasmimport suavexec fetchBidById +//go:noescape +func fetchBidById(inputOffset, inputSize, outputOffset, outputSize, n uint32) uint32 + +//go:wasmimport suavexec fetchBidsByProtocolAndBlock +//go:noescape +func fetchBidsByProtocolAndBlock(inputOffset, inputSize, outputOffset, outputSize, n uint32) uint32 diff --git a/core/vm/suave_wasm/lib/utils.go b/core/vm/suave_wasm/lib/utils.go new file mode 100644 index 000000000..e83cbae9c --- /dev/null +++ b/core/vm/suave_wasm/lib/utils.go @@ -0,0 +1,51 @@ +package lib + +import ( + "bytes" + "fmt" + "io" + "os" + "unsafe" + + "github.com/ethereum/go-ethereum/accounts/abi" +) + +func UnpackInputs(argSpec abi.Arguments) ([]interface{}, error) { + inputBuf := bytes.NewBuffer([]byte{}) + io.Copy(inputBuf, os.Stdin) + + unpacked, err := argSpec.Unpack(inputBuf.Bytes()) + if err != nil { + return nil, err + } + + return unpacked, nil +} + +func ReturnPackedArgs(argSpec abi.Arguments, args ...interface{}) { + packed, err := argSpec.Pack(args...) + if err != nil { + Fail(fmt.Errorf("could not pack output: %w", err)) + } + + ReturnBytes(packed) +} + +func Fail(err error) { + io.Copy(os.Stdout, bytes.NewBufferString(err.Error())) + os.Exit(1) +} + +func ReturnBytes(data []byte) { + io.Copy(os.Stdout, bytes.NewReader(data)) +} + +//go:inline +func bytesToPointer(b []byte) uint32 { + return uint32(uintptr(unsafe.Pointer(unsafe.SliceData(b)))) +} + +//go:inline +func stringToPointer(s string) uint32 { + return uint32(uintptr(unsafe.Pointer(unsafe.StringData(s)))) +} diff --git a/core/vm/suave_wasm/store_put.wasm b/core/vm/suave_wasm/store_put.wasm new file mode 100755 index 000000000..2282e2145 Binary files /dev/null and b/core/vm/suave_wasm/store_put.wasm differ diff --git a/core/vm/suave_wasm/store_put/main.go b/core/vm/suave_wasm/store_put/main.go new file mode 100644 index 000000000..fee1aef05 --- /dev/null +++ b/core/vm/suave_wasm/store_put/main.go @@ -0,0 +1,25 @@ +package main + +import ( + "github.com/ethereum/go-ethereum/suave/artifacts" + + suave_lib "github.com/ethereum/go-ethereum/core/vm/suave_wasm/lib" +) + +func main() { + unpacked, err := suave_lib.UnpackInputs(artifacts.SuaveAbi.Methods["confidentialStoreStore"].Inputs) + if err != nil { + suave_lib.Fail(err) + } + + bidId := unpacked[0].([16]byte) + key := unpacked[1].(string) + value := unpacked[2].([]byte) + + _, err = suave_lib.Store(bidId, key, value) + if err != nil { + suave_lib.Fail(err) + } + + // no return +} diff --git a/core/vm/suave_wasm/store_retrieve.wasm b/core/vm/suave_wasm/store_retrieve.wasm new file mode 100755 index 000000000..201a9629f Binary files /dev/null and b/core/vm/suave_wasm/store_retrieve.wasm differ diff --git a/core/vm/suave_wasm/store_retrieve/main.go b/core/vm/suave_wasm/store_retrieve/main.go new file mode 100644 index 000000000..886158b65 --- /dev/null +++ b/core/vm/suave_wasm/store_retrieve/main.go @@ -0,0 +1,24 @@ +package main + +import ( + "github.com/ethereum/go-ethereum/suave/artifacts" + + suave_lib "github.com/ethereum/go-ethereum/core/vm/suave_wasm/lib" +) + +func main() { + unpacked, err := suave_lib.UnpackInputs(artifacts.SuaveAbi.Methods["confidentialStoreRetrieve"].Inputs) + if err != nil { + suave_lib.Fail(err) + } + + bidId := unpacked[0].([16]byte) + key := unpacked[1].(string) + + data, err := suave_lib.StoreRetrieve(bidId, key) + if err != nil { + suave_lib.Fail(err) + } + + suave_lib.ReturnBytes(data) +} diff --git a/core/vm/suave_wasm/suavexec.wasm b/core/vm/suave_wasm/suavexec.wasm new file mode 100755 index 000000000..fd5516cd2 Binary files /dev/null and b/core/vm/suave_wasm/suavexec.wasm differ diff --git a/core/vm/suave_wasm/suavexec/main.go b/core/vm/suave_wasm/suavexec/main.go new file mode 100644 index 000000000..ad3da3540 --- /dev/null +++ b/core/vm/suave_wasm/suavexec/main.go @@ -0,0 +1,24 @@ +package main + +import ( + "bytes" + "io" + "os" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm/suave_wasm/lib" +) + +var ( + bid = types.BidId{0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef} + key = "someKey" +) + +func main() { + data, err := lib.StoreRetrieve(bid, key) + if err != nil { + os.Exit(1) + } + + io.Copy(os.Stdout, bytes.NewReader(data[:])) +} diff --git a/core/vm/suave_wasm/suavexec/main.wasm b/core/vm/suave_wasm/suavexec/main.wasm new file mode 100755 index 000000000..898b45583 Binary files /dev/null and b/core/vm/suave_wasm/suavexec/main.wasm differ diff --git a/core/vm/suave_wasm_engine_module.go b/core/vm/suave_wasm_engine_module.go new file mode 100644 index 000000000..2e9418454 --- /dev/null +++ b/core/vm/suave_wasm_engine_module.go @@ -0,0 +1,120 @@ +package vm + +import ( + "context" + "encoding/json" + "fmt" + + ethtypes "github.com/ethereum/go-ethereum/core/types" + suave "github.com/ethereum/go-ethereum/suave/core" + suave_wasi "github.com/ethereum/go-ethereum/suave/wasi" + + "github.com/stealthrocket/wazergo" + "github.com/stealthrocket/wazergo/types" + "github.com/tetratelabs/wazero" + "github.com/tetratelabs/wazero/api" +) + +func InstantiateHostModule(ctx context.Context, r wazero.Runtime, suaveCtx *SuaveContext) (api.Closer, context.Context, error) { + sx, err := wazergo.Instantiate(ctx, r, HostModule, + withConfidentialStoreBackend(suaveCtx)) + return sx, wazergo.WithModuleInstance(ctx, sx), err +} + +// Declare the host module from a set of exported functions. +var HostModule wazergo.HostModule[*EngineModule] = functions{ + "initializeBid": wazergo.F3(wrapCall((*EngineModule).InitializeBid)), + "storeRetrieve": wazergo.F3(wrapCall((*EngineModule).StoreRetrieve)), + "storePut": wazergo.F3(wrapCall((*EngineModule).StorePut)), + "fetchBidById": wazergo.F3(wrapCall((*EngineModule).FetchBidById)), + "FetchBidsByProtocolAndBlock": wazergo.F3(wrapCall((*EngineModule).FetchBidsByProtocolAndBlock)), +} + +// The `functions` type impements `HostModule[*Module]`, providing the +// module name, map of exported functions, and the ability to create instances +// of the module type. +type functions wazergo.Functions[*EngineModule] + +func (f functions) Name() string { + return "suavexec" +} + +func (f functions) Functions() wazergo.Functions[*EngineModule] { + return (wazergo.Functions[*EngineModule])(f) +} + +func (f functions) Instantiate(ctx context.Context, opts ...Option) (*EngineModule, error) { + mod := &EngineModule{ + // ... + } + wazergo.Configure(mod, opts...) + return mod, nil +} + +type Option = wazergo.Option[*EngineModule] + +func withConfidentialStoreBackend(suaveCtx *SuaveContext) Option { + return wazergo.OptionFunc(func(m *EngineModule) { + m.SuaveContext = suaveCtx + }) +} + +// EngineModule will be the Go type we use to maintain the state of our module +// instances. +type EngineModule struct { + SuaveContext *SuaveContext +} + +func (EngineModule) Close(context.Context) error { + return nil +} + +type hostFnType = func(m *EngineModule, ctx context.Context, inData types.Bytes, buf types.Bytes, n types.Pointer[types.Uint32]) types.Error + +func wrapCall[In any, Out any](handler func(*EngineModule, context.Context, In) (Out, error)) hostFnType { + return func(m *EngineModule, ctx context.Context, inData types.Bytes, buf types.Bytes, n types.Pointer[types.Uint32]) types.Error { + var inStruct In + err := json.Unmarshal(inData, &inStruct) + if err != nil { + wrappedErr := fmt.Errorf("unable to unmarshal inputs: %w", err) + n.Store(types.Uint32(copy(buf, types.Bytes(wrappedErr.Error())))) + return types.Fail(wrappedErr) + } + + out, err := handler(m, ctx, inStruct) + if err != nil { + n.Store(types.Uint32(copy(buf, types.Bytes(err.Error())))) + return types.Fail(err) + } + + data, err := json.Marshal(out) + if err != nil { + wrappedErr := fmt.Errorf("unable to marshal outputs: %w", err) + n.Store(types.Uint32(copy(buf, types.Bytes(wrappedErr.Error())))) + return types.Fail(wrappedErr) + } + + n.Store(types.Uint32(copy(buf, data))) + return types.OK + } +} + +func (m EngineModule) InitializeBid(ctx context.Context, bid ethtypes.Bid) (ethtypes.Bid, error) { + return m.SuaveContext.Backend.ConfidentialStore.InitializeBid(bid) +} + +func (m EngineModule) StoreRetrieve(ctx context.Context, args suave_wasi.RetrieveHostFnArgs) ([]byte, error) { + return (&confStoreRetrieve{}).runImpl(m.SuaveContext, args.BidId, args.Key) +} + +func (m EngineModule) StorePut(ctx context.Context, args suave_wasi.StoreHostFnArgs) (ethtypes.EngineBid, error) { + return ethtypes.EngineBid{}, (&confStoreStore{}).runImpl(m.SuaveContext, args.BidId, args.Key, args.Value) +} + +func (m EngineModule) FetchBidById(ctx context.Context, bidId suave.BidId) (ethtypes.EngineBid, error) { + return m.SuaveContext.Backend.ConfidentialStore.FetchBidById(bidId) +} + +func (m EngineModule) FetchBidsByProtocolAndBlock(ctx context.Context, args suave_wasi.FetchBidByProtocolFnArgs) ([]ethtypes.EngineBid, error) { + return m.SuaveContext.Backend.ConfidentialStore.FetchBidsByProtocolAndBlock(args.BlockNumber, args.Namespace), nil +} diff --git a/core/vm/suave_wasm_engine_module_test.go b/core/vm/suave_wasm_engine_module_test.go new file mode 100644 index 000000000..9d6c3a9b8 --- /dev/null +++ b/core/vm/suave_wasm_engine_module_test.go @@ -0,0 +1,103 @@ +package vm + +import ( + "bytes" + "context" + _ "embed" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + suave "github.com/ethereum/go-ethereum/suave/core" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/tetratelabs/wazero" + wasi "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1" +) + +//go:embed suave_wasm/suavexec.wasm +var testHostCallSrc []byte + +var wantAddr = common.Address{0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef} + +func TestHostCall(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + // Instantiate the Wazero runtime. + r := wazero.NewRuntimeWithConfig(ctx, wazero.NewRuntimeConfig(). + WithCloseOnContextDone(true)) + defer r.Close(ctx) + + // Instantiate WASI. + sys, err := wasi.Instantiate(ctx, r) + require.NoError(t, err) + defer sys.Close(ctx) + + // Instantiate suavexec host module + // We first need to instantiate a mock SuaveExecutionBackend + suaveCtx := &SuaveContext{ + // TODO: MEVM access to Backend should be restricted to only the necessary functions! + Backend: &SuaveExecutionBackend{ + ConfidentialStore: &MockConfidentialStoreBackend{t}, + }, + ConfidentialComputeRequestTx: nil, //*types.Transaction + CallerStack: []*common.Address{&wantAddr}, + } + + sx, ctx, err := InstantiateHostModule(ctx, r, suaveCtx) + require.NoError(t, err) + defer sx.Close(ctx) + + // Compile the WASM bytecode to Wazero IR. + ir, err := r.CompileModule(ctx, testHostCallSrc) + require.NoError(t, err) + defer ir.Close(ctx) + + stdout := new(bytes.Buffer) + stderr := new(bytes.Buffer) + + mod, err := r.InstantiateModule(ctx, ir, wazero.NewModuleConfig(). + WithStdout(stdout). + WithStderr(stderr)) + require.NoError(t, err, stderr.String()) + defer mod.Close(ctx) + + assert.Equal(t, "test data", stdout.String()) +} + +type MockConfidentialStoreBackend struct { + *testing.T +} + +func (MockConfidentialStoreBackend) InitializeBid(bid types.Bid) (types.Bid, error) { + panic("NOT IMPLEMENTED") +} + +func (MockConfidentialStoreBackend) Store(bidId suave.BidId, caller common.Address, key string, value []byte) (suave.Bid, error) { + panic("NOT IMPLEMENTED") +} + +var wantBid = types.BidId{0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef} + +func (b MockConfidentialStoreBackend) Retrieve(bid suave.BidId, caller common.Address, key string) ([]byte, error) { + + assert.Equal(b.T, wantBid, bid) + assert.Equal(b.T, wantAddr, caller) + assert.Equal(b.T, "someKey", key) + + return []byte("test data"), nil +} + +func (b MockConfidentialStoreBackend) FetchBidById(bid suave.BidId) (suave.Bid, error) { + + assert.Equal(b.T, wantBid, bid) + return suave.Bid{ + AllowedPeekers: []common.Address{wantAddr}, + }, nil +} + +func (b MockConfidentialStoreBackend) FetchBidsByProtocolAndBlock(blockNumber uint64, namespace string) []suave.Bid { + panic("NOT IMPLEMENTED") +} diff --git a/core/vm/wasi.go b/core/vm/wasi.go new file mode 100644 index 000000000..1d1e6cbf9 --- /dev/null +++ b/core/vm/wasi.go @@ -0,0 +1,88 @@ +package vm + +import ( + "bytes" + "context" + _ "embed" + "errors" + "fmt" + "time" + + "github.com/tetratelabs/wazero" + wasi "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1" +) + +var ( + cache = wazero.NewCompilationCache() + + //go:embed suave_wasm/suavexec.wasm + helloworld []byte + wasmHelloWorldAddr = extractHintAddress // common.HexToAddress("0x67000001") + + //go:embed suave_wasm/store_put.wasm + wasmStorePutBytecode []byte + + //go:embed suave_wasm/store_retrieve.wasm + wasmRetrieveBytecode []byte + + //go:embed suave_wasm/extract_hint.wasm + wasmExtractHintBytecode []byte +) + +type WasiPrecompileWrapper struct { + bytecode []byte +} + +func (w *WasiPrecompileWrapper) RequiredGas(input []byte) uint64 { + return 0 +} + +func (w *WasiPrecompileWrapper) Run(input []byte) ([]byte, error) { + return nil, errors.New("not available in this context") +} + +func (w *WasiPrecompileWrapper) RunConfidential(suaveCtx *SuaveContext, input []byte) ([]byte, error) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) + defer cancel() + + // Instantiate the Wazero runtime. + r := wazero.NewRuntimeWithConfig(ctx, wazero.NewRuntimeConfig(). + WithCloseOnContextDone(true). + WithCompilationCache(cache)) + defer r.Close(ctx) + + // Instantiate WASI. + sys, err := wasi.Instantiate(ctx, r) + if err != nil { + return nil, fmt.Errorf("init wasi: %w", err) + } + defer sys.Close(ctx) + + sx, ctx, err := InstantiateHostModule(ctx, r, suaveCtx) + if err != nil { + return nil, fmt.Errorf("init host module: %w", err) + } + defer sx.Close(ctx) + + // Compile the WASM bytecode to Wazero IR. + ir, err := r.CompileModule(ctx, w.bytecode) + if err != nil { + return nil, fmt.Errorf("compile: %w", err) + } + defer ir.Close(ctx) + + stdin := bytes.NewBuffer(input) + stdout := new(bytes.Buffer) + stderr := new(bytes.Buffer) + + mod, err := r.InstantiateModule(ctx, ir, wazero.NewModuleConfig(). + WithStdin(stdin). + WithStdout(stdout). + WithStderr(stderr)) + if err != nil { + return stdout.Bytes(), fmt.Errorf("init module: %w. Output: %s", err, string(stdout.Bytes())) + } + defer mod.Close(ctx) + + return stdout.Bytes(), nil +} diff --git a/go.mod b/go.mod index 13333e729..707e8bc23 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/ethereum/go-ethereum -go 1.19 +go 1.21 require ( github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.3.0 @@ -64,9 +64,11 @@ require ( github.com/rs/cors v1.7.0 github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible github.com/status-im/keycard-go v0.2.0 + github.com/stealthrocket/wazergo v0.19.1 github.com/stretchr/testify v1.8.4 github.com/supranational/blst v0.3.11-0.20230406105308-e9dfc5ee724b github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 + github.com/tetratelabs/wazero v1.5.0 github.com/tyler-smith/go-bip39 v1.1.0 github.com/urfave/cli/v2 v2.17.2-0.20221006022127-8f469abc00aa golang.org/x/crypto v0.13.0 diff --git a/go.sum b/go.sum index 22e4f023a..4db7b73b6 100644 --- a/go.sum +++ b/go.sum @@ -442,6 +442,8 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= +github.com/stealthrocket/wazergo v0.19.1 h1:BPrITETPgSFwiytwmToO0MbUC/+RGC39JScz1JmmG6c= +github.com/stealthrocket/wazergo v0.19.1/go.mod h1:riI0hxw4ndZA5e6z7PesHg2BtTftcZaMxRcoiGGipTs= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= @@ -454,6 +456,8 @@ github.com/supranational/blst v0.3.11-0.20230406105308-e9dfc5ee724b h1:u49mjRnyg github.com/supranational/blst v0.3.11-0.20230406105308-e9dfc5ee724b/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= +github.com/tetratelabs/wazero v1.5.0 h1:Yz3fZHivfDiZFUXnWMPUoiW7s8tC1sjdBtlJn08qYa0= +github.com/tetratelabs/wazero v1.5.0/go.mod h1:0U0G41+ochRKoPKCJlh0jMg1CHkyfK8kDqiirMmKY8A= github.com/tklauser/go-sysconf v0.3.5 h1:uu3Xl4nkLzQfXNsWn15rPc/HQCJKObbt1dKJeWp3vU4= github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI= github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA= diff --git a/suave/core/types.go b/suave/core/types.go index e21605103..26610dd3e 100644 --- a/suave/core/types.go +++ b/suave/core/types.go @@ -16,29 +16,7 @@ var AllowedPeekerAny = common.HexToAddress("0xC8df3686b4Afb2BB53e60EAe97EF043FE0 type Bytes = hexutil.Bytes type BidId = types.BidId -type Bid struct { - Id types.BidId - Salt types.BidId - DecryptionCondition uint64 - AllowedPeekers []common.Address - AllowedStores []common.Address - Version string - CreationTx *types.Transaction - Signature []byte -} - -func (b *Bid) ToInnerBid() types.Bid { - return types.Bid{ - Id: b.Id, - Salt: b.Salt, - DecryptionCondition: b.DecryptionCondition, - AllowedPeekers: b.AllowedPeekers, - AllowedStores: b.AllowedStores, - Version: b.Version, - } -} - -type MEVMBid = types.Bid +type Bid = types.EngineBid type BuildBlockArgs = types.BuildBlockArgs diff --git a/suave/wasi/types.go b/suave/wasi/types.go new file mode 100644 index 000000000..151defba3 --- /dev/null +++ b/suave/wasi/types.go @@ -0,0 +1,19 @@ +package wasi + +import "github.com/ethereum/go-ethereum/core/types" + +type StoreHostFnArgs struct { + BidId types.BidId + Key string + Value []byte +} + +type RetrieveHostFnArgs struct { + BidId types.BidId + Key string +} + +type FetchBidByProtocolFnArgs struct { + BlockNumber uint64 + Namespace string +}