From 061747490032f994234eed4f5db7b00236ab4bdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Leegwater=20Sim=C3=B5es?= Date: Sat, 10 Feb 2024 02:31:12 +0100 Subject: [PATCH] piecrust: allow for a `HostQuery` to take `&mut Session` This will allow for downstream to make use of a `Session` while defining host functions, enabling contracts to be called during their execution. Resolves: #327 --- piecrust/CHANGELOG.md | 2 ++ piecrust/src/session.rs | 5 +++- piecrust/src/vm.rs | 29 +++++++++++++++++++---- piecrust/tests/host.rs | 52 ++++++++++++++++++++++++++++++++++++++--- 4 files changed, 80 insertions(+), 8 deletions(-) diff --git a/piecrust/CHANGELOG.md b/piecrust/CHANGELOG.md index 52c5737b..984d3f79 100644 --- a/piecrust/CHANGELOG.md +++ b/piecrust/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Allow for a `HostQuery` to take a `&mut Session` [#327] - Change to have one instance per contract function call [#325] - Upgrade `dusk-wasmtime` to version `17` @@ -359,6 +360,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [#234]: https://github.com/dusk-network/piecrust/pull/234 +[#327]: https://github.com/dusk-network/piecrust/issues/327 [#325]: https://github.com/dusk-network/piecrust/issues/325 [#301]: https://github.com/dusk-network/piecrust/issues/313 [#301]: https://github.com/dusk-network/piecrust/issues/301 diff --git a/piecrust/src/session.rs b/piecrust/src/session.rs index e0946cbf..0ff85d16 100644 --- a/piecrust/src/session.rs +++ b/piecrust/src/session.rs @@ -542,7 +542,10 @@ impl Session { buf: &mut [u8], arg_len: u32, ) -> Option { - self.inner.host_queries.call(name, buf, arg_len) + let mut session = self.clone(); + self.inner + .host_queries + .call(&mut session, name, buf, arg_len) } pub(crate) fn nth_from_top(&self, n: usize) -> Option { diff --git a/piecrust/src/vm.rs b/piecrust/src/vm.rs index 58e6e8f8..0ac3242b 100644 --- a/piecrust/src/vm.rs +++ b/piecrust/src/vm.rs @@ -236,8 +236,16 @@ impl HostQueries { self.map.insert(name.into(), Arc::new(query)); } - pub fn call(&self, name: &str, buf: &mut [u8], len: u32) -> Option { - self.map.get(name).map(|host_query| host_query(buf, len)) + pub fn call( + &self, + session: &mut Session, + name: &str, + buf: &mut [u8], + len: u32, + ) -> Option { + self.map + .get(name) + .map(|host_query| host_query(session, buf, len)) } } @@ -248,5 +256,18 @@ impl HostQueries { /// function, and should be processed first. Once this is done, the implementor /// should emplace the return of the query in the same buffer, and return the /// length written. -pub trait HostQuery: Send + Sync + Fn(&mut [u8], u32) -> u32 {} -impl HostQuery for F where F: Send + Sync + Fn(&mut [u8], u32) -> u32 {} +/// +/// The host query will have access to the underlying session, and can use it to +/// perform calls to other contracts. +/// +/// # Panics +/// The implementor should panic if any error occurs during the execution of a +/// host function. +pub trait HostQuery: + Send + Sync + Fn(&mut Session, &mut [u8], u32) -> u32 +{ +} +impl HostQuery for F where + F: Send + Sync + Fn(&mut Session, &mut [u8], u32) -> u32 +{ +} diff --git a/piecrust/tests/host.rs b/piecrust/tests/host.rs index 6c847a62..7a31e4cf 100644 --- a/piecrust/tests/host.rs +++ b/piecrust/tests/host.rs @@ -6,7 +6,10 @@ use dusk_plonk::prelude::*; use once_cell::sync::Lazy; -use piecrust::{contract_bytecode, ContractData, Error, SessionData, VM}; +use piecrust::{ + contract_bytecode, ContractData, Error, Session, SessionData, VM, +}; +use piecrust_uplink::ContractId; use rand::rngs::OsRng; use rkyv::Deserialize; @@ -30,7 +33,7 @@ fn get_prover_verifier() -> &'static (Prover, Verifier) { &PROVER_VERIFIER } -fn hash(buf: &mut [u8], len: u32) -> u32 { +fn hash(_: &mut Session, buf: &mut [u8], len: u32) -> u32 { let a = unsafe { rkyv::archived_root::>(&buf[..len as usize]) }; let v: Vec = a.deserialize(&mut rkyv::Infallible).unwrap(); @@ -40,7 +43,7 @@ fn hash(buf: &mut [u8], len: u32) -> u32 { 32 } -fn verify_proof(buf: &mut [u8], len: u32) -> u32 { +fn verify_proof(_: &mut Session, buf: &mut [u8], len: u32) -> u32 { let a = unsafe { rkyv::archived_root::<(Proof, Vec)>(&buf[..len as usize]) }; @@ -58,10 +61,24 @@ fn verify_proof(buf: &mut [u8], len: u32) -> u32 { valid_bytes.len() as u32 } +pub const COUNTER_ID: ContractId = ContractId::from_bytes([1; 32]); + +fn get_counter(session: &mut Session, buf: &mut [u8], _: u32) -> u32 { + let receipt = session + .call_raw(COUNTER_ID, "read_value", &*buf, u64::MAX) + .expect("calling the counter contract should succeed"); + + let data = receipt.data; + buf[..data.len()].copy_from_slice(&data); + + data.len() as u32 +} + fn new_ephemeral_vm() -> Result { let mut vm = VM::ephemeral()?; vm.register_host_query("hash", hash); vm.register_host_query("verify_proof", verify_proof); + vm.register_host_query("get_counter", get_counter); Ok(vm) } @@ -87,6 +104,35 @@ pub fn host_hash() -> Result<(), Error> { Ok(()) } +/// Queries a contract for the value held in the counter contract through the +/// host, using a host query. +#[test] +fn host_counter() -> Result<(), Error> { + let vm = new_ephemeral_vm()?; + + let mut session = vm.session(SessionData::builder())?; + + session.deploy( + contract_bytecode!("counter"), + ContractData::builder(OWNER).contract_id(COUNTER_ID), + LIMIT, + )?; + + let id = session.deploy( + contract_bytecode!("host"), + ContractData::builder(OWNER), + LIMIT, + )?; + + let counter = session + .call::<_, i64>(id, "host_get_counter", &(), LIMIT)? + .data; + + assert_eq!(counter, 0xfc); + + Ok(()) +} + /// Proves that we know a number `c` such that `a + b = c`. #[derive(Default)] struct TestCircuit {