Skip to content
Open
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
2 changes: 2 additions & 0 deletions contracts/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ members = [
"initializer",
"counter",
"counter_float",
"counter_deployer",
"counter_deployer_template",
"debugger",
"double_counter",
"empty_initializer",
Expand Down
14 changes: 14 additions & 0 deletions contracts/counter_deployer/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "counter_deployer"
version = "0.1.0"
authors = [
"Demilade Sonuga <demilade@dusk.network>",
]
edition = "2018"

[lib]
crate-type = ["cdylib", "rlib"]

[dependencies]
piecrust-uplink = { path = "../../piecrust-uplink", features = ["abi", "dlmalloc", "debug"] }
counter_deployer_template = { path = "../counter_deployer_template" }
150 changes: 150 additions & 0 deletions contracts/counter_deployer/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Copyright (c) DUSK NETWORK. All rights reserved.

//! Contract to test the contract deploy functionality.

#![no_std]

extern crate alloc;

use alloc::vec::Vec;
use core::convert::TryInto;

use piecrust_uplink::{self as uplink, ContractError};
use uplink::ContractId;

/// Struct that describes the state of the counter deployer contract
pub struct CounterDeployer<'a> {
bytecode: &'a [u8],
}

/// State of the counter deployer contract
static mut STATE: CounterDeployer = CounterDeployer {
bytecode: include_bytes!("../../../target/wasm64-unknown-unknown/release/counter_deployer_template.wasm"),
};

impl<'a> CounterDeployer<'a> {
pub fn simple_deploy(
&self,
init_value: i32,
owner: Vec<u8>,
deploy_nonce: u64,
) -> Result<ContractId, ContractError> {
if owner.len() == 32 {
let vowner: [u8; 32] = owner.clone().try_into().unwrap();
uplink::deploy(self.bytecode, Some(&(init_value, false, 0u32, 0u32, owner.clone())), vowner, deploy_nonce)
} else {
panic!("The owner must be 32 bytes in length");
}
}

pub fn simple_deploy_fail(
&self,
init_value: i32,
owner: Vec<u8>,
deploy_nonce: u64,
) -> Result<ContractId, ContractError> {
if owner.len() == 32 {
let vowner: [u8; 32] = owner.clone().try_into().unwrap();
uplink::deploy(self.bytecode, Some(&(init_value, true, 0u32, 0u32, owner.clone())), vowner, deploy_nonce)
} else {
panic!("The owner must be 32 bytes in length");
}
}

pub fn multiple_deploy(
&self,
first_init_value: i32,
last_init_value: i32,
owner: Vec<u8>,
deploy_nonce: u64,
) -> Result<Vec<ContractId>, ContractError> {
if first_init_value > last_init_value {
return Ok(Vec::new());
}
let mut ids: Vec<ContractId> = uplink::call::<_, Result<Vec<ContractId>, ContractError>>(
uplink::self_id(),
"multiple_deploy",
&(first_init_value + 1, last_init_value, owner.clone(), deploy_nonce + 1)
)??;
let new_id = uplink::call::<_, Result<ContractId, ContractError>>(
uplink::self_id(),
"simple_deploy",
&(first_init_value, owner, deploy_nonce)
)??;
ids.push(new_id);
Ok(ids)
}

/// Mutually recursive function with the template counter's init.
///
/// Works as follows:
/// - Deploys a contract
/// - If `additional_deploys` != 0, the contract's init function calls this
/// function to deploy another contract
/// - Process repeats until `additional_deploys` == 0
///
/// `fail_at` tells at what point and additional deploy triggered from the
/// contract's init function should fail.
/// `fail` tells whether or not the contract being deployed should panic in its
/// init function.
pub fn recursive_deploy_through_init(
&self,
init_value: i32,
fail: bool,
fail_at: u32,
additional_deploys: u32,
deploy_nonce: u64,
owner: Vec<u8>,
) -> Result<ContractId, ContractError> {
if owner.len() == 32 {
let vowner: [u8; 32] = owner.clone().try_into().unwrap();
uplink::deploy(
self.bytecode,
Some(&(init_value, fail, fail_at, additional_deploys, owner)),
vowner,
deploy_nonce,
)
} else {
panic!("The owner must be 32 bytes in length");
}
}
}

/// Expose `CounterDeployer::simple_deploy` to the host
#[no_mangle]
unsafe fn simple_deploy(arg_len: u32) -> u32 {
uplink::wrap_call(arg_len, |(init_value, owner, nonce)| STATE.simple_deploy(init_value, owner, nonce))
}

/// Expose `CounterDeployer::multiple_deploy` to the host
#[no_mangle]
unsafe fn multiple_deploy(arg_len: u32) -> u32 {
uplink::wrap_call(arg_len, |(first_init_value, last_init_value, owner, nonce)| STATE.multiple_deploy(first_init_value, last_init_value, owner, nonce))
}

/// Expose `CounterDeployer::simple_deploy_fail` to the host
#[no_mangle]
unsafe fn simple_deploy_fail(arg_len: u32) -> u32 {
uplink::wrap_call(arg_len, |(init_value, owner, deploy_nonce)| {
STATE.simple_deploy_fail(init_value, owner, deploy_nonce)
})
}

/// Expose `CounterDeployer::recursive_deploy_through_init` to the host
#[no_mangle]
unsafe fn recursive_deploy_through_init(arg_len: u32) -> u32 {
uplink::wrap_call(arg_len, |args| {
let (init_value,
fail,
fail_at,
additional_deploys,
deploy_nonce,
owner
) = args;
STATE.recursive_deploy_through_init(init_value, fail, fail_at, additional_deploys, deploy_nonce, owner)
})
}
15 changes: 15 additions & 0 deletions contracts/counter_deployer_template/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "counter_deployer_template"
version = "0.1.0"
authors = [
"Demilade Sonuga <demilade@dusk.network>",
]
edition = "2021"

