fix(wasm-runtime): align host function signatures and add storage host functions - #15
fix(wasm-runtime): align host function signatures and add storage host functions#15echobt wants to merge 5 commits into
Conversation
- http_get: 4 params -> 3 params (req_ptr, req_len, resp_ptr) - http_post: 4 params -> 5 params (req_ptr, req_len, resp_ptr, resp_len, extra) - dns_resolve: 4 params -> 3 params (req_ptr, req_len, resp_ptr) - Add write_result_unbounded/write_bytes_unbounded helpers for functions without caller-supplied resp_len, using memory size as bound instead
…rar trait - Add StorageHostFunctions struct implementing HostFunctionRegistrar - Register storage_get and storage_set under platform_storage namespace - Add data HashMap to StorageHostState for in-memory key-value storage - Implement handle_storage_get: reads key, looks up in data, writes value - Implement handle_storage_set: validates key/value, inserts into data - Add memory helper functions (get_memory, read_memory, write_to_memory) - Add HOST_STORAGE_SET constant - Export StorageHostFunctions and HOST_STORAGE_SET from lib.rs
📝 WalkthroughWalkthroughThis PR extends the WASM runtime interface to support persistent key-value storage operations by introducing a new Changes
Sequence Diagram(s)sequenceDiagram
participant Guest as Guest WASM
participant Host as Host Function Handler
participant Memory as Guest Memory
participant Storage as Storage HashMap
Guest->>Host: storage_set(key_ptr, key_len, value_ptr, value_len)
Host->>Memory: get_memory()
Memory-->>Host: memory export
Host->>Memory: read_memory(key_ptr, key_len)
Memory-->>Host: key bytes
Host->>Memory: read_memory(value_ptr, value_len)
Memory-->>Host: value bytes
Host->>Storage: insert(key, value)
Storage-->>Host: ok
Host->>Host: write_result_unbounded(status)
Host-->>Guest: status code
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…n-signatures-and-storage
…n-signatures-and-storage
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
crates/wasm-runtime-interface/src/storage.rs (1)
1-22:⚠️ Potential issue | 🟡 MinorModule doc comment is stale — does not match implemented signatures.
The doc block describes:
storage_get(key_ptr, key_len) -> i64— actual:storage_get(key_ptr, key_len, value_ptr) -> i32storage_propose_write(...)— butstorage_setis what's implemented- Return format described as packed i64 — but the new functions return plain
i32Please update the doc to reflect the actual host function surface.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/wasm-runtime-interface/src/storage.rs` around lines 1 - 22, Update the module doc comment to match the implemented host functions and their actual signatures/return types: replace the described `storage_get(key_ptr, key_len) -> i64` with the real `storage_get(key_ptr, key_len, value_ptr) -> i32`, rename `storage_propose_write` to `storage_set` (or document both if both exist) and document `storage_delete`'s real signature, and remove the packed i64 return description—describe the concrete i32 return conventions used by `storage_get`, `storage_set`, and `storage_delete` (e.g., success/error codes and how value_ptr is used) so the docblock matches the symbols `storage_get`, `storage_set` (or `storage_propose_write` if present), and `storage_delete`.
🧹 Nitpick comments (2)
crates/wasm-runtime-interface/src/network.rs (2)
622-671: Inconsistent response-write strategy betweenhttp_postandhttp_get/dns_resolve.
handle_http_getandhandle_dns_requestswitched towrite_result_unbounded(noresp_len), whilehandle_http_postretains the boundedwrite_resultwithresp_len— plus an unused_extraparameter. This means:
- GET/DNS: guest supplies no buffer length → host writes unconditionally.
- POST: guest supplies
resp_len→ host respects bounded writes.If this asymmetry is intentional to match the guest SDK, consider documenting why the protocols differ. If unintentional,
http_postshould likely follow the same pattern as the others.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/wasm-runtime-interface/src/network.rs` around lines 622 - 671, The handle_http_post function currently uses write_result with a bounded resp_len and retains an unused _extra parameter, making it inconsistent with handle_http_get and handle_dns_request which use write_result_unbounded; change handle_http_post to call write_result_unbounded(caller, resp_ptr, result) instead of write_result(caller, resp_ptr, resp_len, result), remove the unused _extra parameter from the signature, and ensure any callers or exports are updated to match the unbounded response pattern to keep GET/POST/DNS behavior consistent.
890-905: Extractread_memoryandget_memoryto a shared module.Both
network.rsandstorage.rscontain identical implementations ofread_memoryandget_memory. Move these functions to a shared location (e.g.,runtime.rsor a newmemory.rsutility module) to eliminate duplication and maintain a single source of truth for guest-memory I/O operations.Affected locations:
network.rs: lines 890–905 (read_memory) and 1001–1006 (get_memory)storage.rs: lines 558–573 (read_memory) and 551–556 (get_memory)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/wasm-runtime-interface/src/network.rs` around lines 890 - 905, Extract the duplicate read_memory and get_memory implementations into a single shared module (e.g., create crates/wasm-runtime-interface/src/memory.rs or add them to runtime.rs), make them appropriately visible (pub(crate) or pub as needed), and remove the copies from network.rs and storage.rs; then update both network.rs and storage.rs to import and call the shared get_memory and read_memory functions instead of their local versions, and run cargo build/tests to ensure no visibility or import errors remain.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@crates/wasm-runtime-interface/src/network.rs`:
- Around line 975-999: The write_bytes_unbounded function can overwrite adjacent
guest memory because it never verifies the actual guest buffer size; update
write_bytes_unbounded to perform the same safety check as write_bytes by either
(a) require and accept a guest-supplied resp_len and return -(needed_len) when
the provided buffer is too small, (b) call the guest allocator (storage_alloc)
to obtain a buffer large enough before writing, or (c) explicitly document and
enforce a precondition that the guest must allocate sufficient space—implement
one of these approaches in the write_bytes_unbounded implementation (referencing
write_bytes and storage_alloc to mirror the two-phase/allocator pattern) so the
host never blindly writes past the guest buffer.
In `@crates/wasm-runtime-interface/src/storage.rs`:
- Around line 454-499: The return-value collides because handle_storage_get
returns val.len() as i32 for success but StorageHostStatus::NotFound.to_i32() ==
1, making a 1-byte value indistinguishable from NotFound; change the status
encoding so error/status codes are negative (e.g., make
StorageHostStatus::NotFound a negative value) and update the
StorageHostStatus::to_i32/from_i32 mappings accordingly so positive returns from
handle_storage_get always mean bytes written and negative returns indicate
error/status; update all callsites that interpret StorageHostStatus integers to
use the new negative status convention (references: function handle_storage_get,
enum StorageHostStatus and its to_i32/from_i32 conversions).
- Around line 501-549: handle_storage_set currently inserts without enforcing
StorageHostConfig limits; before inserting, check
storage_state.config.max_keys_per_challenge against current
storage_state.data.len() (or add/update a keys_count) and check
storage_state.bytes_written (or a new cumulative total_storage_bytes) +
value.len() <= storage_state.config.max_total_storage; if either limit would be
exceeded return the appropriate StorageHostStatus (e.g., QuotaExceeded or a
mapped error) instead of inserting; also update the cumulative
total_storage_bytes (or bytes_written) on successful insert/remove so future
checks are O(1) rather than O(n).
---
Outside diff comments:
In `@crates/wasm-runtime-interface/src/storage.rs`:
- Around line 1-22: Update the module doc comment to match the implemented host
functions and their actual signatures/return types: replace the described
`storage_get(key_ptr, key_len) -> i64` with the real `storage_get(key_ptr,
key_len, value_ptr) -> i32`, rename `storage_propose_write` to `storage_set` (or
document both if both exist) and document `storage_delete`'s real signature, and
remove the packed i64 return description—describe the concrete i32 return
conventions used by `storage_get`, `storage_set`, and `storage_delete` (e.g.,
success/error codes and how value_ptr is used) so the docblock matches the
symbols `storage_get`, `storage_set` (or `storage_propose_write` if present),
and `storage_delete`.
---
Nitpick comments:
In `@crates/wasm-runtime-interface/src/network.rs`:
- Around line 622-671: The handle_http_post function currently uses write_result
with a bounded resp_len and retains an unused _extra parameter, making it
inconsistent with handle_http_get and handle_dns_request which use
write_result_unbounded; change handle_http_post to call
write_result_unbounded(caller, resp_ptr, result) instead of write_result(caller,
resp_ptr, resp_len, result), remove the unused _extra parameter from the
signature, and ensure any callers or exports are updated to match the unbounded
response pattern to keep GET/POST/DNS behavior consistent.
- Around line 890-905: Extract the duplicate read_memory and get_memory
implementations into a single shared module (e.g., create
crates/wasm-runtime-interface/src/memory.rs or add them to runtime.rs), make
them appropriately visible (pub(crate) or pub as needed), and remove the copies
from network.rs and storage.rs; then update both network.rs and storage.rs to
import and call the shared get_memory and read_memory functions instead of their
local versions, and run cargo build/tests to ensure no visibility or import
errors remain.
| fn write_bytes_unbounded(caller: &mut Caller<RuntimeState>, resp_ptr: i32, bytes: &[u8]) -> i32 { | ||
| if resp_ptr < 0 { | ||
| return -1; | ||
| } | ||
| if bytes.len() > i32::MAX as usize { | ||
| return -1; | ||
| } | ||
|
|
||
| let memory = match get_memory(caller) { | ||
| Some(memory) => memory, | ||
| None => return -1, | ||
| }; | ||
|
|
||
| let ptr = resp_ptr as usize; | ||
| let end = match ptr.checked_add(bytes.len()) { | ||
| Some(end) => end, | ||
| None => return -1, | ||
| }; | ||
| let data = memory.data_mut(caller); | ||
| if end > data.len() { | ||
| return -(bytes.len() as i32); | ||
| } | ||
| data[ptr..end].copy_from_slice(bytes); | ||
| bytes.len() as i32 | ||
| } |
There was a problem hiding this comment.
write_bytes_unbounded can silently corrupt guest memory.
This function writes a response of arbitrary length to a guest-supplied pointer without any buffer-size check from the guest side. The only guard (Line 994) catches writes past the total WASM linear memory, but if the response is larger than the buffer the guest actually allocated at resp_ptr, adjacent guest data (heap, stack, globals) will be silently overwritten.
Contrast with the bounded write_bytes (Line 940) which uses the guest-supplied resp_len to return a negative "need this much space" indicator before writing. The unbounded variant skips this safeguard.
Consider one of:
- A two-phase protocol: first return the required size, then let the guest call back with a sufficiently sized buffer.
- Having the guest provide an allocation function the host can call (e.g.,
storage_alloc, which is already defined as a constant). - At minimum, documenting the contract that the guest must pre-allocate a buffer large enough for the maximum possible response.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@crates/wasm-runtime-interface/src/network.rs` around lines 975 - 999, The
write_bytes_unbounded function can overwrite adjacent guest memory because it
never verifies the actual guest buffer size; update write_bytes_unbounded to
perform the same safety check as write_bytes by either (a) require and accept a
guest-supplied resp_len and return -(needed_len) when the provided buffer is too
small, (b) call the guest allocator (storage_alloc) to obtain a buffer large
enough before writing, or (c) explicitly document and enforce a precondition
that the guest must allocate sufficient space—implement one of these approaches
in the write_bytes_unbounded implementation (referencing write_bytes and
storage_alloc to mirror the two-phase/allocator pattern) so the host never
blindly writes past the guest buffer.
| fn handle_storage_get( | ||
| caller: &mut Caller<RuntimeState>, | ||
| key_ptr: i32, | ||
| key_len: i32, | ||
| value_ptr: i32, | ||
| ) -> i32 { | ||
| let key = match read_memory(caller, key_ptr, key_len) { | ||
| Ok(bytes) => bytes, | ||
| Err(err) => { | ||
| warn!(error = %err, "storage_get: memory read failed"); | ||
| return StorageHostStatus::InternalError.to_i32(); | ||
| } | ||
| }; | ||
|
|
||
| let storage_state = match caller.data().storage_state.as_ref() { | ||
| Some(state) => state, | ||
| None => { | ||
| warn!("storage_get: storage state not initialized"); | ||
| return StorageHostStatus::InternalError.to_i32(); | ||
| } | ||
| }; | ||
|
|
||
| if let Err(err) = storage_state.config.validate_key(&key) { | ||
| let status: StorageHostStatus = err.into(); | ||
| return status.to_i32(); | ||
| } | ||
|
|
||
| let value = storage_state.data.get(&key).cloned(); | ||
|
|
||
| let storage_state = caller.data_mut().storage_state.as_mut().unwrap(); | ||
| storage_state.operations_count = storage_state.operations_count.saturating_add(1); | ||
|
|
||
| match value { | ||
| Some(val) => { | ||
| if let Err(err) = write_to_memory(caller, value_ptr, &val) { | ||
| warn!(error = %err, "storage_get: memory write failed"); | ||
| return StorageHostStatus::InternalError.to_i32(); | ||
| } | ||
| let len = val.len(); | ||
| let storage_state = caller.data_mut().storage_state.as_mut().unwrap(); | ||
| storage_state.bytes_read = storage_state.bytes_read.saturating_add(len as u64); | ||
| len as i32 | ||
| } | ||
| None => StorageHostStatus::NotFound.to_i32(), | ||
| } | ||
| } |
There was a problem hiding this comment.
Return-value collision: NotFound (1) is indistinguishable from a successful 1-byte read.
handle_storage_get returns val.len() as i32 on success (Line 495) and StorageHostStatus::NotFound.to_i32() (= 1) when the key isn't found (Line 497). A guest reading a key whose stored value is exactly 1 byte will receive 1 — the same return code as "not found."
The guest has no way to distinguish these two cases from the return value alone.
Options to fix:
- Reserve positive return values exclusively for byte lengths (0 = empty value, which is allowed by
validate_value) and use a distinct negative code for NotFound. - Return the length via a separate out-pointer and use the return value only for status codes.
Possible fix: change NotFound to a negative status code
If NotFound were assigned a negative value (like the other error statuses), e.g., -9, there would be no ambiguity:
pub enum StorageHostStatus {
Success = 0,
- NotFound = 1,
+ NotFound = -9,
KeyTooLarge = -1,Then positive return values always mean "bytes written" and negative values always mean "error/status." You'd also need to update from_i32 accordingly.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@crates/wasm-runtime-interface/src/storage.rs` around lines 454 - 499, The
return-value collides because handle_storage_get returns val.len() as i32 for
success but StorageHostStatus::NotFound.to_i32() == 1, making a 1-byte value
indistinguishable from NotFound; change the status encoding so error/status
codes are negative (e.g., make StorageHostStatus::NotFound a negative value) and
update the StorageHostStatus::to_i32/from_i32 mappings accordingly so positive
returns from handle_storage_get always mean bytes written and negative returns
indicate error/status; update all callsites that interpret StorageHostStatus
integers to use the new negative status convention (references: function
handle_storage_get, enum StorageHostStatus and its to_i32/from_i32 conversions).
| fn handle_storage_set( | ||
| caller: &mut Caller<RuntimeState>, | ||
| key_ptr: i32, | ||
| key_len: i32, | ||
| value_ptr: i32, | ||
| value_len: i32, | ||
| ) -> i32 { | ||
| let key = match read_memory(caller, key_ptr, key_len) { | ||
| Ok(bytes) => bytes, | ||
| Err(err) => { | ||
| warn!(error = %err, "storage_set: key memory read failed"); | ||
| return StorageHostStatus::InternalError.to_i32(); | ||
| } | ||
| }; | ||
|
|
||
| let value = match read_memory(caller, value_ptr, value_len) { | ||
| Ok(bytes) => bytes, | ||
| Err(err) => { | ||
| warn!(error = %err, "storage_set: value memory read failed"); | ||
| return StorageHostStatus::InternalError.to_i32(); | ||
| } | ||
| }; | ||
|
|
||
| let storage_state = match caller.data().storage_state.as_ref() { | ||
| Some(state) => state, | ||
| None => { | ||
| warn!("storage_set: storage state not initialized"); | ||
| return StorageHostStatus::InternalError.to_i32(); | ||
| } | ||
| }; | ||
|
|
||
| if let Err(err) = storage_state.config.validate_key(&key) { | ||
| let status: StorageHostStatus = err.into(); | ||
| return status.to_i32(); | ||
| } | ||
|
|
||
| if let Err(err) = storage_state.config.validate_value(&value) { | ||
| let status: StorageHostStatus = err.into(); | ||
| return status.to_i32(); | ||
| } | ||
|
|
||
| let val_len = value.len() as u64; | ||
| let storage_state = caller.data_mut().storage_state.as_mut().unwrap(); | ||
| storage_state.data.insert(key, value); | ||
| storage_state.bytes_written = storage_state.bytes_written.saturating_add(val_len); | ||
| storage_state.operations_count = storage_state.operations_count.saturating_add(1); | ||
|
|
||
| StorageHostStatus::Success.to_i32() | ||
| } |
There was a problem hiding this comment.
handle_storage_set does not enforce max_keys_per_challenge or max_total_storage.
StorageHostConfig defines max_keys_per_challenge (default: 10,000) and max_total_storage (default: 100 MB), but handle_storage_set never checks these limits before inserting into the data map. A guest module could exhaust host memory by inserting unbounded keys/data within the configured per-value limit.
Proposed enforcement sketch
if let Err(err) = storage_state.config.validate_value(&value) {
let status: StorageHostStatus = err.into();
return status.to_i32();
}
+ // Enforce per-challenge key limit
+ if !storage_state.data.contains_key(&key)
+ && storage_state.data.len() >= storage_state.config.max_keys_per_challenge
+ {
+ return StorageHostStatus::QuotaExceeded.to_i32();
+ }
+
+ // Enforce total storage limit (approximate)
+ let current_total: usize = storage_state.data.iter().map(|(k, v)| k.len() + v.len()).sum();
+ let new_total = current_total + key.len() + value.len();
+ if new_total > storage_state.config.max_total_storage {
+ return StorageHostStatus::QuotaExceeded.to_i32();
+ }
+
let val_len = value.len() as u64;
let storage_state = caller.data_mut().storage_state.as_mut().unwrap();Note: The total-storage check above is O(n) per write. For better performance, track cumulative size in StorageHostState and update it on insert/remove.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| fn handle_storage_set( | |
| caller: &mut Caller<RuntimeState>, | |
| key_ptr: i32, | |
| key_len: i32, | |
| value_ptr: i32, | |
| value_len: i32, | |
| ) -> i32 { | |
| let key = match read_memory(caller, key_ptr, key_len) { | |
| Ok(bytes) => bytes, | |
| Err(err) => { | |
| warn!(error = %err, "storage_set: key memory read failed"); | |
| return StorageHostStatus::InternalError.to_i32(); | |
| } | |
| }; | |
| let value = match read_memory(caller, value_ptr, value_len) { | |
| Ok(bytes) => bytes, | |
| Err(err) => { | |
| warn!(error = %err, "storage_set: value memory read failed"); | |
| return StorageHostStatus::InternalError.to_i32(); | |
| } | |
| }; | |
| let storage_state = match caller.data().storage_state.as_ref() { | |
| Some(state) => state, | |
| None => { | |
| warn!("storage_set: storage state not initialized"); | |
| return StorageHostStatus::InternalError.to_i32(); | |
| } | |
| }; | |
| if let Err(err) = storage_state.config.validate_key(&key) { | |
| let status: StorageHostStatus = err.into(); | |
| return status.to_i32(); | |
| } | |
| if let Err(err) = storage_state.config.validate_value(&value) { | |
| let status: StorageHostStatus = err.into(); | |
| return status.to_i32(); | |
| } | |
| let val_len = value.len() as u64; | |
| let storage_state = caller.data_mut().storage_state.as_mut().unwrap(); | |
| storage_state.data.insert(key, value); | |
| storage_state.bytes_written = storage_state.bytes_written.saturating_add(val_len); | |
| storage_state.operations_count = storage_state.operations_count.saturating_add(1); | |
| StorageHostStatus::Success.to_i32() | |
| } | |
| fn handle_storage_set( | |
| caller: &mut Caller<RuntimeState>, | |
| key_ptr: i32, | |
| key_len: i32, | |
| value_ptr: i32, | |
| value_len: i32, | |
| ) -> i32 { | |
| let key = match read_memory(caller, key_ptr, key_len) { | |
| Ok(bytes) => bytes, | |
| Err(err) => { | |
| warn!(error = %err, "storage_set: key memory read failed"); | |
| return StorageHostStatus::InternalError.to_i32(); | |
| } | |
| }; | |
| let value = match read_memory(caller, value_ptr, value_len) { | |
| Ok(bytes) => bytes, | |
| Err(err) => { | |
| warn!(error = %err, "storage_set: value memory read failed"); | |
| return StorageHostStatus::InternalError.to_i32(); | |
| } | |
| }; | |
| let storage_state = match caller.data().storage_state.as_ref() { | |
| Some(state) => state, | |
| None => { | |
| warn!("storage_set: storage state not initialized"); | |
| return StorageHostStatus::InternalError.to_i32(); | |
| } | |
| }; | |
| if let Err(err) = storage_state.config.validate_key(&key) { | |
| let status: StorageHostStatus = err.into(); | |
| return status.to_i32(); | |
| } | |
| if let Err(err) = storage_state.config.validate_value(&value) { | |
| let status: StorageHostStatus = err.into(); | |
| return status.to_i32(); | |
| } | |
| // Enforce per-challenge key limit | |
| if !storage_state.data.contains_key(&key) | |
| && storage_state.data.len() >= storage_state.config.max_keys_per_challenge | |
| { | |
| return StorageHostStatus::QuotaExceeded.to_i32(); | |
| } | |
| // Enforce total storage limit (approximate) | |
| let current_total: usize = storage_state.data.iter().map(|(k, v)| k.len() + v.len()).sum(); | |
| let new_total = current_total + key.len() + value.len(); | |
| if new_total > storage_state.config.max_total_storage { | |
| return StorageHostStatus::QuotaExceeded.to_i32(); | |
| } | |
| let val_len = value.len() as u64; | |
| let storage_state = caller.data_mut().storage_state.as_mut().unwrap(); | |
| storage_state.data.insert(key, value); | |
| storage_state.bytes_written = storage_state.bytes_written.saturating_add(val_len); | |
| storage_state.operations_count = storage_state.operations_count.saturating_add(1); | |
| StorageHostStatus::Success.to_i32() | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@crates/wasm-runtime-interface/src/storage.rs` around lines 501 - 549,
handle_storage_set currently inserts without enforcing StorageHostConfig limits;
before inserting, check storage_state.config.max_keys_per_challenge against
current storage_state.data.len() (or add/update a keys_count) and check
storage_state.bytes_written (or a new cumulative total_storage_bytes) +
value.len() <= storage_state.config.max_total_storage; if either limit would be
exceeded return the appropriate StorageHostStatus (e.g., QuotaExceeded or a
mapped error) instead of inserting; also update the cumulative
total_storage_bytes (or bytes_written) on successful insert/remove so future
checks are O(1) rather than O(n).
Summary
Fixes WASM module instantiation failures caused by host function signature mismatches between the guest SDK and the runtime interface, and implements the missing
storage_get/storage_sethost functions.Changes
Network host function signature alignment (
network.rs)http_get: Changed from 4 params(req_ptr, req_len, resp_ptr, resp_len)to 3 params(req_ptr, req_len, resp_ptr)to match guest SDKhttp_post: Changed from 4 params to 5 params(req_ptr, req_len, resp_ptr, resp_len, extra)to match guest SDKdns_resolve: Changed from 4 params(req_ptr, req_len, resp_ptr, resp_len)to 3 params(req_ptr, req_len, resp_ptr)to match guest SDKwrite_result_unboundedandwrite_bytes_unboundedhelpers for writing responses without a length-bounded bufferStorage host functions (
storage.rs)StorageHostFunctionsstruct withHostFunctionRegistrartraitstorage_get(key_ptr, key_len, value_ptr) → i32andstorage_set(key_ptr, key_len, value_ptr, value_len) → i32under theplatform_storageWASM import namespacedatafield toStorageHostStatefor storage operationsread_memory,write_to_memory, andget_memoryhelpersRuntime state integration (
runtime.rs)storage_state: Option<StorageHostState>field toRuntimeStateRuntimeState::new()constructor to accept and store storage stateModule exports (
lib.rs)StorageHostFunctionsandHOST_STORAGE_SETfrom the crate rootSummary by CodeRabbit
New Features
Improvements