diff --git a/contracts/callcenter/src/lib.rs b/contracts/callcenter/src/lib.rs index f69b2b00..b6801c86 100644 --- a/contracts/callcenter/src/lib.rs +++ b/contracts/callcenter/src/lib.rs @@ -76,19 +76,17 @@ impl Callcenter { } /// Return the caller of this contract - pub fn return_caller(&self) -> ContractId { + pub fn return_caller(&self) -> Option { uplink::caller() } /// Make sure that the caller of this contract is the contract itself pub fn call_self(&self) -> Result { let self_id = uplink::self_id(); - let caller = uplink::caller(); - - match caller.is_uninitialized() { - true => uplink::call(self_id, "call_self", &()) + match uplink::caller() { + None => uplink::call(self_id, "call_self", &()) .expect("querying self should succeed"), - false => Ok(caller == self_id), + Some(caller) => Ok(caller == self_id), } } diff --git a/contracts/crossover/src/lib.rs b/contracts/crossover/src/lib.rs index a73f200f..a10f74ef 100644 --- a/contracts/crossover/src/lib.rs +++ b/contracts/crossover/src/lib.rs @@ -79,7 +79,8 @@ impl Crossover { ) { self.set_crossover(value_to_set); - let caller = uplink::caller(); + let caller = + uplink::caller().expect("Should be called by another contract"); uplink::debug!("calling back {caller:?}"); uplink::call::<_, ()>(caller, "set_crossover", &value_to_set_back) diff --git a/contracts/spender/src/lib.rs b/contracts/spender/src/lib.rs index 0d1be269..c9e4faa5 100644 --- a/contracts/spender/src/lib.rs +++ b/contracts/spender/src/lib.rs @@ -25,7 +25,7 @@ impl Spender { let limit = uplink::limit(); let spent_before = uplink::spent(); - match uplink::caller().is_uninitialized() { + match uplink::caller().is_none() { // if this contract has not been called by another contract, // i.e. has been called directly from the outside, call the function // via the host and return the limit and spent values before and diff --git a/piecrust-uplink/CHANGELOG.md b/piecrust-uplink/CHANGELOG.md index d07339ce..6a6e4336 100644 --- a/piecrust-uplink/CHANGELOG.md +++ b/piecrust-uplink/CHANGELOG.md @@ -11,6 +11,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add `impl PartialEq<[u8; 32]> for ContractId` [#375] +### Changed + +- Change `callee` function to return `Option` +- Change `callee` ABI to return an integer + ## [0.15.0] - 2024-07-03 ### Fixed diff --git a/piecrust-uplink/src/abi/state.rs b/piecrust-uplink/src/abi/state.rs index a0b1251c..e4b94b1b 100644 --- a/piecrust-uplink/src/abi/state.rs +++ b/piecrust-uplink/src/abi/state.rs @@ -11,7 +11,7 @@ use rkyv::{ archived_root, ser::serializers::{BufferScratch, BufferSerializer, CompositeSerializer}, ser::Serializer, - Archive, Archived, Deserialize, Infallible, Serialize, + Archive, Deserialize, Infallible, Serialize, }; use crate::{ @@ -57,7 +57,7 @@ mod ext { pub fn emit(topic: *const u8, topic_len: u32, arg_len: u32); pub fn feed(arg_len: u32); - pub fn caller(); + pub fn caller() -> i32; pub fn limit() -> u64; pub fn spent() -> u64; pub fn owner(contract_id: *const u8) -> i32; @@ -269,29 +269,26 @@ pub fn self_owner() -> [u8; N] { /// Return the current contract's id. pub fn self_id() -> ContractId { unsafe { ext::self_id() }; - let id: ContractId = with_arg_buf(|buf| { - let ret = - unsafe { archived_root::(&buf[..CONTRACT_ID_BYTES]) }; - ret.deserialize(&mut Infallible).expect("Infallible") - }); - id -} - -/// Return the ID of the calling contract. The returned id will be -/// uninitialized if there is no caller - meaning this is the first contract -/// to be called. -pub fn caller() -> ContractId { - unsafe { ext::caller() }; with_arg_buf(|buf| { - let ret = unsafe { - archived_root::( - &buf[..core::mem::size_of::>()], - ) - }; - ret.deserialize(&mut Infallible).expect("Infallible") + let mut bytes = [0; CONTRACT_ID_BYTES]; + bytes.copy_from_slice(&buf[..32]); + ContractId::from_bytes(bytes) }) } +/// Returns the ID of the calling contract, or `None` if this is the first +/// contract to be called. +pub fn caller() -> Option { + match unsafe { ext::caller() } { + 0 => None, + _ => with_arg_buf(|buf| { + let mut bytes = [0; CONTRACT_ID_BYTES]; + bytes.copy_from_slice(&buf[..32]); + Some(ContractId::from_bytes(bytes)) + }), + } +} + /// Returns the gas limit with which the contact was called. pub fn limit() -> u64 { unsafe { ext::limit() } diff --git a/piecrust-uplink/src/types.rs b/piecrust-uplink/src/types.rs index 81ce1aa8..df42bd45 100644 --- a/piecrust-uplink/src/types.rs +++ b/piecrust-uplink/src/types.rs @@ -63,13 +63,6 @@ pub const CONTRACT_ID_BYTES: usize = 32; pub struct ContractId([u8; CONTRACT_ID_BYTES]); impl ContractId { - /// Creates a placeholder [`ContractId`] until the host deploys the contract - /// and sets a real [`ContractId`]. This can also be used to determine if a - /// contract is the first to be called. - pub const fn uninitialized() -> Self { - ContractId([0u8; CONTRACT_ID_BYTES]) - } - /// Creates a new [`ContractId`] from an array of bytes pub const fn from_bytes(bytes: [u8; CONTRACT_ID_BYTES]) -> Self { Self(bytes) @@ -91,12 +84,6 @@ impl ContractId { pub fn as_bytes_mut(&mut self) -> &mut [u8] { &mut self.0 } - - /// Determines whether the [`ContractId`] is uninitialized, which can be - /// used to check if this contract is the first to be called. - pub fn is_uninitialized(&self) -> bool { - self == &Self::uninitialized() - } } impl From<[u8; CONTRACT_ID_BYTES]> for ContractId { diff --git a/piecrust/CHANGELOG.md b/piecrust/CHANGELOG.md index af15aaec..e2416278 100644 --- a/piecrust/CHANGELOG.md +++ b/piecrust/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- Change `callee` import to return an integer + ## [0.22.0] - 2024-07-03 ### Added diff --git a/piecrust/src/imports.rs b/piecrust/src/imports.rs index 293de273..7cd9fdcc 100644 --- a/piecrust/src/imports.rs +++ b/piecrust/src/imports.rs @@ -217,7 +217,7 @@ pub(crate) fn hd( pub(crate) fn c( mut fenv: Caller, - mod_id_ofs: usize, + callee_ofs: usize, name_ofs: usize, name_len: u32, arg_len: u32, @@ -229,7 +229,7 @@ pub(crate) fn c( let name_len = name_len as usize; - check_ptr(instance, mod_id_ofs, CONTRACT_ID_BYTES)?; + check_ptr(instance, callee_ofs, CONTRACT_ID_BYTES)?; check_ptr(instance, name_ofs, name_len)?; check_arg(instance, arg_len)?; @@ -248,13 +248,14 @@ pub(crate) fn c( let with_memory = |memory: &mut [u8]| -> Result<_, Error> { let arg_buf = &memory[argbuf_ofs..][..ARGBUF_LEN]; - let mut mod_id = ContractId::uninitialized(); - mod_id.as_bytes_mut().copy_from_slice( - &memory[mod_id_ofs..][..std::mem::size_of::()], + let mut callee_bytes = [0; CONTRACT_ID_BYTES]; + callee_bytes.copy_from_slice( + &memory[callee_ofs..callee_ofs + CONTRACT_ID_BYTES], ); + let callee_id = ContractId::from_bytes(callee_bytes); let callee_stack_element = env - .push_callstack(mod_id, callee_limit) + .push_callstack(callee_id, callee_limit) .expect("pushing to the callstack should succeed"); let callee = env .instance(&callee_stack_element.contract_id) @@ -351,17 +352,20 @@ pub(crate) fn emit( Ok(()) } -fn caller(env: Caller) { +fn caller(env: Caller) -> i32 { let env = env.data(); - let mod_id = env - .nth_from_top(1) - .map_or(ContractId::uninitialized(), |elem| elem.contract_id); - - env.self_instance().with_arg_buf_mut(|arg| { - arg[..std::mem::size_of::()] - .copy_from_slice(mod_id.as_bytes()) - }) + match env.nth_from_top(1) { + Some(call_tree_elem) => { + let instance = env.self_instance(); + instance.with_arg_buf_mut(|buf| { + let caller = call_tree_elem.contract_id; + buf[..CONTRACT_ID_BYTES].copy_from_slice(caller.as_bytes()); + }); + 1 + } + None => 0, + } } fn feed(mut fenv: Caller, arg_len: u32) -> WasmtimeResult<()> { @@ -432,10 +436,13 @@ fn panic(fenv: Caller, arg_len: u32) -> WasmtimeResult<()> { })?) } -fn get_metadata(env: &mut Env, mod_id_ofs: usize) -> Option<&ContractMetadata> { +fn get_metadata( + env: &mut Env, + contract_id_ofs: usize, +) -> Option<&ContractMetadata> { // The null pointer is always zero, so we can use this to check if the // caller wants their own ID. - if mod_id_ofs == 0 { + if contract_id_ofs == 0 { let self_id = env.self_contract_id().to_owned(); let contract_metadata = env @@ -446,15 +453,15 @@ fn get_metadata(env: &mut Env, mod_id_ofs: usize) -> Option<&ContractMetadata> { } else { let instance = env.self_instance(); - let mod_id = instance.with_memory(|memory| { - let mut mod_id = ContractId::uninitialized(); - mod_id.as_bytes_mut().copy_from_slice( - &memory[mod_id_ofs..][..std::mem::size_of::()], + let contract_id = instance.with_memory(|memory| { + let mut contract_id_bytes = [0u8; CONTRACT_ID_BYTES]; + contract_id_bytes.copy_from_slice( + &memory[contract_id_ofs..][..CONTRACT_ID_BYTES], ); - mod_id + ContractId::from_bytes(contract_id_bytes) }); - env.contract_metadata(&mod_id) + env.contract_metadata(&contract_id) } } diff --git a/piecrust/src/session.rs b/piecrust/src/session.rs index 79d77534..d05eebb6 100644 --- a/piecrust/src/session.rs +++ b/piecrust/src/session.rs @@ -12,7 +12,9 @@ use std::sync::{mpsc, Arc}; use bytecheck::CheckBytes; use dusk_wasmtime::{Engine, LinearMemory, MemoryCreator, MemoryType}; -use piecrust_uplink::{ContractId, Event, ARGBUF_LEN, SCRATCH_BUF_BYTES}; +use piecrust_uplink::{ + ContractId, Event, ARGBUF_LEN, CONTRACT_ID_BYTES, SCRATCH_BUF_BYTES, +}; use rkyv::ser::serializers::{ BufferScratch, BufferSerializer, CompositeSerializer, }; @@ -138,7 +140,7 @@ impl Session { data: SessionData, ) -> Self { let inner = SessionInner { - current: ContractId::uninitialized(), + current: ContractId::from_bytes([0; CONTRACT_ID_BYTES]), call_tree: CallTree::new(), instances: BTreeMap::new(), debug: vec![], diff --git a/piecrust/tests/callcenter.rs b/piecrust/tests/callcenter.rs index d3a8a98a..5d8545b8 100644 --- a/piecrust/tests/callcenter.rs +++ b/piecrust/tests/callcenter.rs @@ -235,9 +235,9 @@ pub fn cc_caller_uninit() -> Result<(), Error> { LIMIT, )?; - let caller: ContractId = + let caller: Option = session.call(center_id, "return_caller", &(), LIMIT)?.data; - assert_eq!(caller, ContractId::uninitialized()); + assert_eq!(caller, None); Ok(()) }