Skip to content

Commit 0a4d176

Browse files
author
Eduardo Leegwater Simões
committed
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
1 parent 8ae8901 commit 0a4d176

File tree

4 files changed

+80
-8
lines changed

4 files changed

+80
-8
lines changed

piecrust/CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1313

1414
### Changed
1515

16+
- Allow for a `HostQuery` to take a `&mut Session` [#327]
1617
- Change to have one instance per contract function call [#325]
1718
- Upgrade `dusk-wasmtime` to version `17`
1819

@@ -359,6 +360,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
359360
[#234]: https://github.com/dusk-network/piecrust/pull/234
360361

361362
<!-- ISSUES -->
363+
[#327]: https://github.com/dusk-network/piecrust/issues/327
362364
[#325]: https://github.com/dusk-network/piecrust/issues/325
363365
[#301]: https://github.com/dusk-network/piecrust/issues/313
364366
[#301]: https://github.com/dusk-network/piecrust/issues/301

piecrust/src/session.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -552,7 +552,10 @@ impl Session {
552552
buf: &mut [u8],
553553
arg_len: u32,
554554
) -> Option<u32> {
555-
self.inner.host_queries.call(name, buf, arg_len)
555+
let mut session = self.clone();
556+
self.inner
557+
.host_queries
558+
.call(&mut session, name, buf, arg_len)
556559
}
557560

558561
pub(crate) fn nth_from_top(&self, n: usize) -> Option<CallTreeElem> {

piecrust/src/vm.rs

+25-4
Original file line numberDiff line numberDiff line change
@@ -236,8 +236,16 @@ impl HostQueries {
236236
self.map.insert(name.into(), Arc::new(query));
237237
}
238238

239-
pub fn call(&self, name: &str, buf: &mut [u8], len: u32) -> Option<u32> {
240-
self.map.get(name).map(|host_query| host_query(buf, len))
239+
pub fn call(
240+
&self,
241+
session: &mut Session,
242+
name: &str,
243+
buf: &mut [u8],
244+
len: u32,
245+
) -> Option<u32> {
246+
self.map
247+
.get(name)
248+
.map(|host_query| host_query(session, buf, len))
241249
}
242250
}
243251

@@ -248,5 +256,18 @@ impl HostQueries {
248256
/// function, and should be processed first. Once this is done, the implementor
249257
/// should emplace the return of the query in the same buffer, and return the
250258
/// length written.
251-
pub trait HostQuery: Send + Sync + Fn(&mut [u8], u32) -> u32 {}
252-
impl<F> HostQuery for F where F: Send + Sync + Fn(&mut [u8], u32) -> u32 {}
259+
///
260+
/// The host query will have access to the underlying session, and can use it to
261+
/// perform calls to other contracts.
262+
///
263+
/// # Panics
264+
/// The implementor should panic if any error occurs during the execution of a
265+
/// host function.
266+
pub trait HostQuery:
267+
Send + Sync + Fn(&mut Session, &mut [u8], u32) -> u32
268+
{
269+
}
270+
impl<F> HostQuery for F where
271+
F: Send + Sync + Fn(&mut Session, &mut [u8], u32) -> u32
272+
{
273+
}

piecrust/tests/host.rs

+49-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@
66

77
use dusk_plonk::prelude::*;
88
use once_cell::sync::Lazy;
9-
use piecrust::{contract_bytecode, ContractData, Error, SessionData, VM};
9+
use piecrust::{
10+
contract_bytecode, ContractData, Error, Session, SessionData, VM,
11+
};
12+
use piecrust_uplink::ContractId;
1013
use rand::rngs::OsRng;
1114
use rkyv::Deserialize;
1215

@@ -30,7 +33,7 @@ fn get_prover_verifier() -> &'static (Prover, Verifier) {
3033
&PROVER_VERIFIER
3134
}
3235

33-
fn hash(buf: &mut [u8], len: u32) -> u32 {
36+
fn hash(_: &mut Session, buf: &mut [u8], len: u32) -> u32 {
3437
let a = unsafe { rkyv::archived_root::<Vec<u8>>(&buf[..len as usize]) };
3538
let v: Vec<u8> = a.deserialize(&mut rkyv::Infallible).unwrap();
3639

@@ -40,7 +43,7 @@ fn hash(buf: &mut [u8], len: u32) -> u32 {
4043
32
4144
}
4245

43-
fn verify_proof(buf: &mut [u8], len: u32) -> u32 {
46+
fn verify_proof(_: &mut Session, buf: &mut [u8], len: u32) -> u32 {
4447
let a = unsafe {
4548
rkyv::archived_root::<(Proof, Vec<BlsScalar>)>(&buf[..len as usize])
4649
};
@@ -58,10 +61,24 @@ fn verify_proof(buf: &mut [u8], len: u32) -> u32 {
5861
valid_bytes.len() as u32
5962
}
6063

64+
pub const COUNTER_ID: ContractId = ContractId::from_bytes([1; 32]);
65+
66+
fn get_counter(session: &mut Session, buf: &mut [u8], _: u32) -> u32 {
67+
let receipt = session
68+
.call_raw(COUNTER_ID, "read_value", &*buf, u64::MAX)
69+
.expect("calling the counter contract should succeed");
70+
71+
let data = receipt.data;
72+
buf[..data.len()].copy_from_slice(&data);
73+
74+
data.len() as u32
75+
}
76+
6177
fn new_ephemeral_vm() -> Result<VM, Error> {
6278
let mut vm = VM::ephemeral()?;
6379
vm.register_host_query("hash", hash);
6480
vm.register_host_query("verify_proof", verify_proof);
81+
vm.register_host_query("get_counter", get_counter);
6582
Ok(vm)
6683
}
6784

@@ -87,6 +104,35 @@ pub fn host_hash() -> Result<(), Error> {
87104
Ok(())
88105
}
89106

107+
/// Queries a contract for the value held in the counter contract through the
108+
/// host, using a host query.
109+
#[test]
110+
fn host_counter() -> Result<(), Error> {
111+
let vm = new_ephemeral_vm()?;
112+
113+
let mut session = vm.session(SessionData::builder())?;
114+
115+
session.deploy(
116+
contract_bytecode!("counter"),
117+
ContractData::builder(OWNER).contract_id(COUNTER_ID),
118+
LIMIT,
119+
)?;
120+
121+
let id = session.deploy(
122+
contract_bytecode!("host"),
123+
ContractData::builder(OWNER),
124+
LIMIT,
125+
)?;
126+
127+
let counter = session
128+
.call::<_, i64>(id, "host_get_counter", &(), LIMIT)?
129+
.data;
130+
131+
assert_eq!(counter, 0xfc);
132+
133+
Ok(())
134+
}
135+
90136
/// Proves that we know a number `c` such that `a + b = c`.
91137
#[derive(Default)]
92138
struct TestCircuit {

0 commit comments

Comments
 (0)