Skip to content

Commit

Permalink
piecrust: change owner to accept any contract ID
Browse files Browse the repository at this point in the history
The `owner` import is changed to accept a pointer to a contract ID. If
the pointer is null, it assumed that the calling contract is querying
for its owner, whereas if the pointer is not null, a contract ID is
presumed to exist at that address, and read from its memory.

The relevant owner is then emplaced in the contract's argument buffer,
if it exists, and 1 is returned. If the contract whose owner is asked
for does not exist, 0 is returned instead.
  • Loading branch information
Eduardo Leegwater Simões committed Jan 12, 2024
1 parent e09b3ad commit e53b291
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 10 deletions.
5 changes: 5 additions & 0 deletions piecrust/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Changed

- Change `owner` import to accept the contract ID as argument and return
non-zero upon success

## [0.14.1] - 2024-01-11

### Fixed
Expand Down
54 changes: 44 additions & 10 deletions piecrust/src/imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,10 @@ impl Imports {
"limit" => Func::wrap(store, limit),
"spent" => Func::wrap(store, spent),
"panic" => Func::wrap(store, panic),
"owner" => Func::wrap(store, owner),
"owner" => match is_64 {
false => Func::wrap(store, wasm32::owner),
true => Func::wrap(store, wasm64::owner),
},
"self_id" => Func::wrap(store, self_id),
#[cfg(feature = "debug")]
"hdebug" => Func::wrap(store, hdebug),
Expand Down Expand Up @@ -386,16 +389,47 @@ fn panic(fenv: Caller<Env>, arg_len: u32) -> WasmtimeResult<()> {
})?)
}

fn owner(fenv: Caller<Env>) {
fn owner(fenv: Caller<Env>, mod_id_ofs: usize) -> WasmtimeResult<i32> {
check_ptr(fenv.data().self_instance(), mod_id_ofs, CONTRACT_ID_BYTES)?;

let env = fenv.data();
let self_id = env.self_contract_id();
let contract_metadata = env
.contract_metadata(self_id)
.expect("contract metadata should exist");
let slice = contract_metadata.owner.as_slice();
let len = slice.len();
env.self_instance()
.with_arg_buf_mut(|arg| arg[..len].copy_from_slice(slice));

// The null pointer is always zero, so we can use this to check if the
// caller wants their own ID.
let metadata = if mod_id_ofs == 0 {
let self_id = env.self_contract_id();

let contract_metadata = env
.contract_metadata(self_id)
.expect("contract metadata should exist");

Some(contract_metadata)
} 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>()],
);
mod_id
});

env.contract_metadata(&mod_id)
};

match metadata {
None => Ok(0),
Some(metadata) => {
let owner = metadata.owner.as_slice();

env.self_instance().with_arg_buf_mut(|arg| {
arg[..owner.len()].copy_from_slice(owner)
});

Ok(1)
}
}
}

fn self_id(fenv: Caller<Env>) {
Expand Down
4 changes: 4 additions & 0 deletions piecrust/src/imports/wasm32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,7 @@ pub(crate) fn emit(
) -> WasmtimeResult<()> {
imports::emit(fenv, topic_ofs as usize, topic_len, arg_len)
}

pub(crate) fn owner(fenv: Caller<Env>, mod_id_ofs: u32) -> WasmtimeResult<i32> {
imports::owner(fenv, mod_id_ofs as usize)
}
4 changes: 4 additions & 0 deletions piecrust/src/imports/wasm64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,7 @@ pub(crate) fn emit(
) -> WasmtimeResult<()> {
imports::emit(fenv, topic_ofs as usize, topic_len, arg_len)
}

pub(crate) fn owner(fenv: Caller<Env>, mod_id_ofs: u64) -> WasmtimeResult<i32> {
imports::owner(fenv, mod_id_ofs as usize)
}
72 changes: 72 additions & 0 deletions piecrust/tests/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,75 @@ fn metadata() -> Result<(), Error> {

Ok(())
}

#[test]
fn owner_of() -> Result<(), Error> {
const EXPECTED_OWNER_0: [u8; 33] = [3u8; 33];
const EXPECTED_OWNER_1: [u8; 33] = [4u8; 33];

const CONTRACT_ID_0: ContractId = ContractId::from_bytes([1; 32]);
const CONTRACT_ID_1: ContractId = ContractId::from_bytes([2; 32]);
const CONTRACT_ID_2: ContractId = ContractId::from_bytes([3; 32]);

let vm = VM::ephemeral()?;

let mut session = vm.session(SessionData::builder())?;

session.deploy(
contract_bytecode!("metadata"),
ContractData::builder(EXPECTED_OWNER_0).contract_id(CONTRACT_ID_0),
LIMIT,
)?;
session.deploy(
contract_bytecode!("metadata"),
ContractData::builder(EXPECTED_OWNER_1).contract_id(CONTRACT_ID_1),
LIMIT,
)?;

let owner = session
.call::<_, Option<[u8; 33]>>(
CONTRACT_ID_0,
"read_owner_of",
&CONTRACT_ID_1,
LIMIT,
)?
.data;

assert_eq!(
owner,
Some(EXPECTED_OWNER_1),
"The first contract should think the second contract has the correct owner"
);

let owner = session
.call::<_, Option<[u8; 33]>>(
CONTRACT_ID_1,
"read_owner_of",
&CONTRACT_ID_0,
LIMIT,
)?
.data;

assert_eq!(
owner,
Some(EXPECTED_OWNER_0),
"The second contract should think the first contract has the correct owner"
);

let owner = session
.call::<_, Option<[u8; 33]>>(
CONTRACT_ID_0,
"read_owner_of",
&CONTRACT_ID_2,
LIMIT,
)?
.data;

assert_eq!(
owner,
None,
"The first contract should think that the owner of a non-existing contract in None"
);

Ok(())
}

0 comments on commit e53b291

Please sign in to comment.