From 16b32ebc1bdca3707d0df8281367c2b53ff332bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Leegwater=20Sim=C3=B5es?= Date: Fri, 26 Jul 2024 10:53:35 +0200 Subject: [PATCH] tranfer-contract: add check for wrong target contract in conversion --- contracts/transfer/src/state.rs | 3 + contracts/transfer/tests/common.rs | 7 +- contracts/transfer/tests/transfer.rs | 130 ++++++++++++++++++++++++--- 3 files changed, 126 insertions(+), 14 deletions(-) diff --git a/contracts/transfer/src/state.rs b/contracts/transfer/src/state.rs index f23520e753..5d56b62584 100644 --- a/contracts/transfer/src/state.rs +++ b/contracts/transfer/src/state.rs @@ -216,6 +216,9 @@ impl TransferState { if rusk_abi::caller() != TRANSFER_CONTRACT { panic!("Only a direct call can be a conversion"); } + if *convert.contract() != TRANSFER_CONTRACT.to_bytes() { + panic!("The conversion must target the transfer contract"); + } let deposit = transitory::deposit_info_mut(); match deposit { diff --git a/contracts/transfer/tests/common.rs b/contracts/transfer/tests/common.rs index 6b5e84ed37..fee7cea114 100644 --- a/contracts/transfer/tests/common.rs +++ b/contracts/transfer/tests/common.rs @@ -16,7 +16,8 @@ use execution_core::{ Note, PublicKey, SchnorrSecretKey, SecretKey, Sender, TxSkeleton, ViewKey, }; use rusk_abi::{ - ContractError, ContractId, PiecrustError, Session, TRANSFER_CONTRACT, + CallReceipt, ContractError, ContractId, PiecrustError, Session, + TRANSFER_CONTRACT, }; use dusk_bytes::Serializable; @@ -143,7 +144,7 @@ pub fn prover_verifier(input_notes: usize) -> (Prover, Verifier) { pub fn execute( session: &mut Session, tx: impl Into, -) -> Result { +) -> Result, ContractError>>, PiecrustError> { let tx = tx.into(); let mut receipt = session.call::<_, Result, ContractError>>( @@ -169,7 +170,7 @@ pub fn execute( receipt.events.extend(refund_receipt.events); - Ok(receipt.gas_spent) + Ok(receipt) } /// Returns vector of notes owned by a given view key. diff --git a/contracts/transfer/tests/transfer.rs b/contracts/transfer/tests/transfer.rs index 27a6c89667..5b5cb8a2fa 100644 --- a/contracts/transfer/tests/transfer.rs +++ b/contracts/transfer/tests/transfer.rs @@ -187,7 +187,9 @@ fn phoenix_transfer() { contract_call, ); - let gas_spent = execute(session, tx).expect("Executing TX should succeed"); + let gas_spent = execute(session, tx) + .expect("Executing TX should succeed") + .gas_spent; update_root(session).expect("Updating the root should succeed"); println!("EXECUTE_1_2 : {} gas", gas_spent); @@ -260,8 +262,9 @@ fn moonlight_transfer() { None::, ); - let gas_spent = - execute(session, transaction).expect("Transaction should succeed"); + let gas_spent = execute(session, transaction) + .expect("Transaction should succeed") + .gas_spent; println!("MOONLIGHT TRANSFER: {} gas", gas_spent); @@ -329,7 +332,9 @@ fn phoenix_alice_ping() { contract_call, ); - let gas_spent = execute(session, tx).expect("Executing TX should succeed"); + let gas_spent = execute(session, tx) + .expect("Executing TX should succeed") + .gas_spent; update_root(session).expect("Updating the root should succeed"); println!("EXECUTE_PING: {} gas", gas_spent); @@ -384,8 +389,9 @@ fn moonlight_alice_ping() { contract_call, ); - let gas_spent = - execute(session, transaction).expect("Transaction should succeed"); + let gas_spent = execute(session, transaction) + .expect("Transaction should succeed") + .gas_spent; println!("MOONLIGHT PING: {} gas", gas_spent); @@ -449,8 +455,9 @@ fn phoenix_deposit_and_withdraw() { contract_call, ); - let gas_spent = - execute(session, tx.clone()).expect("Executing TX should succeed"); + let gas_spent = execute(session, tx.clone()) + .expect("Executing TX should succeed") + .gas_spent; update_root(session).expect("Updating the root should succeed"); println!("EXECUTE_DEPOSIT: {} gas", gas_spent); @@ -541,7 +548,9 @@ fn phoenix_deposit_and_withdraw() { contract_call, ); - let gas_spent = execute(session, tx).expect("Executing TX should succeed"); + let gas_spent = execute(session, tx) + .expect("Executing TX should succeed") + .gas_spent; update_root(session).expect("Updating the root should succeed"); println!("EXECUTE_WITHDRAW: {} gas", gas_spent); @@ -618,7 +627,9 @@ fn phoenix_to_moonlight_swap() { Some(contract_call), ); - let gas_spent = execute(session, tx).expect("Executing TX should succeed"); + let gas_spent = execute(session, tx) + .expect("Executing TX should succeed") + .gas_spent; update_root(session).expect("Updating the root should succeed"); println!("CONVERT phoenix to moonlight: {} gas", gas_spent); @@ -714,7 +725,8 @@ fn moonlight_to_phoenix_swap() { ); let gas_spent = execute(&mut session, tx) - .expect("Executing transaction should succeed"); + .expect("Executing transaction should succeed") + .gas_spent; update_root(session).expect("Updating the root should succeed"); println!("CONVERT moonlight to phoenix: {} gas", gas_spent); @@ -737,3 +749,99 @@ fn moonlight_to_phoenix_swap() { assert_eq!(notes.len(), 1, "A new note should have been created"); } + +#[test] +fn swap_wrong_contract_targeted() { + const SWAP_VALUE: u64 = dusk(1.0); + + let rng = &mut StdRng::seed_from_u64(0xfeeb); + + let phoenix_sk = SecretKey::random(rng); + let phoenix_vk = ViewKey::from(&phoenix_sk); + let phoenix_pk = PublicKey::from(&phoenix_sk); + + let moonlight_sk = BlsSecretKey::random(rng); + let moonlight_pk = BlsPublicKey::from(&moonlight_sk); + + let vm = &mut rusk_abi::new_ephemeral_vm() + .expect("Creating ephemeral VM should work"); + let mut session = &mut instantiate(rng, vm, &phoenix_pk, &moonlight_pk); + + let swapper_account = account(&mut session, &moonlight_pk) + .expect("Getting account should succeed"); + let nonce = swapper_account.nonce + 1; + + assert_eq!( + swapper_account.balance, MOONLIGHT_GENESIS_VALUE, + "The swapper's account should have the genesis value" + ); + + let leaves = leaves_from_height(session, 1) + .expect("getting the notes should succeed"); + let notes = filter_notes_owned_by( + phoenix_vk, + leaves.into_iter().map(|leaf| leaf.note), + ); + + assert_eq!(notes.len(), 0, "There should be no notes at this height"); + + let address = + phoenix_pk.gen_stealth_address(&JubJubScalar::random(&mut *rng)); + let note_sk = phoenix_sk.gen_note_sk(&address); + + let convert = Withdraw::new( + rng, + ¬e_sk, + ALICE_ID.to_bytes(), /* this should be the transfer contract, but + * we're testing the "wrong target" case */ + SWAP_VALUE, + WithdrawReceiver::Phoenix(address), + WithdrawReplayToken::Moonlight(nonce), + ); + + let contract_call = ContractCall { + contract: TRANSFER_CONTRACT.to_bytes(), + fn_name: String::from("convert"), + fn_args: rkyv::to_bytes::<_, 1024>(&convert) + .expect("should serialize conversion correctly") + .to_vec(), + }; + + let tx = create_moonlight_transaction( + &moonlight_sk, + None, + 0, + SWAP_VALUE, + GAS_LIMIT, + LUX, + nonce, + Some(contract_call), + ); + + let receipt = execute(&mut session, tx) + .expect("Executing transaction should succeed"); + update_root(session).expect("Updating the root should succeed"); + + let res = receipt.data; + let gas_spent = receipt.gas_spent; + + assert!(matches!(res, Err(_)), "The contract call should error"); + + let swapper_account = account(&mut session, &moonlight_pk) + .expect("Getting account should succeed"); + + assert_eq!( + swapper_account.balance, + MOONLIGHT_GENESIS_VALUE - gas_spent, + "The swapper's account should have only the gas spent subtracted" + ); + + let leaves = leaves_from_height(session, 1) + .expect("getting the notes should succeed"); + let notes = filter_notes_owned_by( + phoenix_vk, + leaves.into_iter().map(|leaf| leaf.note), + ); + + assert!(notes.is_empty(), "A new note should not been created"); +}