Integrate PSM Contracts to AuthMultisig #2527
Conversation
partylikeits1983
left a comment
There was a problem hiding this comment.
Looks good! Not a full review, but left some nit comments on some MASM related things that stood out to me!
crates/miden-standards/asm/account_components/auth/multisig.masm
Outdated
Show resolved
Hide resolved
crates/miden-standards/asm/account_components/auth/multisig.masm
Outdated
Show resolved
Hide resolved
|
Forgot to mention the corresponding issue: #2421 |
|
Hey @PhilippGackstatter @bobbinth Please review, as it's a blocker for spending limits and PSM is also being audited so that we need to make the changes accordingly |
PhilippGackstatter
left a comment
There was a problem hiding this comment.
Looks good, though I haven't gotten through everything yet. Left some initial comments/thoughts.
| # => [proc_threshold, proc_index, num_approvers] | ||
|
|
||
| dup neq.0 | ||
| # => [has_override, proc_threshold, proc_index, num_approvers] | ||
| if.true | ||
| dup.2 | ||
| # => [num_approvers, proc_threshold, proc_index, num_approvers] | ||
|
|
||
| u32assert2.err=ERR_PROC_THRESHOLD_NOT_U32 | ||
| u32gt assertz.err=ERR_PROC_THRESHOLD_EXCEEDS_NUM_APPROVERS | ||
| # => [proc_index, num_approvers] | ||
| else | ||
| drop | ||
| # => [proc_index, num_approvers] | ||
| end |
There was a problem hiding this comment.
I think we don't need the if-else branch. The default proc_threshold is 0 (right?) which is never greater than num_approvers, so the assertion should always succed.
drop drop drop
# => [proc_threshold, proc_index, num_approvers]
dup.2 u32assert2.err=ERR_PROC_THRESHOLD_NOT_U32
u32gt assertz.err=ERR_PROC_THRESHOLD_EXCEEDS_NUM_APPROVERS
# => [proc_index, num_approvers]
| u32assert.err=ERR_MAX_ALLOWED_NOT_U32 | ||
| # => [num_approvers] |
There was a problem hiding this comment.
We could probably remove this because we also do u32assert2 later with the same value.
| u32assert.err=ERR_PROC_THRESHOLD_NOT_U32 | ||
| # => [proc_threshold, PROC_ROOT] |
There was a problem hiding this comment.
We could probably remove this because we also do u32assert2 later with the same value.
| exec.psm::is_psm_initialized | ||
| # => [is_psm_initialized, TX_SUMMARY_COMMITMENT] | ||
|
|
||
| if.true | ||
| exec.psm::verify_psm_signature | ||
| end |
There was a problem hiding this comment.
Nit: Would it be sufficient to call these psm::is_initialized and psm::verify_signature? We basically always use the module name, so "psm" does not really need to be duplicated.
| # The slot in this component's storage layout where the PSM public key map is stored. | ||
| # Map entries: [PSM_MAP_KEY] => [PSM_PUBLIC_KEY] | ||
| const PSM_PUBLIC_KEYS_SLOT = word("miden::standards::auth::psm::pub_key") | ||
|
|
||
| # The slot in this component's storage layout where the scheme id for the corresponding PSM public key map is stored. | ||
| # Map entries: [PSM_MAP_KEY] => [scheme_id, 0, 0, 0] | ||
| const PSM_SCHEME_ID_SLOT = word("miden::standards::auth::psm::scheme") |
There was a problem hiding this comment.
Iiuc, the only reason these two slots need to be a map is because signature::verify_signatures reads the scheme IDs and pub keys from a map slot. As mentioned in the previous PR, I think verify_signatures is a specialized implementation for the multisig that still happens to be in miden::standards, but imo should be moved to the multisig account component code eventually.
I think a reasonable alternative to make public in miden::standards is verify_signature_by_scheme. If we used it for the PSM signature verification, it would avoid the need to pass a map slot, and in turn we could make PSM_SCHEME_ID_SLOT and PSM_PUBLIC_KEYS_SLOT value slots.
I think we can leave things as they are for this PR, but this is something we should do eventually.
There was a problem hiding this comment.
Iiuc, the only reason these two slots need to be a map is because signature::verify_signatures reads the scheme IDs and pub keys from a map slot. As mentioned in the previous PR, I think verify_signatures is a specialized implementation for the multisig that still happens to be in miden::standards, but imo should be moved to the multisig account component code eventually.
This one seems better for later improvements.
| exec.is_psm_initialized | ||
| # => [is_psm_initialized, is_psm_signature_required] | ||
|
|
||
| if.true | ||
| # => [is_psm_signature_required] | ||
| if.true | ||
| push.PSM_CONFIG_ENABLED_INITIALIZED | ||
| else | ||
| push.PSM_CONFIG_DISABLED_INITIALIZED | ||
| end | ||
| else | ||
| # => [is_psm_signature_required] | ||
| if.true | ||
| push.PSM_CONFIG_ENABLED_UNINITIALIZED | ||
| else | ||
| push.PSM_CONFIG_DISABLED_UNINITIALIZED | ||
| end | ||
| end | ||
| # => [NEW_PSM_CONFIG_WORD] | ||
|
|
||
| push.PSM_CONFIG_SLOT[0..2] | ||
| # => [config_slot_prefix, config_slot_suffix, NEW_PSM_CONFIG_WORD] | ||
|
|
||
| exec.native_account::set_item | ||
| # => [OLD_PSM_CONFIG] |
There was a problem hiding this comment.
Could we simplify this?
push.PSM_CONFIG_SLOT[0..2] exec.active_account::get_item
# => [0, 0, is_psm_initialized, is_psm_signature_required, new_is_psm_signature_required]
movup.3 drop
# => [0, 0, is_psm_initialized, new_is_psm_signature_required]
# => [NEW_PSM_CONFIG_WORD]
push.PSM_CONFIG_SLOT[0..2]
exec.native_account::set_item
dropw
# => []
One notable change is that exec.is_psm_initialized used get_initial_item and I've changed it to get_item. I don't think it matters for this particular case, but get_item + set_item would preserve updates to other parts of the slot made during the course of execution (of which there aren't currently any so for now it doesn't matter I believe).
| #! Outputs: [is_psm_initialized] | ||
| #! | ||
| #! Invocation: exec | ||
| pub proc is_psm_initialized |
There was a problem hiding this comment.
| pub proc is_psm_initialized | |
| pub proc is_psm_initialized() -> i1 |
Nit: Not super important currently, but pub procedures in miden::standards should ideally have type signatures. Here, I think i1 is the correct one (docs).
| exec.is_psm_initialized | ||
| # => [is_psm_initialized] | ||
|
|
||
| push.1 eq | ||
| assert.err=ERR_PSM_NOT_INITIALIZED | ||
| # => [] |
There was a problem hiding this comment.
I think we can drop the push.1 eq part as is_psm_initialized is already a bool (or should be). If it isn't assert would panic, which I think is fine - or are we deliberately trying to be conservative?
| # ------ Verifying Private State Manager Signature ------ | ||
|
|
||
| exec.psm::is_psm_initialized | ||
| # => [is_psm_initialized, TX_SUMMARY_COMMITMENT] | ||
|
|
||
| if.true | ||
| exec.psm::verify_psm_signature | ||
| end |
There was a problem hiding this comment.
Architecture question: Would it be possible to have two dedicated account components, AuthMultisig and AuthMultisigPsm?
AuthMultisig would be the same as it is now (plus the newly added validation), but without any PSM code.
AuthMultisigPsm would be the same as this module, but without the exec.psm::is_psm_initialized + if.true check and always calling exec.psm::verify_psm_signature. In other words, it would always be PSM-enabled and there is no runtime flag to opt out of it. If I understand correctly, then we could remove the is_psm_initialized flag entirely and simplify the PSM config slot to just contain is_psm_signature_required.
Or is there a need to enable/disable the PSM during the lifetime of an account (in which case the current approach makes sense)?
There was a problem hiding this comment.
@PhilippGackstatter Thank you for your comment. My first approach was that one and I think that would be better and we don't need to add is_psm_initialized.
But, this time we will have almost the same code as AuthMultisig and AuthMultsigPsm both in the MASM part and the Rust part. What are your thoughts on this?
There was a problem hiding this comment.
Or is there a need to enable/disable the PSM during the lifetime of an account (in which case the current approach makes sense)?
No, enable and disable is just for is_psm_signature_required, we won't provide enable and disable procedures for is_psm_initialized
There was a problem hiding this comment.
I think we should also get input from @bobbinth as both approaches have pros and cons, and this impacts the codebase. I will hold off on making changes until we hear back.
There was a problem hiding this comment.
I think we should be able to deduplicate both of these.
MASM deduplication
Basically, the multisig.masm code is identical except for the extra psm::verify_psm_signature call, right? What we would need ideally is a multisig library that we can use to build the AuthMultisig and AuthMultisigPsm variants. This multisig library would not be part of miden::standards because imo it is not reusable enough. Then we could do:
# AuthMultisig code
use multisig
@auth_script
pub proc auth_tx_multisig(salt: word)
exec.multisig::auth_tx
end
# AuthMultisigPsm code
use multisig
use psm
@auth_script
pub proc auth_tx_multisig_psm(salt: word)
exec.multisig::auth_tx
exec.psm::verify_signature
end
The main difficulty is setting this up with our current approach to building account component code. Account component code in crates/miden-standards/asm/account_components/auth/* cannot rely on other libraries (only miden::{standards, protocol}), so setting this up is difficult right now. I believe once we have proper support for projects (0xMiden/miden-vm#2510) this may become relatively easy and we could define a multisig library and two account components that statically link against that library: a standard multisig account component (AuthMultisig) and a PSM-enabled multisig account component (AuthMultisigPsm).
So instead of doing a lot of extra lifting in build.rs to make this happen, the simplest thing to do for now is to move the multisig code back into miden::standards to make it accessible in library form (where it should not live long-term). This should allow us to do the above while only swapping out the imports for use miden::standards::auth::multisig and use miden::standards::auth::psm.
Rust deduplication
For the Rust side, I imagine we should be able to wrap AuthMultisig into AuthMultisigPsm, something roughly like this:
// todo
pub struct PsmConfig;
pub struct AuthMultisigPsm {
multisig: AuthMultisig,
psm_config: PsmConfig,
}
impl From<AuthMultisigPsm> for AccountComponent {
fn from(multisig: AuthMultisigPsm) -> Self {
let AuthMultisigPsm { multisig, psm_config } = multisig;
// Convert the inner multisig into a component to reuse its storage slots and metadata.
let multisig_component = AccountComponent::from(multisig);
let (psm_slots, psm_slot_metadata) = psm_config.into_component_parts();
// TODO: We can probably add AccountComponent::into_parts for this.
let mut storage_slots = multisig_component.storage_slots().clone();
storage_slots.extend(psm_slots);
let mut metadata = multisig_component.metadata().clone();
metadata.extend(psm_slot_metadata);
AccountComponent::new(multisig_psm_library(), storage_slots, metadata).expect("todo")
}
}So to summarize:
- We can (temporarily) move the multisig code into
miden::standardsto make it accessible in library-form for both multisig components. - We should be able to wrap
AuthMultisigintoAuthMultisigPsmfor deduplication.
Does this makes sense?
There was a problem hiding this comment.
Thank you @PhilippGackstatter. Yes, this makes sense, and I think it is better than the current architecture. I'll make the changes relatively and move the multisig.masm to the miden::standards::auth
There was a problem hiding this comment.
And, what's @auth_script can we use it now, and do we need some other changes for this?
There was a problem hiding this comment.
You can use it as-is. This was a recent change to identify auth procedures via an attribute rather than the procedure's name (#2534).
|
All done! @PhilippGackstatter please review. |
bobbinth
left a comment
There was a problem hiding this comment.
Looks good! Thank you! Not a full review yet, but I left some comments inline - many of them small nits.
For the bigger comments, we should figure out which ones we want to address now, and which ones we should do in follow-up PRs as I'd love to merge this PR sooner rather than later.
crates/miden-standards/asm/account_components/auth/multisig.masm
Outdated
Show resolved
Hide resolved
crates/miden-standards/asm/account_components/auth/multisig_psm.masm
Outdated
Show resolved
Hide resolved
crates/miden-standards/asm/account_components/auth/multisig_psm.masm
Outdated
Show resolved
Hide resolved
| #! Returns 1 if PUB_KEY is a current signer, else 0. | ||
| #! Inputs: [PUB_KEY] | ||
| #! Outputs: [is_signer] | ||
| #! Locals: |
There was a problem hiding this comment.
Same nit as above: I'd put newlines before and after inputs/outputs section.
There was a problem hiding this comment.
I think I would remove the docs here and in account_components/auth/multisig.masm since the docs already exist at the original procedure definition, and now we have it duplicated three times.
| pub proc update_psm_public_key(new_psm_public_key: word) | ||
| exec.disable_psm_signature_requirement | ||
| # => [NEW_PSM_PUBLIC_KEY] | ||
|
|
||
| push.PSM_MAP_KEY | ||
| # => [PSM_MAP_KEY, NEW_PSM_PUBLIC_KEY] | ||
|
|
||
| push.PSM_PUBLIC_KEYS_SLOT[0..2] | ||
| # => [psm_pubkeys_slot_prefix, psm_pubkeys_slot_suffix, PSM_MAP_KEY, NEW_PSM_PUBLIC_KEY] | ||
|
|
||
| exec.native_account::set_map_item | ||
| # => [OLD_PSM_PUBLIC_KEY] | ||
|
|
||
| dropw | ||
| # => [] | ||
| end |
There was a problem hiding this comment.
A few comments about this procedure (probably for future PRs):
- This procedure should probably require one of the highest thresholds.
- I wonder if we should enforce that if this procedure is called, no other procedure should be called in the same (i.e., PSM rotation requires a "single-purpose" transaction).
- It is not clear to me why we need to disable/enable PSM requirement using the storage. Could we not detect that this procedure has been called in the
verify_signatureby usingnative_account::was_procedure_calledkernel procedure?
Separately, this is probably one of the first procedures that we should put a "delay" on in the next iteration (i.e., rotating PSM should not take the effect immediately, and ideally, users should be able to cancel the rotation with enough signatures).
There was a problem hiding this comment.
I wonder if we should enforce that if this procedure is called, no other procedure should be called in the same (i.e., PSM rotation requires a "single-purpose" transaction).
Similar to my comment above related to procedure threshold overrides, we can also efficiently compute and take the highest threshold if one of them are called from the list of overrides.
Let's say, we have the the following procedure and the corresponding threshold configuration, for an M-of-N
receive_asset -> 1
update_psm_signature -> 4
update_signers_and_thresholds -> 4
Also, we have the default_threshold -> 3
So, if we call both of the above 3 procedures at the same time, the highest thresholds will apply. So, in any case, since update_psm_signature is one of the highest thresholds, then, its corresponding thresholds will apply any case.
If we need "single purpose" transaction, as an addition, we might also add this, but I don't think we need it when we compute the highest threshold.
It is not clear to me why we need to disable/enable PSM requirement using the storage. Could we not detect that this procedure has been called in the verify_signature by using native_account::was_procedure_called kernel procedure?
We just enable/disable signature requirement, for the I'm not sure about the exact logic, but PSM is updating the storage without depending on the PSM signature requirement. It is also audited and no issues found related to that, so I believe it would be better to stick it to the current implementation.
Separately, this is probably one of the first procedures that we should put a "delay" on in the next iteration (i.e., rotating PSM should not take the effect immediately, and ideally, users should be able to cancel the rotation with enough signatures).
Yes, the timelock controller is on the way after this PR, and I agree that any key rotation requires a delay of ideally ~48 hours.
| push.PSM_CONFIG_SLOT[0..2] exec.active_account::get_item | ||
| # => [is_psm_signature_required, 0, 0, 0, MSG] |
There was a problem hiding this comment.
As mentioned in the previous comment, it should be possible to replace this with something like
procref.update_psm_public_key
exec.native_account::was_procedure_called
# => [was_update_psm_procedure_called]
There was a problem hiding this comment.
It would be good sticking to the current implementation as it's already audited, but for the next audit iteration, I agree this should be a good candidate to change the logic a bit. wdyt?
There was a problem hiding this comment.
Fine for this PR - but I'd add this to the list of follow-up items.
|
|
||
| #! Updates the private state manager public key. | ||
| #! | ||
| #! Inputs: [NEW_PSM_PUBLIC_KEY] |
There was a problem hiding this comment.
Do we not need to pass the scheme as a parameter here as well? Or is the assumption that the PSAM signature scheme cannot be changed post deployment? (if so, we should state this somewhere).
|
All done apart from some of the comments will be addressed in the following PRs! @bobbinth
|
PhilippGackstatter
left a comment
There was a problem hiding this comment.
Looks good to me!
I left a bunch of nits and comments and the main theme is deduplication. Overall, I'm fine with merging as-is, though.
| #! Returns 1 if PUB_KEY is a current signer, else 0. | ||
| #! Inputs: [PUB_KEY] | ||
| #! Outputs: [is_signer] | ||
| #! Locals: |
There was a problem hiding this comment.
I think I would remove the docs here and in account_components/auth/multisig.masm since the docs already exist at the original procedure definition, and now we have it duplicated three times.
| #! Check if transaction has already been executed and add it to executed transactions for replay protection, and | ||
| #! finalizes multisig authentication. | ||
| #! for replay protection. |
There was a problem hiding this comment.
| #! Check if transaction has already been executed and add it to executed transactions for replay protection, and | |
| #! finalizes multisig authentication. | |
| #! for replay protection. | |
| #! Check if transaction has already been executed and add it to executed transactions for replay protection, and | |
| #! finalizes multisig authentication. |
Nit: I think this is already mentioned in the first sentence.
| #! Conditionally verifies a private state manager signature. | ||
| #! | ||
| #! Inputs: [MSG] | ||
| #! Outputs: [MSG] |
There was a problem hiding this comment.
Nit: I'd remove the MSG output if it is indeed identical to the input (see #2523 for motivation). If callers need it, they should dupw it themselves.
| exec.signature::verify_signatures | ||
| # => [num_verified_signatures, MSG] | ||
|
|
||
| push.1 neq |
There was a problem hiding this comment.
| push.1 neq | |
| neq.1 |
Nit
| } | ||
|
|
||
| impl PsmConfig { | ||
| pub fn new(pub_key: PublicKeyCommitment, auth_scheme: AuthScheme) -> Self { |
There was a problem hiding this comment.
Not for this PR, but at the next opportunity, I think we should introduce a wrapper for these two, because they often appear together and there should be a type encapsulating these, so we could avoid these tuple types (PublicKeyCommitment, AuthScheme) in a couple of places. Since we often call these approvers, we could call that ApproverPublicKey.
Alternatively, we could move AuthScheme into PublicKeyCommitment directly, but I haven't checked if this works at all or whether it's a good idea.
|
|
||
| let updated_psm_public_key = updated_multisig_account | ||
| .storage() | ||
| .get_map_item(AuthMultisigPsm::psm_public_key_slot(), Word::from([0u32, 0, 0, 0]))?; |
There was a problem hiding this comment.
| .get_map_item(AuthMultisigPsm::psm_public_key_slot(), Word::from([0u32, 0, 0, 0]))?; | |
| .get_map_item(AuthMultisigPsm::psm_public_key_slot(), Word::empty())?; |
Nit
| let reenable_salt = Word::from([Felt::new(992); 4]); | ||
| let tx_context_init_reenable = mock_chain | ||
| .build_tx_context(updated_multisig_account.id(), &[], &[])? | ||
| .auth_args(reenable_salt) | ||
| .build()?; | ||
| let tx_summary_reenable = match tx_context_init_reenable.execute().await.unwrap_err() { | ||
| TransactionExecutorError::Unauthorized(tx_effects) => tx_effects, | ||
| error => anyhow::bail!("expected abort with tx effects: {error}"), | ||
| }; |
There was a problem hiding this comment.
After the PSM key rotation, we're running two transactions: "reenable" and "next". Do these test different things? It seems like it would be sufficient to try execute with the old PSM key against the a tx, which should fail, and then try again with the new PSM key against the same tx and it should succeed. I think this is what we're already doing for "next", so can't we drop the "reenable" case?
| /// Tests that multisig authentication requires an additional PSM signature when configured. | ||
| #[tokio::test] | ||
| async fn test_hybrid_multisig_psm_signature_required() -> anyhow::Result<()> { |
There was a problem hiding this comment.
Could we move the PSM-related test to a new crates/miden-testing/tests/auth/multisig_psm.rs module? This module is already 1500 lines of code without the PSM tests.
Edit: Nevermind, I saw these are duplicated there.
| let proc_root = BasicWallet::receive_asset_digest(); | ||
| let proc_root_elements = proc_root.as_elements(); | ||
| let proc_root_word = Word::from([ | ||
| proc_root_elements[0], | ||
| proc_root_elements[1], | ||
| proc_root_elements[2], | ||
| proc_root_elements[3], | ||
| ]); |
There was a problem hiding this comment.
Same comment as in the other file: proc_root is already the Word we need.
Also, I think the tests in crates/miden-testing/tests/auth/hybrid_multisig.rs are duplicated here, right? If so, I'd remove the ones in hybrid multisig because I don't think we're really testing additional code paths by duplicating these.
| /// Tests that multisig authentication requires an additional PSM signature when | ||
| /// configured. | ||
| #[rstest] | ||
| #[case::ecdsa(AuthScheme::EcdsaK256Keccak)] | ||
| #[case::falcon(AuthScheme::Falcon512Poseidon2)] | ||
| #[tokio::test] | ||
| async fn test_multisig_psm_signature_required( |
There was a problem hiding this comment.
Similar comment as before: If these tests are duplicated in hybrid multisig, I would remove the duplication and only keep the ones here, as I don't think we're testing additional code paths.
I think we can later think about ways to run these tests with rstest using uniform and hybrid schemes. This should probably apply to hybrid_multisig.rs and multisig.rs / multisig_psm.rs in general, (but for now I'd only remove the duplicated tests added in this PR).
bobbinth
left a comment
There was a problem hiding this comment.
Looks good! Thank you! As @PhilippGackstatter suggested, I'll merge as is. @onurinanc - could you create an issue with various follow-up items? We could classify them into small fixes and something that could be done later on as a part of a bigger functionality change.
One other thing to add to the follow-ups: we should consider the name change since we now using Guardian instead of PSM. Maybe AuthGuardedMultisig could be a replacement name, but open to other suggestions as well.
| } | ||
| } | ||
|
|
||
| /// An [`AccountComponent`] implementing a multisig based on ECDSA signatures. |
There was a problem hiding this comment.
I don't think we need to single out ECDSA signature here as PSM could work with all standardized signature schemes.
|
hmmm - seems like something broke after merging the latest |
…ase into onur-auth-multisig-psm
|
All done! I have addressed:
I'll be opening an issue for follow-up items. |
bobbinth
left a comment
There was a problem hiding this comment.
Looks good! Thank you! I'll merge as is - and anything else can be addressed in follow-ups.
All done! I have addressed:
@PhilippGackstatter 's comments related to duplications in the tests
@bobbinth 's comments:
- related to the use of
procrefin PSM signature verification- "single-purpose" transaction policy for PSM key rotation.
Thank you! Though, I think you made a good point about "single-purpose" transaction and we may need to adjust this mechanism in the future.
* chore: refactor tx kernel from `ASSET` to `ASSET_KEY` and `ASSET_VALUE` (#2396) * chore: refactor `miden::protocol` from `ASSET` to `ASSET_KEY` and `ASSET_VALUE` (#2410) * feat: adapt the layout of `Asset`s to the double word representation (#2437) * feat: migrate to miden VM 0.21 and miden crypto 0.22 (#2508) * feat: optimize layouts and procedures for little-endian (#2512) * chore: use `get_balance` helper in account_delta * chore: Add `TryFrom<Word> for AssetVaultKey` * feat: refactor `asset.masm` * feat: add `fungible_asset.masm` * feat: refactor `asset_vault.masm` * feat: refactor `faucet.masm` * feat: refactor `account.masm` * feat: refactor `account_delta.masm` * feat: refactor `epilogue.masm` * feat: refactor `output_note.masm` * feat: refactor `prologue.masm` * chore: increase `NOTE_MEM_SIZE` to 3072 * chore: adapt `NoteAssets` commitment * feat: refactor `note.masm` * chore: refactor `api.masm` * chore: regenerate kernel proc hashes * chore: add changelog * fix: faucet::mint output docs * chore: update memory.rs input/output note memory layouts * fix: duplicate num assets in memory.rs table * feat: move `build_asset_vault_key` to shared utils * feat: refactor `faucet::mint` * feat: refactor `faucet::burn` * chore: refactor `create_non_fungible_asset` for uniformity * feat: refactor `native_account::remove_asset` * chore: move `mock::util` lib to miden-standards * feat: refactor `move_asset_to_note` * feat: add asset key to SWAP storage * feat: refactor `native_account::add_asset` * chore: refactor `receive_asset` * feat: refactor `output_note::add_asset` * chore: deduplicate epilogue asset preservation test * chore: remove re-export of vault key builder procedures * chore: regenerate kernel procedure hashes * chore: add changelog * fix: doc link to mock util lib * chore: improve send_note_body impl and test * fix: replace leftover `ASSET`s with `ASSET_VALUE` * chore: update protocol library docs * fix: rename leftover `ASSET` to `ASSET_VALUE` * chore: remove unused error * chore: regenerate tx kernel errors * chore: improve note assets commitment computation * fix: input notes memory assertions * chore: add renamed procedures to changelog * fix: incorrect stack and doc comment * fix: p2id::new test * feat: validate new asset layouts * chore: update asset procedure calls in faucet * feat: add create_fungible_asset_unchecked * chore: change has_non_fungible_asset to take asset key * feat: include full faucet ID limbs in asset delta * chore: remove into `Word` conversions for assets * feat: Implement strongly typed asset vault key * chore: temporarily remove asset vault key hash * chore: remove asset from word conversion * feat: update `Asset` docs * chore: remove unused (non-)fungible asset builders * feat: refactor asset serialization and tests * chore: add validation in get_asset and get_initial_asset * chore: adapt miden-standards to changed asset def * chore: adapt miden-tx to changed asset def * feat: return native asset ID and fee amount as tx output * chore: update create_non_fungible_asset faucet/asset APIs * chore: adapt tests to new asset layouts * feat: validate asset ID prefix in non-fungible assets * chore: drop trailing "asset" from `asset::validate_asset` * chore: rewrite asset validation tests * chore: merge validate_value in `fungible_asset` * chore: remove unused build_asset_vault_key procedures * chore: remove `LexicographicWord` wrapper in non-fungible asset delta * chore: update asset::create_non_fungible_asset signature in docs * fix: test name, vault key display impl, remove unused errors * fix: intra doc links * chore: add changelog * Initial plan * Address review nits: add load_asset_key_and_value utility, use explicit ASSET_PTR constants, rename variables, add test Co-authored-by: mmagician <8402446+mmagician@users.noreply.github.com> * Fix load_asset_key_and_value procedure and usages in prologue and epilogue Co-authored-by: mmagician <8402446+mmagician@users.noreply.github.com> * Run cargo fmt to fix formatting issues Co-authored-by: mmagician <8402446+mmagician@users.noreply.github.com> * Rename asset_value back to asset in tx_event.rs Co-authored-by: mmagician <8402446+mmagician@users.noreply.github.com> * Run cargo fmt to fix formatting after variable rename Co-authored-by: mmagician <8402446+mmagician@users.noreply.github.com> * chore: simplify fungible_asset merge and add split * chore: remove outdated edge case handling in vault * chore: convert u64s from BE to LE * feat: migrate rpo256 -> poseidon2 * chore: upgrade miden VM & crypto in Cargo.toml * chore: refactor memory.masm from BE to LE * chore: update broken imports in `miden-protocol` * chore: introduce `FromNum` and `TryFromNum` traits * chore: FieldElement import, as_int, and read_many_iter in `protocol` * chore: replace imports in miden-protocol-macros * chore: update imports in miden-standards * chore: update imports in miden-tx * chore: deactivate agglayer in workspace and miden-testing * chore: update imports in miden-testing * chore: migrate account ID and asset-related MASM modules to LE * chore: migrate account, delta and note-related MASM modules to LE * chore: migrate prologue & epilogue to LE * chore: update empty SMT root * chore: migrate tx and api.masm modules to LE * chore: reverse tx stack inputs and outputs * chore: migrate miden::protocol from BE to LE * chore: swap order of link map operands on adv stack * chore: swap prefix/suffix in asset vault test * chore: use more resilient way to write test_transaction_epilogue * chore: migrate tests in miden-testing from BE to LE * chore: update schema type IDs for falcon512_poseidon2::pub_key * chore: migrate miden::standards::{access, auth} from BE to LE * chore: migrate miden::standards::faucets from BE to LE * chore: migrate miden::standards::data_structures from BE to LE * chore: migrate miden::standards::notes from BE to LE * chore: migrate miden::standards::{wallets, attachments} from BE to LE * fix: mmr authentication path for latest block * chore: use falcon512_poseidon2::encode_signature for sig prep * chore: update protocol library docs * chore: remove @BigEndian from type defs * fix: avoid using undeclared stack * feat: reexport asset::mem_load * chore: migrate singlesig_acl from be to le * fix: multisig stack layouts after migration * chore: update Cargo.lock and regenerate kernel proc hashes * chore: cargo update to latest crypto and VM patch releases * fix: unused imports after miden-field upgrade * chore: use `Felt::{from, try_from}` and remove `(Try)FromNum` * chore: remove temp mmr fix and adapt find_half_key_value call * chore: regenerate kernel proc hashes * chore: make toml * chore: add changelog entry * fix: outdated doc links * fix: make format * chore: deactivate miden-agglayer in check-features.sh * chore: allow bincode in deny.toml * fix: deny.toml entries and upgrade toml to 1.0 * fix: transaction context builder doc test * feat: Add `Asset::as_elements` * chore: use `asset::mem_load` in swap note * fix: update agglayer::bridge::bridge_out to new asset layout * chore: remove `Ord for Asset` impl * chore: re-add error when asset-to-be-removed is not present * chore: remove whitespace in docs * chore: `cargo update` * chore: use seed digest 2 and 3 as account ID suffix and prefix * chore: remove `AccountId::try_from<[Felt; 2]>`; add `try_from_elements` * chore: consistently use `RATE0, RATE1, CAPACITY` for hasher state * chore: optimize account_id::validate for little-endian order * chore: optimize account ID seed validation * chore: reverse tx summary stack layout * feat: optimize `account::is_slot_id_lt` for little-endian layout * chore: regenerate kernel proc hashes * chore: add changelog * chore: rename `Falcon512Rpo` to `Falcon512Poseidon2` * chore: rename `_ADDRESS` to `_PTR` in swap * chore: rename `asset::{mem_store, mem_load}` to store/load * chore: use imports instead of absolute paths * chore: reword non-fungible asset key collision resistance * chore: add `AssetId::is_empty` * chore: remove unnecessary imports in `AssetVault` * chore: spell out asset key layout in has_non_fungible_asset * chore: validate account ID type in AssetVaultKey::new * fix: typo in Asset docs * chore: prefix `get/into_faucet_id` with `key` * chore: prefix `get/into_asset_id` with `key` * chore: suffix `is_(non_)fungible_asset` with `key` * chore: address review comments * chore: update outdated tx kernel input/output docs * chore: LE layout for upcoming fpi account ID; fix stack comments * chore: replace `type Word` with built-in `word` * fix: asset::mem_store using `_be` instructions * chore: update `add_input_notes` docs * chore: Add `fungible_asset::to_amount` * chore: replace `swap.3` with `movdn.3` * chore: rename `get_balance_from_fungible_asset` to `value_into_amount` * chore: use more explicit `poseidon2::copy_digest` * chore: ensure asset vault key validity for fungible keys --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: mmagician <8402446+mmagician@users.noreply.github.com> Co-authored-by: Bobbin Threadbare <bobbinth@protonmail.com> * fix: remove unused MASM imports (#2543) * feat: Enable warnings_as_errors in assembler * fix: remove unused imports in miden-protocol * fix: remove unused imports in miden-standards * fix: enable `miden-crypto/std` in `testing` feature to fix MSRV check (#2544) The migration to miden-crypto 0.22 replaced `winter_rand_utils::rand_value` with `miden_crypto::rand::test_utils::rand_value`, which is gated behind `#[cfg(any(test, feature = "std"))]`. Since the workspace dependency uses `default-features = false`, crates depending on `miden-protocol/testing` without `std` failed to compile, breaking the release-dry-run MSRV check. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: enable CI on merge queue trigger (#2540) * chore: enable CI on merge queue trigger * Update .github/workflows/build-docs.yml Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * feat: enable warning as errors for `CodeBuilder` (#2558) * refactor: enforce defining supported types on metadata (#2554) * feat: enforce maximum serialized size for output notes (#2205) * feat: introduce `AccountIdKey` (#2495) * Introducing a dedicated AccountIdKey type to unify and centralize all AccountId * changelog for introduce AccountIdKey type * refactor: clean up comments and improve code readability in AccountIdKey and tests * refactor: update AccountIdKey conversion method and clean up imports * refactor: reorganize AccountIdKey indices and clean up related code * Apply suggestions from code review * Update crates/miden-protocol/src/block/account_tree/account_id_key.rs * Update crates/miden-protocol/src/block/account_tree/account_id_key.rs * feat: validate account ID in `AccountTree::new` --------- Co-authored-by: Bobbin Threadbare <43513081+bobbinth@users.noreply.github.com> Co-authored-by: Philipp Gackstatter <PhilippGackstatter@users.noreply.github.com> * fix: make format and remove unused import (#2564) * feat: make `NoteMetadataHeader` public (#2561) * chore: ProvenBlock constructor with validation (#2553) * refactor: include fee in TransactionId computation * refactor: remove `Ord` and `PartialOrd` from `StorageSlot` (#2549) * chore: Replace SMT leaf conversion function (#2271) * feat: add ability to submit user batches for the MockChain (#2565) * chore: Explicitly use `get_native_account_active_storage_slots_ptr` in `account::set_item` and `account::set_map_item` * chore: fix typos * feat: integrate PSM contracts to AuthMultisig (#2527) * chore: remove `ProvenTransactionBuilder` in favor of `ProvenTransaction::new` (#2567) * feat: implement `Ownable2Step` (#2292) * feat: implement two-step ownership management for account components - Add `ownable2step.masm` to provide two-step ownership functionality, requiring explicit acceptance of ownership transfers. - Create Rust module for `Ownable2Step` struct to manage ownership state and storage layout. - Introduce error handling for ownership operations in `standards.rs`. - Develop comprehensive tests for ownership transfer, acceptance, and renouncement scenarios in `ownable2step.rs`. * feat: add Ownable2Step account component for two-step ownership transfer * fix: correct ownership word layout and update transfer ownership logic in ownable2step * Refactor ownership management to use Ownable2Step pattern - Updated the access control mechanism to implement a two-step ownership transfer pattern in the ownable2step module. - Modified the storage layout to accommodate the new ownership structure, reversing the order of owner and nominated owner fields. - Refactored the network fungible faucet to utilize the new Ownable2Step for ownership management. - Adjusted related tests to validate the new ownership transfer logic and ensure correct behavior when transferring and accepting ownership. - Updated error handling to reflect changes in ownership management, including new error messages for ownership transfer scenarios. * refactor: update ownership word layout and adjust related logic in Ownable2Step * refactor: rename owner and nominated_owner to get_owner and get_nominated_owner for consistency * refactor: streamline ownership management in tests by utilizing Ownable2Step according to philip * fix: `make format` --------- Co-authored-by: Bobbin Threadbare <43513081+bobbinth@users.noreply.github.com> Co-authored-by: Philipp Gackstatter <PhilippGackstatter@users.noreply.github.com> * feat: prefix account components with miden::standards namespace (#2400) * refactor: rename output note structs (#2569) * feat: add `create_fungible_key` proc for fungible asset vault keys (#2575) * feat(asm): add create_fungible_key proc for fungible asset vault keys * chore: re-export create_fungible_key from `protocol::asset` * chore: add changelog --------- Co-authored-by: Philipp Gackstatter <PhilippGackstatter@users.noreply.github.com> * feat: migrate miden-agglayer to VM 0.21 and crypto 0.22 (#2546) * feat: migrate miden-agglayer to VM 0.21 and crypto 0.22 Re-enable the miden-agglayer crate in the workspace and apply all necessary API and convention changes from the VM 0.21 migration: Rust changes: - Remove FieldElement trait imports (removed from miden-core) - Felt::as_int() -> Felt::as_canonical_u64() - Program import moved to miden_core::program::Program - bytes_to_packed_u32_felts -> bytes_to_packed_u32_elements (miden_core::utils) - AccountId::try_from([Felt;2]) -> AccountId::try_from_elements(suffix, prefix) - Serializable/Deserializable from miden_assembly::serde (was ::utils) MASM changes: - rpo256 -> poseidon2 (hash function migration) - asset::mem_store/mem_load -> asset::store/load (procedure renames) - u64::overflowing_mul -> u64::widening_mul - u128_sub_no_underflow rewritten for LE convention (u64 procedures now use [lo, hi] input/output order) - verify_u128_to_native_amount_conversion updated for LE result order Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: migrate agglayer tests and fix LE convention issues Re-enable agglayer tests in miden-testing and fix all compilation and runtime errors from the VM 0.21 migration: Test compilation fixes: - FieldElement import removal, as_int -> as_canonical_u64 - AuthScheme::Falcon512Rpo -> Falcon512Poseidon2 - AdviceInputs path: miden_processor::advice::AdviceInputs - FastProcessor::new_debug -> new().with_advice().with_debugging() - StackInputs::new(vec![]) -> new(&[]) - bytes_to_packed_u32_felts -> bytes_to_packed_u32_elements - AccountId::try_from -> try_from_elements MASM runtime fixes: - eth_address.masm: fix u32split LE output order in build_felt verification (movup.4 -> movup.3 for lo/hi comparison) - bridge_out.masm: fix create_burn_note note_idx corruption - loc_loadw_be overwrites top 4 stack elements including both copies from dup; save note_idx to local instead (pre-existing bug that only manifested with multiple output notes) - bridge_out.masm: fix num_leaves storage LE ordering - push new_leaf_count to stack top for Word[0] storage, use mem_storew_le instead of mem_storew_be for loading - bridge_config.masm: update GER hash from Rpo256 to Poseidon2 - canonical_zeros: remove .rev() from build.rs generation, swap push order for LE memory layout - Word element ordering fixes for bridge admin, GER manager, faucet registry keys, and conversion metadata Test expectation fixes: - Rpo256 -> Poseidon2 for GER hash computation - Removed word reversal in root/proof reading (LE convention) - Fixed expected storage value ordering - mem_storew_be -> mem_storew_le in test MASM code All 39 agglayer tests pass. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style: apply rustfmt formatting Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: address PR review comments - Extract `create_id_key(id: AccountId) -> Word` helper and reuse for bridge_admin, ger_manager, bridge_account_id, and faucet_registry_key - Replace `felts_to_bytes` with re-export of `packed_u32_elements_to_bytes` from miden-core (identical implementation) - Remove stale comment in config_note.rs - Use `Felt::ONE` instead of `Felt::new(1)` in config_bridge test - Add TODO comments referencing issue #2548 for getter helpers - Simplify note_idx handling: use `padw` before `loc_loadw_le` instead of saving to a local variable; revert @Locals(15) to @Locals(14) - Switch `loc_storew_be`/`loc_loadw_be` to `_le` variants in create_burn_note - Add stack state comments before third overflowing_sub in u128_sub_no_underflow Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: padding is 15 * remove utils re-export, faucet_registry_key * chore: drop 4 words * fix: pad before call invoc.; explicit stack comments * fix: unnecesary dup and incorrect movup * style: codify multi-element naming convention for MASM stack comments Add convention to masm_doc_comment_fmt.md: - `value` = single felt - `value(N)` = N felts (non-word) - `VALUE` = word (4 felts, no (4) suffix needed) Apply throughout agglayer MASM: drop redundant (4) from ASSET_KEY, ASSET_VALUE, SERIAL_NUM, SCRIPT_ROOT, RECIPIENT, NOTE_ATTACHMENT, PROC_MAST_ROOT, OLD_VALUE, VALUE, U256_LO, U256_HI. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: update comments on poseidon inputs * Update crates/miden-protocol/masm_doc_comment_fmt.md Co-authored-by: Philipp Gackstatter <PhilippGackstatter@users.noreply.github.com> * chore: test for high x3 * fix: correctly rearrange lo/hi for sub * chore: more explicit sub comments * chore: revert changes to faucet config slot * chore: revert prefix<>suffix storage ordering * fix: store flag values at word[0] for LE convention Change GER_KNOWN_FLAG and IS_FAUCET_REGISTERED_FLAG to be stored at word[0] instead of word[3]. Under LE, `push.0.0.0.FLAG` puts FLAG at stack top = word[0], matching the documented layout [1, 0, 0, 0]. Previously `push.FLAG.0.0.0` placed FLAG at stack bottom = word[3], resulting in Word([0, 0, 0, 1]) which contradicted the docs. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: use felts directly instead of extracting u64 * chore: fix build * Apply suggestion from @PhilippGackstatter Co-authored-by: Philipp Gackstatter <PhilippGackstatter@users.noreply.github.com> * chore: use AccountIdKey * refactor: push explicit zeros, dont assume padding is 0 * refactor: simplify assert_faucet_registered by asserting first * chore: re-add miden-agglayer to feature checks Now that miden-agglayer has been migrated, add it back to the check-features.sh loop. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: explicit pointers for storing prefix/suffix * refactor: swap to_account_id output to LE convention [suffix, prefix] All other bridge/faucet MASM procedures use [suffix, prefix] (suffix on top) for account IDs on the stack. Align to_account_id with this convention by adding a swap at the end, and update all callers: - faucet/mod.masm: update get_destination_account_id_data, claim, and build_p2id_output_note to handle the new stack order - solidity_miden_address_conversion test: swap stack index expectations Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude (Opus) <noreply@anthropic.com> Co-authored-by: Bobbin Threadbare <43513081+bobbinth@users.noreply.github.com> Co-authored-by: Philipp Gackstatter <PhilippGackstatter@users.noreply.github.com> Co-authored-by: Bobbin Threadbare <bobbinth@protonmail.com> * feat: make `Ownable2Step` an `AccountComponent` (#2572) * feat: add `from_parts_unchecked()` method for `InputNoteCommitment` (#2588) * feat: support bool types on schemas (#2591) * fix: move recompute logic to `OutputNoteBuilder::add_asset` to avoid repeated hashing (#2577) * fix: move recompute logic to OutputNoteBuilder::add_asset to avoid rehashing * changelog and lint * chore: add `NoteAssets::into_vec` --------- Co-authored-by: Bobbin Threadbare <43513081+bobbinth@users.noreply.github.com> Co-authored-by: Philipp Gackstatter <PhilippGackstatter@users.noreply.github.com> * feat: expose `AccountComponentMetadata` through public method (#2596) * fix: `TokenSymbol::try_from(Felt)` underflow (#2568) * feat: implement asset callback support in tx kernel (#2571) * feat: implement `on_before_asset_added_to_note` callback (#2595) * feat: add support for callbacks in non-fungible assets * chore: add test for block list note callback * feat: implement `on_before_asset_added_to_note` callback * chore: add test to make sure inputs are received correctly * chore: deduplicate test setup code * chore: add changelog * fix: non-fungible asset delta not including asset metadata * chore: deduplicate callback invocation procedures * chore: deduplicate blocked account test * chore: deduplicate note callback test * chore: test empty callback proc root slot * chore: Introduce `end_foreign_callback_context` * feat: validate asset metadata (#2609) * feat: flexible Minting Policies for Token Standards (#2559) * refactor: move storage schema component into a separate file (#2603) * feat: add `Package` support in `MockChainBuilder` & `NoteScript` (#2502) * feat: introduce `SwapNoteStorage` (#2592) * feat: add callback docs (#2604) * feat: add hooks to enable TX debugging (#2574) * Migrate to Miden VM `v0.22.0-alpha.1` (#2625) * refactor: update RandomCoin, use position of LeafIndex * chore: update deny file * chore: use version from crates.io, update deny file * fix: swap FAUCET_ID_SUFFIX/PREFIX constants in CONFIG_AGG_BRIDGE The constants FAUCET_ID_SUFFIX and FAUCET_ID_PREFIX were swapped relative to the actual storage layout in config_note.rs (suffix at index 5, prefix at index 6). This caused register_faucet to store with a mismatched key, so lookups returned empty. Also fix the misleading doc comment in config_note.rs. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * lint * fix: use Ownable2Step and OwnerControlled components for agglayer faucet The NetworkFungibleFaucet's mint_and_send now requires Ownable2Step for ownership verification and OwnerControlled for mint policy management. Migrate the agglayer faucet from a custom owner_config storage slot to these standard components: - Remove bridge_account_id from AggLayerFaucet (ownership is now handled by the Ownable2Step component) - Add Ownable2Step and OwnerControlled as companion components in the faucet account builder - Use Ownable2Step::try_from_storage to extract the owner in bridge_account_id() helper Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: include Ownable2Step and OwnerControlled in faucet code commitment The build.rs computes the faucet code commitment for validation checks. After adding Ownable2Step and OwnerControlled as companion components, the code commitment must include their procedures. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: use mem_storew_le for MINT note storage in bridge_in The MINT note script reads storage with mem_loadw_le (LE convention), so the bridge must store the P2ID script root, serial number, and attachment data using mem_storew_le to match. Using mem_storew_be caused the P2ID script root hash to be stored with reversed elements, making it unresolvable by the kernel. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: bridge_in endianness + clippy warnings - Use mem_storew_le for MINT note storage in bridge_in.masm to match the MINT note script's mem_loadw_le reads (VM 0.21 byte-endianness). - Add MintNote::script() to bridge_in TX3 context. - Fix clippy useless_conversion warnings in bridge.rs. - Formatting fix in faucet.rs. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: add `FixedWidthString<N>` utility (#2633) * Update crates/miden-agglayer/src/bridge.rs * chore: fix padding comments * chore: add owner controlled slot names to faucet --------- Co-authored-by: Philipp Gackstatter <PhilippGackstatter@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Bobbin Threadbare <bobbinth@protonmail.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: igamigo <ignacio.amigo@lambdaclass.com> Co-authored-by: Forostovec <ilonaforostovec22@gmail.com> Co-authored-by: Nikhil Patil <nikhil876706@gmail.com> Co-authored-by: Bobbin Threadbare <43513081+bobbinth@users.noreply.github.com> Co-authored-by: Santiago Pittella <87827390+SantiagoPittella@users.noreply.github.com> Co-authored-by: Serge Radinovich <47865535+sergerad@users.noreply.github.com> Co-authored-by: Percy Dikec <112529374+PercyDikec@users.noreply.github.com> Co-authored-by: onurinanc <e191322@metu.edu.tr> Co-authored-by: Himess <95512809+Himess@users.noreply.github.com> Co-authored-by: Arthur Abeilice <afa7789@gmail.com> Co-authored-by: Poulav <bpoulav@gmail.com> Co-authored-by: juan518munoz <62400508+juan518munoz@users.noreply.github.com> Co-authored-by: Tomas Fabrizio Orsi <tomas.orsi@lambdaclass.com> Co-authored-by: djole <djolertrk@gmail.com> Co-authored-by: Andrey Khmuro <andrey@polygon.technology>
In this PR, we integrate the PSM contracts into
AuthMultisig.In addition, two missing procedures for
AuthMultisigare added:pub proc set_procedure_thresholdproc assert_proc_thresholds_lte_num_approversThis PR will close the following issues: