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

Enrich deploy method and contract's metadata with optional gas limit and gas price hint for free transactions #363

Merged
merged 6 commits into from
Jun 3, 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
22 changes: 22 additions & 0 deletions contracts/metadata/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,16 @@ impl Metadata {
pub fn read_owner_of(&self, id: ContractId) -> Option<[u8; 33]> {
uplink::owner(id)
}

/// Read the value of the given contract's free limit
pub fn read_free_limit_of(&self, id: ContractId) -> Option<u64> {
uplink::free_limit(id)
}

/// Read the value of the given contract's free price hint
pub fn read_free_price_hint_of(&self, id: ContractId) -> Option<(u64, u64)> {
uplink::free_price_hint(id)
}
}

/// Expose `Metadata::read_owner()` to the host
Expand All @@ -51,3 +61,15 @@ unsafe fn read_id(arg_len: u32) -> u32 {
unsafe fn read_owner_of(arg_len: u32) -> u32 {
uplink::wrap_call(arg_len, |id| STATE.read_owner_of(id))
}

/// Expose `Metadata::read_free_limit_of()` to the host
#[no_mangle]
unsafe fn read_free_limit_of(arg_len: u32) -> u32 {
uplink::wrap_call(arg_len, |id| STATE.read_free_limit_of(id))
}

/// Expose `Metadata::read_free_price_hint_of()` to the host
#[no_mangle]
unsafe fn read_free_price_hint_of(arg_len: u32) -> u32 {
uplink::wrap_call(arg_len, |id| STATE.read_free_price_hint_of(id))
}
2 changes: 2 additions & 0 deletions piecrust-uplink/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- Add support for metadata elements: free limit and free price hint [#357]
- Add contract charge setting method [#353]
- Add contract allowance setting method and a corresponding passing mechanism [#350]

Expand Down Expand Up @@ -169,6 +170,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- First `piecrust-uplink` release

<!-- ISSUES -->
[#357]: https://github.com/dusk-network/piecrust/issues/357
[#353]: https://github.com/dusk-network/piecrust/issues/353
[#350]: https://github.com/dusk-network/piecrust/issues/350
[#324]: https://github.com/dusk-network/piecrust/issues/324
Expand Down
49 changes: 49 additions & 0 deletions piecrust-uplink/src/abi/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ use crate::{
CONTRACT_ID_BYTES, ECO_MODE_BUF_LEN, ECO_MODE_LEN, SCRATCH_BUF_BYTES,
};

const LEN_U64: usize = core::mem::size_of::<u64>();

pub mod arg_buf {
use crate::{EconomicMode, ARGBUF_LEN, ECO_MODE_BUF_LEN, ECO_MODE_LEN};
use core::ptr;
Expand Down Expand Up @@ -76,6 +78,8 @@ mod ext {
pub fn limit() -> u64;
pub fn spent() -> u64;
pub fn owner(contract_id: *const u8) -> i32;
pub fn free_limit(contract_id: *const u8) -> i32;
pub fn free_price_hint(contract_id: *const u8) -> i32;
pub fn self_id();
}
}
Expand Down Expand Up @@ -292,6 +296,51 @@ pub fn owner<const N: usize>(contract: ContractId) -> Option<[u8; N]> {
}
}

/// Returns given contract's free limit, if the contract exists and if it
/// has a free limit set.
pub fn free_limit(contract: ContractId) -> Option<u64> {
let contract_id_ptr = contract.as_bytes().as_ptr();
unsafe {
match ext::free_limit(contract_id_ptr) {
0 => None,
_ => with_arg_buf(|buf, _| {
if buf[0] == 0 {
None
} else {
let mut value_bytes = [0; LEN_U64];
value_bytes.copy_from_slice(&buf[1..LEN_U64 + 1]);
Some(u64::from_le_bytes(value_bytes))
}
}),
}
}
}

/// Returns given contract's free gas price hint, if the contract exists and
/// if it has a free price hint set.
pub fn free_price_hint(contract: ContractId) -> Option<(u64, u64)> {
let contract_id_ptr = contract.as_bytes().as_ptr();

unsafe {
match ext::free_price_hint(contract_id_ptr) {
0 => None,
_ => with_arg_buf(|buf, _| {
if buf[0] == 0 {
None
} else {
let mut value_bytes = [0; LEN_U64];
value_bytes.copy_from_slice(&buf[1..LEN_U64 + 1]);
let num = u64::from_le_bytes(value_bytes);
value_bytes
.copy_from_slice(&buf[LEN_U64 + 1..LEN_U64 * 2 + 1]);
let denom = u64::from_le_bytes(value_bytes);
Some((num, denom))
}
}),
}
}
}

/// Returns the current contract's owner.
pub fn self_owner<const N: usize>() -> [u8; N] {
unsafe { ext::owner(ptr::null()) };
Expand Down
2 changes: 2 additions & 0 deletions piecrust/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Apply charging mechanism for host queries [#359]
- Add `HostQuery::execute` and `HostQuery::deserialize_and_price` [#359]
- Add support for metadata elements: free limit and free price hint [#357]

## Changed

Expand Down Expand Up @@ -425,6 +426,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
<!-- ISSUES -->

[#359]: https://github.com/dusk-network/piecrust/issues/359
[#357]: https://github.com/dusk-network/piecrust/issues/357
[#353]: https://github.com/dusk-network/piecrust/issues/353
[#350]: https://github.com/dusk-network/piecrust/issues/350
[#347]: https://github.com/dusk-network/piecrust/issues/347
Expand Down
24 changes: 24 additions & 0 deletions piecrust/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ pub struct ContractData<'a, A> {
pub(crate) contract_id: Option<ContractId>,
pub(crate) constructor_arg: Option<&'a A>,
pub(crate) owner: Option<Vec<u8>>,
pub(crate) free_limit: Option<u64>,
pub(crate) free_price_hint: Option<(u64, u64)>,
}

// `()` is done on purpose, since by default it should be that the constructor
Expand All @@ -31,6 +33,8 @@ impl<'a> ContractData<'a, ()> {
contract_id: None,
constructor_arg: None,
owner: None,
free_limit: None,
free_price_hint: None,
}
}
}
Expand All @@ -45,6 +49,8 @@ pub struct ContractDataBuilder<'a, A> {
contract_id: Option<ContractId>,
owner: Option<Vec<u8>>,
constructor_arg: Option<&'a A>,
free_limit: Option<u64>,
free_price_hint: Option<(u64, u64)>,
}

impl<'a, A> ContractDataBuilder<'a, A> {
Expand All @@ -60,6 +66,8 @@ impl<'a, A> ContractDataBuilder<'a, A> {
contract_id: self.contract_id,
owner: self.owner,
constructor_arg: Some(arg),
free_limit: self.free_limit,
free_price_hint: self.free_price_hint,
}
}

Expand All @@ -69,11 +77,25 @@ impl<'a, A> ContractDataBuilder<'a, A> {
self
}

/// Set the gas limit for the the free execution of contract's methods.
pub fn free_limit(mut self, free_limit: u64) -> Self {
self.free_limit = Some(free_limit);
self
}

/// Set the gas price hint for the the free execution of contract's methods.
pub fn free_price_hint(mut self, price_hint: (u64, u64)) -> Self {
self.free_price_hint = Some(price_hint);
self
}

pub fn build(self) -> ContractData<'a, A> {
ContractData {
contract_id: self.contract_id,
constructor_arg: self.constructor_arg,
owner: self.owner,
free_limit: self.free_limit,
free_price_hint: self.free_price_hint,
}
}
}
Expand All @@ -83,6 +105,8 @@ impl<'a, A> ContractDataBuilder<'a, A> {
pub struct ContractMetadata {
pub contract_id: ContractId,
pub owner: Vec<u8>,
pub free_limit: Option<u64>,
pub free_price_hint: Option<(u64, u64)>,
}

#[derive(Clone)]
Expand Down
105 changes: 88 additions & 17 deletions piecrust/src/imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ mod wasm64;
use std::any::Any;
use std::sync::Arc;

use crate::contract::ContractMetadata;
use dusk_wasmtime::{
Caller, Extern, Func, Module, Result as WasmtimeResult, Store,
};
Expand All @@ -23,6 +24,8 @@ use crate::Error;

pub const GAS_PASS_PCT: u64 = 93;

const LEN_U64: usize = core::mem::size_of::<u64>();

pub(crate) struct Imports;

impl Imports {
Expand Down Expand Up @@ -78,6 +81,14 @@ impl Imports {
false => Func::wrap(store, wasm32::owner),
true => Func::wrap(store, wasm64::owner),
},
"free_limit" => match is_64 {
false => Func::wrap(store, wasm32::free_limit),
true => Func::wrap(store, wasm64::free_limit),
},
"free_price_hint" => match is_64 {
false => Func::wrap(store, wasm32::free_price_hint),
true => Func::wrap(store, wasm64::free_price_hint),
},
"self_id" => Func::wrap(store, self_id),
#[cfg(feature = "debug")]
"hdebug" => Func::wrap(store, hdebug),
Expand Down Expand Up @@ -426,18 +437,14 @@ fn panic(fenv: Caller<Env>, arg_len: u32) -> WasmtimeResult<()> {
})?)
}

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();

fn get_metadata(env: &mut Env, mod_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.
let metadata = if mod_id_ofs == 0 {
let self_id = env.self_contract_id();
if mod_id_ofs == 0 {
let self_id = env.self_contract_id().to_owned();

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

Some(contract_metadata)
Expand All @@ -453,14 +460,19 @@ fn owner(fenv: Caller<Env>, mod_id_ofs: usize) -> WasmtimeResult<i32> {
});

env.contract_metadata(&mod_id)
};
}
}

match metadata {
fn owner(mut fenv: Caller<Env>, mod_id_ofs: usize) -> WasmtimeResult<i32> {
let instance = fenv.data().self_instance();
check_ptr(instance, mod_id_ofs, CONTRACT_ID_BYTES)?;
let env = fenv.data_mut();
match get_metadata(env, mod_id_ofs) {
None => Ok(0),
Some(metadata) => {
let owner = metadata.owner.as_slice();

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

Expand All @@ -469,14 +481,73 @@ fn owner(fenv: Caller<Env>, mod_id_ofs: usize) -> WasmtimeResult<i32> {
}
}

fn self_id(fenv: Caller<Env>) {
let env = fenv.data();
let self_id = env.self_contract_id();
fn free_limit(mut fenv: Caller<Env>, mod_id_ofs: usize) -> WasmtimeResult<i32> {
let instance = fenv.data().self_instance();
check_ptr(instance, mod_id_ofs, CONTRACT_ID_BYTES)?;
let env = fenv.data_mut();
match get_metadata(env, mod_id_ofs) {
None => Ok(0),
Some(metadata) => {
let len = instance.with_arg_buf_mut(|arg| {
match metadata.free_limit {
Some(free_limit) => {
arg[0] = 1;
arg[1..LEN_U64 + 1]
.copy_from_slice(&free_limit.to_le_bytes()[..]);
}
_ => {
arg[0] = 0;
arg[1..LEN_U64 + 1].fill(0);
}
}
LEN_U64 + 1
});

Ok(len as i32)
}
}
}

fn free_price_hint(
mut fenv: Caller<Env>,
mod_id_ofs: usize,
) -> WasmtimeResult<i32> {
let instance = fenv.data().self_instance();
check_ptr(instance, mod_id_ofs, CONTRACT_ID_BYTES)?;
let env = fenv.data_mut();
match get_metadata(env, mod_id_ofs) {
None => Ok(0),
Some(metadata) => {
let len = instance.with_arg_buf_mut(|arg| {
match metadata.free_price_hint {
Some((num, denom)) => {
arg[0] = 1;
arg[1..LEN_U64 + 1]
.copy_from_slice(&num.to_le_bytes()[..]);
arg[LEN_U64 + 1..LEN_U64 * 2 + 1]
.copy_from_slice(&denom.to_le_bytes()[..]);
}
_ => {
arg[0] = 0;
arg[1..LEN_U64 * 2 + 1].fill(0);
}
}
LEN_U64 * 2 + 1
});

Ok(len as i32)
}
}
}

fn self_id(mut fenv: Caller<Env>) {
let env = fenv.data_mut();
let self_id = env.self_contract_id().to_owned();
let contract_metadata = env
.contract_metadata(self_id)
.contract_metadata(&self_id)
.expect("contract metadata should exist");
let slice = contract_metadata.contract_id.as_bytes();
let slice = contract_metadata.contract_id.to_bytes();
let len = slice.len();
env.self_instance()
.with_arg_buf_mut(|arg| arg[..len].copy_from_slice(slice));
.with_arg_buf_mut(|arg| arg[..len].copy_from_slice(&slice));
}
14 changes: 14 additions & 0 deletions piecrust/src/imports/wasm32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,17 @@ pub(crate) fn emit(
pub(crate) fn owner(fenv: Caller<Env>, mod_id_ofs: u32) -> WasmtimeResult<i32> {
imports::owner(fenv, mod_id_ofs as usize)
}

pub(crate) fn free_limit(
fenv: Caller<Env>,
mod_id_ofs: u32,
) -> WasmtimeResult<i32> {
imports::free_limit(fenv, mod_id_ofs as usize)
}

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

pub(crate) fn free_limit(
fenv: Caller<Env>,
mod_id_ofs: u64,
) -> WasmtimeResult<i32> {
imports::free_limit(fenv, mod_id_ofs as usize)
}

pub(crate) fn free_price_hint(
fenv: Caller<Env>,
mod_id_ofs: u64,
) -> WasmtimeResult<i32> {
imports::free_price_hint(fenv, mod_id_ofs as usize)
}
Loading