diff --git a/.circleci/config.yml b/.circleci/config.yml index 5f77c5fde..2aa9a6eda 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -3,6 +3,17 @@ version: 2.1 orbs: win: circleci/windows@5.0 +commands: + show_cpu_info: + steps: + - run: + name: Show CPU info + command: | + set -o errexit + curl -sS -L --output py-cpuinfo.zip https://github.com/workhorsy/py-cpuinfo/archive/refs/tags/v9.0.0.zip + unzip -q py-cpuinfo.zip + python py-cpuinfo-*/cpuinfo/cpuinfo.py + jobs: # All checks on the codebase that can run in parallel to build_shared_library libwasmvm_sanity: @@ -79,6 +90,7 @@ jobs: - run: name: Reset git config set by CircleCI to make Cargo work command: git config --global --unset url.ssh://git@git.colasdn.top.insteadof + - show_cpu_info - run: name: Install Rust command: | @@ -233,7 +245,7 @@ jobs: name: Create release build of libwasmvm command: make build-rust - persist_to_workspace: - root: ./internal/api + root: internal/api paths: - libwasmvm.x86_64.so - save_cache: @@ -247,6 +259,55 @@ jobs: - libwasmvm/target/release/deps key: cargocache-v3-build_shared_library-rust:1.60.0-{{ checksum "libwasmvm/Cargo.lock" }} + # Analogue to build_shared_library this builds wasmvm.dll using a native Rust installation + # (i.e. no docker builders). In contrast to libwasmvm_sanity_windows this is a release build + # and gets its own build cache. + build_shared_library_windows: + executor: + name: win/default + size: large + shell: bash.exe + steps: + - checkout + - run: + name: Reset git config set by CircleCI to make Cargo work + command: git config --global --unset url.ssh://git@git.colasdn.top.insteadof + - show_cpu_info + - run: + name: Install make + command: | + set -o errexit + choco install -y make + - run: + name: Install Rust + command: | + set -o errexit + curl -sS --output rustup-init.exe https://static.rust-lang.org/rustup/dist/x86_64-pc-windows-msvc/rustup-init.exe + ./rustup-init.exe --no-modify-path --profile minimal --default-toolchain 1.60.0 -y + echo 'export PATH="$PATH;$USERPROFILE/.cargo/bin"' >> "$BASH_ENV" + - run: + name: Show Rust version information + command: rustc --version; cargo --version; rustup --version + - restore_cache: + keys: + - cachev4-build_shared_library_windows-rust:1.60.0-{{ checksum "libwasmvm/Cargo.lock" }} + - cachev4-build_shared_library_windows-rust:1.60.0- + - run: + name: Create release build of libwasmvm + command: make build-rust + - persist_to_workspace: + root: internal\api + paths: + - libwasmvm.a + - save_cache: + paths: + # ".." is the easiest way to get $HOME here (pwd is $HOME\project) + - ../.cargo/registry + - libwasmvm/target/release/.fingerprint + - libwasmvm/target/release/build + - libwasmvm/target/release/deps + key: cachev4-build_shared_library_windows-rust:1.60.0-{{ checksum "libwasmvm/Cargo.lock" }} + # Test the Go project wasmvm_test: docker: @@ -269,6 +330,37 @@ jobs: command: make test-safety - run: make build-go + # Test the Go project on Windows + wasmvm_test_windows: + executor: + name: win/default + shell: bash.exe + environment: + GORACE: "halt_on_error=1" + steps: + - checkout + - show_cpu_info + - run: + name: Install make and mingw + # See https://github.com/docker/containerd-packaging/pull/294 for discussion around mingw version + command: | + set -o errexit + choco install -y make + choco install -y mingw --version 10.2.0 --allow-downgrade + - run: + name: Show Go version information + command: | + go version + make --version + - attach_workspace: + at: C:/builds + - run: + name: Copy libwasmvm.a to system library path + command: cp /c/builds/libwasmvm.a /c/Windows/System32 + - run: + name: Run Go tests + command: make test + test_alpine_build: machine: image: ubuntu-2004:202101-01 @@ -379,9 +471,13 @@ workflows: filters: # required since other jobs with tag filters require this one tags: only: /.*/ + - build_shared_library_windows - wasmvm_test: requires: - build_shared_library + - wasmvm_test_windows: + requires: + - build_shared_library_windows - build_static_lib: requires: - build_shared_library @@ -415,6 +511,7 @@ workflows: - format-scripts - lint-scripts - wasmvm_test + - wasmvm_test_windows filters: tags: ignore: diff --git a/docs/NO_ERRNO.md b/docs/NO_ERRNO.md new file mode 100644 index 000000000..d88ac1b01 --- /dev/null +++ b/docs/NO_ERRNO.md @@ -0,0 +1,21 @@ +# Why we are not using errno anymore + +wasmvm up until 1.1.1 used errno to communicate errors over the FFI that +happened in the Rust code. The [errno](https://crates.io/crates/errno) crate was +used to set error values in Rust and +[cgo automatically read them](https://utcc.utoronto.ca/~cks/space/blog/programming/GoCgoErrorReturns) +and made them available in the error return value of +`ptr, err := C.init_cache(...)`. + +Unfortunately errno does not work well for us when trying to support Windows. On +Windows, `errno` +[uses a Windows API](https://github.com/lambda-fairy/rust-errno/blob/v0.2.8/src/windows.rs#L60-L70) +which then is not what cgo expects. Also Rust's +[libc does not help us](https://github.com/rust-lang/libc/issues/1644) solving +the issue. Using the C errno variable via +[rust-errno](https://github.com/lambda-fairy/rust-errno) caused linker errors +using the Go "internal" linker. + +In order to avoid wasting more time and remaining flexible when it comes to the +linker we use, we let all FFI functions return their error as an integer. The +result then goes into an output pointer. diff --git a/internal/api/bindings.h b/internal/api/bindings.h index 5f7e31f8b..3458e32d7 100644 --- a/internal/api/bindings.h +++ b/internal/api/bindings.h @@ -9,12 +9,16 @@ #include #include -enum ErrnoValue { - ErrnoValue_Success = 0, - ErrnoValue_Other = 1, - ErrnoValue_OutOfGas = 2, +/** + * An error code used to communicate the errors of FFI calls. + * Similar to shell codes and errno, 0 means no error. + */ +enum ErrorCode { + ErrorCode_Success = 0, + ErrorCode_Other = 1, + ErrorCode_OutOfGas = 2, }; -typedef int32_t ErrnoValue; +typedef int32_t ErrorCode; /** * This enum gives names to the status codes returned from Go callbacks to Rust. @@ -53,10 +57,6 @@ enum GoError { }; typedef int32_t GoError; -typedef struct cache_t { - -} cache_t; - /** * A view into an externally owned byte slice (Go `[]byte`). * Use this for the current call only. A view cannot be copied for safety reasons. @@ -112,10 +112,10 @@ typedef struct ByteSliceView { * Transferring ownership from Rust to Go using return values of FFI calls: * * ``` - * # use wasmvm::{cache_t, ByteSliceView, UnmanagedVector}; + * # use wasmvm::{CachePtr, ByteSliceView, UnmanagedVector}; * #[no_mangle] * pub extern "C" fn save_wasm_to_cache( - * cache: *mut cache_t, + * cache: CachePtr, * wasm: ByteSliceView, * error_msg: Option<&mut UnmanagedVector>, * ) -> UnmanagedVector { @@ -193,6 +193,18 @@ typedef struct UnmanagedVector { uintptr_t cap; } UnmanagedVector; +/** + * A struct that holds a pointer to the cache. This struct can be + * copied freely. + * + * In case of `init_cache` we need a pointer to a pointer for the output + * and in order to be able to do this consistently with e.g. `AnalysisReport` + * and `Metrics`, we use thiy type. + */ +typedef struct CachePtr { + void *ptr; +} CachePtr; + /** * The result type of the FFI function analyze_code. * @@ -303,83 +315,146 @@ typedef struct GoQuerier { struct Querier_vtable vtable; } GoQuerier; -struct cache_t *init_cache(struct ByteSliceView data_dir, - struct ByteSliceView available_capabilities, - uint32_t cache_size, - uint32_t instance_memory_limit, - struct UnmanagedVector *error_msg); +int32_t init_cache(struct ByteSliceView data_dir, + struct ByteSliceView available_capabilities, + uint32_t cache_size, + uint32_t instance_memory_limit, + struct UnmanagedVector *error_msg, + struct CachePtr *out); -struct UnmanagedVector save_wasm(struct cache_t *cache, - struct ByteSliceView wasm, - struct UnmanagedVector *error_msg); +int32_t save_wasm(struct CachePtr cache, + struct ByteSliceView wasm, + struct UnmanagedVector *error_msg, + struct UnmanagedVector *out); -void remove_wasm(struct cache_t *cache, - struct ByteSliceView checksum, - struct UnmanagedVector *error_msg); +int32_t remove_wasm(struct CachePtr cache, + struct ByteSliceView checksum, + struct UnmanagedVector *error_msg); -struct UnmanagedVector load_wasm(struct cache_t *cache, - struct ByteSliceView checksum, - struct UnmanagedVector *error_msg); +int32_t load_wasm(struct CachePtr cache, + struct ByteSliceView checksum, + struct UnmanagedVector *error_msg, + struct UnmanagedVector *out); -void pin(struct cache_t *cache, struct ByteSliceView checksum, struct UnmanagedVector *error_msg); +int32_t pin(struct CachePtr cache, + struct ByteSliceView checksum, + struct UnmanagedVector *error_msg); -void unpin(struct cache_t *cache, struct ByteSliceView checksum, struct UnmanagedVector *error_msg); +int32_t unpin(struct CachePtr cache, + struct ByteSliceView checksum, + struct UnmanagedVector *error_msg); -struct AnalysisReport analyze_code(struct cache_t *cache, - struct ByteSliceView checksum, - struct UnmanagedVector *error_msg); +int32_t analyze_code(struct CachePtr cache, + struct ByteSliceView checksum, + struct UnmanagedVector *error_msg, + struct AnalysisReport *out); -struct Metrics get_metrics(struct cache_t *cache, struct UnmanagedVector *error_msg); +int32_t get_metrics(struct CachePtr cache, struct UnmanagedVector *error_msg, struct Metrics *out); /** * frees a cache reference * * # Safety * - * This must be called exactly once for any `*cache_t` returned by `init_cache` + * This must be called exactly once for any `CachePtr` returned by `init_cache` * and cannot be called on any other pointer. */ -void release_cache(struct cache_t *cache); - -struct UnmanagedVector instantiate(struct cache_t *cache, - struct ByteSliceView checksum, - struct ByteSliceView env, - struct ByteSliceView info, - struct ByteSliceView msg, - struct Db db, - struct GoApi api, - struct GoQuerier querier, - uint64_t gas_limit, - bool print_debug, - uint64_t *gas_used, - struct UnmanagedVector *error_msg); - -struct UnmanagedVector execute(struct cache_t *cache, - struct ByteSliceView checksum, - struct ByteSliceView env, - struct ByteSliceView info, - struct ByteSliceView msg, - struct Db db, - struct GoApi api, - struct GoQuerier querier, - uint64_t gas_limit, - bool print_debug, - uint64_t *gas_used, - struct UnmanagedVector *error_msg); - -struct UnmanagedVector migrate(struct cache_t *cache, - struct ByteSliceView checksum, - struct ByteSliceView env, - struct ByteSliceView msg, - struct Db db, - struct GoApi api, - struct GoQuerier querier, - uint64_t gas_limit, - bool print_debug, - uint64_t *gas_used, - struct UnmanagedVector *error_msg); - -struct UnmanagedVector sudo(struct cache_t *cache, +void release_cache(struct CachePtr cache); + +int32_t instantiate(struct CachePtr cache, + struct ByteSliceView checksum, + struct ByteSliceView env, + struct ByteSliceView info, + struct ByteSliceView msg, + struct Db db, + struct GoApi api, + struct GoQuerier querier, + uint64_t gas_limit, + bool print_debug, + uint64_t *gas_used, + struct UnmanagedVector *error_msg, + struct UnmanagedVector *out); + +int32_t execute(struct CachePtr cache, + struct ByteSliceView checksum, + struct ByteSliceView env, + struct ByteSliceView info, + struct ByteSliceView msg, + struct Db db, + struct GoApi api, + struct GoQuerier querier, + uint64_t gas_limit, + bool print_debug, + uint64_t *gas_used, + struct UnmanagedVector *error_msg, + struct UnmanagedVector *out); + +int32_t migrate(struct CachePtr cache, + struct ByteSliceView checksum, + struct ByteSliceView env, + struct ByteSliceView msg, + struct Db db, + struct GoApi api, + struct GoQuerier querier, + uint64_t gas_limit, + bool print_debug, + uint64_t *gas_used, + struct UnmanagedVector *error_msg, + struct UnmanagedVector *out); + +int32_t sudo(struct CachePtr cache, + struct ByteSliceView checksum, + struct ByteSliceView env, + struct ByteSliceView msg, + struct Db db, + struct GoApi api, + struct GoQuerier querier, + uint64_t gas_limit, + bool print_debug, + uint64_t *gas_used, + struct UnmanagedVector *error_msg, + struct UnmanagedVector *out); + +int32_t reply(struct CachePtr cache, + struct ByteSliceView checksum, + struct ByteSliceView env, + struct ByteSliceView msg, + struct Db db, + struct GoApi api, + struct GoQuerier querier, + uint64_t gas_limit, + bool print_debug, + uint64_t *gas_used, + struct UnmanagedVector *error_msg, + struct UnmanagedVector *out); + +int32_t query(struct CachePtr cache, + struct ByteSliceView checksum, + struct ByteSliceView env, + struct ByteSliceView msg, + struct Db db, + struct GoApi api, + struct GoQuerier querier, + uint64_t gas_limit, + bool print_debug, + uint64_t *gas_used, + struct UnmanagedVector *error_msg, + struct UnmanagedVector *out); + +int32_t ibc_channel_open(struct CachePtr cache, + struct ByteSliceView checksum, + struct ByteSliceView env, + struct ByteSliceView msg, + struct Db db, + struct GoApi api, + struct GoQuerier querier, + uint64_t gas_limit, + bool print_debug, + uint64_t *gas_used, + struct UnmanagedVector *error_msg, + struct UnmanagedVector *out); + +int32_t ibc_channel_connect(struct CachePtr cache, struct ByteSliceView checksum, struct ByteSliceView env, struct ByteSliceView msg, @@ -389,103 +464,60 @@ struct UnmanagedVector sudo(struct cache_t *cache, uint64_t gas_limit, bool print_debug, uint64_t *gas_used, - struct UnmanagedVector *error_msg); - -struct UnmanagedVector reply(struct cache_t *cache, - struct ByteSliceView checksum, - struct ByteSliceView env, - struct ByteSliceView msg, - struct Db db, - struct GoApi api, - struct GoQuerier querier, - uint64_t gas_limit, - bool print_debug, - uint64_t *gas_used, - struct UnmanagedVector *error_msg); - -struct UnmanagedVector query(struct cache_t *cache, - struct ByteSliceView checksum, - struct ByteSliceView env, - struct ByteSliceView msg, - struct Db db, - struct GoApi api, - struct GoQuerier querier, - uint64_t gas_limit, - bool print_debug, - uint64_t *gas_used, - struct UnmanagedVector *error_msg); - -struct UnmanagedVector ibc_channel_open(struct cache_t *cache, - struct ByteSliceView checksum, - struct ByteSliceView env, - struct ByteSliceView msg, - struct Db db, - struct GoApi api, - struct GoQuerier querier, - uint64_t gas_limit, - bool print_debug, - uint64_t *gas_used, - struct UnmanagedVector *error_msg); - -struct UnmanagedVector ibc_channel_connect(struct cache_t *cache, - struct ByteSliceView checksum, - struct ByteSliceView env, - struct ByteSliceView msg, - struct Db db, - struct GoApi api, - struct GoQuerier querier, - uint64_t gas_limit, - bool print_debug, - uint64_t *gas_used, - struct UnmanagedVector *error_msg); - -struct UnmanagedVector ibc_channel_close(struct cache_t *cache, - struct ByteSliceView checksum, - struct ByteSliceView env, - struct ByteSliceView msg, - struct Db db, - struct GoApi api, - struct GoQuerier querier, - uint64_t gas_limit, - bool print_debug, - uint64_t *gas_used, - struct UnmanagedVector *error_msg); - -struct UnmanagedVector ibc_packet_receive(struct cache_t *cache, - struct ByteSliceView checksum, - struct ByteSliceView env, - struct ByteSliceView msg, - struct Db db, - struct GoApi api, - struct GoQuerier querier, - uint64_t gas_limit, - bool print_debug, - uint64_t *gas_used, - struct UnmanagedVector *error_msg); - -struct UnmanagedVector ibc_packet_ack(struct cache_t *cache, - struct ByteSliceView checksum, - struct ByteSliceView env, - struct ByteSliceView msg, - struct Db db, - struct GoApi api, - struct GoQuerier querier, - uint64_t gas_limit, - bool print_debug, - uint64_t *gas_used, - struct UnmanagedVector *error_msg); - -struct UnmanagedVector ibc_packet_timeout(struct cache_t *cache, - struct ByteSliceView checksum, - struct ByteSliceView env, - struct ByteSliceView msg, - struct Db db, - struct GoApi api, - struct GoQuerier querier, - uint64_t gas_limit, - bool print_debug, - uint64_t *gas_used, - struct UnmanagedVector *error_msg); + struct UnmanagedVector *error_msg, + struct UnmanagedVector *out); + +int32_t ibc_channel_close(struct CachePtr cache, + struct ByteSliceView checksum, + struct ByteSliceView env, + struct ByteSliceView msg, + struct Db db, + struct GoApi api, + struct GoQuerier querier, + uint64_t gas_limit, + bool print_debug, + uint64_t *gas_used, + struct UnmanagedVector *error_msg, + struct UnmanagedVector *out); + +int32_t ibc_packet_receive(struct CachePtr cache, + struct ByteSliceView checksum, + struct ByteSliceView env, + struct ByteSliceView msg, + struct Db db, + struct GoApi api, + struct GoQuerier querier, + uint64_t gas_limit, + bool print_debug, + uint64_t *gas_used, + struct UnmanagedVector *error_msg, + struct UnmanagedVector *out); + +int32_t ibc_packet_ack(struct CachePtr cache, + struct ByteSliceView checksum, + struct ByteSliceView env, + struct ByteSliceView msg, + struct Db db, + struct GoApi api, + struct GoQuerier querier, + uint64_t gas_limit, + bool print_debug, + uint64_t *gas_used, + struct UnmanagedVector *error_msg, + struct UnmanagedVector *out); + +int32_t ibc_packet_timeout(struct CachePtr cache, + struct ByteSliceView checksum, + struct ByteSliceView env, + struct ByteSliceView msg, + struct Db db, + struct GoApi api, + struct GoQuerier querier, + uint64_t gas_limit, + bool print_debug, + uint64_t *gas_used, + struct UnmanagedVector *error_msg, + struct UnmanagedVector *out); struct UnmanagedVector new_unmanaged_vector(bool nil, const uint8_t *ptr, uintptr_t length); diff --git a/internal/api/lib.go b/internal/api/lib.go index b4d96d264..659f90be8 100644 --- a/internal/api/lib.go +++ b/internal/api/lib.go @@ -7,7 +7,7 @@ import "C" import ( "fmt" "runtime" - "syscall" + "unsafe" "github.com/CosmWasm/wasmvm/types" ) @@ -29,7 +29,13 @@ type ( type cu8_ptr = *C.uint8_t type Cache struct { - ptr *C.cache_t + ptr unsafe.Pointer +} + +func toCachePtr(cache Cache) C.CachePtr { + return C.CachePtr{ + ptr: cache.ptr, + } } type Querier = types.Querier @@ -43,37 +49,40 @@ func InitCache(dataDir string, supportedFeatures string, cacheSize uint32, insta f := makeView(supportedFeaturesBytes) defer runtime.KeepAlive(supportedFeaturesBytes) - errmsg := newUnmanagedVector(nil) + errmsg := uninitializedUnmanagedVector() + out := C.CachePtr{} - ptr, err := C.init_cache(d, f, cu32(cacheSize), cu32(instanceMemoryLimit), &errmsg) - if err != nil { - return Cache{}, errorWithMessage(err, errmsg) + err := C.init_cache(d, f, cu32(cacheSize), cu32(instanceMemoryLimit), &errmsg, &out) + if err != C.ErrorCode_Success { + return Cache{}, ffiErrorWithMessage2(err, errmsg) } - return Cache{ptr: ptr}, nil + return Cache{ptr: out.ptr}, nil } func ReleaseCache(cache Cache) { - C.release_cache(cache.ptr) + C.release_cache(toCachePtr(cache)) // No error case that needs handling } +// / StoreCode stored the Wasm blob and returns the checksum func StoreCode(cache Cache, wasm []byte) ([]byte, error) { w := makeView(wasm) defer runtime.KeepAlive(wasm) - errmsg := newUnmanagedVector(nil) - checksum, err := C.save_wasm(cache.ptr, w, &errmsg) - if err != nil { - return nil, errorWithMessage(err, errmsg) + errmsg := uninitializedUnmanagedVector() + out := uninitializedUnmanagedVector() + err := C.save_wasm(toCachePtr(cache), w, &errmsg, &out) + if err != C.ErrorCode_Success { + return nil, ffiErrorWithMessage2(err, errmsg) } - return copyAndDestroyUnmanagedVector(checksum), nil + return copyAndDestroyUnmanagedVector(out), nil } func RemoveCode(cache Cache, checksum []byte) error { cs := makeView(checksum) defer runtime.KeepAlive(checksum) errmsg := newUnmanagedVector(nil) - _, err := C.remove_wasm(cache.ptr, cs, &errmsg) - if err != nil { - return errorWithMessage(err, errmsg) + err := C.remove_wasm(toCachePtr(cache), cs, &errmsg) + if err != 0 { + return ffiErrorWithMessage2(err, errmsg) } return nil } @@ -81,21 +90,22 @@ func RemoveCode(cache Cache, checksum []byte) error { func GetCode(cache Cache, checksum []byte) ([]byte, error) { cs := makeView(checksum) defer runtime.KeepAlive(checksum) - errmsg := newUnmanagedVector(nil) - wasm, err := C.load_wasm(cache.ptr, cs, &errmsg) - if err != nil { - return nil, errorWithMessage(err, errmsg) + errmsg := uninitializedUnmanagedVector() + out := uninitializedUnmanagedVector() + err := C.load_wasm(toCachePtr(cache), cs, &errmsg, &out) + if err != C.ErrorCode_Success { + return nil, ffiErrorWithMessage2(err, errmsg) } - return copyAndDestroyUnmanagedVector(wasm), nil + return copyAndDestroyUnmanagedVector(out), nil } func Pin(cache Cache, checksum []byte) error { cs := makeView(checksum) defer runtime.KeepAlive(checksum) - errmsg := newUnmanagedVector(nil) - _, err := C.pin(cache.ptr, cs, &errmsg) - if err != nil { - return errorWithMessage(err, errmsg) + errmsg := uninitializedUnmanagedVector() + err := C.pin(toCachePtr(cache), cs, &errmsg) + if err != C.ErrorCode_Success { + return ffiErrorWithMessage2(err, errmsg) } return nil } @@ -103,10 +113,10 @@ func Pin(cache Cache, checksum []byte) error { func Unpin(cache Cache, checksum []byte) error { cs := makeView(checksum) defer runtime.KeepAlive(checksum) - errmsg := newUnmanagedVector(nil) - _, err := C.unpin(cache.ptr, cs, &errmsg) - if err != nil { - return errorWithMessage(err, errmsg) + errmsg := uninitializedUnmanagedVector() + err := C.unpin(toCachePtr(cache), cs, &errmsg) + if err != C.ErrorCode_Success { + return ffiErrorWithMessage2(err, errmsg) } return nil } @@ -114,14 +124,15 @@ func Unpin(cache Cache, checksum []byte) error { func AnalyzeCode(cache Cache, checksum []byte) (*types.AnalysisReport, error) { cs := makeView(checksum) defer runtime.KeepAlive(checksum) - errmsg := newUnmanagedVector(nil) - report, err := C.analyze_code(cache.ptr, cs, &errmsg) - if err != nil { - return nil, errorWithMessage(err, errmsg) + errmsg := uninitializedUnmanagedVector() + out := C.AnalysisReport{} + err := C.analyze_code(toCachePtr(cache), cs, &errmsg, &out) + if err != C.ErrorCode_Success { + return nil, ffiErrorWithMessage2(err, errmsg) } - requiredCapabilities := string(copyAndDestroyUnmanagedVector(report.required_capabilities)) + requiredCapabilities := string(copyAndDestroyUnmanagedVector(out.required_capabilities)) res := types.AnalysisReport{ - HasIBCEntryPoints: bool(report.has_ibc_entry_points), + HasIBCEntryPoints: bool(out.has_ibc_entry_points), RequiredFeatures: requiredCapabilities, RequiredCapabilities: requiredCapabilities, } @@ -129,10 +140,11 @@ func AnalyzeCode(cache Cache, checksum []byte) (*types.AnalysisReport, error) { } func GetMetrics(cache Cache) (*types.Metrics, error) { - errmsg := newUnmanagedVector(nil) - metrics, err := C.get_metrics(cache.ptr, &errmsg) - if err != nil { - return nil, errorWithMessage(err, errmsg) + errmsg := uninitializedUnmanagedVector() + metrics := C.Metrics{} + err := C.get_metrics(toCachePtr(cache), &errmsg, &metrics) + if err != C.ErrorCode_Success { + return nil, ffiErrorWithMessage2(err, errmsg) } return &types.Metrics{ @@ -177,14 +189,15 @@ func Instantiate( a := buildAPI(api) q := buildQuerier(querier) var gasUsed cu64 - errmsg := newUnmanagedVector(nil) + errmsg := uninitializedUnmanagedVector() + out := uninitializedUnmanagedVector() - res, err := C.instantiate(cache.ptr, cs, e, i, m, db, a, q, cu64(gasLimit), cbool(printDebug), &gasUsed, &errmsg) - if err != nil && err.(syscall.Errno) != C.ErrnoValue_Success { + err := C.instantiate(toCachePtr(cache), cs, e, i, m, db, a, q, cu64(gasLimit), cbool(printDebug), &gasUsed, &errmsg, &out) + if err != C.ErrorCode_Success { // Depending on the nature of the error, `gasUsed` will either have a meaningful value, or just 0. - return nil, uint64(gasUsed), errorWithMessage(err, errmsg) + return nil, uint64(gasUsed), ffiErrorWithMessage2(err, errmsg) } - return copyAndDestroyUnmanagedVector(res), uint64(gasUsed), nil + return copyAndDestroyUnmanagedVector(out), uint64(gasUsed), nil } func Execute( @@ -217,14 +230,15 @@ func Execute( a := buildAPI(api) q := buildQuerier(querier) var gasUsed cu64 - errmsg := newUnmanagedVector(nil) + errmsg := uninitializedUnmanagedVector() + out := uninitializedUnmanagedVector() - res, err := C.execute(cache.ptr, cs, e, i, m, db, a, q, cu64(gasLimit), cbool(printDebug), &gasUsed, &errmsg) - if err != nil && err.(syscall.Errno) != C.ErrnoValue_Success { + err := C.execute(toCachePtr(cache), cs, e, i, m, db, a, q, cu64(gasLimit), cbool(printDebug), &gasUsed, &errmsg, &out) + if err != C.ErrorCode_Success { // Depending on the nature of the error, `gasUsed` will either have a meaningful value, or just 0. - return nil, uint64(gasUsed), errorWithMessage(err, errmsg) + return nil, uint64(gasUsed), ffiErrorWithMessage2(err, errmsg) } - return copyAndDestroyUnmanagedVector(res), uint64(gasUsed), nil + return copyAndDestroyUnmanagedVector(out), uint64(gasUsed), nil } func Migrate( @@ -254,14 +268,15 @@ func Migrate( a := buildAPI(api) q := buildQuerier(querier) var gasUsed cu64 - errmsg := newUnmanagedVector(nil) + errmsg := uninitializedUnmanagedVector() + out := uninitializedUnmanagedVector() - res, err := C.migrate(cache.ptr, cs, e, m, db, a, q, cu64(gasLimit), cbool(printDebug), &gasUsed, &errmsg) - if err != nil && err.(syscall.Errno) != C.ErrnoValue_Success { + err := C.migrate(toCachePtr(cache), cs, e, m, db, a, q, cu64(gasLimit), cbool(printDebug), &gasUsed, &errmsg, &out) + if err != C.ErrorCode_Success { // Depending on the nature of the error, `gasUsed` will either have a meaningful value, or just 0. - return nil, uint64(gasUsed), errorWithMessage(err, errmsg) + return nil, uint64(gasUsed), ffiErrorWithMessage2(err, errmsg) } - return copyAndDestroyUnmanagedVector(res), uint64(gasUsed), nil + return copyAndDestroyUnmanagedVector(out), uint64(gasUsed), nil } func Sudo( @@ -291,14 +306,15 @@ func Sudo( a := buildAPI(api) q := buildQuerier(querier) var gasUsed cu64 - errmsg := newUnmanagedVector(nil) + errmsg := uninitializedUnmanagedVector() + out := uninitializedUnmanagedVector() - res, err := C.sudo(cache.ptr, cs, e, m, db, a, q, cu64(gasLimit), cbool(printDebug), &gasUsed, &errmsg) - if err != nil && err.(syscall.Errno) != C.ErrnoValue_Success { + err := C.sudo(toCachePtr(cache), cs, e, m, db, a, q, cu64(gasLimit), cbool(printDebug), &gasUsed, &errmsg, &out) + if err != C.ErrorCode_Success { // Depending on the nature of the error, `gasUsed` will either have a meaningful value, or just 0. - return nil, uint64(gasUsed), errorWithMessage(err, errmsg) + return nil, uint64(gasUsed), ffiErrorWithMessage2(err, errmsg) } - return copyAndDestroyUnmanagedVector(res), uint64(gasUsed), nil + return copyAndDestroyUnmanagedVector(out), uint64(gasUsed), nil } func Reply( @@ -328,14 +344,15 @@ func Reply( a := buildAPI(api) q := buildQuerier(querier) var gasUsed cu64 - errmsg := newUnmanagedVector(nil) + errmsg := uninitializedUnmanagedVector() + out := uninitializedUnmanagedVector() - res, err := C.reply(cache.ptr, cs, e, r, db, a, q, cu64(gasLimit), cbool(printDebug), &gasUsed, &errmsg) - if err != nil && err.(syscall.Errno) != C.ErrnoValue_Success { + err := C.reply(toCachePtr(cache), cs, e, r, db, a, q, cu64(gasLimit), cbool(printDebug), &gasUsed, &errmsg, &out) + if err != C.ErrorCode_Success { // Depending on the nature of the error, `gasUsed` will either have a meaningful value, or just 0. - return nil, uint64(gasUsed), errorWithMessage(err, errmsg) + return nil, uint64(gasUsed), ffiErrorWithMessage2(err, errmsg) } - return copyAndDestroyUnmanagedVector(res), uint64(gasUsed), nil + return copyAndDestroyUnmanagedVector(out), uint64(gasUsed), nil } func Query( @@ -365,14 +382,15 @@ func Query( a := buildAPI(api) q := buildQuerier(querier) var gasUsed cu64 - errmsg := newUnmanagedVector(nil) + errmsg := uninitializedUnmanagedVector() + out := uninitializedUnmanagedVector() - res, err := C.query(cache.ptr, cs, e, m, db, a, q, cu64(gasLimit), cbool(printDebug), &gasUsed, &errmsg) - if err != nil && err.(syscall.Errno) != C.ErrnoValue_Success { + err := C.query(toCachePtr(cache), cs, e, m, db, a, q, cu64(gasLimit), cbool(printDebug), &gasUsed, &errmsg, &out) + if err != C.ErrorCode_Success { // Depending on the nature of the error, `gasUsed` will either have a meaningful value, or just 0. - return nil, uint64(gasUsed), errorWithMessage(err, errmsg) + return nil, uint64(gasUsed), ffiErrorWithMessage2(err, errmsg) } - return copyAndDestroyUnmanagedVector(res), uint64(gasUsed), nil + return copyAndDestroyUnmanagedVector(out), uint64(gasUsed), nil } func IBCChannelOpen( @@ -402,14 +420,15 @@ func IBCChannelOpen( a := buildAPI(api) q := buildQuerier(querier) var gasUsed cu64 - errmsg := newUnmanagedVector(nil) + errmsg := uninitializedUnmanagedVector() + out := uninitializedUnmanagedVector() - res, err := C.ibc_channel_open(cache.ptr, cs, e, m, db, a, q, cu64(gasLimit), cbool(printDebug), &gasUsed, &errmsg) - if err != nil && err.(syscall.Errno) != C.ErrnoValue_Success { + err := C.ibc_channel_open(toCachePtr(cache), cs, e, m, db, a, q, cu64(gasLimit), cbool(printDebug), &gasUsed, &errmsg, &out) + if err != C.ErrorCode_Success { // Depending on the nature of the error, `gasUsed` will either have a meaningful value, or just 0. - return nil, uint64(gasUsed), errorWithMessage(err, errmsg) + return nil, uint64(gasUsed), ffiErrorWithMessage2(err, errmsg) } - return copyAndDestroyUnmanagedVector(res), uint64(gasUsed), nil + return copyAndDestroyUnmanagedVector(out), uint64(gasUsed), nil } func IBCChannelConnect( @@ -439,14 +458,15 @@ func IBCChannelConnect( a := buildAPI(api) q := buildQuerier(querier) var gasUsed cu64 - errmsg := newUnmanagedVector(nil) + errmsg := uninitializedUnmanagedVector() + out := uninitializedUnmanagedVector() - res, err := C.ibc_channel_connect(cache.ptr, cs, e, m, db, a, q, cu64(gasLimit), cbool(printDebug), &gasUsed, &errmsg) - if err != nil && err.(syscall.Errno) != C.ErrnoValue_Success { + err := C.ibc_channel_connect(toCachePtr(cache), cs, e, m, db, a, q, cu64(gasLimit), cbool(printDebug), &gasUsed, &errmsg, &out) + if err != C.ErrorCode_Success { // Depending on the nature of the error, `gasUsed` will either have a meaningful value, or just 0. - return nil, uint64(gasUsed), errorWithMessage(err, errmsg) + return nil, uint64(gasUsed), ffiErrorWithMessage2(err, errmsg) } - return copyAndDestroyUnmanagedVector(res), uint64(gasUsed), nil + return copyAndDestroyUnmanagedVector(out), uint64(gasUsed), nil } func IBCChannelClose( @@ -476,14 +496,15 @@ func IBCChannelClose( a := buildAPI(api) q := buildQuerier(querier) var gasUsed cu64 - errmsg := newUnmanagedVector(nil) + errmsg := uninitializedUnmanagedVector() + out := uninitializedUnmanagedVector() - res, err := C.ibc_channel_close(cache.ptr, cs, e, m, db, a, q, cu64(gasLimit), cbool(printDebug), &gasUsed, &errmsg) - if err != nil && err.(syscall.Errno) != C.ErrnoValue_Success { + err := C.ibc_channel_close(toCachePtr(cache), cs, e, m, db, a, q, cu64(gasLimit), cbool(printDebug), &gasUsed, &errmsg, &out) + if err != C.ErrorCode_Success { // Depending on the nature of the error, `gasUsed` will either have a meaningful value, or just 0. - return nil, uint64(gasUsed), errorWithMessage(err, errmsg) + return nil, uint64(gasUsed), ffiErrorWithMessage2(err, errmsg) } - return copyAndDestroyUnmanagedVector(res), uint64(gasUsed), nil + return copyAndDestroyUnmanagedVector(out), uint64(gasUsed), nil } func IBCPacketReceive( @@ -513,14 +534,15 @@ func IBCPacketReceive( a := buildAPI(api) q := buildQuerier(querier) var gasUsed cu64 - errmsg := newUnmanagedVector(nil) + errmsg := uninitializedUnmanagedVector() + out := uninitializedUnmanagedVector() - res, err := C.ibc_packet_receive(cache.ptr, cs, e, pa, db, a, q, cu64(gasLimit), cbool(printDebug), &gasUsed, &errmsg) - if err != nil && err.(syscall.Errno) != C.ErrnoValue_Success { + err := C.ibc_packet_receive(toCachePtr(cache), cs, e, pa, db, a, q, cu64(gasLimit), cbool(printDebug), &gasUsed, &errmsg, &out) + if err != C.ErrorCode_Success { // Depending on the nature of the error, `gasUsed` will either have a meaningful value, or just 0. - return nil, uint64(gasUsed), errorWithMessage(err, errmsg) + return nil, uint64(gasUsed), ffiErrorWithMessage2(err, errmsg) } - return copyAndDestroyUnmanagedVector(res), uint64(gasUsed), nil + return copyAndDestroyUnmanagedVector(out), uint64(gasUsed), nil } func IBCPacketAck( @@ -550,14 +572,15 @@ func IBCPacketAck( a := buildAPI(api) q := buildQuerier(querier) var gasUsed cu64 - errmsg := newUnmanagedVector(nil) + errmsg := uninitializedUnmanagedVector() + out := uninitializedUnmanagedVector() - res, err := C.ibc_packet_ack(cache.ptr, cs, e, ac, db, a, q, cu64(gasLimit), cbool(printDebug), &gasUsed, &errmsg) - if err != nil && err.(syscall.Errno) != C.ErrnoValue_Success { + err := C.ibc_packet_ack(toCachePtr(cache), cs, e, ac, db, a, q, cu64(gasLimit), cbool(printDebug), &gasUsed, &errmsg, &out) + if err != C.ErrorCode_Success { // Depending on the nature of the error, `gasUsed` will either have a meaningful value, or just 0. - return nil, uint64(gasUsed), errorWithMessage(err, errmsg) + return nil, uint64(gasUsed), ffiErrorWithMessage2(err, errmsg) } - return copyAndDestroyUnmanagedVector(res), uint64(gasUsed), nil + return copyAndDestroyUnmanagedVector(out), uint64(gasUsed), nil } func IBCPacketTimeout( @@ -587,26 +610,34 @@ func IBCPacketTimeout( a := buildAPI(api) q := buildQuerier(querier) var gasUsed cu64 - errmsg := newUnmanagedVector(nil) + errmsg := uninitializedUnmanagedVector() + out := uninitializedUnmanagedVector() - res, err := C.ibc_packet_timeout(cache.ptr, cs, e, pa, db, a, q, cu64(gasLimit), cbool(printDebug), &gasUsed, &errmsg) - if err != nil && err.(syscall.Errno) != C.ErrnoValue_Success { + err := C.ibc_packet_timeout(toCachePtr(cache), cs, e, pa, db, a, q, cu64(gasLimit), cbool(printDebug), &gasUsed, &errmsg, &out) + if err != C.ErrorCode_Success { // Depending on the nature of the error, `gasUsed` will either have a meaningful value, or just 0. - return nil, uint64(gasUsed), errorWithMessage(err, errmsg) + return nil, uint64(gasUsed), ffiErrorWithMessage2(err, errmsg) } - return copyAndDestroyUnmanagedVector(res), uint64(gasUsed), nil + return copyAndDestroyUnmanagedVector(out), uint64(gasUsed), nil } -/**** To error module ***/ +// ffiErrorWithMessage2 takes an error code, tries to read the error message +// written by the Rust code and returns a readable error. +// +// This function must only be called on non-zero error codes. +func ffiErrorWithMessage2(errorCode cint, errMsg C.UnmanagedVector) error { + if errorCode == 0 { + panic("Called ffiErrorWithMessage2 with a 0 error code (no error)") + } -func errorWithMessage(err error, b C.UnmanagedVector) error { - // this checks for out of gas as a special case - if errno, ok := err.(syscall.Errno); ok && int(errno) == 2 { + // Checks for out of gas as a special case. See "ErrnoValue" in libwasmvm/src/error/rust.rs. + if errorCode == C.ErrorCode_OutOfGas { return types.OutOfGasError{} } - msg := copyAndDestroyUnmanagedVector(b) - if msg == nil { - return err + + msg := copyAndDestroyUnmanagedVector(errMsg) + if msg == nil || len(msg) == 0 { + return fmt.Errorf("FFI call errored with error code %#v and no error message.", errorCode) } return fmt.Errorf("%s", string(msg)) } diff --git a/internal/api/libwasmvm.a b/internal/api/libwasmvm.a new file mode 100644 index 000000000..5946ea58a Binary files /dev/null and b/internal/api/libwasmvm.a differ diff --git a/internal/api/memory.go b/internal/api/memory.go index c7479c249..549e221fd 100644 --- a/internal/api/memory.go +++ b/internal/api/memory.go @@ -39,6 +39,12 @@ func constructUnmanagedVector(is_none cbool, ptr cu8_ptr, len cusize, cap cusize } } +// uninitializedUnmanagedVector returns an invalid C.UnmanagedVector +// instance. Only use then after someone wrote an instance to it. +func uninitializedUnmanagedVector() C.UnmanagedVector { + return C.UnmanagedVector{} +} + func newUnmanagedVector(data []byte) C.UnmanagedVector { if data == nil { return C.new_unmanaged_vector(cbool(true), cu8_ptr(nil), cusize(0)) @@ -65,7 +71,7 @@ func copyAndDestroyUnmanagedVector(v C.UnmanagedVector) []byte { // C.GoBytes create a copy (https://stackoverflow.com/a/40950744/2013738) out = C.GoBytes(unsafe.Pointer(v.ptr), cint(v.len)) } - C.destroy_unmanaged_vector(v) + C.destroy_unmanaged_vector(v) // No error case that needs handling return out } diff --git a/internal/api/wasmvm.dll b/internal/api/wasmvm.dll deleted file mode 100755 index 7a3cc7017..000000000 Binary files a/internal/api/wasmvm.dll and /dev/null differ diff --git a/libwasmvm/Cargo.lock b/libwasmvm/Cargo.lock index b4a94f0dc..0a69cb49e 100644 --- a/libwasmvm/Cargo.lock +++ b/libwasmvm/Cargo.lock @@ -156,9 +156,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.72" +version = "1.0.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" +checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4" [[package]] name = "cfg-if" @@ -641,27 +641,6 @@ dependencies = [ "syn", ] -[[package]] -name = "errno" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" -dependencies = [ - "errno-dragonfly", - "libc", - "winapi", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - [[package]] name = "fallible-iterator" version = "0.2.0" @@ -850,9 +829,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.132" +version = "0.2.137" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5" +checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" [[package]] name = "libloading" @@ -1929,7 +1908,6 @@ dependencies = [ "cbindgen", "cosmwasm-std", "cosmwasm-vm", - "errno", "hex", "serde", "serde_json", diff --git a/libwasmvm/Cargo.toml b/libwasmvm/Cargo.toml index 83e5feba6..012865a73 100644 --- a/libwasmvm/Cargo.toml +++ b/libwasmvm/Cargo.toml @@ -28,7 +28,6 @@ backtraces = [] [dependencies] cosmwasm-std = { git = "https://github.com/CosmWasm/cosmwasm.git", rev = "v1.2.0-rc.1", features = ["staking", "stargate", "iterator"] } cosmwasm-vm = { git = "https://github.com/CosmWasm/cosmwasm.git", rev = "v1.2.0-rc.1", features = ["staking", "stargate", "iterator"] } -errno = "0.2" serde_json = "1.0" thiserror = "1.0" hex = "0.4" diff --git a/libwasmvm/bindings.h b/libwasmvm/bindings.h index 5f7e31f8b..3458e32d7 100644 --- a/libwasmvm/bindings.h +++ b/libwasmvm/bindings.h @@ -9,12 +9,16 @@ #include #include -enum ErrnoValue { - ErrnoValue_Success = 0, - ErrnoValue_Other = 1, - ErrnoValue_OutOfGas = 2, +/** + * An error code used to communicate the errors of FFI calls. + * Similar to shell codes and errno, 0 means no error. + */ +enum ErrorCode { + ErrorCode_Success = 0, + ErrorCode_Other = 1, + ErrorCode_OutOfGas = 2, }; -typedef int32_t ErrnoValue; +typedef int32_t ErrorCode; /** * This enum gives names to the status codes returned from Go callbacks to Rust. @@ -53,10 +57,6 @@ enum GoError { }; typedef int32_t GoError; -typedef struct cache_t { - -} cache_t; - /** * A view into an externally owned byte slice (Go `[]byte`). * Use this for the current call only. A view cannot be copied for safety reasons. @@ -112,10 +112,10 @@ typedef struct ByteSliceView { * Transferring ownership from Rust to Go using return values of FFI calls: * * ``` - * # use wasmvm::{cache_t, ByteSliceView, UnmanagedVector}; + * # use wasmvm::{CachePtr, ByteSliceView, UnmanagedVector}; * #[no_mangle] * pub extern "C" fn save_wasm_to_cache( - * cache: *mut cache_t, + * cache: CachePtr, * wasm: ByteSliceView, * error_msg: Option<&mut UnmanagedVector>, * ) -> UnmanagedVector { @@ -193,6 +193,18 @@ typedef struct UnmanagedVector { uintptr_t cap; } UnmanagedVector; +/** + * A struct that holds a pointer to the cache. This struct can be + * copied freely. + * + * In case of `init_cache` we need a pointer to a pointer for the output + * and in order to be able to do this consistently with e.g. `AnalysisReport` + * and `Metrics`, we use thiy type. + */ +typedef struct CachePtr { + void *ptr; +} CachePtr; + /** * The result type of the FFI function analyze_code. * @@ -303,83 +315,146 @@ typedef struct GoQuerier { struct Querier_vtable vtable; } GoQuerier; -struct cache_t *init_cache(struct ByteSliceView data_dir, - struct ByteSliceView available_capabilities, - uint32_t cache_size, - uint32_t instance_memory_limit, - struct UnmanagedVector *error_msg); +int32_t init_cache(struct ByteSliceView data_dir, + struct ByteSliceView available_capabilities, + uint32_t cache_size, + uint32_t instance_memory_limit, + struct UnmanagedVector *error_msg, + struct CachePtr *out); -struct UnmanagedVector save_wasm(struct cache_t *cache, - struct ByteSliceView wasm, - struct UnmanagedVector *error_msg); +int32_t save_wasm(struct CachePtr cache, + struct ByteSliceView wasm, + struct UnmanagedVector *error_msg, + struct UnmanagedVector *out); -void remove_wasm(struct cache_t *cache, - struct ByteSliceView checksum, - struct UnmanagedVector *error_msg); +int32_t remove_wasm(struct CachePtr cache, + struct ByteSliceView checksum, + struct UnmanagedVector *error_msg); -struct UnmanagedVector load_wasm(struct cache_t *cache, - struct ByteSliceView checksum, - struct UnmanagedVector *error_msg); +int32_t load_wasm(struct CachePtr cache, + struct ByteSliceView checksum, + struct UnmanagedVector *error_msg, + struct UnmanagedVector *out); -void pin(struct cache_t *cache, struct ByteSliceView checksum, struct UnmanagedVector *error_msg); +int32_t pin(struct CachePtr cache, + struct ByteSliceView checksum, + struct UnmanagedVector *error_msg); -void unpin(struct cache_t *cache, struct ByteSliceView checksum, struct UnmanagedVector *error_msg); +int32_t unpin(struct CachePtr cache, + struct ByteSliceView checksum, + struct UnmanagedVector *error_msg); -struct AnalysisReport analyze_code(struct cache_t *cache, - struct ByteSliceView checksum, - struct UnmanagedVector *error_msg); +int32_t analyze_code(struct CachePtr cache, + struct ByteSliceView checksum, + struct UnmanagedVector *error_msg, + struct AnalysisReport *out); -struct Metrics get_metrics(struct cache_t *cache, struct UnmanagedVector *error_msg); +int32_t get_metrics(struct CachePtr cache, struct UnmanagedVector *error_msg, struct Metrics *out); /** * frees a cache reference * * # Safety * - * This must be called exactly once for any `*cache_t` returned by `init_cache` + * This must be called exactly once for any `CachePtr` returned by `init_cache` * and cannot be called on any other pointer. */ -void release_cache(struct cache_t *cache); - -struct UnmanagedVector instantiate(struct cache_t *cache, - struct ByteSliceView checksum, - struct ByteSliceView env, - struct ByteSliceView info, - struct ByteSliceView msg, - struct Db db, - struct GoApi api, - struct GoQuerier querier, - uint64_t gas_limit, - bool print_debug, - uint64_t *gas_used, - struct UnmanagedVector *error_msg); - -struct UnmanagedVector execute(struct cache_t *cache, - struct ByteSliceView checksum, - struct ByteSliceView env, - struct ByteSliceView info, - struct ByteSliceView msg, - struct Db db, - struct GoApi api, - struct GoQuerier querier, - uint64_t gas_limit, - bool print_debug, - uint64_t *gas_used, - struct UnmanagedVector *error_msg); - -struct UnmanagedVector migrate(struct cache_t *cache, - struct ByteSliceView checksum, - struct ByteSliceView env, - struct ByteSliceView msg, - struct Db db, - struct GoApi api, - struct GoQuerier querier, - uint64_t gas_limit, - bool print_debug, - uint64_t *gas_used, - struct UnmanagedVector *error_msg); - -struct UnmanagedVector sudo(struct cache_t *cache, +void release_cache(struct CachePtr cache); + +int32_t instantiate(struct CachePtr cache, + struct ByteSliceView checksum, + struct ByteSliceView env, + struct ByteSliceView info, + struct ByteSliceView msg, + struct Db db, + struct GoApi api, + struct GoQuerier querier, + uint64_t gas_limit, + bool print_debug, + uint64_t *gas_used, + struct UnmanagedVector *error_msg, + struct UnmanagedVector *out); + +int32_t execute(struct CachePtr cache, + struct ByteSliceView checksum, + struct ByteSliceView env, + struct ByteSliceView info, + struct ByteSliceView msg, + struct Db db, + struct GoApi api, + struct GoQuerier querier, + uint64_t gas_limit, + bool print_debug, + uint64_t *gas_used, + struct UnmanagedVector *error_msg, + struct UnmanagedVector *out); + +int32_t migrate(struct CachePtr cache, + struct ByteSliceView checksum, + struct ByteSliceView env, + struct ByteSliceView msg, + struct Db db, + struct GoApi api, + struct GoQuerier querier, + uint64_t gas_limit, + bool print_debug, + uint64_t *gas_used, + struct UnmanagedVector *error_msg, + struct UnmanagedVector *out); + +int32_t sudo(struct CachePtr cache, + struct ByteSliceView checksum, + struct ByteSliceView env, + struct ByteSliceView msg, + struct Db db, + struct GoApi api, + struct GoQuerier querier, + uint64_t gas_limit, + bool print_debug, + uint64_t *gas_used, + struct UnmanagedVector *error_msg, + struct UnmanagedVector *out); + +int32_t reply(struct CachePtr cache, + struct ByteSliceView checksum, + struct ByteSliceView env, + struct ByteSliceView msg, + struct Db db, + struct GoApi api, + struct GoQuerier querier, + uint64_t gas_limit, + bool print_debug, + uint64_t *gas_used, + struct UnmanagedVector *error_msg, + struct UnmanagedVector *out); + +int32_t query(struct CachePtr cache, + struct ByteSliceView checksum, + struct ByteSliceView env, + struct ByteSliceView msg, + struct Db db, + struct GoApi api, + struct GoQuerier querier, + uint64_t gas_limit, + bool print_debug, + uint64_t *gas_used, + struct UnmanagedVector *error_msg, + struct UnmanagedVector *out); + +int32_t ibc_channel_open(struct CachePtr cache, + struct ByteSliceView checksum, + struct ByteSliceView env, + struct ByteSliceView msg, + struct Db db, + struct GoApi api, + struct GoQuerier querier, + uint64_t gas_limit, + bool print_debug, + uint64_t *gas_used, + struct UnmanagedVector *error_msg, + struct UnmanagedVector *out); + +int32_t ibc_channel_connect(struct CachePtr cache, struct ByteSliceView checksum, struct ByteSliceView env, struct ByteSliceView msg, @@ -389,103 +464,60 @@ struct UnmanagedVector sudo(struct cache_t *cache, uint64_t gas_limit, bool print_debug, uint64_t *gas_used, - struct UnmanagedVector *error_msg); - -struct UnmanagedVector reply(struct cache_t *cache, - struct ByteSliceView checksum, - struct ByteSliceView env, - struct ByteSliceView msg, - struct Db db, - struct GoApi api, - struct GoQuerier querier, - uint64_t gas_limit, - bool print_debug, - uint64_t *gas_used, - struct UnmanagedVector *error_msg); - -struct UnmanagedVector query(struct cache_t *cache, - struct ByteSliceView checksum, - struct ByteSliceView env, - struct ByteSliceView msg, - struct Db db, - struct GoApi api, - struct GoQuerier querier, - uint64_t gas_limit, - bool print_debug, - uint64_t *gas_used, - struct UnmanagedVector *error_msg); - -struct UnmanagedVector ibc_channel_open(struct cache_t *cache, - struct ByteSliceView checksum, - struct ByteSliceView env, - struct ByteSliceView msg, - struct Db db, - struct GoApi api, - struct GoQuerier querier, - uint64_t gas_limit, - bool print_debug, - uint64_t *gas_used, - struct UnmanagedVector *error_msg); - -struct UnmanagedVector ibc_channel_connect(struct cache_t *cache, - struct ByteSliceView checksum, - struct ByteSliceView env, - struct ByteSliceView msg, - struct Db db, - struct GoApi api, - struct GoQuerier querier, - uint64_t gas_limit, - bool print_debug, - uint64_t *gas_used, - struct UnmanagedVector *error_msg); - -struct UnmanagedVector ibc_channel_close(struct cache_t *cache, - struct ByteSliceView checksum, - struct ByteSliceView env, - struct ByteSliceView msg, - struct Db db, - struct GoApi api, - struct GoQuerier querier, - uint64_t gas_limit, - bool print_debug, - uint64_t *gas_used, - struct UnmanagedVector *error_msg); - -struct UnmanagedVector ibc_packet_receive(struct cache_t *cache, - struct ByteSliceView checksum, - struct ByteSliceView env, - struct ByteSliceView msg, - struct Db db, - struct GoApi api, - struct GoQuerier querier, - uint64_t gas_limit, - bool print_debug, - uint64_t *gas_used, - struct UnmanagedVector *error_msg); - -struct UnmanagedVector ibc_packet_ack(struct cache_t *cache, - struct ByteSliceView checksum, - struct ByteSliceView env, - struct ByteSliceView msg, - struct Db db, - struct GoApi api, - struct GoQuerier querier, - uint64_t gas_limit, - bool print_debug, - uint64_t *gas_used, - struct UnmanagedVector *error_msg); - -struct UnmanagedVector ibc_packet_timeout(struct cache_t *cache, - struct ByteSliceView checksum, - struct ByteSliceView env, - struct ByteSliceView msg, - struct Db db, - struct GoApi api, - struct GoQuerier querier, - uint64_t gas_limit, - bool print_debug, - uint64_t *gas_used, - struct UnmanagedVector *error_msg); + struct UnmanagedVector *error_msg, + struct UnmanagedVector *out); + +int32_t ibc_channel_close(struct CachePtr cache, + struct ByteSliceView checksum, + struct ByteSliceView env, + struct ByteSliceView msg, + struct Db db, + struct GoApi api, + struct GoQuerier querier, + uint64_t gas_limit, + bool print_debug, + uint64_t *gas_used, + struct UnmanagedVector *error_msg, + struct UnmanagedVector *out); + +int32_t ibc_packet_receive(struct CachePtr cache, + struct ByteSliceView checksum, + struct ByteSliceView env, + struct ByteSliceView msg, + struct Db db, + struct GoApi api, + struct GoQuerier querier, + uint64_t gas_limit, + bool print_debug, + uint64_t *gas_used, + struct UnmanagedVector *error_msg, + struct UnmanagedVector *out); + +int32_t ibc_packet_ack(struct CachePtr cache, + struct ByteSliceView checksum, + struct ByteSliceView env, + struct ByteSliceView msg, + struct Db db, + struct GoApi api, + struct GoQuerier querier, + uint64_t gas_limit, + bool print_debug, + uint64_t *gas_used, + struct UnmanagedVector *error_msg, + struct UnmanagedVector *out); + +int32_t ibc_packet_timeout(struct CachePtr cache, + struct ByteSliceView checksum, + struct ByteSliceView env, + struct ByteSliceView msg, + struct Db db, + struct GoApi api, + struct GoQuerier querier, + uint64_t gas_limit, + bool print_debug, + uint64_t *gas_used, + struct UnmanagedVector *error_msg, + struct UnmanagedVector *out); struct UnmanagedVector new_unmanaged_vector(bool nil, const uint8_t *ptr, uintptr_t length); diff --git a/libwasmvm/cbindgen.toml b/libwasmvm/cbindgen.toml index 4c3f41cae..9d524169e 100644 --- a/libwasmvm/cbindgen.toml +++ b/libwasmvm/cbindgen.toml @@ -51,7 +51,7 @@ style = "both" [export] include = [ "GoError", - "ErrnoValue", + "ErrorCode", ] exclude = [] # prefix = "CAPI_" diff --git a/libwasmvm/clippy.toml b/libwasmvm/clippy.toml index 85355417c..6f40f2cdc 100644 --- a/libwasmvm/clippy.toml +++ b/libwasmvm/clippy.toml @@ -1 +1 @@ -too-many-arguments-threshold = 13 +too-many-arguments-threshold = 14 diff --git a/libwasmvm/src/cache.rs b/libwasmvm/src/cache.rs index f55f4b6cf..ad94a48e7 100644 --- a/libwasmvm/src/cache.rs +++ b/libwasmvm/src/cache.rs @@ -7,19 +7,48 @@ use cosmwasm_vm::{capabilities_from_csv, Cache, CacheOptions, Checksum, Size}; use crate::api::GoApi; use crate::args::{AVAILABLE_CAPABILITIES_ARG, CACHE_ARG, CHECKSUM_ARG, DATA_DIR_ARG, WASM_ARG}; -use crate::error::{handle_c_error_binary, handle_c_error_default, handle_c_error_ptr, Error}; +use crate::error::{to_c_result, to_c_result_binary, to_c_result_unit, Error}; use crate::memory::{ByteSliceView, UnmanagedVector}; use crate::querier::GoQuerier; use crate::storage::GoStorage; +/// A struct that holds a pointer to the cache. This struct can be +/// copied freely. +/// +/// In case of `init_cache` we need a pointer to a pointer for the output +/// and in order to be able to do this consistently with e.g. `AnalysisReport` +/// and `Metrics`, we use thiy type. #[repr(C)] -pub struct cache_t {} +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct CachePtr { + pub ptr: *mut std::ffi::c_void, +} + +impl Default for CachePtr { + fn default() -> Self { + Self { + ptr: std::ptr::null_mut(), + } + } +} -pub fn to_cache(ptr: *mut cache_t) -> Option<&'static mut Cache> { - if ptr.is_null() { +impl CachePtr { + pub fn new(rust_ptr: *mut Cache) -> Self { + Self { + ptr: rust_ptr as *mut std::ffi::c_void, + } + } + + pub fn is_null(self) -> bool { + self.ptr.is_null() + } +} + +pub fn to_cache(cache: CachePtr) -> Option<&'static mut Cache> { + if cache.is_null() { None } else { - let c = unsafe { &mut *(ptr as *mut Cache) }; + let c = unsafe { &mut *(cache.ptr as *mut Cache) }; Some(c) } } @@ -31,7 +60,8 @@ pub extern "C" fn init_cache( cache_size: u32, // in MiB instance_memory_limit: u32, // in MiB error_msg: Option<&mut UnmanagedVector>, -) -> *mut cache_t { + out: Option<&mut CachePtr>, +) -> i32 { let r = catch_unwind(|| { do_init_cache( data_dir, @@ -41,7 +71,8 @@ pub extern "C" fn init_cache( ) }) .unwrap_or_else(|_| Err(Error::panic())); - handle_c_error_ptr(r, error_msg) as *mut cache_t + let r = r.map(CachePtr::new); + to_c_result(r, error_msg, out) } fn do_init_cache( @@ -81,18 +112,20 @@ fn do_init_cache( } #[no_mangle] +#[must_use] pub extern "C" fn save_wasm( - cache: *mut cache_t, + cache: CachePtr, wasm: ByteSliceView, error_msg: Option<&mut UnmanagedVector>, -) -> UnmanagedVector { + out: Option<&mut UnmanagedVector>, +) -> i32 { let r = match to_cache(cache) { Some(c) => catch_unwind(AssertUnwindSafe(move || do_save_wasm(c, wasm))) .unwrap_or_else(|_| Err(Error::panic())), None => Err(Error::unset_arg(CACHE_ARG)), }; - let checksum = handle_c_error_binary(r, error_msg); - UnmanagedVector::new(Some(checksum)) + let r = r.map(UnmanagedVector::some); + to_c_result_binary(r, error_msg, out) } fn do_save_wasm( @@ -105,17 +138,18 @@ fn do_save_wasm( } #[no_mangle] +#[must_use] pub extern "C" fn remove_wasm( - cache: *mut cache_t, + cache: CachePtr, checksum: ByteSliceView, error_msg: Option<&mut UnmanagedVector>, -) { +) -> i32 { let r = match to_cache(cache) { Some(c) => catch_unwind(AssertUnwindSafe(move || do_remove_wasm(c, checksum))) .unwrap_or_else(|_| Err(Error::panic())), None => Err(Error::unset_arg(CACHE_ARG)), }; - handle_c_error_default(r, error_msg) + to_c_result_unit(r, error_msg) } fn do_remove_wasm( @@ -131,18 +165,20 @@ fn do_remove_wasm( } #[no_mangle] +#[must_use] pub extern "C" fn load_wasm( - cache: *mut cache_t, + cache: CachePtr, checksum: ByteSliceView, error_msg: Option<&mut UnmanagedVector>, -) -> UnmanagedVector { + out: Option<&mut UnmanagedVector>, +) -> i32 { let r = match to_cache(cache) { Some(c) => catch_unwind(AssertUnwindSafe(move || do_load_wasm(c, checksum))) .unwrap_or_else(|_| Err(Error::panic())), None => Err(Error::unset_arg(CACHE_ARG)), }; - let data = handle_c_error_binary(r, error_msg); - UnmanagedVector::new(Some(data)) + let r = r.map(UnmanagedVector::some); + to_c_result_binary(r, error_msg, out) } fn do_load_wasm( @@ -158,17 +194,18 @@ fn do_load_wasm( } #[no_mangle] +#[must_use] pub extern "C" fn pin( - cache: *mut cache_t, + cache: CachePtr, checksum: ByteSliceView, error_msg: Option<&mut UnmanagedVector>, -) { +) -> i32 { let r = match to_cache(cache) { Some(c) => catch_unwind(AssertUnwindSafe(move || do_pin(c, checksum))) .unwrap_or_else(|_| Err(Error::panic())), None => Err(Error::unset_arg(CACHE_ARG)), }; - handle_c_error_default(r, error_msg) + to_c_result_unit(r, error_msg) } fn do_pin( @@ -184,17 +221,18 @@ fn do_pin( } #[no_mangle] +#[must_use] pub extern "C" fn unpin( - cache: *mut cache_t, + cache: CachePtr, checksum: ByteSliceView, error_msg: Option<&mut UnmanagedVector>, -) { +) -> i32 { let r = match to_cache(cache) { Some(c) => catch_unwind(AssertUnwindSafe(move || do_unpin(c, checksum))) .unwrap_or_else(|_| Err(Error::panic())), None => Err(Error::unset_arg(CACHE_ARG)), }; - handle_c_error_default(r, error_msg) + to_c_result_unit(r, error_msg) } fn do_unpin( @@ -215,7 +253,7 @@ fn do_unpin( /// has to be destroyed exactly once. When calling `analyze_code` /// from Go this is done via `C.destroy_unmanaged_vector`. #[repr(C)] -#[derive(Copy, Clone, Default, Debug, PartialEq)] +#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)] pub struct AnalysisReport { pub has_ibc_entry_points: bool, /// An UTF-8 encoded comma separated list of reqired capabilities. @@ -245,17 +283,19 @@ fn set_to_csv(set: HashSet) -> String { } #[no_mangle] +#[must_use] pub extern "C" fn analyze_code( - cache: *mut cache_t, + cache: CachePtr, checksum: ByteSliceView, error_msg: Option<&mut UnmanagedVector>, -) -> AnalysisReport { + out: Option<&mut AnalysisReport>, +) -> i32 { let r = match to_cache(cache) { Some(c) => catch_unwind(AssertUnwindSafe(move || do_analyze_code(c, checksum))) .unwrap_or_else(|_| Err(Error::panic())), None => Err(Error::unset_arg(CACHE_ARG)), }; - handle_c_error_default(r, error_msg) + to_c_result(r, error_msg, out) } fn do_analyze_code( @@ -271,7 +311,7 @@ fn do_analyze_code( } #[repr(C)] -#[derive(Copy, Clone, Default, Debug, PartialEq)] +#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)] pub struct Metrics { pub hits_pinned_memory_cache: u32, pub hits_memory_cache: u32, @@ -321,16 +361,18 @@ impl From for Metrics { } #[no_mangle] +#[must_use] pub extern "C" fn get_metrics( - cache: *mut cache_t, + cache: CachePtr, error_msg: Option<&mut UnmanagedVector>, -) -> Metrics { + out: Option<&mut Metrics>, +) -> i32 { let r = match to_cache(cache) { Some(c) => catch_unwind(AssertUnwindSafe(move || do_get_metrics(c))) .unwrap_or_else(|_| Err(Error::panic())), None => Err(Error::unset_arg(CACHE_ARG)), }; - handle_c_error_default(r, error_msg) + to_c_result(r, error_msg, out) } #[allow(clippy::unnecessary_wraps)] // Keep unused Result for consistent boilerplate for all fn do_* @@ -342,13 +384,13 @@ fn do_get_metrics(cache: &mut Cache) -> Result) }; + let _ = unsafe { Box::from_raw(cache.ptr as *mut Cache) }; } } @@ -369,17 +411,20 @@ mod tests { let capabilities = b"staking"; let mut error_msg = UnmanagedVector::default(); - let cache_ptr = init_cache( + let mut out = CachePtr::default(); + let error = init_cache( ByteSliceView::new(dir.as_bytes()), ByteSliceView::new(capabilities), 512, 32, Some(&mut error_msg), + Some(&mut out), ); + assert_eq!(error, 0); assert!(error_msg.is_none()); let _ = error_msg.consume(); - release_cache(cache_ptr); + release_cache(out); } #[test] @@ -388,14 +433,17 @@ mod tests { let capabilities = b"staking"; let mut error_msg = UnmanagedVector::default(); - let cache_ptr = init_cache( + let mut out = CachePtr::default(); + let error = init_cache( ByteSliceView::new(dir.as_bytes()), ByteSliceView::new(capabilities), 512, 32, Some(&mut error_msg), + Some(&mut out), ); - assert!(cache_ptr.is_null()); + assert_ne!(error, 0); + assert!(out.is_null()); assert!(error_msg.is_some()); let msg = String::from_utf8(error_msg.consume().unwrap()).unwrap(); assert_eq!( @@ -410,24 +458,33 @@ mod tests { let capabilities = b"staking"; let mut error_msg = UnmanagedVector::default(); - let cache_ptr = init_cache( + let mut out = CachePtr::default(); + let error = init_cache( ByteSliceView::new(dir.as_bytes()), ByteSliceView::new(capabilities), 512, 32, Some(&mut error_msg), + Some(&mut out), ); + assert_eq!(error, 0); assert!(error_msg.is_none()); let _ = error_msg.consume(); + let cache_ptr = out; let mut error_msg = UnmanagedVector::default(); - save_wasm( + let mut out = UnmanagedVector::default(); + let error = save_wasm( cache_ptr, ByteSliceView::new(HACKATOM), Some(&mut error_msg), + Some(&mut out), ); + assert_eq!(error, 0); assert!(error_msg.is_none()); let _ = error_msg.consume(); + let checksum = out.consume().unwrap_or_default(); + assert_eq!(checksum.len(), 32); release_cache(cache_ptr); } @@ -438,43 +495,52 @@ mod tests { let capabilities = b"staking"; let mut error_msg = UnmanagedVector::default(); - let cache_ptr = init_cache( + let mut out = CachePtr::default(); + let error = init_cache( ByteSliceView::new(dir.as_bytes()), ByteSliceView::new(capabilities), 512, 32, Some(&mut error_msg), + Some(&mut out), ); + assert_eq!(error, 0); assert!(error_msg.is_none()); let _ = error_msg.consume(); + let cache_ptr = out; let mut error_msg = UnmanagedVector::default(); - let checksum = save_wasm( + let mut out = UnmanagedVector::default(); + let error = save_wasm( cache_ptr, ByteSliceView::new(HACKATOM), Some(&mut error_msg), + Some(&mut out), ); + assert_eq!(error, 0); assert!(error_msg.is_none()); let _ = error_msg.consume(); - let checksum = checksum.consume().unwrap_or_default(); + let checksum = out.consume().unwrap_or_default(); // Removing once works let mut error_msg = UnmanagedVector::default(); - remove_wasm( + let error = remove_wasm( cache_ptr, ByteSliceView::new(&checksum), Some(&mut error_msg), ); + assert_eq!(error, 0); assert!(error_msg.is_none()); let _ = error_msg.consume(); // Removing again fails let mut error_msg = UnmanagedVector::default(); - remove_wasm( + let error = remove_wasm( cache_ptr, ByteSliceView::new(&checksum), Some(&mut error_msg), ); + assert_eq!(error, 1); let error_msg = error_msg .consume() .map(|e| String::from_utf8_lossy(&e).into_owned()); @@ -492,35 +558,45 @@ mod tests { let capabilities = b"staking"; let mut error_msg = UnmanagedVector::default(); - let cache_ptr = init_cache( + let mut out = CachePtr::default(); + let error = init_cache( ByteSliceView::new(dir.as_bytes()), ByteSliceView::new(capabilities), 512, 32, Some(&mut error_msg), + Some(&mut out), ); + assert_eq!(error, 0); assert!(error_msg.is_none()); let _ = error_msg.consume(); + let cache_ptr = out; let mut error_msg = UnmanagedVector::default(); - let checksum = save_wasm( + let mut out = UnmanagedVector::default(); + let error = save_wasm( cache_ptr, ByteSliceView::new(HACKATOM), Some(&mut error_msg), + Some(&mut out), ); + assert_eq!(error, 0); assert!(error_msg.is_none()); let _ = error_msg.consume(); - let checksum = checksum.consume().unwrap_or_default(); + let checksum = out.consume().unwrap_or_default(); let mut error_msg = UnmanagedVector::default(); - let wasm = load_wasm( + let mut out = UnmanagedVector::default(); + let error = load_wasm( cache_ptr, ByteSliceView::new(&checksum), Some(&mut error_msg), + Some(&mut out), ); + assert_eq!(error, 0); assert!(error_msg.is_none()); let _ = error_msg.consume(); - let wasm = wasm.consume().unwrap_or_default(); + let wasm = out.consume().unwrap_or_default(); assert_eq!(wasm, HACKATOM); release_cache(cache_ptr); @@ -532,42 +608,51 @@ mod tests { let capabilities = b"staking"; let mut error_msg = UnmanagedVector::default(); - let cache_ptr = init_cache( + let mut out = CachePtr::default(); + let error = init_cache( ByteSliceView::new(dir.as_bytes()), ByteSliceView::new(capabilities), 512, 32, Some(&mut error_msg), + Some(&mut out), ); + assert_eq!(error, 0); assert!(error_msg.is_none()); let _ = error_msg.consume(); + let cache_ptr = out; let mut error_msg = UnmanagedVector::default(); - let checksum = save_wasm( + let mut out = UnmanagedVector::default(); + let error = save_wasm( cache_ptr, ByteSliceView::new(HACKATOM), Some(&mut error_msg), + Some(&mut out), ); + assert_eq!(error, 0); assert!(error_msg.is_none()); let _ = error_msg.consume(); - let checksum = checksum.consume().unwrap_or_default(); + let checksum = out.consume().unwrap_or_default(); let mut error_msg = UnmanagedVector::default(); - pin( + let error = pin( cache_ptr, ByteSliceView::new(&checksum), Some(&mut error_msg), ); + assert_eq!(error, 0); assert!(error_msg.is_none()); let _ = error_msg.consume(); // pinning again has no effect let mut error_msg = UnmanagedVector::default(); - pin( + let error = pin( cache_ptr, ByteSliceView::new(&checksum), Some(&mut error_msg), ); + assert_eq!(error, 0); assert!(error_msg.is_none()); let _ = error_msg.consume(); @@ -580,51 +665,61 @@ mod tests { let capabilities = b"staking"; let mut error_msg = UnmanagedVector::default(); - let cache_ptr = init_cache( + let mut out = CachePtr::default(); + let error = init_cache( ByteSliceView::new(dir.as_bytes()), ByteSliceView::new(capabilities), 512, 32, Some(&mut error_msg), + Some(&mut out), ); + assert_eq!(error, 0); assert!(error_msg.is_none()); let _ = error_msg.consume(); + let cache_ptr = out; let mut error_msg = UnmanagedVector::default(); - let checksum = save_wasm( + let mut out = UnmanagedVector::default(); + let error = save_wasm( cache_ptr, ByteSliceView::new(HACKATOM), Some(&mut error_msg), + Some(&mut out), ); + assert_eq!(error, 0); assert!(error_msg.is_none()); let _ = error_msg.consume(); - let checksum = checksum.consume().unwrap_or_default(); + let checksum = out.consume().unwrap_or_default(); let mut error_msg = UnmanagedVector::default(); - pin( + let error = pin( cache_ptr, ByteSliceView::new(&checksum), Some(&mut error_msg), ); + assert_eq!(error, 0); assert!(error_msg.is_none()); let _ = error_msg.consume(); let mut error_msg = UnmanagedVector::default(); - unpin( + let error = unpin( cache_ptr, ByteSliceView::new(&checksum), Some(&mut error_msg), ); + assert_eq!(error, 0); assert!(error_msg.is_none()); let _ = error_msg.consume(); // Unpinning again has no effect let mut error_msg = UnmanagedVector::default(); - unpin( + let error = unpin( cache_ptr, ByteSliceView::new(&checksum), Some(&mut error_msg), ); + assert_eq!(error, 0); assert!(error_msg.is_none()); let _ = error_msg.consume(); @@ -637,60 +732,72 @@ mod tests { let capabilities = b"staking,stargate,iterator"; let mut error_msg = UnmanagedVector::default(); - let cache_ptr = init_cache( + let mut out = CachePtr::default(); + let error = init_cache( ByteSliceView::new(dir.as_bytes()), ByteSliceView::new(capabilities), 512, 32, Some(&mut error_msg), + Some(&mut out), ); + assert_eq!(error, 0); assert!(error_msg.is_none()); let _ = error_msg.consume(); + let cache_ptr = out; let mut error_msg = UnmanagedVector::default(); - let checksum_hackatom = save_wasm( + let mut out = UnmanagedVector::default(); + let error = save_wasm( cache_ptr, ByteSliceView::new(HACKATOM), Some(&mut error_msg), + Some(&mut out), ); + assert_eq!(error, 0); assert!(error_msg.is_none()); let _ = error_msg.consume(); - let checksum_hackatom = checksum_hackatom.consume().unwrap_or_default(); + let checksum_hackatom = out.consume().unwrap_or_default(); let mut error_msg = UnmanagedVector::default(); - let checksum_ibc_reflect = save_wasm( + let mut out = UnmanagedVector::default(); + let error = save_wasm( cache_ptr, ByteSliceView::new(IBC_REFLECT), Some(&mut error_msg), + Some(&mut out), ); + assert_eq!(error, 0); assert!(error_msg.is_none()); let _ = error_msg.consume(); - let checksum_ibc_reflect = checksum_ibc_reflect.consume().unwrap_or_default(); + let checksum_ibc_reflect = out.consume().unwrap_or_default(); let mut error_msg = UnmanagedVector::default(); - let hackatom_report = analyze_code( + let mut out = AnalysisReport::default(); + let error = analyze_code( cache_ptr, ByteSliceView::new(&checksum_hackatom), Some(&mut error_msg), + Some(&mut out), ); + assert_eq!(error, 0); let _ = error_msg.consume(); - assert!(!hackatom_report.has_ibc_entry_points); - assert_eq!( - hackatom_report.required_capabilities.consume().unwrap(), - b"" - ); + assert!(!out.has_ibc_entry_points); + assert_eq!(out.required_capabilities.consume().unwrap(), b""); let mut error_msg = UnmanagedVector::default(); - let ibc_reflect_report = analyze_code( + let mut out = AnalysisReport::default(); + let error = analyze_code( cache_ptr, ByteSliceView::new(&checksum_ibc_reflect), Some(&mut error_msg), + Some(&mut out), ); + assert_eq!(error, 0); let _ = error_msg.consume(); - assert!(ibc_reflect_report.has_ibc_entry_points); + assert!(out.has_ibc_entry_points); let required_capabilities = - String::from_utf8_lossy(&ibc_reflect_report.required_capabilities.consume().unwrap()) - .to_string(); + String::from_utf8_lossy(&out.required_capabilities.consume().unwrap()).to_string(); assert_eq!(required_capabilities, "iterator,stargate"); release_cache(cache_ptr); @@ -733,52 +840,66 @@ mod tests { // Init cache let mut error_msg = UnmanagedVector::default(); - let cache_ptr = init_cache( + let mut out = CachePtr::default(); + let error = init_cache( ByteSliceView::new(dir.as_bytes()), ByteSliceView::new(capabilities), 512, 32, Some(&mut error_msg), + Some(&mut out), ); + assert_eq!(error, 0); assert!(error_msg.is_none()); let _ = error_msg.consume(); + let cache_ptr = out; // Get metrics 1 let mut error_msg = UnmanagedVector::default(); - let metrics = get_metrics(cache_ptr, Some(&mut error_msg)); + let mut out = Metrics::default(); + let error = get_metrics(cache_ptr, Some(&mut error_msg), Some(&mut out)); + assert_eq!(error, 0); let _ = error_msg.consume(); - assert_eq!(metrics, Metrics::default()); + assert_eq!(out, Metrics::default()); // Save wasm let mut error_msg = UnmanagedVector::default(); - let checksum_hackatom = save_wasm( + let mut out = UnmanagedVector::default(); + let error = save_wasm( cache_ptr, ByteSliceView::new(HACKATOM), Some(&mut error_msg), + Some(&mut out), ); + assert_eq!(error, 0); assert!(error_msg.is_none()); let _ = error_msg.consume(); - let checksum = checksum_hackatom.consume().unwrap_or_default(); + let checksum = out.consume().unwrap_or_default(); // Get metrics 2 let mut error_msg = UnmanagedVector::default(); - let metrics = get_metrics(cache_ptr, Some(&mut error_msg)); + let mut out = Metrics::default(); + let error = get_metrics(cache_ptr, Some(&mut error_msg), Some(&mut out)); + assert_eq!(error, 0); let _ = error_msg.consume(); - assert_eq!(metrics, Metrics::default()); + assert_eq!(out, Metrics::default()); // Pin let mut error_msg = UnmanagedVector::default(); - pin( + let error = pin( cache_ptr, ByteSliceView::new(&checksum), Some(&mut error_msg), ); + assert_eq!(error, 0); assert!(error_msg.is_none()); let _ = error_msg.consume(); // Get metrics 3 let mut error_msg = UnmanagedVector::default(); - let metrics = get_metrics(cache_ptr, Some(&mut error_msg)); + let mut out = Metrics::default(); + let error = get_metrics(cache_ptr, Some(&mut error_msg), Some(&mut out)); + assert_eq!(error, 0); let _ = error_msg.consume(); let Metrics { hits_pinned_memory_cache, @@ -789,7 +910,7 @@ mod tests { elements_memory_cache, size_pinned_memory_cache, size_memory_cache, - } = metrics; + } = out; assert_eq!(hits_pinned_memory_cache, 0); assert_eq!(hits_memory_cache, 0); assert_eq!(hits_fs_cache, 1); @@ -806,20 +927,23 @@ mod tests { // Unpin let mut error_msg = UnmanagedVector::default(); - unpin( + let error = unpin( cache_ptr, ByteSliceView::new(&checksum), Some(&mut error_msg), ); + assert_eq!(error, 0); assert!(error_msg.is_none()); let _ = error_msg.consume(); // Get metrics 4 let mut error_msg = UnmanagedVector::default(); - let metrics = get_metrics(cache_ptr, Some(&mut error_msg)); + let mut out = Metrics::default(); + let error = get_metrics(cache_ptr, Some(&mut error_msg), Some(&mut out)); + assert_eq!(error, 0); let _ = error_msg.consume(); assert_eq!( - metrics, + out, Metrics { hits_pinned_memory_cache: 0, hits_memory_cache: 0, diff --git a/libwasmvm/src/calls.rs b/libwasmvm/src/calls.rs index c0622409b..feebf5361 100644 --- a/libwasmvm/src/calls.rs +++ b/libwasmvm/src/calls.rs @@ -12,9 +12,9 @@ use cosmwasm_vm::{ use crate::api::GoApi; use crate::args::{ARG1, ARG2, ARG3, CACHE_ARG, CHECKSUM_ARG, GAS_USED_ARG}; -use crate::cache::{cache_t, to_cache}; +use crate::cache::{to_cache, CachePtr}; use crate::db::Db; -use crate::error::{handle_c_error_binary, Error}; +use crate::error::{to_c_result, Error}; use crate::memory::{ByteSliceView, UnmanagedVector}; use crate::querier::GoQuerier; use crate::storage::GoStorage; @@ -28,8 +28,9 @@ fn into_backend(db: Db, api: GoApi, querier: GoQuerier) -> Backend, error_msg: Option<&mut UnmanagedVector>, -) -> UnmanagedVector { + out: Option<&mut UnmanagedVector>, +) -> i32 { call_3_args( call_instantiate_raw, cache, @@ -56,12 +58,14 @@ pub extern "C" fn instantiate( print_debug, gas_used, error_msg, + out, ) } #[no_mangle] +#[must_use] pub extern "C" fn execute( - cache: *mut cache_t, + cache: CachePtr, checksum: ByteSliceView, env: ByteSliceView, info: ByteSliceView, @@ -73,7 +77,8 @@ pub extern "C" fn execute( print_debug: bool, gas_used: Option<&mut u64>, error_msg: Option<&mut UnmanagedVector>, -) -> UnmanagedVector { + out: Option<&mut UnmanagedVector>, +) -> i32 { call_3_args( call_execute_raw, cache, @@ -88,12 +93,14 @@ pub extern "C" fn execute( print_debug, gas_used, error_msg, + out, ) } #[no_mangle] +#[must_use] pub extern "C" fn migrate( - cache: *mut cache_t, + cache: CachePtr, checksum: ByteSliceView, env: ByteSliceView, msg: ByteSliceView, @@ -104,7 +111,8 @@ pub extern "C" fn migrate( print_debug: bool, gas_used: Option<&mut u64>, error_msg: Option<&mut UnmanagedVector>, -) -> UnmanagedVector { + out: Option<&mut UnmanagedVector>, +) -> i32 { call_2_args( call_migrate_raw, cache, @@ -118,12 +126,14 @@ pub extern "C" fn migrate( print_debug, gas_used, error_msg, + out, ) } #[no_mangle] +#[must_use] pub extern "C" fn sudo( - cache: *mut cache_t, + cache: CachePtr, checksum: ByteSliceView, env: ByteSliceView, msg: ByteSliceView, @@ -134,7 +144,8 @@ pub extern "C" fn sudo( print_debug: bool, gas_used: Option<&mut u64>, error_msg: Option<&mut UnmanagedVector>, -) -> UnmanagedVector { + out: Option<&mut UnmanagedVector>, +) -> i32 { call_2_args( call_sudo_raw, cache, @@ -148,12 +159,14 @@ pub extern "C" fn sudo( print_debug, gas_used, error_msg, + out, ) } #[no_mangle] +#[must_use] pub extern "C" fn reply( - cache: *mut cache_t, + cache: CachePtr, checksum: ByteSliceView, env: ByteSliceView, msg: ByteSliceView, @@ -164,7 +177,8 @@ pub extern "C" fn reply( print_debug: bool, gas_used: Option<&mut u64>, error_msg: Option<&mut UnmanagedVector>, -) -> UnmanagedVector { + out: Option<&mut UnmanagedVector>, +) -> i32 { call_2_args( call_reply_raw, cache, @@ -178,12 +192,14 @@ pub extern "C" fn reply( print_debug, gas_used, error_msg, + out, ) } #[no_mangle] +#[must_use] pub extern "C" fn query( - cache: *mut cache_t, + cache: CachePtr, checksum: ByteSliceView, env: ByteSliceView, msg: ByteSliceView, @@ -194,7 +210,8 @@ pub extern "C" fn query( print_debug: bool, gas_used: Option<&mut u64>, error_msg: Option<&mut UnmanagedVector>, -) -> UnmanagedVector { + out: Option<&mut UnmanagedVector>, +) -> i32 { call_2_args( call_query_raw, cache, @@ -208,12 +225,14 @@ pub extern "C" fn query( print_debug, gas_used, error_msg, + out, ) } #[no_mangle] +#[must_use] pub extern "C" fn ibc_channel_open( - cache: *mut cache_t, + cache: CachePtr, checksum: ByteSliceView, env: ByteSliceView, msg: ByteSliceView, @@ -224,7 +243,8 @@ pub extern "C" fn ibc_channel_open( print_debug: bool, gas_used: Option<&mut u64>, error_msg: Option<&mut UnmanagedVector>, -) -> UnmanagedVector { + out: Option<&mut UnmanagedVector>, +) -> i32 { call_2_args( call_ibc_channel_open_raw, cache, @@ -238,12 +258,14 @@ pub extern "C" fn ibc_channel_open( print_debug, gas_used, error_msg, + out, ) } #[no_mangle] +#[must_use] pub extern "C" fn ibc_channel_connect( - cache: *mut cache_t, + cache: CachePtr, checksum: ByteSliceView, env: ByteSliceView, msg: ByteSliceView, @@ -254,7 +276,8 @@ pub extern "C" fn ibc_channel_connect( print_debug: bool, gas_used: Option<&mut u64>, error_msg: Option<&mut UnmanagedVector>, -) -> UnmanagedVector { + out: Option<&mut UnmanagedVector>, +) -> i32 { call_2_args( call_ibc_channel_connect_raw, cache, @@ -268,12 +291,14 @@ pub extern "C" fn ibc_channel_connect( print_debug, gas_used, error_msg, + out, ) } #[no_mangle] +#[must_use] pub extern "C" fn ibc_channel_close( - cache: *mut cache_t, + cache: CachePtr, checksum: ByteSliceView, env: ByteSliceView, msg: ByteSliceView, @@ -284,7 +309,8 @@ pub extern "C" fn ibc_channel_close( print_debug: bool, gas_used: Option<&mut u64>, error_msg: Option<&mut UnmanagedVector>, -) -> UnmanagedVector { + out: Option<&mut UnmanagedVector>, +) -> i32 { call_2_args( call_ibc_channel_close_raw, cache, @@ -298,12 +324,14 @@ pub extern "C" fn ibc_channel_close( print_debug, gas_used, error_msg, + out, ) } #[no_mangle] +#[must_use] pub extern "C" fn ibc_packet_receive( - cache: *mut cache_t, + cache: CachePtr, checksum: ByteSliceView, env: ByteSliceView, msg: ByteSliceView, @@ -314,7 +342,8 @@ pub extern "C" fn ibc_packet_receive( print_debug: bool, gas_used: Option<&mut u64>, error_msg: Option<&mut UnmanagedVector>, -) -> UnmanagedVector { + out: Option<&mut UnmanagedVector>, +) -> i32 { call_2_args( call_ibc_packet_receive_raw, cache, @@ -328,12 +357,14 @@ pub extern "C" fn ibc_packet_receive( print_debug, gas_used, error_msg, + out, ) } #[no_mangle] +#[must_use] pub extern "C" fn ibc_packet_ack( - cache: *mut cache_t, + cache: CachePtr, checksum: ByteSliceView, env: ByteSliceView, msg: ByteSliceView, @@ -344,7 +375,8 @@ pub extern "C" fn ibc_packet_ack( print_debug: bool, gas_used: Option<&mut u64>, error_msg: Option<&mut UnmanagedVector>, -) -> UnmanagedVector { + out: Option<&mut UnmanagedVector>, +) -> i32 { call_2_args( call_ibc_packet_ack_raw, cache, @@ -358,12 +390,14 @@ pub extern "C" fn ibc_packet_ack( print_debug, gas_used, error_msg, + out, ) } #[no_mangle] +#[must_use] pub extern "C" fn ibc_packet_timeout( - cache: *mut cache_t, + cache: CachePtr, checksum: ByteSliceView, env: ByteSliceView, msg: ByteSliceView, @@ -374,7 +408,8 @@ pub extern "C" fn ibc_packet_timeout( print_debug: bool, gas_used: Option<&mut u64>, error_msg: Option<&mut UnmanagedVector>, -) -> UnmanagedVector { + out: Option<&mut UnmanagedVector>, +) -> i32 { call_2_args( call_ibc_packet_timeout_raw, cache, @@ -388,6 +423,7 @@ pub extern "C" fn ibc_packet_timeout( print_debug, gas_used, error_msg, + out, ) } @@ -400,9 +436,10 @@ type VmFn2Args = fn( // this wraps all error handling and ffi for the 6 ibc entry points and query. // (all of which take env and one "msg" argument). // the only difference is which low-level function they dispatch to. +#[must_use] fn call_2_args( vm_fn: VmFn2Args, - cache: *mut cache_t, + cache: CachePtr, checksum: ByteSliceView, arg1: ByteSliceView, arg2: ByteSliceView, @@ -413,7 +450,8 @@ fn call_2_args( print_debug: bool, gas_used: Option<&mut u64>, error_msg: Option<&mut UnmanagedVector>, -) -> UnmanagedVector { + out: Option<&mut UnmanagedVector>, +) -> i32 { let r = match to_cache(cache) { Some(c) => catch_unwind(AssertUnwindSafe(move || { do_call_2_args( @@ -433,8 +471,8 @@ fn call_2_args( .unwrap_or_else(|_| Err(Error::panic())), None => Err(Error::unset_arg(CACHE_ARG)), }; - let data = handle_c_error_binary(r, error_msg); - UnmanagedVector::new(Some(data)) + let r = r.map(UnmanagedVector::some); + to_c_result(r, error_msg, out) } // this is internal processing, same for all the 6 ibc entry points @@ -482,9 +520,10 @@ type VmFn3Args = fn( // This wraps all error handling and ffi for instantiate, execute and migrate // (and anything else that takes env, info and msg arguments). // The only difference is which low-level function they dispatch to. +#[must_use] fn call_3_args( vm_fn: VmFn3Args, - cache: *mut cache_t, + cache: CachePtr, checksum: ByteSliceView, arg1: ByteSliceView, arg2: ByteSliceView, @@ -496,7 +535,8 @@ fn call_3_args( print_debug: bool, gas_used: Option<&mut u64>, error_msg: Option<&mut UnmanagedVector>, -) -> UnmanagedVector { + out: Option<&mut UnmanagedVector>, +) -> i32 { let r = match to_cache(cache) { Some(c) => catch_unwind(AssertUnwindSafe(move || { do_call_3_args( @@ -517,8 +557,8 @@ fn call_3_args( .unwrap_or_else(|_| Err(Error::panic())), None => Err(Error::unset_arg(CACHE_ARG)), }; - let data = handle_c_error_binary(r, error_msg); - UnmanagedVector::new(Some(data)) + let r = r.map(UnmanagedVector::some); + to_c_result(r, error_msg, out) } fn do_call_3_args( diff --git a/libwasmvm/src/error/go.rs b/libwasmvm/src/error/go.rs index 959212b45..1d8d65edd 100644 --- a/libwasmvm/src/error/go.rs +++ b/libwasmvm/src/error/go.rs @@ -14,7 +14,7 @@ use crate::memory::UnmanagedVector; // You have been warned. // #[repr(i32)] // This makes it so the enum looks like a simple i32 to Go -#[derive(PartialEq)] +#[derive(PartialEq, Eq)] pub enum GoError { None = 0, /// Go panicked for an unexpected reason. diff --git a/libwasmvm/src/error/mod.rs b/libwasmvm/src/error/mod.rs index 96130ed83..1c2767a6d 100644 --- a/libwasmvm/src/error/mod.rs +++ b/libwasmvm/src/error/mod.rs @@ -2,6 +2,4 @@ mod go; mod rust; pub use go::GoError; -pub use rust::{ - handle_c_error_binary, handle_c_error_default, handle_c_error_ptr, RustError as Error, -}; +pub use rust::{to_c_result, to_c_result_binary, to_c_result_unit, RustError as Error}; diff --git a/libwasmvm/src/error/rust.rs b/libwasmvm/src/error/rust.rs index 1938d3cb3..14c3ee9af 100644 --- a/libwasmvm/src/error/rust.rs +++ b/libwasmvm/src/error/rust.rs @@ -1,5 +1,4 @@ use cosmwasm_vm::VmError; -use errno::{set_errno, Errno}; #[cfg(feature = "backtraces")] use std::backtrace::Backtrace; use thiserror::Error; @@ -114,27 +113,27 @@ impl From for RustError { } } +/// An error code used to communicate the errors of FFI calls. +/// Similar to shell codes and errno, 0 means no error. /// cbindgen:prefix-with-name #[repr(i32)] -enum ErrnoValue { +#[derive(Debug, Clone, Copy)] +pub enum ErrorCode { Success = 0, Other = 1, OutOfGas = 2, } -pub fn clear_error() { - set_errno(Errno(ErrnoValue::Success as i32)); +impl ErrorCode { + pub fn to_int(self) -> i32 { + self as i32 + } } -pub fn set_error(err: RustError, error_msg: Option<&mut UnmanagedVector>) { +/// Takes the `RustError`, writes the error message to +/// the `error_msg` pointer and returns an Error code. +pub fn set_error_msg(err: RustError, error_msg: Option<&mut UnmanagedVector>) -> ErrorCode { if let Some(error_msg) = error_msg { - if error_msg.is_some() { - panic!( - "There is an old error message in the given pointer that has not been \ - cleaned up. Error message pointers should not be reused for multiple calls." - ) - } - let msg: Vec = err.to_string().into(); *error_msg = UnmanagedVector::new(Some(msg)); } else { @@ -142,84 +141,73 @@ pub fn set_error(err: RustError, error_msg: Option<&mut UnmanagedVector>) { // That's not nice but we can live with it. } - let errno = match err { - RustError::OutOfGas { .. } => ErrnoValue::OutOfGas, - _ => ErrnoValue::Other, - } as i32; - set_errno(Errno(errno)); + let code: ErrorCode = match err { + RustError::OutOfGas { .. } => ErrorCode::OutOfGas, + _ => ErrorCode::Other, + }; + code } -/// If `result` is Ok, this returns the Ok value and clears [errno]. -/// Otherwise it returns a null pointer, writes the error message to `error_msg` and sets [errno]. -/// -/// [errno]: https://utcc.utoronto.ca/~cks/space/blog/programming/GoCgoErrorReturns -pub fn handle_c_error_ptr( - result: Result<*mut T, RustError>, - error_msg: Option<&mut UnmanagedVector>, -) -> *mut T { - match result { - Ok(value) => { - clear_error(); - value - } - Err(error) => { - set_error(error, error_msg); - std::ptr::null_mut() - } +pub fn set_out(value: T, out_ptr: Option<&mut T>) { + if let Some(out_ref) = out_ptr { + *out_ref = value; + } else { + // The caller provided a nil pointer for the output message. + // That's not nice but we can live with it. } } -/// If `result` is Ok, this returns the binary representation of the Ok value and clears [errno]. -/// Otherwise it returns an empty vector, writes the error message to `error_msg` and sets [errno]. -/// -/// [errno]: https://utcc.utoronto.ca/~cks/space/blog/programming/GoCgoErrorReturns -pub fn handle_c_error_binary( - result: Result, - error_msg: Option<&mut UnmanagedVector>, -) -> Vec -where - T: Into>, -{ - match result { +/// If `result` is Ok, this writes the Ok value to `out` and returns 0. +/// Otherwise it writes the error message to `error_msg` and returns the error code. +pub fn to_c_result_binary( + result: Result, + error_msg_ptr: Option<&mut UnmanagedVector>, + out_ptr: Option<&mut UnmanagedVector>, +) -> i32 { + let code = match result { Ok(value) => { - clear_error(); - value.into() + set_out(value, out_ptr); + ErrorCode::Success } - Err(error) => { - set_error(error, error_msg); - Vec::new() - } - } + Err(error) => set_error_msg(error, error_msg_ptr), + }; + code.to_int() } -/// If `result` is Ok, this returns the Ok value and clears [errno]. -/// Otherwise it returns the default value, writes the error message to `error_msg` and sets [errno]. -/// -/// [errno]: https://utcc.utoronto.ca/~cks/space/blog/programming/GoCgoErrorReturns -pub fn handle_c_error_default( +/// If `result` is Ok, this writes the Ok value to `out` and returns 0. +/// Otherwise it writes the error message to `error_msg` and returns the error code. +pub fn to_c_result( result: Result, - error_msg: Option<&mut UnmanagedVector>, -) -> T -where - T: Default, -{ - match result { + error_msg_ptr: Option<&mut UnmanagedVector>, + out_ptr: Option<&mut T>, +) -> i32 { + let code = match result { Ok(value) => { - clear_error(); - value + set_out(value, out_ptr); + ErrorCode::Success } - Err(error) => { - set_error(error, error_msg); - Default::default() - } - } + Err(error) => set_error_msg(error, error_msg_ptr), + }; + code.to_int() +} + +/// If `result` is Ok, this writes the Ok value to `out` and returns 0. +/// Otherwise it writes the error message to `error_msg` and returns the error code. +pub fn to_c_result_unit( + result: Result<(), RustError>, + error_msg_ptr: Option<&mut UnmanagedVector>, +) -> i32 { + let code = match result { + Ok(_) => ErrorCode::Success, + Err(error) => set_error_msg(error, error_msg_ptr), + }; + code.to_int() } #[cfg(test)] mod tests { use super::*; - use cosmwasm_vm::{BackendError, Checksum}; - use errno::errno; + use cosmwasm_vm::BackendError; use std::str; #[test] @@ -327,205 +315,4 @@ mod tests { _ => panic!("expect different error"), } } - - #[test] - fn handle_c_error_binary_works() { - // Ok (non-empty vector) - let mut error_msg = UnmanagedVector::default(); - let res: Result, RustError> = Ok(vec![0xF0, 0x0B, 0xAA]); - let data = handle_c_error_binary(res, Some(&mut error_msg)); - assert_eq!(errno().0, ErrnoValue::Success as i32); - assert!(error_msg.is_none()); - assert_eq!(data, vec![0xF0, 0x0B, 0xAA]); - let _ = error_msg.consume(); - - // Ok (empty vector) - let mut error_msg = UnmanagedVector::default(); - let res: Result, RustError> = Ok(vec![]); - let data = handle_c_error_binary(res, Some(&mut error_msg)); - assert_eq!(errno().0, ErrnoValue::Success as i32); - assert!(error_msg.is_none()); - assert_eq!(data, Vec::::new()); - let _ = error_msg.consume(); - - // Ok (non-empty slice) - let mut error_msg = UnmanagedVector::default(); - let res: Result<&[u8], RustError> = Ok(b"foobar"); - let data = handle_c_error_binary(res, Some(&mut error_msg)); - assert_eq!(errno().0, ErrnoValue::Success as i32); - assert!(error_msg.is_none()); - assert_eq!(data, Vec::::from(b"foobar" as &[u8])); - let _ = error_msg.consume(); - - // Ok (empty slice) - let mut error_msg = UnmanagedVector::default(); - let res: Result<&[u8], RustError> = Ok(b""); - let data = handle_c_error_binary(res, Some(&mut error_msg)); - assert_eq!(errno().0, ErrnoValue::Success as i32); - assert!(error_msg.is_none()); - assert_eq!(data, Vec::::new()); - let _ = error_msg.consume(); - - // Ok (checksum) - let mut error_msg = UnmanagedVector::default(); - let res: Result = Ok(Checksum::from([ - 0x72, 0x2c, 0x8c, 0x99, 0x3f, 0xd7, 0x5a, 0x76, 0x27, 0xd6, 0x9e, 0xd9, 0x41, 0x34, - 0x4f, 0xe2, 0xa1, 0x42, 0x3a, 0x3e, 0x75, 0xef, 0xd3, 0xe6, 0x77, 0x8a, 0x14, 0x28, - 0x84, 0x22, 0x71, 0x04, - ])); - let data = handle_c_error_binary(res, Some(&mut error_msg)); - assert_eq!(errno().0, ErrnoValue::Success as i32); - assert!(error_msg.is_none()); - assert_eq!( - data, - vec![ - 0x72, 0x2c, 0x8c, 0x99, 0x3f, 0xd7, 0x5a, 0x76, 0x27, 0xd6, 0x9e, 0xd9, 0x41, 0x34, - 0x4f, 0xe2, 0xa1, 0x42, 0x3a, 0x3e, 0x75, 0xef, 0xd3, 0xe6, 0x77, 0x8a, 0x14, 0x28, - 0x84, 0x22, 0x71, 0x04, - ] - ); - let _ = error_msg.consume(); - - // Err (vector) - let mut error_msg = UnmanagedVector::default(); - let res: Result, RustError> = Err(RustError::panic()); - let data = handle_c_error_binary(res, Some(&mut error_msg)); - assert_eq!(errno().0, ErrnoValue::Other as i32); - assert!(error_msg.is_some()); - assert_eq!(data, Vec::::new()); - let _ = error_msg.consume(); - - // Err (slice) - let mut error_msg = UnmanagedVector::default(); - let res: Result<&[u8], RustError> = Err(RustError::panic()); - let data = handle_c_error_binary(res, Some(&mut error_msg)); - assert_eq!(errno().0, ErrnoValue::Other as i32); - assert!(error_msg.is_some()); - assert_eq!(data, Vec::::new()); - let _ = error_msg.consume(); - - // Err (checksum) - let mut error_msg = UnmanagedVector::default(); - let res: Result = Err(RustError::panic()); - let data = handle_c_error_binary(res, Some(&mut error_msg)); - assert_eq!(errno().0, ErrnoValue::Other as i32); - assert!(error_msg.is_some()); - assert_eq!(data, Vec::::new()); - let _ = error_msg.consume(); - } - - #[test] - fn handle_c_error_binary_clears_an_old_error() { - // Err - let mut error_msg = UnmanagedVector::default(); - let res: Result, RustError> = Err(RustError::panic()); - let data = handle_c_error_binary(res, Some(&mut error_msg)); - assert_eq!(errno().0, ErrnoValue::Other as i32); - assert!(error_msg.is_some()); - assert_eq!(data, Vec::::new()); - let _ = error_msg.consume(); - - // Ok - let mut error_msg = UnmanagedVector::default(); - let res: Result, RustError> = Ok(vec![0xF0, 0x0B, 0xAA]); - let data = handle_c_error_binary(res, Some(&mut error_msg)); - assert_eq!(errno().0, ErrnoValue::Success as i32); - assert!(error_msg.is_none()); - assert_eq!(data, vec![0xF0, 0x0B, 0xAA]); - let _ = error_msg.consume(); - } - - #[test] - fn handle_c_error_default_works() { - // Ok (non-empty vector) - let mut error_msg = UnmanagedVector::default(); - let res: Result, RustError> = Ok(vec![0xF0, 0x0B, 0xAA]); - let data = handle_c_error_default(res, Some(&mut error_msg)); - assert_eq!(errno().0, ErrnoValue::Success as i32); - assert!(error_msg.is_none()); - assert_eq!(data, vec![0xF0, 0x0B, 0xAA]); - let _ = error_msg.consume(); - - // Ok (empty vector) - let mut error_msg = UnmanagedVector::default(); - let res: Result, RustError> = Ok(vec![]); - let data = handle_c_error_default(res, Some(&mut error_msg)); - assert_eq!(errno().0, ErrnoValue::Success as i32); - assert!(error_msg.is_none()); - assert_eq!(data, Vec::::new()); - let _ = error_msg.consume(); - - // Ok (non-empty slice) - let mut error_msg = UnmanagedVector::default(); - let res: Result<&[u8], RustError> = Ok(b"foobar"); - let data = handle_c_error_default(res, Some(&mut error_msg)); - assert_eq!(errno().0, ErrnoValue::Success as i32); - assert!(error_msg.is_none()); - assert_eq!(data, Vec::::from(b"foobar" as &[u8])); - let _ = error_msg.consume(); - - // Ok (empty slice) - let mut error_msg = UnmanagedVector::default(); - let res: Result<&[u8], RustError> = Ok(b""); - let data = handle_c_error_default(res, Some(&mut error_msg)); - assert_eq!(errno().0, ErrnoValue::Success as i32); - assert!(error_msg.is_none()); - assert_eq!(data, Vec::::new()); - let _ = error_msg.consume(); - - // Ok (unit) - let mut error_msg = UnmanagedVector::default(); - let res: Result<(), RustError> = Ok(()); - let _data = handle_c_error_default(res, Some(&mut error_msg)); - assert_eq!(errno().0, ErrnoValue::Success as i32); - assert!(error_msg.is_none()); - let _ = error_msg.consume(); - - // Err (vector) - let mut error_msg = UnmanagedVector::default(); - let res: Result, RustError> = Err(RustError::panic()); - let data = handle_c_error_default(res, Some(&mut error_msg)); - assert_eq!(errno().0, ErrnoValue::Other as i32); - assert!(error_msg.is_some()); - assert_eq!(data, Vec::::new()); - let _ = error_msg.consume(); - - // Err (slice) - let mut error_msg = UnmanagedVector::default(); - let res: Result<&[u8], RustError> = Err(RustError::panic()); - let data = handle_c_error_default(res, Some(&mut error_msg)); - assert_eq!(errno().0, ErrnoValue::Other as i32); - assert!(error_msg.is_some()); - assert_eq!(data, Vec::::new()); - let _ = error_msg.consume(); - - // Err (unit) - let mut error_msg = UnmanagedVector::default(); - let res: Result<(), RustError> = Err(RustError::panic()); - let _data = handle_c_error_default(res, Some(&mut error_msg)); - assert_eq!(errno().0, ErrnoValue::Other as i32); - assert!(error_msg.is_some()); - let _ = error_msg.consume(); - } - - #[test] - fn handle_c_error_default_clears_an_old_error() { - // Err - let mut error_msg = UnmanagedVector::default(); - let res: Result, RustError> = Err(RustError::panic()); - let data = handle_c_error_default(res, Some(&mut error_msg)); - assert_eq!(errno().0, ErrnoValue::Other as i32); - assert!(error_msg.is_some()); - assert_eq!(data, Vec::::new()); - let _ = error_msg.consume(); - - // Ok - let mut error_msg = UnmanagedVector::default(); - let res: Result, RustError> = Ok(vec![0xF0, 0x0B, 0xAA]); - let data = handle_c_error_default(res, Some(&mut error_msg)); - assert_eq!(errno().0, ErrnoValue::Success as i32); - assert!(error_msg.is_none()); - assert_eq!(data, vec![0xF0, 0x0B, 0xAA]); - let _ = error_msg.consume(); - } } diff --git a/libwasmvm/src/lib.rs b/libwasmvm/src/lib.rs index 8cf1a0a17..0bce628d3 100644 --- a/libwasmvm/src/lib.rs +++ b/libwasmvm/src/lib.rs @@ -20,7 +20,7 @@ mod version; // exports. There are no guarantees those exports are stable. // We keep them here such that we can access them in the docs (`cargo doc`). pub use api::GoApi; -pub use cache::{cache_t, load_wasm}; +pub use cache::{load_wasm, CachePtr}; pub use db::{db_t, Db}; pub use error::GoError; pub use memory::{ diff --git a/libwasmvm/src/memory.rs b/libwasmvm/src/memory.rs index 8abc01c2a..c43c70cb9 100644 --- a/libwasmvm/src/memory.rs +++ b/libwasmvm/src/memory.rs @@ -129,10 +129,10 @@ impl U8SliceView { /// Transferring ownership from Rust to Go using return values of FFI calls: /// /// ``` -/// # use wasmvm::{cache_t, ByteSliceView, UnmanagedVector}; +/// # use wasmvm::{CachePtr, ByteSliceView, UnmanagedVector}; /// #[no_mangle] /// pub extern "C" fn save_wasm_to_cache( -/// cache: *mut cache_t, +/// cache: CachePtr, /// wasm: ByteSliceView, /// error_msg: Option<&mut UnmanagedVector>, /// ) -> UnmanagedVector { @@ -200,7 +200,7 @@ impl U8SliceView { /// // `output` is ready to be passed around /// ``` #[repr(C)] -#[derive(Copy, Clone, Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct UnmanagedVector { /// True if and only if this is None. If this is true, the other fields must be ignored. is_none: bool,