license = "MPL-2.0"

[dependencies]
piecrust-uplink = { path = "../../piecrust-uplink", features = ["abi", "dlmalloc"] }

[lib]
crate-type = ["cdylib", "rlib"]
71 changes: 71 additions & 0 deletions contracts/counter_deployer_template/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Copyright (c) DUSK NETWORK. All rights reserved.

//! Contract to act as a template for the counter deployer sample contract.

#![no_std]

extern crate alloc;

use piecrust_uplink as uplink;
use uplink::{ContractError, ContractId};
use alloc::string::ToString;
use alloc::vec::Vec;

/// Struct that describes the state of the Counter contract
pub struct Counter {
value: i32,
}

impl Counter {
pub fn init(&mut self, value: i32, fail: bool, fail_at: u32, additional_deploys: u32, owner: Vec<u8>) {
if fail {
panic!("Failed to deploy");
}
self.value = value;

if additional_deploys > 0 {
let deploy_nonce = additional_deploys as u64 + 100_000;
let fail = fail_at == additional_deploys;
let _ = uplink::call::<_, Result<ContractId, ContractError>>(
ContractId::try_from("0101010101010101010101010101010101010101010101010101010101010101".to_string()).unwrap(),
"recursive_deploy_through_init",
&(value, fail, fail_at, additional_deploys - 1, deploy_nonce, owner.clone())
);
}
}
}

/// State of the Counter contract
static mut STATE: Counter = Counter { value: 0 };

impl Counter {
pub fn read_value(&self) -> i32 {
self.value
}

pub fn increment(&mut self) {
self.value += 1;
}
}

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

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

/// Expose `Initializer::init()` to the host
#[no_mangle]
unsafe fn init(arg_len: u32) -> u32 {
uplink::wrap_call(arg_len, |(arg, fail, fail_at, additional_deploys, owner)| STATE.init(arg, fail, fail_at, additional_deploys, owner))
}
90 changes: 90 additions & 0 deletions piecrust-uplink/src/abi/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,16 @@ mod ext {
pub fn spent() -> u64;
pub fn owner(contract_id: *const u8) -> i32;
pub fn self_id();
pub fn deploy(
bytecode: *const u8,
bytecode_len: u64,
init_arg: *const u8,
init_arg_len: u32,
owner: *const u8,
owner_len: u32,
deploy_nonce: u64,
gas_limit: u64,
) -> i32;
}
}

Expand Down Expand Up @@ -219,6 +229,86 @@ pub fn call_raw_with_limit(
})
}

/// Deploys the contract `bytecode` with init argument `init_arg` and
/// deploy nonce `deploy_nonce`, assigning `owner` as the owner.
///
/// To specify the gas allowed to be spent by the called contract, use
/// [`deploy_with_limit`].
pub fn deploy<D, const N: usize>(
bytecode: &[u8],
init_arg: Option<&D>,
owner: [u8; N],
deploy_nonce: u64,
) -> Result<ContractId, ContractError>
where
for<'a> D: Serialize<StandardBufSerializer<'a>>,
{
deploy_with_limit(bytecode, init_arg, owner, deploy_nonce, 0)
}

/// Deploys the contract `bytecode` with init argument `init_arg` and
/// deploy nonce `deploy_nonce`, assigning `owner` as the owner.
///
/// A gas limit of `0` will use the default behavior - `93%` of the remaining
/// gas will be used to deploy the contract. If the gas limit given is above or
/// equal the remaining amount, the default behavior will be used instead.
///
/// On invocation, the deploy charge, which is the length of the bytecode * gas
/// per deploy byte, will first be deducted.
/// The remaining gas after that deduction will be used to call the contract's
/// init function, if any.
/// If the gas is exhausted at any point, the entire gas limit used for
/// deployment will be consumed.
pub fn deploy_with_limit<D, const N: usize>(
bytecode: &[u8],
init_arg: Option<&D>,
owner: [u8; N],
deploy_nonce: u64,
gas_limit: u64,
) -> Result<ContractId, ContractError>
where
for<'a> D: Serialize<StandardBufSerializer<'a>>,
{
let (init_arg, init_arg_len) = with_arg_buf(|buf| {
if let Some(init_arg) = init_arg {
let mut sbuf = [0u8; SCRATCH_BUF_BYTES];
let scratch = BufferScratch::new(&mut sbuf);
let ptr = buf.as_ptr();
let ser = BufferSerializer::new(buf);
let mut ser = CompositeSerializer::new(ser, scratch, Infallible);
ser.serialize_value(init_arg)
.expect("should not fail to serialize");
let pos = ser.pos();
(ptr, pos)
} else {
(ptr::null(), 0)
}
});

let ret = unsafe {
ext::deploy(
bytecode.as_ptr(),
bytecode.len() as u64,
init_arg,
init_arg_len as u32,
owner.as_ptr(),
owner.len() as u32,
deploy_nonce,
gas_limit,
)
};

with_arg_buf(|buf| {
if ret == 0 {
let mut id = [0; CONTRACT_ID_BYTES];
id.copy_from_slice(&buf[..CONTRACT_ID_BYTES]);
Ok(ContractId::from_bytes(id))
} else {
Err(ContractError::from_parts(ret, buf))
}
})
}

/// Returns data made available by the host under the given name. The type `D`
/// must be correctly specified, otherwise undefined behavior will occur.
pub fn meta_data<D>(name: &str) -> Option<D>
Expand Down
Loading