Skip to content

Commit

Permalink
Merge pull request #1735 from dusk-network/issue-1630-contract-earns-fee
Browse files Browse the repository at this point in the history
Economic Protocol scenario 3 - "contract earns fee"
  • Loading branch information
miloszm authored Jun 4, 2024
2 parents 69163eb + d31bf5e commit b19ab25
Show file tree
Hide file tree
Showing 31 changed files with 1,565 additions and 162 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ members = [
# Test contracts
"contracts/alice",
"contracts/bob",
"contracts/charlie",
"contracts/host_fn",

# Genesis contracts
Expand Down
2 changes: 1 addition & 1 deletion contracts/Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
SUBDIRS := alice bob license transfer stake host_fn
SUBDIRS := alice bob charlie license transfer stake host_fn

all: $(SUBDIRS) ## Build all the contracts

Expand Down
15 changes: 15 additions & 0 deletions contracts/charlie/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "charlie"
version = "0.3.0"
edition = "2021"
resolver = "2"

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

[dependencies]
execution-core = { version = "0.1.0", path = "../../execution-core" }
rusk-abi = { version = "0.13.0-rc", path = "../../rusk-abi", features = ["dlmalloc"] }
rkyv = { version = "0.7", default-features = false, features = ["size_32"] }
bytecheck = { version = "0.6", default-features = false }
dusk-bytes = "0.1"
18 changes: 18 additions & 0 deletions contracts/charlie/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
TARGET_DIR:="../../target/dusk"

all: wasm

wasm: ## Generate the optimized WASM for the contract given
@RUSTFLAGS="$(RUSTFLAGS) --remap-path-prefix $(HOME)= -C link-args=-zstack-size=65536" \
CARGO_TARGET_DIR=$(TARGET_DIR) \
cargo +dusk build \
--release \
--color=always \
-Z build-std=core,alloc,panic_abort \
-Z build-std-features=panic_immediate_abort \
--target wasm32-unknown-unknown
clippy:

test:

.PHONY: all test wasm
63 changes: 63 additions & 0 deletions contracts/charlie/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// 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.

#![no_std]
#![feature(arbitrary_self_types)]

extern crate alloc;

mod state;
use state::Charlie;

#[cfg(target_family = "wasm")]
#[path = ""]
mod wasm {
use super::*;

use rusk_abi::{ContractId, PaymentInfo};

#[no_mangle]
static SELF_ID: ContractId = ContractId::uninitialized();

static mut STATE: Charlie = Charlie;

#[no_mangle]
unsafe fn pay(arg_len: u32) -> u32 {
rusk_abi::wrap_call(arg_len, |()| STATE.pay())
}

#[no_mangle]
unsafe fn pay_and_fail(arg_len: u32) -> u32 {
rusk_abi::wrap_call(arg_len, |()| STATE.pay_and_fail())
}

#[no_mangle]
unsafe fn earn(arg_len: u32) -> u32 {
rusk_abi::wrap_call(arg_len, |()| STATE.earn())
}

#[no_mangle]
unsafe fn earn_and_fail(arg_len: u32) -> u32 {
rusk_abi::wrap_call(arg_len, |()| STATE.earn_and_fail())
}

#[no_mangle]
unsafe fn earn_indirectly_and_fail(arg_len: u32) -> u32 {
rusk_abi::wrap_call(arg_len, |()| STATE.earn_indirectly_and_fail())
}

#[no_mangle]
unsafe fn subsidize(arg_len: u32) -> u32 {
rusk_abi::wrap_call(arg_len, |arg| STATE.subsidize(arg))
}

const PAYMENT_INFO: PaymentInfo = PaymentInfo::Any(None);

#[no_mangle]
fn payment_info(arg_len: u32) -> u32 {
rusk_abi::wrap_call(arg_len, |_: ()| PAYMENT_INFO)
}
}
130 changes: 130 additions & 0 deletions contracts/charlie/src/state.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// 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.

use alloc::vec::Vec;
use dusk_bytes::Serializable;
use execution_core::transfer::Stct;
use execution_core::{BlsPublicKey, BlsSignature};
use rkyv::{Archive, Deserialize, Serialize};
use rusk_abi::TRANSFER_CONTRACT;

#[derive(Debug, Clone)]
pub struct Charlie;

/// Subsidy a contract with a value.
#[derive(Debug, Clone, PartialEq, Eq, Archive, Serialize, Deserialize)]
#[archive_attr(derive(bytecheck::CheckBytes))]
pub struct Subsidy {
/// Public key to which the subsidy will belong.
pub public_key: BlsPublicKey,
/// Signature belonging to the given public key.
pub signature: BlsSignature,
/// Value of the subsidy.
pub value: u64,
/// Proof of the `STCT` circuit.
pub proof: Vec<u8>,
}

const SUBSIDY_MESSAGE_SIZE: usize = u64::SIZE + u64::SIZE;

/// Return the digest to be signed in the `subsidize` function of a contract.
#[must_use]
pub fn subsidy_signature_message(
counter: u64,
value: u64,
) -> [u8; SUBSIDY_MESSAGE_SIZE] {
let mut bytes = [0u8; SUBSIDY_MESSAGE_SIZE];

bytes[..u64::SIZE].copy_from_slice(&counter.to_bytes());
bytes[u64::SIZE..].copy_from_slice(&value.to_bytes());

bytes
}

impl Charlie {
fn gas_price() -> u64 {
rusk_abi::call::<(), u64>(TRANSFER_CONTRACT, "gas_price", &())
.expect("Obtaining gas price should succeed")
}

/// calling this method will be paid by the contract
pub fn pay(&mut self) {
const ALLOWANCE: u64 = 40_000_000;
let allowance = ALLOWANCE / Self::gas_price();
// this call is paid for by the contract, up to 'allowance'
rusk_abi::set_allowance(allowance);
}

/// calling this method should be paid by the contract, yet it
/// sets the allowance to a value too small to cover
/// the execution cost, transaction will fail
/// and contract balance won't be affected
pub fn pay_and_fail(&mut self) {
const ALLOWANCE: u64 = 8_000_000;
let allowance = ALLOWANCE / Self::gas_price();
// this call is paid for by the contract, up to 'allowance'
rusk_abi::set_allowance(allowance);
}

/// calling this method will cause the transaction fee to be
/// increased so that contract can earn the difference between
/// the contract's charge and the cost of gas actually spent
pub fn earn(&mut self) {
const CHARGE: u64 = 80_000_000;
let charge = CHARGE / Self::gas_price();
// charging 'charge' for the call
rusk_abi::set_charge(charge);
}

/// calling this method will cause the transaction fee to be
/// increased, yet its sets the charge to a value too small to cover
/// the actual execution cost, as a result the transaction will fail
/// and contract balance won't be affected
pub fn earn_and_fail(&mut self) {
const CHARGE: u64 = 8_000_000;
let charge = CHARGE / Self::gas_price();
// charging 'charge' for the call
rusk_abi::set_charge(charge);
}

/// this method calls the `earn` method indirectly, and in such case, since
/// charge is set by an indirectly called method, it won't have effect and
/// contract balance won't be affected
pub fn earn_indirectly_and_fail(&mut self) {
rusk_abi::call::<_, ()>(rusk_abi::self_id(), "earn", &())
.expect("earn call should succeed");
}

/// Subsidizes the contract with funds which can then be used
/// for sponsoring free uses of other methods of this contract.
/// Funds passed in this call will be used when granting allowances.
/// The subsidy operation is similar to staking, yet the funds
/// are deposited in this contract's "wallet".
pub fn subsidize(&mut self, subsidy: Subsidy) {
// verify the signature is over the correct digest
// note: counter is always zero - make sure that this is safe
let digest = subsidy_signature_message(0, subsidy.value).to_vec();

if !rusk_abi::verify_bls(digest, subsidy.public_key, subsidy.signature)
{
panic!("Invalid signature!");
}

// make call to transfer contract to transfer balance from the user to
// this contract
let transfer_module = TRANSFER_CONTRACT;

let stct = Stct {
module: rusk_abi::self_id().to_bytes(),
value: subsidy.value,
proof: subsidy.proof,
};

// subsidizing self with 'subsidy.value'
rusk_abi::call::<_, bool>(transfer_module, "stct", &stct)
.expect("Sending note to contract should succeed");
}
}
12 changes: 10 additions & 2 deletions contracts/stake/tests/common/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ use execution_core::{
transfer::{TreeLeaf, TRANSFER_TREE_DEPTH},
Note, Transaction, ViewKey,
};
use rusk_abi::{CallReceipt, ContractError, Error, Session, TRANSFER_CONTRACT};
use rusk_abi::{
CallReceipt, ContractError, ContractId, EconomicMode, Error, Session,
TRANSFER_CONTRACT,
};

const POINT_LIMIT: u64 = 0x100000000;

Expand Down Expand Up @@ -110,7 +113,12 @@ pub fn execute(
.call::<_, ()>(
TRANSFER_CONTRACT,
"refund",
&(tx.fee, receipt.gas_spent),
&(
tx.fee,
receipt.gas_spent,
EconomicMode::None,
None::<ContractId>,
),
u64::MAX,
)
.expect("Refunding must succeed");
Expand Down
7 changes: 7 additions & 0 deletions contracts/transfer/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

### Added

- Added support for Economic Protocol scenario 3 [#1630]
- Added method which exposes the current gas price [#1604]

### Changed

- Change dependencies declarations enforce bytecheck [#1371]
Expand All @@ -17,5 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [0.7.0] - 2023-12-15

[#1630]: https://github.com/dusk-network/rusk/issues/1630
[#1604]: https://github.com/dusk-network/rusk/issues/1604
[#1675]: https://github.com/dusk-network/rusk/issues/1675
[#1371]: https://github.com/dusk-network/rusk/issues/1371
1 change: 1 addition & 0 deletions contracts/transfer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ rusk-profile = { version = "0.6", path = "../../rusk-profile" }
rusk-abi = { version = "0.13.0-rc", path = "../../rusk-abi", default-features = false, features = ["host"] }
transfer-circuits = { version = "0.5", path = "../../circuits/transfer" }
rkyv = { version = "0.7", default-features = false, features = ["size_32"] }
bytecheck = { version = "0.6", default-features = false }
hex = "0.4"
rand = "0.8"
ff = { version = "0.13", default-features = false }
Expand Down
16 changes: 12 additions & 4 deletions contracts/transfer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,11 @@ unsafe fn num_notes(arg_len: u32) -> u32 {
rusk_abi::wrap_call(arg_len, |_: ()| STATE.num_notes())
}

#[no_mangle]
unsafe fn gas_price(arg_len: u32) -> u32 {
rusk_abi::wrap_call(arg_len, |_: ()| STATE.gas_price())
}

// "Feeder" queries

#[no_mangle]
Expand All @@ -109,10 +114,13 @@ unsafe fn spend_and_execute(arg_len: u32) -> u32 {

#[no_mangle]
unsafe fn refund(arg_len: u32) -> u32 {
rusk_abi::wrap_call(arg_len, |(fee, gas_spent)| {
assert_external_caller();
STATE.refund(fee, gas_spent)
})
rusk_abi::wrap_call(
arg_len,
|(fee, gas_spent, economic_mode, contract_id)| {
assert_external_caller();
STATE.refund(fee, gas_spent, economic_mode, contract_id)
},
)
}

#[no_mangle]
Expand Down
Loading

0 comments on commit b19ab25

Please sign in to comment.