Skip to content

Commit

Permalink
Merge pull request #363 from dusk-network/issue-357-limit-and-price-hint
Browse files Browse the repository at this point in the history
Enrich deploy method and contract's metadata with optional gas limit and gas price hint for free transactions
  • Loading branch information
miloszm authored Jun 3, 2024
2 parents 67f96c8 + 76e0917 commit 840e06d
Show file tree
Hide file tree
Showing 11 changed files with 360 additions and 21 deletions.
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

0 comments on commit 840e06d

Please sign in to comment.