Skip to content

Commit

Permalink
transfer-contract: moved applying economic mode to refund
Browse files Browse the repository at this point in the history
  • Loading branch information
miloszm committed May 20, 2024
1 parent 0724d22 commit d3ea128
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 184 deletions.
19 changes: 7 additions & 12 deletions contracts/transfer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,20 +112,15 @@ unsafe fn spend_and_execute(arg_len: u32) -> u32 {
})
}

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

#[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
92 changes: 57 additions & 35 deletions contracts/transfer/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,6 @@ pub const A: usize = 4;
/// Number of roots stored
pub const MAX_ROOTS: usize = 5000;

// Estimated cost of returning from the execute method
const SURCHARGE_POINTS: u64 = 10000;

pub struct TransferState {
tree: Tree,
nullifiers: BTreeSet<BlsScalar>,
Expand Down Expand Up @@ -231,19 +228,6 @@ impl TransferState {
self.var_crossover = tx.crossover;
self.var_crossover_addr.replace(*tx.fee.stealth_address());

self.execute(tx)
}

/// Executes the contract call if present.
///
/// # Panics
/// Any failure in the checks performed in processing will result in a
/// panic. The contract expects the environment to roll back any change
/// in state.
pub fn execute(
&mut self,
tx: Transaction,
) -> Result<Vec<u8>, ContractError> {
let mut result = Ok(rusk_abi::RawResult::empty());

if let Some((contract_id, fn_name, fn_args)) = tx.call {
Expand All @@ -257,14 +241,11 @@ impl TransferState {
}) = result.clone()
{
match economic_mode {
EconomicMode::Charge(charge) if charge != 0 => self
.apply_charge(&contract_id, charge, tx.fee.gas_price),
EconomicMode::Charge(charge) if charge != 0 => {
rusk_abi::set_charge(charge)
}
EconomicMode::Allowance(allowance) if allowance != 0 => {
self.apply_allowance(
&contract_id,
allowance,
tx.fee.gas_price,
)
rusk_abi::set_allowance(allowance)
}
_ => (),
}
Expand All @@ -279,17 +260,20 @@ impl TransferState {
/// charge and the actual cost of the call.
/// Charge has no effect if the actual cost of the call is greater
/// than the charge.
/// Returns economic gas spent:
/// - charge if it has been applied
/// - gas spent if the charge has not been applied.
fn apply_charge(
&mut self,
contract_id: &ContractId,
charge: u64,
gas_spent: u64,
gas_price: u64,
) {
let cost = (rusk_abi::spent() + SURCHARGE_POINTS) * gas_price;
) -> u64 {
let cost = gas_spent * gas_price;
if charge > cost {
let earning = charge - cost;
let earning = charge * gas_price - cost;
self.add_balance(*contract_id, earning);
rusk_abi::set_charge(earning);
rusk_abi::emit(
"earning",
EconomicEvent {
Expand All @@ -298,38 +282,45 @@ impl TransferState {
result: EconomicResult::ChargeApplied,
},
);
charge
} else {
rusk_abi::emit(
"earning",
EconomicEvent {
module: contract_id.to_bytes(),
value: charge,
value: charge * gas_price,
result: EconomicResult::ChargeNotSufficient,
},
);
gas_spent
}
}

/// Applies contract's allowance. Caller of the contract's method
/// won't pay a fee and all the cost will be covered by the contract.
/// Allowance has no effect if contract does not have enough funds or
/// if the actual cost of the call is greater than allowance.
/// Returns economic gas spent:
/// - 0 if allowance has been applied
/// - gas spent if allowance has not been applied.
fn apply_allowance(
&mut self,
contract_id: &ContractId,
allowance: u64,
gas_spent: u64,
gas_price: u64,
) {
let spent = (rusk_abi::spent() + SURCHARGE_POINTS) * gas_price;
if allowance < spent {
) -> u64 {
let spent = gas_spent * gas_price;
if allowance * gas_price < spent {
rusk_abi::emit(
"sponsoring",
EconomicEvent {
module: contract_id.to_bytes(),
value: allowance,
value: allowance * gas_price,
result: EconomicResult::AllowanceNotSufficient,
},
);
gas_spent
} else {
let contract_balance = self.balance(contract_id);
if spent > contract_balance {
Expand All @@ -341,12 +332,12 @@ impl TransferState {
result: EconomicResult::BalanceNotSufficient,
},
);
gas_spent
} else {
self.sub_balance(contract_id, spent).expect(
"Subtracting callee contract balance should succeed",
);
self.add_balance(rusk_abi::self_id(), spent);
rusk_abi::set_allowance(allowance);
rusk_abi::emit(
"sponsoring",
EconomicEvent {
Expand All @@ -355,19 +346,50 @@ impl TransferState {
result: EconomicResult::AllowanceApplied,
},
);
0u64
}
}
}

/// Refund the previously performed transaction, taking into account the
/// given gas spent. The notes produced will be refunded to the address
/// present in the fee structure.
/// If contract id is present, it applies economic mode to the the contract
/// and refund is based on the economic calculation.
///
/// This function guarantees that it will not panic.
pub fn refund(&mut self, fee: Fee, gas_spent: u64) {
pub fn refund(
&mut self,
fee: Fee,
gas_spent: u64,
economic_mode: EconomicMode,
contract_id: Option<ContractId>,
) {
let economic_gas_spent = if let Some(contract_id) = contract_id {
match economic_mode {
EconomicMode::Charge(charge) if charge != 0 => self
.apply_charge(
&contract_id,
charge,
gas_spent,
fee.gas_price,
),
EconomicMode::Allowance(allowance) if allowance != 0 => self
.apply_allowance(
&contract_id,
allowance,
gas_spent,
fee.gas_price,
),
_ => gas_spent,
}
} else {
gas_spent
};

let block_height = rusk_abi::block_height();

let remainder = fee.gen_remainder(gas_spent);
let remainder = fee.gen_remainder(economic_gas_spent);
let remainder = Note::from(remainder);

let remainder_value = remainder
Expand Down
39 changes: 19 additions & 20 deletions contracts/transfer/tests/common/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,45 +123,44 @@ pub fn prover_verifier(circuit_name: &str) -> (Prover, Verifier) {
(prover, verifier)
}

/// Executes a regular (not call-only) transaction.
/// Executes a transaction.
/// Returns result containing gas spent and economic mode.
pub fn execute(
session: &mut Session,
tx: Transaction,
) -> Result<ExecutionResult, Error> {
let receipt = session.call::<_, Result<Vec<u8>, ContractError>>(
let mut receipt = session.call::<_, Result<Vec<u8>, ContractError>>(
TRANSFER_CONTRACT,
"spend_and_execute",
&tx,
u64::MAX,
)?;

let gas_spent = receipt.gas_spent;
// Ensure all gas is consumed if there's an error in the contract call
if receipt.data.is_err() {
receipt.gas_spent = receipt.gas_limit;
}

session
let contract_id = tx
.call
.clone()
.map(|(module_id, _, _)| ContractId::from_bytes(module_id));

let refund_receipt = session
.call::<_, ()>(
TRANSFER_CONTRACT,
"refund",
&(tx.fee, gas_spent),
&(
tx.fee,
receipt.gas_spent,
receipt.economic_mode.clone(),
contract_id,
),
u64::MAX,
)
.expect("Refunding must succeed");

Ok(ExecutionResult::new(gas_spent, receipt.economic_mode))
}

/// Executes a call-only transaction.
/// Returns result containing gas spent and economic mode.
pub fn execute_call(
session: &mut Session,
tx: Transaction,
) -> Result<ExecutionResult, Error> {
let receipt = session.call::<_, Result<Vec<u8>, ContractError>>(
TRANSFER_CONTRACT,
"execute",
&tx,
u64::MAX,
)?;
receipt.events.extend(refund_receipt.events);

Ok(ExecutionResult::new(
receipt.gas_spent,
Expand Down
Loading

0 comments on commit d3ea128

Please sign in to comment.