-
Notifications
You must be signed in to change notification settings - Fork 54
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
test: MockChain
, CodeExecutor
, TransactionContext
proposals and general improvements
#735
Conversation
MockChain
, CodeExecutor
, TransactionContext
proposals and improvementsMockChain
, CodeExecutor
, TransactionContext
proposals and general improvements
…nto igamigo-mock-chain
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good! Left some minor comments.
.collect::<Vec<_>>(); | ||
assert_eq!(account_id, self.tx_inputs.account().id()); | ||
assert_eq!(block_num, self.block_header().block_num()); | ||
assert_eq!(notes.len(), self.tx_inputs.input_notes().num_notes()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should probably assert that all the requested notes are contained within self.tx_inputs.input_notes()
.
mock_account_code(assembler), | ||
Account::mock( | ||
ACCOUNT_ID_REGULAR_ACCOUNT_UPDATABLE_CODE_ON_CHAIN, | ||
Felt::ZERO, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
was there a reason we changed from Felt::ONE
to Felt::ZERO
? Was there a reason it was one instead of zero before?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you! Looks good! This is not an exhaustive review (I mostly looked at CodeExecutor
and TransactionContext
structs) but I did leave some comments inline.
Overall, I like the idea of CodeExecutor
and TransactionContext
, especially for granular tests (i.e., tests which test some narrow functionality). As you've noted, I think we can improve the APIs - but this can be done in follow-up PRs.
I haven't looked too deeply into the MockChain
structs, but it seems like there may be a lot of obsolete code there. On the other hand, I wonder if we should make the MockChain
a more central component of higher level tests (i.e., tests which execute entire transaction flow).
For example, something like:
let chain = MockChain::new();
// this could return something like MockFungibleFaucet(Account)
let pol_faucet = chain.add_fungible_faucet("POL");
let asset = pol_faucet.mint(100);
// this would create a basic wallet account but without requiring signature
// for authentication
let alice = chain.add_wallet_account(Auth::NoAuth);
// similar to alice - but the account would be "new" (i.e., nonce=0)
let bob = chain.add_new_wallet_account(Auth::NoAuth);
// this would create a P2ID note and add it to the chain
let note = chain.add_p2id_note(&alice, &bob, asset, NoteType::OffChian);
// build transaction context for consuming note by bob's account
let tx_context = chain.build_tx_context(&bob)
.add_input_note(note)
.build();
let executed_tx = tx_context.execute().unwrap();
The above is just a sketch - so, could be missing some detail - but I think having something like this, would simplify higher-level tests quite a bit.
miden-tx/src/testing/executor.rs
Outdated
pub fn module_file(mut self, file_path: PathBuf) -> Self { | ||
self.file_path = Some(file_path); | ||
self | ||
} | ||
|
||
pub fn imports(mut self, imports: &str) -> Self { | ||
self.imports = imports.to_string(); | ||
self | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These two functions seem to be used exclusively in epilogue tests (and they always seem to be used together). I haven't thought it through yet, but I'm wondering if there is a better way of handling these tests without this general structure (could also be done in a follow-up PR).
pub fn extend_advice_inputs(mut self, advice_inputs: AdviceInputs) -> Self { | ||
self.advice_inputs.extend(advice_inputs); | ||
self | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't seems like this function is currently being used anywhere?
pub fn set_tx_args(&mut self, tx_args: TransactionArgs) { | ||
self.tx_args = tx_args; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why this method is needed? Shouldn't all of the properties be set via the TransactionContextBuilder
?
miden-tx/src/testing/tx_context.rs
Outdated
/// Populates input and expected notes with the results from [mock_notes()] | ||
pub fn with_mock_notes(self, asset_preservation: AssetPreservationStatus) -> Self { | ||
let (notes, output_notes) = mock_notes(&self.assembler, &asset_preservation); | ||
self.notes(notes).expected_notes(output_notes) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe not for this PR, but the this method seems rather confusing to me. We are creating a pre-defined set of notes in a pretty opaque way (i.e., I need to read and understand what mock_notes()
function is doing and then how AssetPreservationStatus
is used in various places to figure out what's going on.
We should probably refactor this to get rid of AssetPreservationStatus
and potentially this method altogether.
…nto igamigo-mock-chain
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good! Thank you! As mentioned in the previous review - there are still quite a few things we can refactor/improve here, but given the amount of changes here, I think it makes sense to merge it sooner rather than later.
… general improvements (#735)
Previously, there were a series of functions created for mocking objects (such as
non_fungible_faucet_2()
). Most of these were moved to their own object'simpl
blocks. The remainders were left as helpers for specific tests, likemock_executed_transaction()
, mainly because they assume too much of the use-case and wouldn't be of much help being a struct function. The kernel tests onmiden-tx
were moved to be a submodule of thetests
module as well.Introduces 3 structs:
CodeExecutor
is a helper struct for running arbitrary code onHost
s. This struct is meant to group and replace therun_*
functions. It implements a fluent API for simpler use, and so now something like this:would look like this:
MockChain
was simplified and repurposed for simulating a blockchain for tests, essentially replacingmock_chain_data()
. Since most mocking problems stem from not being able to simulate relations between objects correctly, I think having a good way to relate all entities in a struct can eventually solve most problems. It's not very complete but it serves for simulating blocks and transformingNote
s intoInputNote
s for executing transactions (and exposes an API for getting validTransactionInputs
). There's aMockChainBuilder
as well.TransactionContext
is meant to replace themock_inputs_*
family of functions. These functions assumed a specific set of use-cases, and were related to other mock objects such as theMockDataStore
or functions such asmock_executed_transaction
. For any time a tests would callmock_inputs()
, for example, notes would be mocked and transformed intoInputNote
even when the tests weren't making use of notes. So the idea ofTransactionContext
is to encapsulate these possible use-cases but also expose a way of creating more hand-made scenarios. It also has a builder struct for simple initialization, although it's not the cleanest API/structure right now (planning to review this next alongsideMockDataStore
).As a result of this, a decent amount of (sometimes duplicate) code was removed. The API is not necessarily less verbose than it was, but it hopefully makes some more sense semantically. I'm not sure about the names of these structs, or even if the structs themselves make sense, so it is all up for discussion.