Skip to content

Commit

Permalink
test: Add tests, add cdocs
Browse files Browse the repository at this point in the history
  • Loading branch information
igamigo committed Sep 16, 2024
1 parent a9938cd commit 5a04926
Show file tree
Hide file tree
Showing 16 changed files with 908 additions and 1,103 deletions.
2 changes: 1 addition & 1 deletion miden-lib/src/accounts/faucets/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ pub fn create_basic_fungible_faucet(
auth_scheme: AuthScheme,
) -> Result<(Account, Word), AccountError> {
// Atm we only have RpoFalcon512 as authentication scheme and this is also the default in the
// faucet contract, so we can just use the public key as storage slot 0.
// faucet contract.

let (auth_scheme_procedure, auth_data): (&str, Word) = match auth_scheme {
AuthScheme::RpoFalcon512 { pub_key } => ("auth_tx_rpo_falcon512", pub_key.into()),
Expand Down
2 changes: 1 addition & 1 deletion miden-tx/src/testing/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ pub struct CodeExecutor<H> {
impl<H: Host> CodeExecutor<H> {
// CONSTRUCTOR
// --------------------------------------------------------------------------------------------
pub fn new(host: H) -> Self {
pub(crate) fn new(host: H) -> Self {
Self {
host,
stack_inputs: None,
Expand Down

Large diffs are not rendered by default.

149 changes: 94 additions & 55 deletions miden-tx/src/testing/tx_context/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,50 @@ use miden_objects::{
prepare_word,
storage::prepare_assets,
},
transaction::{OutputNote, TransactionArgs, TransactionScript},
transaction::{OutputNote, TransactionArgs, TransactionInputs, TransactionScript},
FieldElement,
};
use rand::{Rng, SeedableRng};
use rand_chacha::ChaCha20Rng;
use vm_processor::{AdviceInputs, AdviceMap, Felt, Word};

use super::TransactionContext;
use crate::testing::mock_chain::{MockAuthenticator, MockChain, MockChainBuilder};
use crate::{auth::BasicAuthenticator, testing::mock_chain::MockChain};

pub type MockAuthenticator = BasicAuthenticator<ChaCha20Rng>;

// TRANSACTION CONTEXT BUILDER
// ================================================================================================

/// [TransactionContextBuilder] is a utility to construct [TransactionContext] for testing
/// purposes. It allows users to build accounts, create notes, provide advice inputs, and
/// execute code.
///
/// # Examples
///
/// ## Create a new account and
/// ```
/// let tx_context = TransactionContextBuilder::with_fungible_faucet(
/// acct_id.into(),
/// Felt::ZERO,
/// Felt::new(1000),
/// )
/// .build();
///
/// let code = "
/// use.kernel::prologue
/// use.test::account
///
/// begin
/// exec.prologue::prepare_transaction
/// push.0
/// exec.account::get_item
/// end
/// ";
///
/// let process = tx_context.execute_code(code);
/// assert!(process.is_ok());
/// ```
pub struct TransactionContextBuilder {
assembler: Assembler,
account: Account,
Expand All @@ -45,8 +80,8 @@ pub struct TransactionContextBuilder {
expected_output_notes: Vec<Note>,
tx_script: Option<TransactionScript>,
note_args: BTreeMap<NoteId, Word>,
transaction_inputs: Option<TransactionInputs>,
rng: ChaCha20Rng,
mock_chain: Option<MockChain>,
}

impl TransactionContextBuilder {
Expand All @@ -62,11 +97,12 @@ impl TransactionContextBuilder {
tx_script: None,
authenticator: None,
advice_inputs: Default::default(),
transaction_inputs: None,
note_args: BTreeMap::new(),
mock_chain: None,
}
}

/// Initializes a [TransactionContextBuilder] with a mocked standard wallet.
pub fn with_standard_account(nonce: Felt) -> Self {
// Build standard account with normal assembler because the testing one already contains it
let account = Account::mock(
Expand All @@ -88,86 +124,72 @@ impl TransactionContextBuilder {
advice_inputs: Default::default(),
rng: ChaCha20Rng::from_seed([0_u8; 32]),
tx_script: None,
transaction_inputs: None,
note_args: BTreeMap::new(),
mock_chain: None,
}
}

/// Initializes a [TransactionContextBuilder] with a mocked fungible faucet.
pub fn with_fungible_faucet(acct_id: u64, nonce: Felt, initial_balance: Felt) -> Self {
let account = Account::mock_fungible_faucet(
acct_id,
nonce,
initial_balance,
TransactionKernel::testing_assembler(),
);
let assembler = TransactionKernel::testing_assembler_with_mock_account();

Self {
assembler,
account,
account_seed: None,
authenticator: None,
input_notes: Vec::new(),
expected_output_notes: Vec::new(),
advice_inputs: Default::default(),
advice_map: None,
rng: ChaCha20Rng::from_seed([0_u8; 32]),
tx_script: None,
note_args: BTreeMap::new(),
mock_chain: None,
}
Self { account, ..Self::default() }
}

/// Initializes a [TransactionContextBuilder] with a mocked non-fungible faucet.
pub fn with_non_fungible_faucet(acct_id: u64, nonce: Felt, empty_reserved_slot: bool) -> Self {
let account = Account::mock_non_fungible_faucet(
acct_id,
nonce,
empty_reserved_slot,
TransactionKernel::testing_assembler(),
);
let assembler = TransactionKernel::testing_assembler_with_mock_account();

Self {
assembler,
account,
account_seed: None,
authenticator: None,
input_notes: Vec::new(),
expected_output_notes: Vec::new(),
advice_map: None,
advice_inputs: Default::default(),
rng: ChaCha20Rng::from_seed([0_u8; 32]),
tx_script: None,
note_args: BTreeMap::new(),
mock_chain: None,
}
Self { account, ..Self::default() }
}

/// Override and set the account seed manually
pub fn account_seed(mut self, account_seed: Option<Word>) -> Self {
self.account_seed = account_seed;
self
}

/// Override and set the [AdviceInputs]
pub fn advice_inputs(mut self, advice_inputs: AdviceInputs) -> Self {
self.advice_inputs = advice_inputs;
self
}

/// Set the authenticator for the transaction (if needed)
pub fn authenticator(mut self, authenticator: Option<MockAuthenticator>) -> Self {
self.authenticator = authenticator;
self
}

/// Extend the set of used input notes
pub fn input_notes(mut self, input_notes: Vec<Note>) -> Self {
self.input_notes.extend(input_notes);
self
}

/// Set the desired transaction script
pub fn tx_script(mut self, tx_script: TransactionScript) -> Self {
self.tx_script = Some(tx_script);
self
}

/// Set the desired transaction inputs
pub fn tx_inputs(mut self, tx_inputs: TransactionInputs) -> Self {
self.transaction_inputs = Some(tx_inputs);
self
}

/// Defines the expected output notes
pub fn expected_notes(mut self, output_notes: Vec<OutputNote>) -> Self {
let output_notes = output_notes.into_iter().filter_map(|n| match n {
OutputNote::Full(note) => Some(note),
Expand Down Expand Up @@ -196,6 +218,7 @@ impl TransactionContextBuilder {
note
}

/// Add a note from a [NoteBuilder]
fn input_note_simple(
&mut self,
sender: AccountId,
Expand All @@ -210,6 +233,7 @@ impl TransactionContextBuilder {
.unwrap()
}

/// Adds one input note with a note script that creates another ouput note.
fn input_note_with_one_output_note(
&mut self,
sender: AccountId,
Expand Down Expand Up @@ -255,6 +279,7 @@ impl TransactionContextBuilder {
.unwrap()
}

/// Adds one input note with a note script that creates 2 ouput notes.
fn input_note_with_two_output_notes(
&mut self,
sender: AccountId,
Expand Down Expand Up @@ -359,6 +384,8 @@ impl TransactionContextBuilder {
.unwrap()
}

/// Adds a set of input notes that output notes where inputs are smaller than needed and
/// do not add up to match the output.
pub fn with_mock_notes_too_few_input(mut self) -> Self {
// ACCOUNT IDS
// --------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -393,6 +420,7 @@ impl TransactionContextBuilder {
self.input_notes(vec![input_note1])
}

/// Adds a set of input notes that output notes in an asset-preserving manner.
pub fn with_mock_notes_preserved(mut self) -> Self {
// ACCOUNT IDS
// --------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -559,35 +587,45 @@ impl TransactionContextBuilder {
self.input_notes(vec![input_note1, input_note2, input_note4])
}

/// Builds the [TransactionContext].
///
/// If no transaction inputs were provided manually, an ad-hoc MockChain is created in order
/// to generate valid block data for the required notes.
pub fn build(self) -> TransactionContext {
let mut mock_chain = if let Some(mock_chain) = self.mock_chain {
mock_chain
} else {
MockChainBuilder::default().notes(self.input_notes.clone()).build()
let tx_inputs = match self.transaction_inputs {
Some(tx_inputs) => tx_inputs,
None => {
// If no specific transaction inputs was provided, initialize an ad-hoc mockchain
// to generate valid block header/MMR data

let mut mock_chain = MockChain::default();
for i in self.input_notes {
mock_chain.add_note(i);
}

mock_chain.seal_block(None);

let input_note_ids: Vec<NoteId> =
mock_chain.available_notes().iter().map(|n| n.id()).collect();

mock_chain.get_transaction_inputs(
self.account.clone(),
self.account_seed,
&input_note_ids,
&[],
)
},
};
for _ in 0..4 {
mock_chain.seal_block(None);
}

let mut tx_args = TransactionArgs::new(
self.tx_script,
Some(self.note_args),
self.advice_map.unwrap_or_default(),
);

let input_note_ids: Vec<NoteId> =
mock_chain.available_notes().iter().map(|n| n.id()).collect();

let tx_inputs = mock_chain.get_transaction_inputs(
self.account.clone(),
self.account_seed,
&input_note_ids,
);

tx_args.extend_expected_output_notes(self.expected_output_notes.clone());

TransactionContext {
mock_chain,
expected_output_notes: self.expected_output_notes,
tx_args,
tx_inputs,
Expand All @@ -596,9 +634,10 @@ impl TransactionContextBuilder {
assembler: self.assembler,
}
}
}

pub fn mock_chain(mut self, mock_chain: MockChain) -> TransactionContextBuilder {
self.mock_chain = Some(mock_chain);
self
impl Default for TransactionContextBuilder {
fn default() -> Self {
Self::with_standard_account(Felt::ZERO)
}
}
20 changes: 6 additions & 14 deletions miden-tx/src/testing/tx_context/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use alloc::{rc::Rc, sync::Arc, vec::Vec};

use builder::MockAuthenticator;
use miden_lib::transaction::TransactionKernel;
use miden_objects::{
accounts::{Account, AccountId},
Expand All @@ -10,11 +11,7 @@ use miden_objects::{
use vm_processor::{AdviceInputs, ExecutionError, Process};
use winter_maybe_async::{maybe_async, maybe_await};

use super::{
executor::CodeExecutor,
mock_chain::{MockAuthenticator, MockChain},
MockHost,
};
use super::{executor::CodeExecutor, MockHost};
use crate::{
DataStore, DataStoreError, TransactionExecutor, TransactionExecutorError, TransactionMastStore,
};
Expand All @@ -31,7 +28,6 @@ pub use builder::TransactionContextBuilder;
/// It implements [DataStore], so transactions may be executed with
/// [TransactionExecutor](crate::TransactionExecutor)
pub struct TransactionContext {
mock_chain: MockChain,
expected_output_notes: Vec<Note>,
tx_args: TransactionArgs,
tx_inputs: TransactionInputs,
Expand Down Expand Up @@ -94,18 +90,14 @@ impl TransactionContext {
&self.expected_output_notes
}

pub fn mock_chain(&self) -> &MockChain {
&self.mock_chain
}

pub fn input_notes(&self) -> InputNotes<InputNote> {
InputNotes::new(self.mock_chain.available_notes().clone()).unwrap()
}

pub fn tx_args(&self) -> &TransactionArgs {
&self.tx_args
}

pub fn input_notes(&self) -> &InputNotes<InputNote> {
self.tx_inputs.input_notes()
}

pub fn set_tx_args(&mut self, tx_args: TransactionArgs) {
self.tx_args = tx_args;
}
Expand Down
5 changes: 5 additions & 0 deletions miden-tx/src/tests/kernel_tests/test_account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,11 @@ fn test_storage_offset() {

let (mut account, _) = AccountBuilder::new(ChaCha20Rng::from_entropy())
.code(code)
.add_storage_slots([
AccountStorage::mock_item_2().slot,
AccountStorage::mock_item_0().slot,
AccountStorage::mock_item_1().slot,
])
.nonce(ONE)
.build()
.unwrap();
Expand Down
4 changes: 1 addition & 3 deletions miden-tx/src/tests/kernel_tests/test_note.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,8 +229,6 @@ fn test_get_inputs() {
.with_mock_notes_preserved()
.build();

let notes = tx_context.mock_chain().available_notes();

fn construct_input_assertions(note: &Note) -> String {
let mut code = String::new();
for input_chunk in note.inputs().values().chunks(WORD_SIZE) {
Expand All @@ -248,7 +246,7 @@ fn test_get_inputs() {
code
}

let note0 = notes[0].note();
let note0 = tx_context.input_notes().get_note(0).note();

let code = format!(
"
Expand Down
Loading

0 comments on commit 5a04926

Please sign in to comment.