Skip to content

Commit

Permalink
WIP 2
Browse files Browse the repository at this point in the history
  • Loading branch information
Eduardo Leegwater Simões committed Jul 4, 2024
1 parent ecbe563 commit 1ae3a7c
Show file tree
Hide file tree
Showing 22 changed files with 505 additions and 140 deletions.
15 changes: 9 additions & 6 deletions contracts/stake/tests/common/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ use rand::SeedableRng;

use execution_core::{
transfer::{
ContractCall, Fee, Payload, Transaction, TreeLeaf, TRANSFER_TREE_DEPTH,
ContractCall, Fee, PhoenixPayload, Transaction, TreeLeaf,
TRANSFER_TREE_DEPTH,
},
value_commitment, JubJubScalar, Note, PublicKey, SchnorrSecretKey,
SecretKey, Sender, TxSkeleton, ViewKey,
Expand Down Expand Up @@ -121,15 +122,17 @@ pub fn filter_notes_owned_by<I: IntoIterator<Item = Note>>(
/// Executes a transaction, returning the call receipt
pub fn execute(
session: &mut Session,
tx: Transaction,
tx: impl Into<Transaction>,
) -> Result<CallReceipt<Result<Vec<u8>, ContractError>>, Error> {
let tx = tx.into();

// Spend the inputs and execute the call. If this errors the transaction is
// unspendable.
let mut receipt = session.call::<_, Result<Vec<u8>, ContractError>>(
TRANSFER_CONTRACT,
"spend_and_execute",
&tx,
tx.payload().fee.gas_limit,
tx.gas_limit(),
)?;

// Ensure all gas is consumed if there's an error in the contract call
Expand All @@ -144,7 +147,7 @@ pub fn execute(
.call::<_, ()>(
TRANSFER_CONTRACT,
"refund",
&(tx.payload().fee, receipt.gas_spent),
&receipt.gas_spent,
u64::MAX,
)
.expect("Refunding must succeed");
Expand Down Expand Up @@ -269,7 +272,7 @@ pub fn create_transaction<const I: usize>(
deposit,
};

let tx_payload = Payload {
let tx_payload = PhoenixPayload {
tx_skeleton,
fee,
call: contract_call,
Expand Down Expand Up @@ -360,5 +363,5 @@ pub fn create_transaction<const I: usize>(
.expect("creating a proof should succeed");

// build the transaction from the payload and proof
Transaction::new(tx_payload, proof.to_bytes())
Transaction::phoenix(tx_payload, proof.to_bytes())
}
4 changes: 2 additions & 2 deletions contracts/transfer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,9 @@ 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)| {
rusk_abi::wrap_call(arg_len, |gas_spent| {
assert_external_caller();
STATE.refund(fee, gas_spent)
STATE.refund(gas_spent)
})
}

Expand Down
88 changes: 73 additions & 15 deletions contracts/transfer/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ use rusk_abi::{ContractError, ContractId, PublicInput, STAKE_CONTRACT};

use execution_core::{
transfer::{
Fee, Mint, SenderAccount, Transaction, TreeLeaf, TRANSFER_TREE_DEPTH,
Mint, MoonlightTransaction, PhoenixTransaction, SenderAccount,
Transaction, TreeLeaf, TRANSFER_TREE_DEPTH,
},
BlsScalar, Note,
};
Expand All @@ -32,6 +33,10 @@ pub struct TransferState {
roots: ConstGenericRingBuffer<BlsScalar, MAX_ROOTS>,
balances: BTreeMap<ContractId, u64>,
deposit: Option<(ContractId, u64)>,

// The transaction currently being executed. It is kept here to avoid
// passing it again during a refund call.
tx: Option<Transaction>,
}

impl TransferState {
Expand All @@ -42,6 +47,7 @@ impl TransferState {
roots: ConstGenericRingBuffer::new(),
balances: BTreeMap::new(),
deposit: None,
tx: None,
}
}

Expand Down Expand Up @@ -143,10 +149,21 @@ impl TransferState {
}
}

/// Spends the inputs and creates the given UTXO, and executes the contract
/// call if present. It performs all checks necessary to ensure the
/// transaction is valid - hash matches, anchor has been a root of the
/// tree, proof checks out, etc...
pub fn spend_and_execute(
&mut self,
tx: Transaction,
) -> Result<Vec<u8>, ContractError> {
match tx {
Transaction::Phoenix(tx) => self.spend_and_execute_phoenix(tx),
Transaction::Moonlight(tx) => self.spend_and_execute_moonlight(tx),
}
}

/// Spends the inputs and creates the given UTXO within the given phoenix
/// transaction, and executes the contract call if present. It performs
/// all checks necessary to ensure the transaction is valid - hash
/// matches, anchor has been a root of the tree, proof checks out,
/// etc...
///
/// This will emplace the deposit in the state, if it exists - making it
/// available for any contracts called.
Expand All @@ -160,9 +177,9 @@ impl TransferState {
/// change in state.
///
/// [`refund`]: [`TransferState::refund`]
pub fn spend_and_execute(
fn spend_and_execute_phoenix(
&mut self,
tx: Transaction,
tx: PhoenixTransaction,
) -> Result<Vec<u8>, ContractError> {
let tx_skeleton = &tx.payload().tx_skeleton;

Expand Down Expand Up @@ -209,23 +226,64 @@ impl TransferState {
&call.fn_args,
);
}

// place the transaction in the state for a subsequent "refund" call
self.tx = Some(tx.into());

result
}

/// Spends the amount available to the moonlight transaction, and executes
/// the contract call if present. It performs all checks necessary to ensure
/// the transaction is valid - signature check, available funds, etc...
///
/// This will emplace the deposit in the state, if it exists - making it
/// available for any contracts called.
///
/// [`refund`] **must** be called if this function succeeds, otherwise we
/// will have an inconsistent state.
///
/// # Panics
/// Any failure in the checks performed in processing the transaction will
/// result in a panic. The contract expects the environment to roll back any
/// change in state.
///
/// [`refund`]: [`TransferState::refund`]
fn spend_and_execute_moonlight(
&mut self,
_tx: MoonlightTransaction,
) -> Result<Vec<u8>, ContractError> {
todo!("Moonlight is currently unsupported");
}

/// 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.
///
/// This function guarantees that it will not panic.
pub fn refund(&mut self, fee: Fee, gas_spent: u64) {
let remainder_note = fee.gen_remainder_note(gas_spent);
pub fn refund(&mut self, gas_spent: u64) {
let tx = self
.tx
.take()
.expect("`spend_and_execute` must be called before `refund`");

match tx {
Transaction::Phoenix(tx) => {
let fee = &tx.payload().fee;

let remainder_value = remainder_note
.value(None)
.expect("Should always succeed for a transparent note");
let remainder_note = fee.gen_remainder_note(gas_spent);

if remainder_value > 0 {
self.push_note_current_height(remainder_note);
let remainder_value = remainder_note
.value(None)
.expect("Should always succeed for a transparent note");

if remainder_value > 0 {
self.push_note_current_height(remainder_note);
}
}
Transaction::Moonlight(_tx) => {
todo!("Moonlight is currently unsupported");
}
}
}

Expand Down Expand Up @@ -357,7 +415,7 @@ impl TransferState {
}
}

fn verify_tx_proof(tx: &Transaction) -> bool {
fn verify_tx_proof(tx: &PhoenixTransaction) -> bool {
let pis: Vec<PublicInput> =
tx.public_inputs().iter().map(|pi| pi.into()).collect();

Expand Down
22 changes: 14 additions & 8 deletions contracts/transfer/tests/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ use std::sync::mpsc;

use execution_core::{
transfer::{
ContractCall, Fee, Payload, Transaction, TreeLeaf, TRANSFER_TREE_DEPTH,
ContractCall, Fee, PhoenixPayload, PhoenixTransaction, Transaction,
TreeLeaf, TRANSFER_TREE_DEPTH,
},
value_commitment, BlsScalar, JubJubScalar, Note, PublicKey,
SchnorrSecretKey, SecretKey, Sender, TxSkeleton, ViewKey,
Expand Down Expand Up @@ -132,12 +133,17 @@ pub fn prover_verifier(input_notes: usize) -> (Prover, Verifier) {

/// Executes a transaction.
/// Returns result containing gas spent.
pub fn execute(session: &mut Session, tx: Transaction) -> Result<u64, Error> {
pub fn execute(
session: &mut Session,
tx: impl Into<Transaction>,
) -> Result<u64, Error> {
let tx = tx.into();

let mut receipt = session.call::<_, Result<Vec<u8>, ContractError>>(
TRANSFER_CONTRACT,
"spend_and_execute",
&tx,
tx.payload().fee.gas_limit,
tx.gas_limit(),
)?;

// Ensure all gas is consumed if there's an error in the contract call
Expand All @@ -149,7 +155,7 @@ pub fn execute(session: &mut Session, tx: Transaction) -> Result<u64, Error> {
.call::<_, ()>(
TRANSFER_CONTRACT,
"refund",
&(tx.payload().fee, receipt.gas_spent),
&receipt.gas_spent,
u64::MAX,
)
.expect("Refunding must succeed");
Expand All @@ -171,7 +177,7 @@ pub fn filter_notes_owned_by<I: IntoIterator<Item = Note>>(

/// Generate a TxCircuit given the sender secret-key, receiver public-key, the
/// input note positions in the transaction tree and the new output-notes.
pub fn create_transaction<const I: usize>(
pub fn create_phoenix_transaction<const I: usize>(
session: &mut Session,
sender_sk: &SecretKey,
receiver_pk: &PublicKey,
Expand All @@ -182,7 +188,7 @@ pub fn create_transaction<const I: usize>(
is_obfuscated: bool,
deposit: u64,
contract_call: Option<ContractCall>,
) -> Transaction {
) -> PhoenixTransaction {
let mut rng = StdRng::seed_from_u64(0xfeeb);
let sender_vk = ViewKey::from(sender_sk);
let sender_pk = PublicKey::from(sender_sk);
Expand Down Expand Up @@ -284,7 +290,7 @@ pub fn create_transaction<const I: usize>(
deposit,
};

let tx_payload = Payload {
let tx_payload = PhoenixPayload {
tx_skeleton,
fee,
call: contract_call,
Expand Down Expand Up @@ -374,5 +380,5 @@ pub fn create_transaction<const I: usize>(
.expect("creating a proof should succeed");

// build the transaction from the payload and proof
Transaction::new(tx_payload, proof.to_bytes())
PhoenixTransaction::new(tx_payload, proof.to_bytes())
}
13 changes: 7 additions & 6 deletions contracts/transfer/tests/transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
pub mod common;

use crate::common::{
contract_balance, create_transaction, execute, filter_notes_owned_by,
leaves_from_height, leaves_from_pos, num_notes, update_root,
contract_balance, create_phoenix_transaction, execute,
filter_notes_owned_by, leaves_from_height, leaves_from_pos, num_notes,
update_root,
};

use dusk_bytes::Serializable;
Expand Down Expand Up @@ -149,7 +150,7 @@ fn transfer() {
let deposit = 0;
let contract_call = None;

let tx = create_transaction(
let tx = create_phoenix_transaction(
session,
&sender_sk,
&receiver_pk,
Expand Down Expand Up @@ -224,7 +225,7 @@ fn alice_ping() {
fn_args: vec![],
});

let tx = create_transaction(
let tx = create_phoenix_transaction(
session,
&sender_sk,
&sender_pk,
Expand Down Expand Up @@ -287,7 +288,7 @@ fn deposit_and_withdraw() {
fn_args: deposit_value.to_bytes().into(),
});

let tx = create_transaction(
let tx = create_phoenix_transaction(
session,
&sender_sk,
&sender_pk,
Expand Down Expand Up @@ -371,7 +372,7 @@ fn deposit_and_withdraw() {
.to_vec(),
});

let tx = create_transaction(
let tx = create_phoenix_transaction(
session,
&sender_sk,
&sender_pk,
Expand Down
5 changes: 4 additions & 1 deletion execution-core/src/transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ use crate::{
};

mod transaction;
pub use transaction::{Payload, Transaction};
pub use transaction::{
MoonlightPayload, MoonlightTransaction, PhoenixPayload, PhoenixTransaction,
Transaction,
};

/// Unique ID to identify a contract.
pub type ContractId = [u8; 32];
Expand Down
Loading

0 comments on commit 1ae3a7c

Please sign in to comment.