Skip to content

Commit

Permalink
Add new filse
Browse files Browse the repository at this point in the history
  • Loading branch information
igamigo committed May 29, 2024
1 parent ec508d6 commit d3b6d59
Show file tree
Hide file tree
Showing 16 changed files with 2,683 additions and 0 deletions.
126 changes: 126 additions & 0 deletions miden-tx/src/executor/testing/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// MOCK DATA STORE
// ================================================================================================

use alloc::vec::Vec;

use miden_lib::transaction::TransactionKernel;
use miden_objects::{
accounts::{
testing::{
transaction::{mock_inputs, mock_inputs_with_existing, notes::AssetPreservationStatus},
MockAccountType,
},
Account, AccountId,
},
assembly::ModuleAst,
notes::{Note, NoteId},
transaction::{
ChainMmr, InputNote, InputNotes, OutputNote, TransactionArgs, TransactionInputs,
},
BlockHeader,
};

use crate::{DataStore, DataStoreError};

#[derive(Clone)]
pub struct MockDataStore {
pub account: Account,
pub block_header: BlockHeader,
pub block_chain: ChainMmr,
pub notes: Vec<InputNote>,
pub tx_args: TransactionArgs,
}

impl MockDataStore {
pub fn new(asset_preservation_status: AssetPreservationStatus) -> Self {
let (tx_inputs, tx_args) = mock_inputs(
MockAccountType::StandardExisting,
asset_preservation_status,
&TransactionKernel::assembler(),
);
let (account, _, block_header, block_chain, notes) = tx_inputs.into_parts();
Self {
account,
block_header,
block_chain,
notes: notes.into_vec(),
tx_args,
}
}

pub fn with_existing(account: Option<Account>, input_notes: Option<Vec<Note>>) -> Self {
let (
account,
block_header,
block_chain,
consumed_notes,
_auxiliary_data_inputs,
created_notes,
) = mock_inputs_with_existing(
MockAccountType::StandardExisting,
AssetPreservationStatus::Preserved,
account,
input_notes,
&TransactionKernel::assembler(),
);
let output_notes = created_notes.into_iter().filter_map(|note| match note {
OutputNote::Full(note) => Some(note),
OutputNote::Partial(_) => None,
OutputNote::Header(_) => None,
});
let mut tx_args = TransactionArgs::default();
tx_args.extend_expected_output_notes(output_notes);

Self {
account,
block_header,
block_chain,
notes: consumed_notes,
tx_args,
}
}

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

impl Default for MockDataStore {
fn default() -> Self {
Self::new(AssetPreservationStatus::Preserved)
}
}

impl DataStore for MockDataStore {
fn get_transaction_inputs(
&self,
account_id: AccountId,
block_num: u32,
notes: &[NoteId],
) -> Result<TransactionInputs, DataStoreError> {
assert_eq!(account_id, self.account.id());
assert_eq!(block_num, self.block_header.block_num());
assert_eq!(notes.len(), self.notes.len());

let notes = self
.notes
.iter()
.filter(|note| notes.contains(&note.id()))
.cloned()
.collect::<Vec<_>>();

Ok(TransactionInputs::new(
self.account.clone(),
None,
self.block_header,
self.block_chain.clone(),
InputNotes::new(notes).unwrap(),
)
.unwrap())
}

fn get_account_code(&self, account_id: AccountId) -> Result<ModuleAst, DataStoreError> {
assert_eq!(account_id, self.account.id());
Ok(self.account.code().module().clone())
}
}
63 changes: 63 additions & 0 deletions miden-tx/src/host/testing/account_procs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
use alloc::collections::BTreeMap;

use miden_lib::transaction::TransactionKernelError;
use miden_objects::accounts::AccountCode;

use super::{AdviceProvider, Digest, NodeIndex, ProcessState};

// ACCOUNT PROCEDURE INDEX MAP
// ================================================================================================

/// A map of proc_root |-> proc_index for all known procedures of an account interface.
pub struct AccountProcedureIndexMap(BTreeMap<Digest, u8>);

impl AccountProcedureIndexMap {
/// Returns a new [AccountProcedureIndexMap] instantiated with account procedures present in
/// the provided advice provider.
///
/// This function assumes that the account procedure tree (or a part thereof) is loaded into the
/// Merkle store of the provided advice provider.
pub fn new<A: AdviceProvider>(account_code_root: Digest, adv_provider: &A) -> Self {
// get the Merkle store with the procedure tree from the advice provider
let proc_store = adv_provider.get_store_subset([account_code_root].iter());

// iterate over all possible procedure indexes
let mut result = BTreeMap::new();
for i in 0..AccountCode::MAX_NUM_PROCEDURES {
let index = NodeIndex::new(AccountCode::PROCEDURE_TREE_DEPTH, i as u64)
.expect("procedure tree index is valid");
// if the node at the current index does not exist, skip it and try the next node;this
// situation is valid if not all account procedures are loaded into the advice provider
if let Ok(proc_root) = proc_store.get_node(account_code_root, index) {
// if we got an empty digest, this means we got to the end of the procedure list
if proc_root == Digest::default() {
break;
}
result.insert(proc_root, i as u8);
}
}
Self(result)
}

/// Returns index of the procedure whose root is currently at the top of the operand stack in
/// the provided process.
///
/// # Errors
/// Returns an error if the procedure at the top of the operand stack is not present in this
/// map.
pub fn get_proc_index<S: ProcessState>(
&self,
process: &S,
) -> Result<u8, TransactionKernelError> {
let proc_root = process.get_stack_word(0).into();
// mock account method for testing from root context
// TODO: figure out if we can get rid of this
if proc_root == Digest::default() {
return Ok(255);
}
self.0
.get(&proc_root)
.cloned()
.ok_or(TransactionKernelError::UnknownAccountProcedure(proc_root))
}
}
103 changes: 103 additions & 0 deletions miden-tx/src/host/testing/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
use alloc::string::ToString;

use miden_lib::transaction::TransactionEvent;
use miden_objects::{
accounts::{delta::AccountVaultDelta, AccountStub},
Digest,
};
use vm_processor::{
crypto::NodeIndex, AdviceExtractor, AdviceInjector, AdviceInputs, AdviceProvider, AdviceSource,
ContextId, ExecutionError, Host, HostResponse, MemAdviceProvider, ProcessState,
};

mod account_procs;
pub mod procedures;
pub mod utils;

use account_procs::AccountProcedureIndexMap;

// MOCK HOST
// ================================================================================================

/// This is very similar to the TransactionHost in miden-tx. The differences include:
/// - We do not track account delta here.
/// - There is special handling of EMPTY_DIGEST in account procedure index map.
/// - This host uses `MemAdviceProvider` which is instantiated from the passed in advice inputs.
pub struct MockHost {
adv_provider: MemAdviceProvider,
acct_procedure_index_map: AccountProcedureIndexMap,
}

impl MockHost {
/// Returns a new [MockHost] instance with the provided [AdviceInputs].
pub fn new(account: AccountStub, advice_inputs: AdviceInputs) -> Self {
let adv_provider: MemAdviceProvider = advice_inputs.into();
let proc_index_map = AccountProcedureIndexMap::new(account.code_root(), &adv_provider);
Self {
adv_provider,
acct_procedure_index_map: proc_index_map,
}
}

/// Consumes `self` and returns the advice provider and account vault delta.
pub fn into_parts(self) -> (MemAdviceProvider, AccountVaultDelta) {
(self.adv_provider, AccountVaultDelta::default())
}

// EVENT HANDLERS
// --------------------------------------------------------------------------------------------

fn on_push_account_procedure_index<S: ProcessState>(
&mut self,
process: &S,
) -> Result<(), ExecutionError> {
let proc_idx = self
.acct_procedure_index_map
.get_proc_index(process)
.map_err(|err| ExecutionError::EventError(err.to_string()))?;
self.adv_provider.push_stack(AdviceSource::Value(proc_idx.into()))?;
Ok(())
}
}

impl Host for MockHost {
fn get_advice<S: ProcessState>(
&mut self,
process: &S,
extractor: AdviceExtractor,
) -> Result<HostResponse, ExecutionError> {
self.adv_provider.get_advice(process, &extractor)
}

fn set_advice<S: ProcessState>(
&mut self,
process: &S,
injector: AdviceInjector,
) -> Result<HostResponse, ExecutionError> {
self.adv_provider.set_advice(process, &injector)
}

fn on_event<S: ProcessState>(
&mut self,
process: &S,
event_id: u32,
) -> Result<HostResponse, ExecutionError> {
let event = TransactionEvent::try_from(event_id)
.map_err(|err| ExecutionError::EventError(err.to_string()))?;

if process.ctx() != ContextId::root() {
return Err(ExecutionError::EventError(format!(
"{event} event can only be emitted from the root context"
)));
}

match event {
TransactionEvent::AccountPushProcedureIndex => {
self.on_push_account_procedure_index(process)
},
_ => Ok(()),
}?;

Ok(HostResponse::None)
}
}
96 changes: 96 additions & 0 deletions miden-tx/src/host/testing/procedures.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
use alloc::string::String;

use miden_lib::transaction::memory::{
CREATED_NOTE_ASSETS_OFFSET, CREATED_NOTE_METADATA_OFFSET, CREATED_NOTE_NUM_ASSETS_OFFSET,
CREATED_NOTE_RECIPIENT_OFFSET, CREATED_NOTE_SECTION_OFFSET, NUM_CREATED_NOTES_PTR,
};
use miden_objects::{
accounts::testing::{prepare_assets, prepare_word},
transaction::{OutputNote, OutputNotes},
};

pub fn output_notes_data_procedure(notes: &OutputNotes) -> String {
let OutputNote::Full(note0) = notes.get_note(0) else {
panic!("Note 0 must be a full note")
};
let note_0_metadata = prepare_word(&note0.metadata().into());
let note_0_recipient = prepare_word(&note0.recipient().digest());
let note_0_assets = prepare_assets(note0.assets());
let note_0_num_assets = 1;

let OutputNote::Full(note1) = notes.get_note(1) else {
panic!("Note 1 must be a full note")
};
let note_1_metadata = prepare_word(&note1.metadata().into());
let note_1_recipient = prepare_word(&note1.recipient().digest());
let note_1_assets = prepare_assets(note1.assets());
let note_1_num_assets = 1;

let OutputNote::Full(note2) = notes.get_note(2) else {
panic!("Note 2 must be a full note")
};
let note_2_metadata = prepare_word(&note2.metadata().into());
let note_2_recipient = prepare_word(&note2.recipient().digest());
let note_2_assets = prepare_assets(note2.assets());
let note_2_num_assets = 1;

// todo: remove this
const NOTE_MEM_SIZE: u32 = 512;
const NOTE_1_OFFSET: u32 = NOTE_MEM_SIZE;
const NOTE_2_OFFSET: u32 = NOTE_MEM_SIZE * 2;

format!(
"
proc.create_mock_notes
# remove padding from prologue
dropw dropw dropw dropw
# populate note 0
push.{note_0_metadata}
push.{CREATED_NOTE_SECTION_OFFSET}.{CREATED_NOTE_METADATA_OFFSET} add mem_storew dropw
push.{note_0_recipient}
push.{CREATED_NOTE_SECTION_OFFSET}.{CREATED_NOTE_RECIPIENT_OFFSET} add mem_storew dropw
push.{note_0_num_assets}
push.{CREATED_NOTE_SECTION_OFFSET}.{CREATED_NOTE_NUM_ASSETS_OFFSET} add mem_store
push.{}
push.{CREATED_NOTE_SECTION_OFFSET}.{CREATED_NOTE_ASSETS_OFFSET} add mem_storew dropw
# populate note 1
push.{note_1_metadata}
push.{CREATED_NOTE_SECTION_OFFSET}.{NOTE_1_OFFSET}.{CREATED_NOTE_METADATA_OFFSET} add add mem_storew dropw
push.{note_1_recipient}
push.{CREATED_NOTE_SECTION_OFFSET}.{NOTE_1_OFFSET}.{CREATED_NOTE_RECIPIENT_OFFSET} add add mem_storew dropw
push.{note_1_num_assets}
push.{CREATED_NOTE_SECTION_OFFSET}.{NOTE_1_OFFSET}.{CREATED_NOTE_NUM_ASSETS_OFFSET} add add mem_store
push.{}
push.{CREATED_NOTE_SECTION_OFFSET}.{NOTE_1_OFFSET}.{CREATED_NOTE_ASSETS_OFFSET} add add mem_storew dropw
# populate note 2
push.{note_2_metadata}
push.{CREATED_NOTE_SECTION_OFFSET}.{NOTE_2_OFFSET}.{CREATED_NOTE_METADATA_OFFSET} add add mem_storew dropw
push.{note_2_recipient}
push.{CREATED_NOTE_SECTION_OFFSET}.{NOTE_2_OFFSET}.{CREATED_NOTE_RECIPIENT_OFFSET} add add mem_storew dropw
push.{note_2_num_assets}
push.{CREATED_NOTE_SECTION_OFFSET}.{NOTE_2_OFFSET}.{CREATED_NOTE_NUM_ASSETS_OFFSET} add add mem_store
push.{}
push.{CREATED_NOTE_SECTION_OFFSET}.{NOTE_2_OFFSET}.{CREATED_NOTE_ASSETS_OFFSET} add add mem_storew dropw
# set num created notes
push.{}.{NUM_CREATED_NOTES_PTR} mem_store
end
",
note_0_assets[0],
note_1_assets[0],
note_2_assets[0],
notes.num_notes()
)
}
Loading

0 comments on commit d3b6d59

Please sign in to comment.