From f74d59018a28ca87518b83880ae63a78db5084c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Leegwater=20Sim=C3=B5es?= Date: Wed, 4 Sep 2024 12:49:03 +0200 Subject: [PATCH 1/6] uplink: add `emit_raw` The new function, `emit_raw`, allows for emitting events that are *not* serialized using `rkyv`. This effectively allows for user-defined serialization of event data. --- piecrust-uplink/CHANGELOG.md | 1 + piecrust-uplink/src/abi/state.rs | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/piecrust-uplink/CHANGELOG.md b/piecrust-uplink/CHANGELOG.md index 85c3e960..28ab4145 100644 --- a/piecrust-uplink/CHANGELOG.md +++ b/piecrust-uplink/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Add `emit_raw` function - Add `ContractError::DoesNotExist` variant ## [0.16.0] - 2024-08-01 diff --git a/piecrust-uplink/src/abi/state.rs b/piecrust-uplink/src/abi/state.rs index e4b94b1b..9c7ad142 100644 --- a/piecrust-uplink/src/abi/state.rs +++ b/piecrust-uplink/src/abi/state.rs @@ -299,7 +299,7 @@ pub fn spent() -> u64 { unsafe { ext::spent() } } -/// Emits an event with the given data. +/// Emits an event with the given data, serializing it using [`rkyv`]. pub fn emit(topic: &'static str, data: D) where for<'a> D: Serialize>, @@ -321,6 +321,23 @@ where }); } +/// Emits an event with the given data. +pub fn emit_raw(topic: &'static str, data: impl AsRef<[u8]>) { + with_arg_buf(|buf| { + let data = data.as_ref(); + + let arg_len = data.len(); + buf[..arg_len].copy_from_slice(&data); + + let arg_len = arg_len as u32; + + let topic_ptr = topic.as_ptr(); + let topic_len = topic.len() as u32; + + unsafe { ext::emit(topic_ptr, topic_len, arg_len) } + }); +} + /// Feeds the host with data. /// /// This is only allowed to be called in the context of a `feed_call`, and From 9cf113d5a0bc82cbfd5de27e491e378a58990f1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Leegwater=20Sim=C3=B5es?= Date: Wed, 4 Sep 2024 12:51:59 +0200 Subject: [PATCH 2/6] contracts: add `emit_events_raw` function We expose the `emit_events_raw` which should function efectivelly the same as `emit_events`, but uses the `emit_raw` function. --- contracts/eventer/src/lib.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/contracts/eventer/src/lib.rs b/contracts/eventer/src/lib.rs index dfec48de..31f1ab99 100644 --- a/contracts/eventer/src/lib.rs +++ b/contracts/eventer/src/lib.rs @@ -27,6 +27,13 @@ impl Eventer { } } + /// Emits an event with the given number, using `emit_raw` + pub fn emit_num_raw(&mut self, num: u32) { + for i in 0..num { + uplink::emit_raw("number", i.to_le_bytes()); + } + } + pub fn emit_input(&mut self, input: Vec) -> (u64, u64) { let spent_before = uplink::spent(); uplink::emit("input", input); @@ -41,6 +48,12 @@ unsafe fn emit_events(arg_len: u32) -> u32 { uplink::wrap_call(arg_len, |num| STATE.emit_num(num)) } +/// Expose `Eventer::emit_num_raw()` to the host +#[no_mangle] +unsafe fn emit_events_raw(arg_len: u32) -> u32 { + uplink::wrap_call(arg_len, |num| STATE.emit_num_raw(num)) +} + /// Expose `Eventer::emit_input()` to the host #[no_mangle] unsafe fn emit_input(arg_len: u32) -> u32 { From e608f89ac082f3357e9ef0dfbd008fa3548d10fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Leegwater=20Sim=C3=B5es?= Date: Wed, 4 Sep 2024 12:53:58 +0200 Subject: [PATCH 3/6] piecrust: add test for raw event emission --- piecrust/tests/eventer.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/piecrust/tests/eventer.rs b/piecrust/tests/eventer.rs index 6b91fd66..d75a9636 100644 --- a/piecrust/tests/eventer.rs +++ b/piecrust/tests/eventer.rs @@ -36,6 +36,23 @@ pub fn vm_center_events() -> Result<(), Error> { assert_eq!(events[index].data, i.to_le_bytes()); } + let receipt = session.call::<_, ()>( + eventer_id, + "emit_events_raw", + &EVENT_NUM, + LIMIT, + )?; + + let events = receipt.events; + assert_eq!(events.len() as u32, EVENT_NUM); + + for i in 0..EVENT_NUM { + let index = i as usize; + assert_eq!(events[index].source, eventer_id); + assert_eq!(events[index].topic, "number"); + assert_eq!(events[index].data, i.to_le_bytes()); + } + Ok(()) } From c6210b2585864e5d0ddfbd89a61c20fc297aa1d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Leegwater=20Sim=C3=B5es?= Date: Wed, 4 Sep 2024 13:03:49 +0200 Subject: [PATCH 4/6] uplink: add `feed_raw` The new function, `feed_raw`, allows for emitting events that are *not* serialized using `rkyv`. This effectively allows for user-defined serialization of feed call data. --- piecrust-uplink/CHANGELOG.md | 2 +- piecrust-uplink/src/abi/state.rs | 20 +++++++++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/piecrust-uplink/CHANGELOG.md b/piecrust-uplink/CHANGELOG.md index 28ab4145..3fae4cf4 100644 --- a/piecrust-uplink/CHANGELOG.md +++ b/piecrust-uplink/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Add `emit_raw` function +- Add `emit_raw` and `feed_raw` functions - Add `ContractError::DoesNotExist` variant ## [0.16.0] - 2024-08-01 diff --git a/piecrust-uplink/src/abi/state.rs b/piecrust-uplink/src/abi/state.rs index 9c7ad142..031c752f 100644 --- a/piecrust-uplink/src/abi/state.rs +++ b/piecrust-uplink/src/abi/state.rs @@ -338,7 +338,7 @@ pub fn emit_raw(topic: &'static str, data: impl AsRef<[u8]>) { }); } -/// Feeds the host with data. +/// Feeds the host with data, serializing it using [`rkyv`]. /// /// This is only allowed to be called in the context of a `feed_call`, and /// will error out otherwise. It is meant for contracts to be able to report @@ -360,3 +360,21 @@ where unsafe { ext::feed(arg_len) } }); } + +/// Feeds the host with data. +/// +/// This is only allowed to be called in the context of a `feed_call`, and +/// will error out otherwise. It is meant for contracts to be able to report +/// large amounts of data to the host, in the span of a single call. +pub fn feed_raw(data: impl AsRef<[u8]>) { + with_arg_buf(|buf| { + let data = data.as_ref(); + + let arg_len = data.len(); + buf[..arg_len].copy_from_slice(&data); + + let arg_len = arg_len as u32; + + unsafe { ext::feed(arg_len) } + }); +} From 5102b4da32a303dd5930140f7527564a42182b43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Leegwater=20Sim=C3=B5es?= Date: Wed, 4 Sep 2024 14:43:28 +0200 Subject: [PATCH 5/6] contracts: add `feed_num_raw` function We expose the `feed_num_raw` which should function efectivelly the same as `feed_num`, but uses the `feed_raw` function. --- contracts/feeder/src/lib.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/contracts/feeder/src/lib.rs b/contracts/feeder/src/lib.rs index e8eb6f3b..332cb4fa 100644 --- a/contracts/feeder/src/lib.rs +++ b/contracts/feeder/src/lib.rs @@ -25,6 +25,13 @@ impl Feeder { uplink::feed(i); } } + + /// Feed the host with 32-bit integers sequentially in the `0..num` range. + pub fn feed_num_raw(&self, num: u32) { + for i in 0..num { + uplink::feed_raw(i.to_le_bytes()); + } + } } /// Expose `Feeder::feed_num()` to the host @@ -32,3 +39,9 @@ impl Feeder { unsafe fn feed_num(arg_len: u32) -> u32 { uplink::wrap_call(arg_len, |num| STATE.feed_num(num)) } + +/// Expose `Feeder::feed_num_raw()` to the host +#[no_mangle] +unsafe fn feed_num_raw(arg_len: u32) -> u32 { + uplink::wrap_call(arg_len, |num| STATE.feed_num_raw(num)) +} From 776799b1fcc06ce6bd5bd2f5cb540857fd258e65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Leegwater=20Sim=C3=B5es?= Date: Wed, 4 Sep 2024 14:44:35 +0200 Subject: [PATCH 6/6] piecrust: add test for raw feed call --- piecrust/tests/feeder.rs | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/piecrust/tests/feeder.rs b/piecrust/tests/feeder.rs index a9bd7dee..a13f2851 100644 --- a/piecrust/tests/feeder.rs +++ b/piecrust/tests/feeder.rs @@ -26,10 +26,42 @@ fn feed() -> Result<(), Error> { const FEED_NUM: u32 = 10; const GAS_LIMIT: u64 = 1_000_000; - let (sender, receiver) = mpsc::channel(); + let (first_sender, receiver) = mpsc::channel(); + + session.feeder_call::<_, ()>( + id, + "feed_num", + &FEED_NUM, + GAS_LIMIT, + first_sender, + )?; - session - .feeder_call::<_, ()>(id, "feed_num", &FEED_NUM, GAS_LIMIT, sender)?; + let numbers = receiver + .into_iter() + .map(|data| { + rkyv::from_bytes(&data).expect("Fed data should be a number") + }) + .collect::>(); + + assert_eq!( + numbers.len(), + FEED_NUM as usize, + "The correct number of numbers should be fed" + ); + + for (i, n) in numbers.into_iter().enumerate() { + assert_eq!(i as u32, n, "Numbers should be fed in order"); + } + + let (second_sender, receiver) = mpsc::channel(); + + session.feeder_call::<_, ()>( + id, + "feed_num_raw", + &FEED_NUM, + GAS_LIMIT, + second_sender, + )?; let numbers = receiver .into_iter()