From 7c53d2c6bc814939a49283ab8098a89b8dab947b Mon Sep 17 00:00:00 2001 From: moana Date: Fri, 13 Dec 2024 15:37:24 +0100 Subject: [PATCH] stash commit --- contracts/stake/tests/common/utils.rs | 7 +- contracts/stake/tests/partial_stake.rs | 26 ++--- contracts/stake/tests/stake.rs | 39 ++++--- contracts/transfer/tests/common/utils.rs | 40 +------- contracts/transfer/tests/moonlight.rs | 90 ++++++++--------- contracts/transfer/tests/phoenix.rs | 40 ++++---- execution-core/Cargo.toml | 1 + execution-core/src/transfer.rs | 11 ++ execution-core/src/transfer/data.rs | 6 ++ node/src/mempool.rs | 5 +- node/src/vm.rs | 9 -- rusk-abi/Cargo.toml | 9 +- rusk-abi/src/host.rs | 109 +++++++++++++++++++- rusk-abi/src/host/deploy.rs | 112 +++++++++++++++++++++ rusk-abi/src/lib.rs | 6 +- rusk/src/lib/gen_id.rs | 57 ----------- rusk/src/lib/lib.rs | 1 - rusk/src/lib/node/rusk.rs | 15 ++- rusk/tests/services/contract_deployment.rs | 3 +- rusk/tests/services/contract_stake.rs | 2 +- rusk/tests/services/owner_calls.rs | 3 +- 21 files changed, 367 insertions(+), 224 deletions(-) create mode 100644 rusk-abi/src/host/deploy.rs delete mode 100644 rusk/src/lib/gen_id.rs diff --git a/contracts/stake/tests/common/utils.rs b/contracts/stake/tests/common/utils.rs index 584a1bf37b..7f496bf9cb 100644 --- a/contracts/stake/tests/common/utils.rs +++ b/contracts/stake/tests/common/utils.rs @@ -13,10 +13,9 @@ use execution_core::transfer::phoenix::{ ViewKey as PhoenixViewKey, }; use execution_core::transfer::{Transaction, TRANSFER_CONTRACT}; -use execution_core::LUX; -use execution_core::{BlsScalar, ContractError}; +use execution_core::{BlsScalar, LUX}; use rand::rngs::StdRng; -use rusk_abi::{CallReceipt, PiecrustError, Session}; +use rusk_abi::{PiecrustError, Session}; use rusk_prover::LocalProver; pub const GAS_LIMIT: u64 = 0x100_000_000; @@ -98,6 +97,7 @@ pub fn filter_notes_owned_by>( .collect() } +/* /// Executes a transaction, returning the call receipt pub fn execute( session: &mut Session, @@ -135,6 +135,7 @@ pub fn execute( Ok(receipt) } +*/ /// Generate a TxCircuit given the sender secret-key, receiver public-key, the /// input note positions in the transaction tree and the new output-notes. diff --git a/contracts/stake/tests/partial_stake.rs b/contracts/stake/tests/partial_stake.rs index 537d4b03d5..1e5e4b0fc4 100644 --- a/contracts/stake/tests/partial_stake.rs +++ b/contracts/stake/tests/partial_stake.rs @@ -12,7 +12,7 @@ use execution_core::stake::{Reward, RewardReason, EPOCH, STAKE_CONTRACT}; use execution_core::transfer::TRANSFER_CONTRACT; use rand::rngs::StdRng; use rand::SeedableRng; -use rusk_abi::{ContractData, PiecrustError, Session, VM}; +use rusk_abi::{execute, ContractData, PiecrustError, Session, VM}; use wallet_core::transaction::{ moonlight_stake, moonlight_stake_reward, moonlight_unstake, }; @@ -59,7 +59,7 @@ fn stake() -> Result<(), PiecrustError> { CHAIN_ID, ) .expect("tx creation should pass"); - let receipt = execute(&mut session, tx)?; + let receipt = execute(&mut session, &tx, 0, 0)?; // verify 1st stake transaction let gas_spent_1 = receipt.gas_spent; @@ -86,7 +86,7 @@ fn stake() -> Result<(), PiecrustError> { CHAIN_ID, ) .expect("tx creation should pass"); - let receipt = execute(&mut session, tx)?; + let receipt = execute(&mut session, &tx, 0, 0)?; // verify 2nd stake transaction let gas_spent_2 = receipt.gas_spent; @@ -119,7 +119,7 @@ fn stake() -> Result<(), PiecrustError> { CHAIN_ID, ) .expect("tx creation should pass"); - let receipt = execute(&mut session, tx)?; + let receipt = execute(&mut session, &tx, 0, 0)?; // verify 3rd stake transaction let gas_spent_3 = receipt.gas_spent; @@ -153,7 +153,7 @@ fn stake() -> Result<(), PiecrustError> { CHAIN_ID, ) .expect("tx creation should pass"); - assert!(execute(&mut session, tx,).is_err()); + assert!(execute(&mut session, &tx, 0, 0,).is_err()); Ok(()) } @@ -186,7 +186,7 @@ fn unstake() -> Result<(), PiecrustError> { CHAIN_ID, ) .expect("tx creation should pass"); - let receipt = execute(&mut session, tx)?; + let receipt = execute(&mut session, &tx, 0, 0)?; let mut moonlight_balance = GENESIS_VALUE - STAKE_VALUE - receipt.gas_spent; assert_moonlight(&mut session, &moonlight_pk, moonlight_balance, nonce); @@ -207,7 +207,7 @@ fn unstake() -> Result<(), PiecrustError> { CHAIN_ID, ) .expect("tx creation should pass"); - let receipt = execute(&mut session, tx)?; + let receipt = execute(&mut session, &tx, 0, 0)?; // verify 1st unstake transaction let gas_spent_1 = receipt.gas_spent; @@ -236,7 +236,7 @@ fn unstake() -> Result<(), PiecrustError> { CHAIN_ID, ) .expect("tx creation should pass"); - let receipt = execute(&mut session, tx)?; + let receipt = execute(&mut session, &tx, 0, 0)?; total_stake = STAKE_VALUE; let mut locked = unstake_1 / 10; assert_stake(&mut session, &stake_pk, total_stake, locked, 0); @@ -261,7 +261,7 @@ fn unstake() -> Result<(), PiecrustError> { CHAIN_ID, ) .expect("tx creation should pass"); - let receipt = execute(&mut session, tx)?; + let receipt = execute(&mut session, &tx, 0, 0)?; // verify 2nd unstake transaction let gas_spent_2 = receipt.gas_spent; @@ -298,7 +298,7 @@ fn unstake() -> Result<(), PiecrustError> { CHAIN_ID, ) .expect("tx creation should pass"); - let receipt = execute(&mut session, tx)?; + let receipt = execute(&mut session, &tx, 0, 0)?; // verify 3rd unstake transaction let gas_spent_3 = receipt.gas_spent; @@ -340,7 +340,7 @@ fn withdraw_reward() -> Result<(), PiecrustError> { CHAIN_ID, ) .expect("tx creation should pass"); - let receipt = execute(&mut session, tx)?; + let receipt = execute(&mut session, &tx, 0, 0)?; let mut moonlight_balance = GENESIS_VALUE - STAKE_VALUE - receipt.gas_spent; assert_moonlight(&mut session, &moonlight_pk, moonlight_balance, nonce); // add a reward to the staked key @@ -364,7 +364,7 @@ fn withdraw_reward() -> Result<(), PiecrustError> { CHAIN_ID, ) .expect("tx creation should pass"); - let receipt = execute(&mut session, tx)?; + let receipt = execute(&mut session, &tx, 0, 0)?; // verify 1st reward withdrawal let gas_spent_1 = receipt.gas_spent; @@ -398,7 +398,7 @@ fn withdraw_reward() -> Result<(), PiecrustError> { CHAIN_ID, ) .expect("tx creation should pass"); - let receipt = execute(&mut session, tx)?; + let receipt = execute(&mut session, &tx, 0, 0)?; // verify 1st reward withdrawal let gas_spent_2 = receipt.gas_spent; diff --git a/contracts/stake/tests/stake.rs b/contracts/stake/tests/stake.rs index 7719bd700d..3c2841278c 100644 --- a/contracts/stake/tests/stake.rs +++ b/contracts/stake/tests/stake.rs @@ -6,26 +6,25 @@ pub mod common; +use execution_core::signatures::bls::{ + PublicKey as BlsPublicKey, SecretKey as BlsSecretKey, +}; +use execution_core::stake::{ + Reward, RewardReason, Stake, Withdraw as StakeWithdraw, STAKE_CONTRACT, +}; +use execution_core::transfer::data::ContractCall; +use execution_core::transfer::phoenix::{ + PublicKey as PhoenixPublicKey, SecretKey as PhoenixSecretKey, + ViewKey as PhoenixViewKey, +}; +use execution_core::transfer::withdraw::{ + Withdraw, WithdrawReceiver, WithdrawReplayToken, +}; +use execution_core::{dusk, JubJubScalar}; use ff::Field; use rand::rngs::StdRng; use rand::SeedableRng; - -use execution_core::{ - dusk, - signatures::bls::{PublicKey as BlsPublicKey, SecretKey as BlsSecretKey}, - stake::{ - Reward, RewardReason, Stake, Withdraw as StakeWithdraw, STAKE_CONTRACT, - }, - transfer::{ - data::ContractCall, - phoenix::{ - PublicKey as PhoenixPublicKey, SecretKey as PhoenixSecretKey, - ViewKey as PhoenixViewKey, - }, - withdraw::{Withdraw, WithdrawReceiver, WithdrawReplayToken}, - }, - JubJubScalar, -}; +use rusk_abi::execute; use crate::common::assert::{ assert_reward_event, assert_stake, assert_stake_event, @@ -89,7 +88,7 @@ fn stake_withdraw_unstake() { ); let receipt = - execute(&mut session, tx).expect("Executing TX should succeed"); + execute(&mut session, &tx, 0, 0).expect("Executing TX should succeed"); let gas_spent = receipt.gas_spent; receipt.data.expect("Executed TX should not error"); @@ -184,7 +183,7 @@ fn stake_withdraw_unstake() { .expect("Instantiating new session should succeed"); let receipt = - execute(&mut session, tx).expect("Executing TX should succeed"); + execute(&mut session, &tx, 0, 0).expect("Executing TX should succeed"); let gas_spent = receipt.gas_spent; receipt.data.expect("Executed TX should not error"); @@ -280,7 +279,7 @@ fn stake_withdraw_unstake() { .expect("Instantiating new session should succeed"); let receipt = - execute(&mut session, tx).expect("Executing TX should succeed"); + execute(&mut session, &tx, 0, 0).expect("Executing TX should succeed"); update_root(&mut session).expect("Updating the root should succeed"); let gas_spent = receipt.gas_spent; diff --git a/contracts/transfer/tests/common/utils.rs b/contracts/transfer/tests/common/utils.rs index 44847090c5..c72950dfbc 100644 --- a/contracts/transfer/tests/common/utils.rs +++ b/contracts/transfer/tests/common/utils.rs @@ -11,9 +11,9 @@ use execution_core::transfer::moonlight::AccountData; use execution_core::transfer::phoenix::{ Note, NoteLeaf, ViewKey as PhoenixViewKey, }; -use execution_core::transfer::{Transaction, TRANSFER_CONTRACT}; -use execution_core::{BlsScalar, ContractError, ContractId}; -use rusk_abi::{CallReceipt, PiecrustError, Session}; +use execution_core::transfer::TRANSFER_CONTRACT; +use execution_core::{BlsScalar, ContractId}; +use rusk_abi::{PiecrustError, Session}; const GAS_LIMIT: u64 = 0x10_000_000; @@ -32,40 +32,6 @@ pub fn chain_id(session: &mut Session) -> Result { .map(|r| r.data) } -/// Executes a transaction. -/// Returns result containing gas spent. -pub fn execute( - session: &mut Session, - tx: impl Into, -) -> Result, ContractError>>, PiecrustError> { - let tx = tx.into(); - - let mut receipt = session.call::<_, Result, ContractError>>( - TRANSFER_CONTRACT, - "spend_and_execute", - &tx, - tx.gas_limit(), - )?; - - // 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; - } - - let refund_receipt = session - .call::<_, ()>( - TRANSFER_CONTRACT, - "refund", - &receipt.gas_spent, - u64::MAX, - ) - .expect("Refunding must succeed"); - - receipt.events.extend(refund_receipt.events); - - Ok(receipt) -} - // moonlight helper functions pub fn account( diff --git a/contracts/transfer/tests/moonlight.rs b/contracts/transfer/tests/moonlight.rs index fe9827e796..34adc55588 100644 --- a/contracts/transfer/tests/moonlight.rs +++ b/contracts/transfer/tests/moonlight.rs @@ -7,7 +7,7 @@ pub mod common; use crate::common::utils::{ - account, chain_id, contract_balance, execute, existing_nullifiers, + account, chain_id, contract_balance, existing_nullifiers, filter_notes_owned_by, leaves_from_height, owned_notes_value, update_root, }; @@ -15,24 +15,23 @@ use ff::Field; use rand::rngs::StdRng; use rand::SeedableRng; -use execution_core::{ - dusk, - signatures::bls::{ - PublicKey as AccountPublicKey, SecretKey as AccountSecretKey, - }, - transfer::{ - data::{ContractCall, TransactionData}, - moonlight::Transaction as MoonlightTransaction, - phoenix::{ - Note, PublicKey as PhoenixPublicKey, SecretKey as PhoenixSecretKey, - ViewKey as PhoenixViewKey, - }, - withdraw::{Withdraw, WithdrawReceiver, WithdrawReplayToken}, - ContractToAccount, ContractToContract, TRANSFER_CONTRACT, - }, - BlsScalar, ContractError, ContractId, JubJubScalar, LUX, +use execution_core::signatures::bls::{ + PublicKey as AccountPublicKey, SecretKey as AccountSecretKey, }; -use rusk_abi::{ContractData, Session}; +use execution_core::transfer::data::{ContractCall, TransactionData}; +use execution_core::transfer::moonlight::Transaction as MoonlightTransaction; +use execution_core::transfer::phoenix::{ + Note, PublicKey as PhoenixPublicKey, SecretKey as PhoenixSecretKey, + ViewKey as PhoenixViewKey, +}; +use execution_core::transfer::withdraw::{ + Withdraw, WithdrawReceiver, WithdrawReplayToken, +}; +use execution_core::transfer::{ + ContractToAccount, ContractToContract, Transaction, TRANSFER_CONTRACT, +}; +use execution_core::{dusk, ContractError, ContractId, JubJubScalar, LUX}; +use rusk_abi::{execute, ContractData, Session}; const MOONLIGHT_GENESIS_VALUE: u64 = dusk(1_000.0); const MOONLIGHT_GENESIS_NONCE: u64 = 0; @@ -172,7 +171,7 @@ fn transfer() { let session = &mut instantiate(&moonlight_sender_pk); - let transaction = MoonlightTransaction::new( + let transaction = Transaction::moonlight( &moonlight_sender_sk, Some(moonlight_receiver_pk), TRANSFER_VALUE, @@ -185,7 +184,7 @@ fn transfer() { ) .expect("Creating moonlight transaction should succeed"); - let gas_spent = execute(session, transaction) + let gas_spent = execute(session, &transaction, 0, 0) .expect("Transaction should succeed") .gas_spent; @@ -234,7 +233,7 @@ fn transfer_with_refund() { "The receiver account should be empty" ); - let transaction = MoonlightTransaction::new_with_refund( + let transaction: Transaction = MoonlightTransaction::new_with_refund( &moonlight_sender_sk, &moonlight_refund_pk, Some(moonlight_receiver_pk), @@ -246,10 +245,11 @@ fn transfer_with_refund() { CHAIN_ID, None::, ) - .expect("Creating moonlight transaction should succeed"); + .expect("Creating moonlight transaction should succeed") + .into(); let max_gas = GAS_LIMIT * LUX; - let gas_spent = execute(session, transaction) + let gas_spent = execute(session, &transaction, 0, 0) .expect("Transaction should succeed") .gas_spent; let gas_refund = max_gas - gas_spent; @@ -301,7 +301,7 @@ fn transfer_gas_fails() { "The receiver account should be empty" ); - let transaction = MoonlightTransaction::new( + let transaction = Transaction::moonlight( &moonlight_sender_sk, Some(moonlight_receiver_pk), TRANSFER_VALUE, @@ -314,7 +314,7 @@ fn transfer_gas_fails() { ) .expect("Creating moonlight transaction should succeed"); - let result = execute(session, transaction); + let result = execute(session, &transaction, 0, 0); assert!( result.is_err(), @@ -353,7 +353,7 @@ fn alice_ping() { fn_args: vec![], }); - let transaction = MoonlightTransaction::new( + let transaction = Transaction::moonlight( &moonlight_sk, None, 0, @@ -366,7 +366,7 @@ fn alice_ping() { ) .expect("Creating moonlight transaction should succeed"); - let gas_spent = execute(session, transaction) + let gas_spent = execute(session, &transaction, 0, 0) .expect("Transaction should succeed") .gas_spent; @@ -433,7 +433,7 @@ fn convert_to_phoenix() { .to_vec(), }; - let tx = MoonlightTransaction::new( + let tx = Transaction::moonlight( &moonlight_sk, None, 0, @@ -447,7 +447,7 @@ fn convert_to_phoenix() { ) .expect("Creating moonlight transaction should succeed"); - let gas_spent = execute(&mut session, tx) + let gas_spent = execute(&mut session, &tx, 0, 0) .expect("Executing transaction should succeed") .gas_spent; update_root(session).expect("Updating the root should succeed"); @@ -553,7 +553,7 @@ fn convert_to_moonlight_fails() { .to_vec(), }; - let tx = MoonlightTransaction::new( + let tx = Transaction::moonlight( &moonlight_sk, None, 0, @@ -568,7 +568,7 @@ fn convert_to_moonlight_fails() { .expect("Creating moonlight transaction should succeed"); let receipt = - execute(&mut session, tx).expect("Executing TX should succeed"); + execute(&mut session, &tx, 0, 0).expect("Executing TX should succeed"); // check that the transaction execution panicked with the correct message assert!(receipt.data.is_err()); @@ -660,7 +660,7 @@ fn convert_wrong_contract_targeted() { .to_vec(), }; - let tx = MoonlightTransaction::new( + let tx = Transaction::moonlight( &moonlight_sk, None, 0, @@ -673,7 +673,7 @@ fn convert_wrong_contract_targeted() { ) .expect("Creating moonlight transaction should succeed"); - let receipt = execute(&mut session, tx) + let receipt = execute(&mut session, &tx, 0, 0) .expect("Executing transaction should succeed"); update_root(session).expect("Updating the root should succeed"); @@ -732,7 +732,7 @@ fn contract_to_contract() { .to_vec(), }); - let transaction = MoonlightTransaction::new( + let transaction = Transaction::moonlight( &moonlight_sk, None, 0, @@ -745,8 +745,8 @@ fn contract_to_contract() { ) .expect("Creating moonlight transaction should succeed"); - let receipt = - execute(session, transaction).expect("Transaction should succeed"); + let receipt = execute(session, &transaction, 0, 0) + .expect("Transaction should succeed"); let gas_spent = receipt.gas_spent; println!("SEND TO CONTRACT: {:?}", receipt.data); @@ -799,7 +799,7 @@ fn contract_to_account() { .to_vec(), }); - let transaction = MoonlightTransaction::new( + let transaction = Transaction::moonlight( &moonlight_sk, None, 0, @@ -812,8 +812,8 @@ fn contract_to_account() { ) .expect("Creating moonlight transaction should succeed"); - let receipt = - execute(session, transaction).expect("Transaction should succeed"); + let receipt = execute(session, &transaction, 0, 0) + .expect("Transaction should succeed"); let gas_spent = receipt.gas_spent; println!("SEND TO ACCOUNT: {:?}", receipt.data); @@ -863,7 +863,7 @@ fn contract_to_account_insufficient_funds() { .to_vec(), }); - let transaction = MoonlightTransaction::new( + let transaction = Transaction::moonlight( &moonlight_sk, None, 0, @@ -876,8 +876,8 @@ fn contract_to_account_insufficient_funds() { ) .expect("Creating moonlight transaction should succeed"); - let receipt = - execute(session, transaction).expect("Transaction should succeed"); + let receipt = execute(session, &transaction, 0, 0) + .expect("Transaction should succeed"); let gas_spent = receipt.gas_spent; println!("SEND TO ACCOUNT (insufficient funds): {:?}", receipt.data); @@ -934,7 +934,7 @@ fn contract_to_account_direct_call() { .to_vec(), }); - let transaction = MoonlightTransaction::new( + let transaction = Transaction::moonlight( &moonlight_sk, None, 0, @@ -947,8 +947,8 @@ fn contract_to_account_direct_call() { ) .expect("Creating moonlight transaction should succeed"); - let receipt = - execute(session, transaction).expect("Transaction should succeed"); + let receipt = execute(session, &transaction, 0, 0) + .expect("Transaction should succeed"); let gas_spent = receipt.gas_spent; println!( diff --git a/contracts/transfer/tests/phoenix.rs b/contracts/transfer/tests/phoenix.rs index 38e85e5260..cdef63adac 100644 --- a/contracts/transfer/tests/phoenix.rs +++ b/contracts/transfer/tests/phoenix.rs @@ -9,7 +9,7 @@ use std::sync::mpsc; pub mod common; use crate::common::utils::{ - account, chain_id, contract_balance, execute, existing_nullifiers, + account, chain_id, contract_balance, existing_nullifiers, filter_notes_owned_by, leaves_from_height, new_owned_notes_value, owned_notes_value, update_root, }; @@ -32,11 +32,11 @@ use execution_core::{ Transaction as PhoenixTransaction, ViewKey as PhoenixViewKey, }, withdraw::{Withdraw, WithdrawReceiver, WithdrawReplayToken}, - ContractToAccount, ContractToContract, TRANSFER_CONTRACT, + ContractToAccount, ContractToContract, Transaction, TRANSFER_CONTRACT, }, BlsScalar, ContractId, JubJubScalar, LUX, }; -use rusk_abi::{ContractData, PiecrustError, Session}; +use rusk_abi::{execute, ContractData, PiecrustError, Session}; use rusk_prover::LocalProver; const PHOENIX_GENESIS_VALUE: u64 = dusk(1_200.0); @@ -252,7 +252,7 @@ fn transfer_1_2() { contract_call, ); - let gas_spent = execute(session, tx) + let gas_spent = execute(session, &tx, 0, 0) .expect("Executing TX should succeed") .gas_spent; update_root(session).expect("Updating the root should succeed"); @@ -359,7 +359,7 @@ fn transfer_2_2() { contract_call, ); - let gas_spent = execute(session, tx) + let gas_spent = execute(session, &tx, 0, 0) .expect("Executing TX should succeed") .gas_spent; update_root(session).expect("Updating the root should succeed"); @@ -467,7 +467,7 @@ fn transfer_3_2() { contract_call, ); - let gas_spent = execute(session, tx) + let gas_spent = execute(session, &tx, 0, 0) .expect("Executing TX should succeed") .gas_spent; update_root(session).expect("Updating the root should succeed"); @@ -575,7 +575,7 @@ fn transfer_4_2() { contract_call, ); - let gas_spent = execute(session, tx) + let gas_spent = execute(session, &tx, 0, 0) .expect("Executing TX should succeed") .gas_spent; update_root(session).expect("Updating the root should succeed"); @@ -679,7 +679,7 @@ fn transfer_gas_fails() { let total_num_notes_before_tx = num_notes(session).expect("Getting num_notes should succeed"); - let result = execute(session, tx); + let result = execute(session, &tx, 0, 0); assert!( result.is_err(), @@ -750,7 +750,7 @@ fn alice_ping() { contract_call, ); - let gas_spent = execute(session, tx) + let gas_spent = execute(session, &tx, 0, 0) .expect("Executing TX should succeed") .gas_spent; update_root(session).expect("Updating the root should succeed"); @@ -820,7 +820,7 @@ fn contract_deposit() { contract_call, ); - let gas_spent = execute(session, tx.clone()) + let gas_spent = execute(session, &tx, 0, 0) .expect("Executing TX should succeed") .gas_spent; update_root(session).expect("Updating the root should succeed"); @@ -833,7 +833,7 @@ fn contract_deposit() { PHOENIX_GENESIS_VALUE, transfer_value + tx.deposit() - + tx.max_fee() + + tx.gas_limit() * tx.gas_price() + tx.outputs()[1] .value(Some(&PhoenixViewKey::from(&phoenix_sender_sk))) .unwrap() @@ -928,7 +928,7 @@ fn contract_withdraw() { contract_call, ); - let gas_spent = execute(session, tx) + let gas_spent = execute(session, &tx, 0, 0) .expect("Executing TX should succeed") .gas_spent; update_root(session).expect("Updating the root should succeed"); @@ -1054,7 +1054,8 @@ fn convert_to_phoenix_fails() { Some(contract_call), ); - let receipt = execute(session, tx).expect("Executing TX should succeed"); + let receipt = + execute(session, &tx, 0, 0).expect("Executing TX should succeed"); // check that the transaction execution panicked with the correct message assert!(receipt.data.is_err()); @@ -1174,7 +1175,7 @@ fn convert_to_moonlight() { Some(contract_call), ); - let gas_spent = execute(session, tx) + let gas_spent = execute(session, &tx, 0, 0) .expect("Executing TX should succeed") .gas_spent; update_root(session).expect("Updating the root should succeed"); @@ -1284,7 +1285,7 @@ fn convert_wrong_contract_targeted() { Some(contract_call), ); - let receipt = execute(&mut session, tx) + let receipt = execute(&mut session, &tx, 0, 0) .expect("Executing transaction should succeed"); update_root(session).expect("Updating the root should succeed"); @@ -1375,7 +1376,8 @@ fn contract_to_contract() { Some(contract_call), ); - let receipt = execute(session, tx).expect("Transaction should succeed"); + let receipt = + execute(session, &tx, 0, 0).expect("Transaction should succeed"); let gas_spent = receipt.gas_spent; println!("CONTRACT TO CONTRACT: {gas_spent} gas"); @@ -1468,7 +1470,8 @@ fn contract_to_account() { Some(contract_call), ); - let receipt = execute(session, tx).expect("Transaction should succeed"); + let receipt = + execute(session, &tx, 0, 0).expect("Transaction should succeed"); let gas_spent = receipt.gas_spent; println!("CONTRACT TO ACCOUNT: {gas_spent} gas"); @@ -1583,7 +1586,7 @@ fn create_phoenix_transaction( obfuscated_transaction: bool, deposit: u64, data: Option>, -) -> PhoenixTransaction { +) -> Transaction { // Get the root of the tree of phoenix-notes. let root = root(session).expect("Getting the anchor should be successful"); @@ -1627,4 +1630,5 @@ fn create_phoenix_transaction( &LocalProver, ) .expect("creating the creation shouldn't fail") + .into() } diff --git a/execution-core/Cargo.toml b/execution-core/Cargo.toml index 9243deca2d..99930b89d2 100644 --- a/execution-core/Cargo.toml +++ b/execution-core/Cargo.toml @@ -18,6 +18,7 @@ rkyv = { workspace = true, features = ["size_32"] } bytecheck = { workspace = true } rand = { workspace = true } ff = { workspace = true } +blake3 = { workspace = true } # plonk dependencies dusk-plonk = { workspace = true, features = ["rkyv-impl", "alloc"], optional = true } diff --git a/execution-core/src/transfer.rs b/execution-core/src/transfer.rs index c61ce0d30c..326b8b1a47 100644 --- a/execution-core/src/transfer.rs +++ b/execution-core/src/transfer.rs @@ -285,6 +285,17 @@ impl Transaction { }) } + /// Returns gas charge for the deployment of a contract bytecode. + /// This will be zero if the transaction is not a deploy transaction. + #[must_use] + pub fn deploy_bytecode_charge(&self, gas_per_deploy_byte: u64) -> u64 { + let bytecode_len = self + .deploy() + .map(|deploy_contract| deploy_contract.bytecode.bytes.len()) + .unwrap_or_default(); + bytecode_len as u64 * gas_per_deploy_byte + } + /// Serialize the transaction into a byte buffer. #[must_use] pub fn to_var_bytes(&self) -> Vec { diff --git a/execution-core/src/transfer/data.rs b/execution-core/src/transfer/data.rs index 92da9f0230..e33210fdce 100644 --- a/execution-core/src/transfer/data.rs +++ b/execution-core/src/transfer/data.rs @@ -212,6 +212,12 @@ impl ContractBytecode { self.hash.to_vec() } + /// Computes the hash of the bytecode bytes + #[must_use] + pub fn compute_hash(&self) -> [u8; 32] { + blake3::hash(self.bytes.as_slice()).into() + } + /// Serializes this object into a variable length buffer #[must_use] pub fn to_var_bytes(&self) -> Vec { diff --git a/node/src/mempool.rs b/node/src/mempool.rs index b8abd6b34e..ec35e52fb9 100644 --- a/node/src/mempool.rs +++ b/node/src/mempool.rs @@ -204,7 +204,7 @@ impl MempoolSrv { return Err(TxAcceptanceError::GasPriceTooLow(1)); } - if let Some(deploy) = tx.inner.deploy() { + if tx.inner.deploy().is_some() { let vm = vm.read().await; let min_deployment_gas_price = vm.min_deployment_gas_price(); if tx.gas_price() < min_deployment_gas_price { @@ -215,7 +215,8 @@ impl MempoolSrv { let gas_per_deploy_byte = vm.gas_per_deploy_byte(); let min_gas_limit = - vm::bytecode_charge(&deploy.bytecode, gas_per_deploy_byte); + tx.inner.deploy_bytecode_charge(gas_per_deploy_byte); + // vm::bytecode_charge(&deploy.bytecode, gas_per_deploy_byte); if tx.inner.gas_limit() < min_gas_limit { return Err(TxAcceptanceError::GasLimitTooLow(min_gas_limit)); } diff --git a/node/src/vm.rs b/node/src/vm.rs index 00b68df8f4..d41643f2b6 100644 --- a/node/src/vm.rs +++ b/node/src/vm.rs @@ -8,7 +8,6 @@ use dusk_consensus::operations::{CallParams, VerificationOutput, Voter}; use dusk_consensus::user::provisioners::Provisioners; use dusk_consensus::user::stake::Stake; use execution_core::signatures::bls::PublicKey as BlsPublicKey; -use execution_core::transfer::data::ContractBytecode; use execution_core::transfer::moonlight::AccountData; use node_data::events::contract::ContractEvent; use node_data::ledger::{Block, SpentTransaction, Transaction}; @@ -97,11 +96,3 @@ pub enum PreverificationResult { nonce_used: u64, }, } - -// Returns gas charge for bytecode deployment. -pub fn bytecode_charge( - bytecode: &ContractBytecode, - gas_per_deploy_byte: u64, -) -> u64 { - bytecode.bytes.len() as u64 * gas_per_deploy_byte -} diff --git a/rusk-abi/Cargo.toml b/rusk-abi/Cargo.toml index 0c22cff7e0..81a1311726 100644 --- a/rusk-abi/Cargo.toml +++ b/rusk-abi/Cargo.toml @@ -40,7 +40,14 @@ dlmalloc = ["piecrust-uplink/dlmalloc"] # These are the features available for when one wishes to use `rusk-abi` as a # host. -host = ["piecrust", "lru", "execution-core/parallel", "blake2b_simd", "dusk-poseidon", "rkyv"] +host = [ + "piecrust", + "lru", + "execution-core/parallel", + "blake2b_simd", + "dusk-poseidon", + "rkyv", +] host_debug = ["piecrust/debug", "lru"] [[test]] diff --git a/rusk-abi/src/host.rs b/rusk-abi/src/host.rs index c3505a7211..2f57080278 100644 --- a/rusk-abi/src/host.rs +++ b/rusk-abi/src/host.rs @@ -22,15 +22,120 @@ use execution_core::signatures::bls::{ use execution_core::signatures::schnorr::{ PublicKey as SchnorrPublicKey, Signature as SchnorrSignature, }; -use execution_core::BlsScalar; -use piecrust::{Error as PiecrustError, Session, SessionData, VM}; +use execution_core::transfer::{Transaction, TRANSFER_CONTRACT}; +use execution_core::{BlsScalar, ContractError}; +use piecrust::{CallReceipt, Error as PiecrustError, Session, SessionData, VM}; use rkyv::ser::serializers::AllocSerializer; use rkyv::{Archive, Deserialize, Serialize}; mod cache; +pub(crate) mod deploy; use crate::{Metadata, Query}; +/// Executes a transaction, returning the receipt of the call and the gas spent. +/// The following steps are performed: +/// +/// 1. Check if the transaction contains contract deployment data, and if so, +/// verifies if gas limit is enough for deployment and if the gas price is +/// sufficient for deployment. If either gas price or gas limit is not +/// sufficient for deployment, transaction is discarded. +/// +/// 2. Call the "spend_and_execute" function on the transfer contract with +/// unlimited gas. If this fails, an error is returned. If an error is +/// returned the transaction should be considered unspendable/invalid, but no +/// re-execution of previous transactions is required. +/// +/// 3. If the transaction contains contract deployment data, additional checks +/// are performed and if they pass, deployment is executed. The following +/// checks are performed: +/// - gas limit should be is smaller than deploy charge plus gas used for +/// spending funds +/// - transaction's bytecode's bytes are consistent with bytecode's hash +/// Deployment execution may fail for deployment-specific reasons, such as +/// for example: +/// - contract already deployed +/// - corrupted bytecode +/// If deployment execution fails, the entire gas limit is consumed and error +/// is returned. +/// +/// 4. Call the "refund" function on the transfer contract with unlimited gas. +/// The amount charged depends on the gas spent by the transaction, and the +/// optional contract call in steps 2 or 3. +/// +/// Note that deployment transaction will never be re-executed for reasons +/// related to deployment, as it is either discarded or it charges the +/// full gas limit. It might be re-executed only if some other transaction +/// failed to fit the block. +pub fn execute( + session: &mut Session, + tx: &Transaction, + gas_per_deploy_byte: u64, + min_deployment_gas_price: u64, +) -> Result, ContractError>>, PiecrustError> { + // Transaction will be discarded if it is a deployment transaction + // with gas limit smaller than deploy charge. + if tx.deploy().is_some() { + let deploy_charge = tx.deploy_bytecode_charge(gas_per_deploy_byte); + if tx.gas_price() < min_deployment_gas_price { + return Err(PiecrustError::Panic( + "gas price too low to deploy".into(), + )); + } + if tx.gas_limit() < deploy_charge { + return Err(PiecrustError::Panic( + "not enough gas to deploy".into(), + )); + } + } + + let tx_stripped = tx.strip_off_bytecode(); + // Spend the inputs and execute the call. If this errors the transaction is + // unspendable. + let mut receipt = session.call::<_, Result, ContractError>>( + TRANSFER_CONTRACT, + "spend_and_execute", + tx_stripped.as_ref().unwrap_or(tx), + tx.gas_limit(), + )?; + + // Deploy if this is a deployment transaction and spend part is successful. + if let Some(deploy) = tx.deploy() { + if receipt.data.is_ok() { + let gas_left = tx.gas_limit() - receipt.gas_spent; + let deploy_charge = tx.deploy_bytecode_charge(gas_per_deploy_byte); + deploy::contract( + session, + deploy, + deploy_charge, + gas_left, + &mut receipt, + ); + } + }; + + // 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; + } + + // Refund the appropriate amount to the transaction. This call is guaranteed + // to never error. If it does, then a programming error has occurred. As + // such, the call to `Result::expect` is warranted. + let refund_receipt = session + .call::<_, ()>( + TRANSFER_CONTRACT, + "refund", + &receipt.gas_spent, + u64::MAX, + ) + .expect("Refunding must succeed"); + + receipt.events.extend(refund_receipt.events); + + Ok(receipt) +} + /// Create a new session based on the given `vm`. The vm *must* have been /// created using [`new_vm`] or [`new_ephemeral_vm`]. pub fn new_session( diff --git a/rusk-abi/src/host/deploy.rs b/rusk-abi/src/host/deploy.rs new file mode 100644 index 0000000000..97c7d00e33 --- /dev/null +++ b/rusk-abi/src/host/deploy.rs @@ -0,0 +1,112 @@ +// 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 blake2b_simd::Params; +use execution_core::transfer::data::ContractDeploy; +use execution_core::ContractError; +use piecrust::{CallReceipt, ContractId, Session, CONTRACT_ID_BYTES}; + +// Contract deployment will fail and charge full gas limit in the +// following cases: +// 1) Transaction gas limit is smaller than deploy charge plus gas used for +// spending funds. +// 2) Transaction's bytecode's bytes are not consistent with bytecode's hash. +// 3) Deployment fails for deploy-specific reasons like e.g.: +// - contract already deployed +// - corrupted bytecode +// - sufficient gas to spend funds yet insufficient for deployment +pub(crate) fn contract( + session: &mut Session, + deploy: &ContractDeploy, + deploy_charge: u64, + gas_limit: u64, + receipt: &mut CallReceipt, ContractError>>, +) { + let min_gas_limit = receipt.gas_spent + deploy_charge; + let hash = deploy.bytecode.compute_hash(); + // let hash = blake3::hash(deploy.bytecode.bytes.as_slice()); + if gas_limit < min_gas_limit { + receipt.data = Err(ContractError::OutOfGas); + } else if hash != deploy.bytecode.hash { + receipt.data = + Err(ContractError::Panic("failed bytecode hash check".into())) + } else { + let result = session.deploy_raw( + Some(gen_contract_id( + &deploy.bytecode.bytes, + deploy.nonce, + &deploy.owner, + )), + deploy.bytecode.bytes.as_slice(), + deploy.init_args.clone(), + deploy.owner.clone(), + gas_limit, + ); + match result { + // Should the gas spent by the INIT method charged too? + Ok(_) => receipt.gas_spent += deploy_charge, + Err(err) => { + let msg = format!("failed deployment: {err:?}"); + receipt.data = Err(ContractError::Panic(msg)) + } + } + } +} + +/// Generate a [`ContractId`] address from: +/// - slice of bytes, +/// - nonce +/// - owner +pub fn gen_contract_id( + bytes: impl AsRef<[u8]>, + nonce: u64, + owner: impl AsRef<[u8]>, +) -> ContractId { + let mut hasher = Params::new().hash_length(CONTRACT_ID_BYTES).to_state(); + hasher.update(bytes.as_ref()); + hasher.update(&nonce.to_le_bytes()[..]); + hasher.update(owner.as_ref()); + let hash_bytes: [u8; CONTRACT_ID_BYTES] = hasher + .finalize() + .as_bytes() + .try_into() + .expect("the hash result is exactly `CONTRACT_ID_BYTES` long"); + ContractId::from_bytes(hash_bytes) +} + +#[cfg(test)] +mod tests { + use super::*; + use rand::rngs::StdRng; + use rand::{RngCore, SeedableRng}; + + #[test] + fn test_gen_contract_id() { + let mut rng = StdRng::seed_from_u64(42); + + let mut bytes = vec![0; 1000]; + rng.fill_bytes(&mut bytes); + + let nonce = rng.next_u64(); + + let mut owner = vec![0, 100]; + rng.fill_bytes(&mut owner); + + let contract_id = + gen_contract_id(bytes.as_slice(), nonce, owner.as_slice()); + + assert_eq!( + contract_id.as_bytes(), + [ + 45, 168, 182, 39, 119, 137, 168, 140, 114, 21, 120, 158, 34, + 126, 244, 221, 151, 72, 109, 178, 82, 229, 84, 128, 92, 123, + 135, 74, 23, 224, 119, 133 + ] + ); + } +} diff --git a/rusk-abi/src/lib.rs b/rusk-abi/src/lib.rs index ab8a29ca4c..eb3715e34b 100644 --- a/rusk-abi/src/lib.rs +++ b/rusk-abi/src/lib.rs @@ -57,9 +57,9 @@ pub use piecrust_uplink::{ mod host; #[cfg(feature = "host")] pub use host::{ - hash, new_ephemeral_vm, new_genesis_session, new_session, new_vm, - poseidon_hash, verify_bls, verify_bls_multisig, verify_plonk, - verify_schnorr, + deploy::gen_contract_id, execute, hash, new_ephemeral_vm, + new_genesis_session, new_session, new_vm, poseidon_hash, verify_bls, + verify_bls_multisig, verify_plonk, verify_schnorr, }; #[cfg(feature = "host")] pub use piecrust::{ diff --git a/rusk/src/lib/gen_id.rs b/rusk/src/lib/gen_id.rs deleted file mode 100644 index 82c52fa445..0000000000 --- a/rusk/src/lib/gen_id.rs +++ /dev/null @@ -1,57 +0,0 @@ -// 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 blake2b_simd::Params; -use execution_core::{ContractId, CONTRACT_ID_BYTES}; - -/// Generate a [`ContractId`] address from: -/// - slice of bytes, -/// - nonce -/// - owner -pub fn gen_contract_id( - bytes: impl AsRef<[u8]>, - nonce: u64, - owner: impl AsRef<[u8]>, -) -> ContractId { - let mut hasher = Params::new().hash_length(CONTRACT_ID_BYTES).to_state(); - hasher.update(bytes.as_ref()); - hasher.update(&nonce.to_le_bytes()[..]); - hasher.update(owner.as_ref()); - let hash_bytes: [u8; CONTRACT_ID_BYTES] = hasher - .finalize() - .as_bytes() - .try_into() - .expect("the hash result is exactly `CONTRACT_ID_BYTES` long"); - ContractId::from_bytes(hash_bytes) -} - -#[cfg(test)] -mod tests { - use super::*; - use rand::rngs::StdRng; - use rand::{RngCore, SeedableRng}; - - #[test] - fn test_gen_contract_id() { - let mut rng = StdRng::seed_from_u64(42); - - let mut bytes = vec![0; 1000]; - rng.fill_bytes(&mut bytes); - - let nonce = rng.next_u64(); - - let mut owner = vec![0, 100]; - rng.fill_bytes(&mut owner); - - let contract_id = - gen_contract_id(bytes.as_slice(), nonce, owner.as_slice()); - - assert_eq!( - hex::encode(contract_id.as_bytes()), - "2da8b6277789a88c7215789e227ef4dd97486db252e554805c7b874a17e07785" - ); - } -} diff --git a/rusk/src/lib/lib.rs b/rusk/src/lib/lib.rs index 541c42bc2b..603cef789f 100644 --- a/rusk/src/lib/lib.rs +++ b/rusk/src/lib/lib.rs @@ -8,7 +8,6 @@ mod bloom; mod error; -pub mod gen_id; pub mod http; #[cfg(feature = "chain")] pub mod node; diff --git a/rusk/src/lib/node/rusk.rs b/rusk/src/lib/node/rusk.rs index b762236572..cbce56cdae 100644 --- a/rusk/src/lib/node/rusk.rs +++ b/rusk/src/lib/node/rusk.rs @@ -24,23 +24,18 @@ use dusk_consensus::operations::{CallParams, VerificationOutput, Voter}; use execution_core::{ signatures::bls::PublicKey as BlsPublicKey, stake::{Reward, RewardReason, StakeData, STAKE_CONTRACT}, - transfer::{ - data::ContractDeploy, moonlight::AccountData, - Transaction as ProtocolTransaction, TRANSFER_CONTRACT, - }, - BlsScalar, ContractError, Dusk, Event, + transfer::{moonlight::AccountData, TRANSFER_CONTRACT}, + BlsScalar, Dusk, Event, }; -use node::vm::bytecode_charge; use node_data::events::contract::{ContractEvent, ContractTxEvent}; use node_data::ledger::{Hash, Slash, SpentTransaction, Transaction}; -use rusk_abi::{CallReceipt, PiecrustError, Session}; +use rusk_abi::{execute, CallReceipt, PiecrustError, Session}; use rusk_profile::to_rusk_state_id_path; use tokio::sync::broadcast; #[cfg(feature = "archive")] use {node_data::archive::ArchivalData, tokio::sync::mpsc::Sender}; use crate::bloom::Bloom; -use crate::gen_id::gen_contract_id; use crate::http::RuesEvent; use crate::node::{coinbase_value, Rusk, RuskTip}; use crate::Error::InvalidCreditsCount; @@ -661,6 +656,7 @@ fn accept( )) } +/* // Contract deployment will fail and charge full gas limit in the // following cases: // 1) Transaction gas limit is smaller than deploy charge plus gas used for @@ -708,7 +704,9 @@ fn contract_deploy( } } } +*/ +/* /// Executes a transaction, returning the receipt of the call and the gas spent. /// The following steps are performed: /// @@ -811,6 +809,7 @@ fn execute( Ok(receipt) } +*/ fn reward_slash_and_update_root( session: &mut Session, diff --git a/rusk/tests/services/contract_deployment.rs b/rusk/tests/services/contract_deployment.rs index a40099a67f..200695c1fc 100644 --- a/rusk/tests/services/contract_deployment.rs +++ b/rusk/tests/services/contract_deployment.rs @@ -14,9 +14,8 @@ use execution_core::{ }; use rand::prelude::*; use rand::rngs::StdRng; -use rusk::gen_id::gen_contract_id; use rusk::{Result, Rusk}; -use rusk_abi::{ContractData, PiecrustError}; +use rusk_abi::{gen_contract_id, ContractData, PiecrustError}; use rusk_recovery_tools::state; use tempfile::tempdir; use test_wallet::{self as wallet, Wallet}; diff --git a/rusk/tests/services/contract_stake.rs b/rusk/tests/services/contract_stake.rs index 43038d9e07..255bcaf987 100644 --- a/rusk/tests/services/contract_stake.rs +++ b/rusk/tests/services/contract_stake.rs @@ -16,8 +16,8 @@ use execution_core::ContractId; use node_data::ledger::SpentTransaction; use rand::prelude::*; use rand::rngs::StdRng; -use rusk::gen_id::gen_contract_id; use rusk::{Result, Rusk}; +use rusk_abi::gen_contract_id; use std::collections::HashMap; use tempfile::tempdir; use test_wallet::{self as wallet}; diff --git a/rusk/tests/services/owner_calls.rs b/rusk/tests/services/owner_calls.rs index c580ada478..a273dc6be9 100644 --- a/rusk/tests/services/owner_calls.rs +++ b/rusk/tests/services/owner_calls.rs @@ -21,9 +21,8 @@ use execution_core::{ }, ContractId, }; -use rusk::gen_id::gen_contract_id; use rusk::{Error, Result, Rusk}; -use rusk_abi::{CallReceipt, ContractData, Session}; +use rusk_abi::{gen_contract_id, CallReceipt, ContractData, Session}; use rusk_recovery_tools::state; use tempfile::tempdir; use test_wallet::{self as wallet, Wallet};