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 { 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)) +} diff --git a/piecrust-uplink/CHANGELOG.md b/piecrust-uplink/CHANGELOG.md index 85c3e960..3fae4cf4 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` 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 e4b94b1b..031c752f 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,7 +321,24 @@ where }); } -/// Feeds the host with data. +/// 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, 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 @@ -343,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) } + }); +} 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(()) } 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()