Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove the concept of an "uninitialized" ContractId #377

Merged
merged 3 commits into from
Jul 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions contracts/callcenter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,19 +76,17 @@ impl Callcenter {
}

/// Return the caller of this contract
pub fn return_caller(&self) -> ContractId {
pub fn return_caller(&self) -> Option<ContractId> {
uplink::caller()
}

/// Make sure that the caller of this contract is the contract itself
pub fn call_self(&self) -> Result<bool, ContractError> {
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),
}
}

Expand Down
3 changes: 2 additions & 1 deletion contracts/crossover/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion contracts/spender/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions piecrust-uplink/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<ContractId>`
- Change `callee` ABI to return an integer

## [0.15.0] - 2024-07-03

### Fixed
Expand Down
39 changes: 18 additions & 21 deletions piecrust-uplink/src/abi/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -269,29 +269,26 @@ pub fn self_owner<const N: usize>() -> [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::<ContractId>(&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::<ContractId>(
&buf[..core::mem::size_of::<Archived<ContractId>>()],
)
};
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<ContractId> {
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() }
Expand Down
13 changes: 0 additions & 13 deletions piecrust-uplink/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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 {
Expand Down
4 changes: 4 additions & 0 deletions piecrust/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
53 changes: 30 additions & 23 deletions piecrust/src/imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ pub(crate) fn hd(

pub(crate) fn c(
mut fenv: Caller<Env>,
mod_id_ofs: usize,
callee_ofs: usize,
name_ofs: usize,
name_len: u32,
arg_len: u32,
Expand All @@ -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)?;

Expand All @@ -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::<ContractId>()],
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)
Expand Down Expand Up @@ -351,17 +352,20 @@ pub(crate) fn emit(
Ok(())
}

fn caller(env: Caller<Env>) {
fn caller(env: Caller<Env>) -> 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::<ContractId>()]
.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<Env>, arg_len: u32) -> WasmtimeResult<()> {
Expand Down Expand Up @@ -432,10 +436,13 @@ fn panic(fenv: Caller<Env>, 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
Expand All @@ -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::<ContractId>()],
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)
}
}

Expand Down
6 changes: 4 additions & 2 deletions piecrust/src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down Expand Up @@ -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![],
Expand Down
4 changes: 2 additions & 2 deletions piecrust/tests/callcenter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,9 +235,9 @@ pub fn cc_caller_uninit() -> Result<(), Error> {
LIMIT,
)?;

let caller: ContractId =
let caller: Option<ContractId> =
session.call(center_id, "return_caller", &(), LIMIT)?.data;
assert_eq!(caller, ContractId::uninitialized());
assert_eq!(caller, None);

Ok(())
}
Expand Down