Skip to content

Commit 57e1eef

Browse files
authored
feat(hermes): auto stub untyped (#519)
1 parent 209eac7 commit 57e1eef

File tree

92 files changed

+704
-3567
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

92 files changed

+704
-3567
lines changed

docs/src/architecture/08_concepts/hermes_runtime_engine/hre_structure.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ impl HermesEventPayload for OnCronEvent {
108108
}
109109

110110
fn execute(&self, module: &mut crate::wasm::module::ModuleInstance) -> anyhow::Result<()> {
111-
module.instance.hermes_cron_event().call_on_cron(
111+
module.instance.call_hermes_cron_event_on_cron(
112112
&mut module.store,
113113
&self.tag,
114114
self.last,

hermes/Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

hermes/Earthfile

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,8 @@ test-wasm-integration:
9595
COPY ../wasm/integration-test/localtime+build/localtime.wasm ../wasm/test-components/
9696
COPY ../wasm/integration-test/logger+build/logger.wasm ../wasm/test-components/
9797
COPY ../wasm/integration-test/sqlite+build/sqlite.wasm ../wasm/test-components/
98-
COPY ../wasm/integration-test/smoke-test+build/smoke-test.wasm ../wasm/test-components/
99-
COPY ../wasm/integration-test/wasi-filesystem+build/wasi-filesystem.wasm ../wasm/test-components/
98+
COPY ../wasm/integration-test/smoke-test+build/smoke_test.wasm ../wasm/test-components/
99+
COPY ../wasm/integration-test/wasi-filesystem+build/wasi_filesystem.wasm ../wasm/test-components/
100100

101101
# List all WASM integration tests/benches and also run them.
102102
RUN cargo test --release --test wasm-component-integration-tests -- --list
@@ -111,19 +111,19 @@ test-wasm-examples:
111111

112112
# Copy all wasm module artifacts for running as Hermes applications
113113

114-
COPY ../wasm/examples/c/cardano_age+build/cardano_age.c.wasm ../wasm/app-components/
115-
COPY ../wasm/examples/c/next_century+build/next_century.c.wasm ../wasm/app-components/
116-
COPY ../wasm/examples/rust/cardano_age+build/cardano_age.rs.wasm ../wasm/app-components/
117-
COPY ../wasm/examples/rust/next_century+build/next_century.rs.wasm ../wasm/app-components/
118-
# COPY ../wasm/examples/rust/cardano+build/cardano.rs.wasm ../wasm/app-components/
114+
COPY ../wasm/examples/c/cardano_age+build/cardano_age_c.wasm ../wasm/app-components/
115+
COPY ../wasm/examples/c/next_century+build/next_century_c.wasm ../wasm/app-components/
116+
COPY ../wasm/examples/rust/cardano_age+build/cardano_age_rs.wasm ../wasm/app-components/
117+
COPY ../wasm/examples/rust/next_century+build/next_century_rs.wasm ../wasm/app-components/
118+
# COPY ../wasm/examples/rust/cardano+build/cardano_rs.wasm ../wasm/app-components/
119119

120-
RUN ./hermes playground ../wasm/app-components/cardano_age.c.wasm --timeout-ms 10000; \
120+
RUN ./hermes playground ../wasm/app-components/cardano_age_c.wasm --timeout-ms 10000; \
121121
test $? -eq 0
122-
RUN ./hermes playground ../wasm/app-components/next_century.c.wasm --timeout-ms 10000; \
122+
RUN ./hermes playground ../wasm/app-components/next_century_c.wasm --timeout-ms 10000; \
123123
test $? -eq 1
124-
RUN ./hermes playground ../wasm/app-components/cardano_age.rs.wasm --timeout-ms 10000; \
124+
RUN ./hermes playground ../wasm/app-components/cardano_age_rs.wasm --timeout-ms 10000; \
125125
test $? -eq 0
126-
RUN ./hermes playground ../wasm/app-components/next_century.rs.wasm --timeout-ms 10000; \
126+
RUN ./hermes playground ../wasm/app-components/next_century_rs.wasm --timeout-ms 10000; \
127127
test $? -eq 1
128128
# Example of how to run the Cardano module test
129129
# Comment out because this is a long running test

hermes/bin/Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ uuid = { version = "1.18.0", features = ["v4"] }
9595
reqwest = "0.12.23"
9696
url = "2.5.7"
9797
num-traits = "0.2.19"
98+
paste = "1.0.15"
9899
traitreg = "0.3.0"
99100
orx-concurrent-vec = "3.7.0"
100101
keyed-lock = "0.2.3"
@@ -104,8 +105,8 @@ build-info-build = "0.0.41"
104105

105106
[dev-dependencies]
106107
serial_test = { version = "3.2.0", features = ["file_locks"] }
107-
# An override with the "wat" feature added.
108-
wasmtime = { version = "36.0.2", default-features = false, features = ["runtime", "cranelift", "component-model", "wat"] }
108+
# An override with the "wat" and "addr2line" features added (debug purposes).
109+
wasmtime = { version = "36.0.2", default-features = false, features = ["runtime", "cranelift", "component-model", "wat", "addr2line"] }
109110
httpmock = "0.7.0"
110111

111112
[package.metadata.cargo-machete]

hermes/bin/src/cli/playground.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use temp_dir::TempDir;
1515
use crate::{
1616
app::{Application, ApplicationName},
1717
event::queue::Exit,
18-
ipfs, reactor,
18+
ipfs, pool, reactor,
1919
vfs::VfsBootstrapper,
2020
wasm::module::Module,
2121
};
@@ -56,13 +56,15 @@ impl Playground {
5656
init_ipfs(&temp_dir)?;
5757

5858
let exit_lock = reactor::init()?;
59-
59+
pool::init()?;
6060
println!(
6161
"{} Loading {} application(s)...",
6262
Emoji::new("🛠️", ""),
6363
apps.len(),
6464
);
6565
for app in apps {
66+
// TODO[RC]: Prevent the app from receiving any events until it is fully initialized.
67+
// TODO[RC]: Currently, when a module fails to initialize, the whole app fails to run.
6668
reactor::load_app(app)?;
6769
}
6870

@@ -72,6 +74,8 @@ impl Playground {
7274
exit_lock.wait()
7375
};
7476

77+
// Wait for scheduled tasks to be finished.
78+
pool::terminate();
7579
Ok(exit)
7680
}
7781
}

hermes/bin/src/runtime_extensions/bindings.rs renamed to hermes/bin/src/runtime_extensions/bindings/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
99
use wasmtime::component::bindgen;
1010

11+
pub(crate) mod unchecked_exports;
12+
1113
bindgen!({
1214
path: "../../wasm/wasi/wit",
1315
imports: {
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
//! Hermes WIT exports for event handlers.
2+
//!
3+
//! Unlike the functions of [`super::Hermes`] (generated by
4+
//! [`wasmtime::component::bindgen`]), these aren't checked for presence in WASM until
5+
//! use.
6+
7+
use wasmtime::{
8+
component::{self, ComponentNamedList, TypedFunc},
9+
AsContextMut,
10+
};
11+
12+
use crate::runtime_context::HermesRuntimeContext;
13+
14+
/// WASM function lookup failure.
15+
#[derive(Debug, thiserror::Error)]
16+
pub(crate) enum Error {
17+
/// A WIT interface and/or function is missing from WASM exports.
18+
#[error("event handler is not exported")]
19+
NotExported,
20+
/// An export is found but its signature doesn't match the one defined in WIT.
21+
#[error("invalid event handler signature")]
22+
InvalidSignature,
23+
}
24+
25+
/// Lookup a function in WASM by its path and signature.
26+
pub(crate) fn get_typed_func<Params, Return>(
27+
instance: &component::Instance,
28+
store: &mut wasmtime::Store<HermesRuntimeContext>,
29+
wit_interface_name: &str,
30+
wit_func_name: &str,
31+
) -> Result<TypedFunc<Params, Return>, Error>
32+
where
33+
Params: ComponentNamedList + component::Lower,
34+
Return: ComponentNamedList + component::Lift,
35+
{
36+
let Some(untyped) = instance
37+
.get_export_index(store.as_context_mut(), None, wit_interface_name)
38+
.and_then(|interface_idx| {
39+
instance.get_export_index(store.as_context_mut(), Some(&interface_idx), wit_func_name)
40+
})
41+
.and_then(|func_idx| instance.get_func(store.as_context_mut(), func_idx))
42+
else {
43+
return Err(Error::NotExported);
44+
};
45+
untyped.typed(store).map_err(|_| Error::InvalidSignature)
46+
}
47+
48+
/// Defines an extension trait for [`wasmtime::component::Instance`] and immediately
49+
/// implements it. Uses [`get_typed_func`] internally.
50+
macro_rules! define {
51+
($(#[$attr:meta])* $vis:vis trait $ext_trait:ident {$(
52+
#[wit($wit_interface:literal, $wit_func:literal)]
53+
fn $rust_func:ident$(<$l:lifetime>)?($($param_name:ident: $param:ty),* $(,)?) $(-> $return:ty)?;
54+
)*}) => {::paste::paste! {
55+
$vis trait $ext_trait {$(
56+
#[doc = concat!("Looks up \"", $wit_func , "\" from \"", $wit_interface, "\".\n\n")]
57+
#[doc = "# Panic\n\n Lookup never panics, calling the export can panic if misused (see [`wasmtime::component::Func::call`]).\n"]
58+
fn [<lookup_ $rust_func>]$(<$l>)?(
59+
&self,
60+
store: &mut ::wasmtime::Store<$crate::runtime_context::HermesRuntimeContext>
61+
) -> Result<
62+
::wasmtime::component::TypedFunc<($($param,)*), ($($return,)?)>,
63+
$crate::runtime_extensions::bindings::unchecked_exports::Error
64+
>;
65+
66+
#[doc = concat!("Looks up and calls \"", $wit_func , "\" from \"", $wit_interface, "\".\n")]
67+
#[doc = concat!("This wraps around [`Self::", stringify!([<lookup_ $rust_func>]), "`].\n\n")]
68+
#[doc = "# Panic\n\n Calling the export can panic if misused (see [`wasmtime::component::Func::call`]).\n"]
69+
#[allow(unused_parens, dead_code, reason = "needed for codegen in no-return case")]
70+
fn $rust_func$(<$l>)?(
71+
&self,
72+
store: &mut ::wasmtime::Store<$crate::runtime_context::HermesRuntimeContext>,
73+
$($param_name: $param,)*
74+
) -> ::anyhow::Result<($($return)?)> {
75+
use anyhow::Context as _;
76+
self.[<lookup_ $rust_func>](store)
77+
.context(concat!("when looking up the export (",$wit_func, " from ", $wit_interface, ')'))?
78+
.call(store, ($($param_name,)*))
79+
.context(concat!("when calling the export (",$wit_func, " from ", $wit_interface, ')'))
80+
$(.map(|(ret,): ($return,)| ret))?
81+
}
82+
)*}
83+
84+
impl $ext_trait for ::wasmtime::component::Instance {$(
85+
fn [<lookup_ $rust_func>]$(<$l>)?(
86+
&self,
87+
store: &mut ::wasmtime::Store<$crate::runtime_context::HermesRuntimeContext>
88+
) -> Result<
89+
::wasmtime::component::TypedFunc<($($param,)*), ($($return,)?)>,
90+
$crate::runtime_extensions::bindings::unchecked_exports::Error
91+
>
92+
{
93+
$crate::runtime_extensions::bindings::unchecked_exports::get_typed_func(self, store, $wit_interface, $wit_func)
94+
}
95+
)*}
96+
}};
97+
}
98+
99+
pub(crate) use define;

hermes/bin/src/runtime_extensions/hermes/cardano/event.rs

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,36 @@
11
//! Cardano Blockchain runtime extension event handler implementation.
22
33
use tracing::error;
4+
use wasmtime::component::Resource;
45

56
use crate::{
67
app::ApplicationName,
78
event::{HermesEvent, HermesEventPayload, TargetApp, TargetModule},
8-
runtime_extensions::hermes::cardano::STATE,
9+
runtime_extensions::{
10+
bindings::{
11+
hermes::cardano::api::{Block, SubscriptionId},
12+
unchecked_exports,
13+
},
14+
hermes::cardano::STATE,
15+
},
916
wasm::module::ModuleId,
1017
};
1118

19+
unchecked_exports::define! {
20+
/// Extends [`wasmtime::component::Instance`] with guest functions for cardano.
21+
trait ComponentInstanceExt {
22+
#[wit("hermes:cardano/event-on-block", "on-cardano-block")]
23+
fn hermes_cardano_event_on_block_on_cardano_block(
24+
subscription_id: Resource<SubscriptionId>, block_id: Resource<Block>,
25+
);
26+
27+
#[wit("hermes:cardano/event-on-immutable-roll-forward", "on-immutable-roll-forward")]
28+
fn hermes_cardano_event_on_immutable_roll_forward_on_cardano_immutable_roll_forward(
29+
subscription_id: Resource<SubscriptionId>, block_id: Resource<Block>,
30+
);
31+
}
32+
}
33+
1234
/// On Cardano block event
1335
pub(super) struct OnCardanoBlockEvent {
1436
/// An underlying 32-bit integer representation used to originally create this
@@ -31,13 +53,16 @@ impl HermesEventPayload for OnCardanoBlockEvent {
3153
module: &mut crate::wasm::module::ModuleInstance,
3254
) -> anyhow::Result<()> {
3355
// Create borrow resources to send to wasm
34-
let subscription_id = wasmtime::component::Resource::new_borrow(self.subscription_id);
35-
let block = wasmtime::component::Resource::new_borrow(self.block);
56+
let subscription_id = Resource::new_borrow(self.subscription_id);
57+
let block = Resource::new_borrow(self.block);
3658

3759
module
3860
.instance
39-
.hermes_cardano_event_on_block()
40-
.call_on_cardano_block(&mut module.store, subscription_id, block)?;
61+
.hermes_cardano_event_on_block_on_cardano_block(
62+
&mut module.store,
63+
subscription_id,
64+
block,
65+
)?;
4166
Ok(())
4267
}
4368
}
@@ -80,13 +105,16 @@ impl HermesEventPayload for OnCardanoImmutableRollForwardEvent {
80105
module: &mut crate::wasm::module::ModuleInstance,
81106
) -> anyhow::Result<()> {
82107
// Create borrow resources to send to wasm
83-
let subscription_id = wasmtime::component::Resource::new_borrow(self.subscription_id);
84-
let block = wasmtime::component::Resource::new_borrow(self.block);
108+
let subscription_id = Resource::new_borrow(self.subscription_id);
109+
let block = Resource::new_borrow(self.block);
85110

86111
module
87112
.instance
88-
.hermes_cardano_event_on_immutable_roll_forward()
89-
.call_on_cardano_immutable_roll_forward(&mut module.store, subscription_id, block)?;
113+
.hermes_cardano_event_on_immutable_roll_forward_on_cardano_immutable_roll_forward(
114+
&mut module.store,
115+
subscription_id,
116+
block,
117+
)?;
90118
Ok(())
91119
}
92120
}

hermes/bin/src/runtime_extensions/hermes/cron/event.rs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,18 @@ use saffron::Cron;
66

77
use super::state::cron_queue_rm;
88
use crate::{
9-
event::HermesEventPayload, runtime_extensions::bindings::hermes::cron::api::CronTagged,
9+
event::HermesEventPayload,
10+
runtime_extensions::bindings::{hermes::cron::api::CronTagged, unchecked_exports},
1011
};
1112

13+
unchecked_exports::define! {
14+
/// Extends [`wasmtime::component::Instance`] with guest functions for cron.
15+
trait ComponentInstanceExt {
16+
#[wit("hermes:cron/event", "on-cron")]
17+
fn hermes_cron_event_on_cron(event: &CronTagged, last: bool) -> bool;
18+
}
19+
}
20+
1221
/// Duration in nanoseconds used for the Cron Service.
1322
#[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
1423
#[cfg_attr(debug_assertions, derive(Debug))]
@@ -71,11 +80,10 @@ impl HermesEventPayload for OnCronEvent {
7180
&self,
7281
module: &mut crate::wasm::module::ModuleInstance,
7382
) -> anyhow::Result<()> {
74-
let res: bool = module.instance.hermes_cron_event().call_on_cron(
75-
&mut module.store,
76-
&self.tag,
77-
self.last,
78-
)?;
83+
let res =
84+
module
85+
.instance
86+
.hermes_cron_event_on_cron(&mut module.store, &self.tag, self.last)?;
7987
// if the response is `false`, check if the event would
8088
// re-trigger, if so, remove it.
8189
if !res && !self.last {

hermes/bin/src/runtime_extensions/hermes/http_gateway/event.rs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ use url::Url;
1313

1414
use crate::{
1515
event::HermesEventPayload,
16-
runtime_extensions::bindings::exports::hermes::http_gateway::event::HttpGatewayResponse,
16+
runtime_extensions::bindings::{
17+
exports::hermes::http_gateway::event::HttpGatewayResponse, unchecked_exports,
18+
},
1719
};
1820

1921
// ============================================================================
@@ -204,6 +206,19 @@ fn validate_path(
204206
// HTTP Event Processing
205207
// ============================================================================
206208

209+
unchecked_exports::define! {
210+
/// Extends [`wasmtime::component::Instance`] with guest functions for HTTP gateway.
211+
trait ComponentInstanceExt {
212+
#[wit("hermes:http-gateway/event", "reply")]
213+
fn hermes_http_gateway_event_reply<'p> (
214+
body: &'p [u8],
215+
headers: &'p [(String, Vec<String>)],
216+
path: &'p str,
217+
method: &'p str,
218+
) -> Option<HttpGatewayResponse>;
219+
}
220+
}
221+
207222
impl HermesEventPayload for HTTPEvent {
208223
fn event_name(&self) -> &'static str {
209224
"http-event"
@@ -213,9 +228,9 @@ impl HermesEventPayload for HTTPEvent {
213228
&self,
214229
module: &mut crate::wasm::module::ModuleInstance,
215230
) -> anyhow::Result<()> {
216-
let event_response = module.instance.hermes_http_gateway_event().call_reply(
231+
let event_response = module.instance.hermes_http_gateway_event_reply(
217232
&mut module.store,
218-
&self.body.as_ref().to_vec(),
233+
&self.body,
219234
&self.headers,
220235
&self.path,
221236
&self.method,

0 commit comments

Comments
 (0)