Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

### Changes

- [BREAKING] Replaced `StandardNote::expected_num_storage_items` with `StandardNote::accepts_num_storage_items`, which reports every storage size a note kind accepts instead of a single value that was neither exact nor a bound for MINT and the config notes ([#3810](https://github.com/0xMiden/protocol/pull/3810)).
- Added a check that the guardian public key is not one of the approver public keys ([#3764](https://github.com/0xMiden/protocol/pull/3764)).
- [BREAKING] Updated the Miden VM and crypto crate family to v0.31.0 and `midenc-hir-type` to v0.13.0. Execution proofs now include a format version and compatible VM and PVM verifier roots, and protocol deserialization rejects unversioned proof bytes from earlier releases. Verifier outcomes now report separate VM and precompile security parameters ([#3806](https://github.com/0xMiden/protocol/pull/3806)).
- [BREAKING] Updated the Miden VM and crypto crate family to v0.30.0 and `midenc-hir-type` to v0.12.0. `LocalTransactionProver::new` now takes `miden_prover::Prover`, `CoreLibrary` exposes one merged package, and `TransactionVerifier::verify` now returns `VerificationOutcome` so callers can handle outstanding precompile work ([#3782](https://github.com/0xMiden/protocol/pull/3782)).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,13 @@ impl FaucetMetadataConfigNote {
/// the three string actions use 32 (`[selector, 0, 0, 0, value(28)]`).
pub const MAX_NUM_STORAGE_ITEMS: usize = 4 + STRING_NUM_ELEMENTS;

/// Lower bound on the number of storage items of a FaucetMetadataConfig note.
///
/// `SetMaxSupply` uses this size; no size between it and [`Self::MAX_NUM_STORAGE_ITEMS`]
/// is valid. Keep in sync with `NUM_ITEMS_SET_MAX_SUPPLY` in
/// `faucet_metadata_config.masm`.
pub const MIN_NUM_STORAGE_ITEMS: usize = 2;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't necessarily have to be in this PR, but maybe it makes sense to start representing the number of storage items with a simple enum as we have very distinct variants now? Something like:

enum NumStorageItems {
    // An exact count of storage items.
    Exact(usize),
    // A range of acceptable storage item counts.
    Range { min: usize, max: usize },
}


// PUBLIC ACCESSORS
// --------------------------------------------------------------------------------------------

Expand Down
7 changes: 7 additions & 0 deletions crates/miden-standards/src/note/config/owner_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,13 @@ impl OwnerConfigNote {
/// new_owner_prefix]`), while `AcceptOwnership` / `RenounceOwnership` use 1 (`[selector]`).
pub const MAX_NUM_STORAGE_ITEMS: usize = 3;

/// Lower bound on the number of storage items of an OwnerConfig note.
///
/// `AcceptOwnership` / `RenounceOwnership` use this size; no size between it and
/// [`Self::MAX_NUM_STORAGE_ITEMS`] is valid. Keep in sync with `NUM_ITEMS_*` in
/// `owner_config.masm`.
pub const MIN_NUM_STORAGE_ITEMS: usize = 1;

// PUBLIC ACCESSORS
// --------------------------------------------------------------------------------------------

Expand Down
6 changes: 6 additions & 0 deletions crates/miden-standards/src/note/config/rbac_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,12 @@ impl RbacConfigNote {
/// account_suffix, account_prefix]`), `SetRoleAdmin` uses 3, and `RenounceRole` uses 2.
pub const MAX_NUM_STORAGE_ITEMS: usize = 4;

/// Lower bound on the number of storage items of an RbacConfig note.
///
/// `RenounceRole` uses this size; every size up to [`Self::MAX_NUM_STORAGE_ITEMS`] is used
/// by one of the actions. Keep in sync with `NUM_ITEMS_*` in `rbac_config.masm`.
pub const MIN_NUM_STORAGE_ITEMS: usize = 2;

// PUBLIC ACCESSORS
// --------------------------------------------------------------------------------------------

Expand Down
14 changes: 14 additions & 0 deletions crates/miden-standards/src/note/mint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,20 @@ mod tests {
assert_eq!(target.execution_hint(), NoteExecutionHint::Always);
}

/// The MINT script asserts these sizes itself, so the Rust constants must mirror the ones
/// the script is built with.
#[test]
fn storage_item_counts_match_the_masm_constants() {
// Must stay in sync with `FUNGIBLE_NUM_STORAGE_ITEMS_PRIVATE` /
// `NFT_NUM_STORAGE_ITEMS_PRIVATE` and `FUNGIBLE_MIN_NUM_STORAGE_ITEMS_PUBLIC` /
// `NFT_MIN_NUM_STORAGE_ITEMS_PUBLIC` in asm/standards/notes/mint/.
const MASM_NUM_STORAGE_ITEMS_PRIVATE: usize = 13;
const MASM_MIN_NUM_STORAGE_ITEMS_PUBLIC: usize = 20;

assert_eq!(MintNote::NUM_STORAGE_ITEMS_PRIVATE, MASM_NUM_STORAGE_ITEMS_PRIVATE);
assert_eq!(MintNote::MIN_NUM_STORAGE_ITEMS_PUBLIC, MASM_MIN_NUM_STORAGE_ITEMS_PUBLIC);
}

/// A private faucet is never a network account, so no target is derived for it. The note is
/// still tagged for the faucet and remains consumable by it.
#[test]
Expand Down
149 changes: 125 additions & 24 deletions crates/miden-standards/src/note/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ use alloc::boxed::Box;
use alloc::string::ToString;
use core::error::Error;

use miden_protocol::Felt;
use miden_protocol::account::AccountId;
use miden_protocol::block::BlockNumber;
use miden_protocol::note::{Note, NoteScript, NoteScriptRoot};
use miden_protocol::{Felt, MAX_NOTE_STORAGE_ITEMS};

use self::config::{
AllowlistConfigNote,
Expand Down Expand Up @@ -187,30 +187,57 @@ impl StandardNote {
}
}

/// Returns the expected number of storage items of the active note.
pub fn expected_num_storage_items(&self) -> usize {
/// Returns `true` if `num_storage_items` is a valid number of storage items for this kind of
/// note.
pub fn accepts_num_storage_items(&self, num_storage_items: usize) -> bool {
match self {
Self::P2ID => P2idNote::NUM_STORAGE_ITEMS,
Self::P2IDE => P2ideNote::NUM_STORAGE_ITEMS,
Self::SWAP => SwapNote::NUM_STORAGE_ITEMS,
Self::PSWAP => PswapNote::NUM_STORAGE_ITEMS,
Self::MINT => MintNote::NUM_STORAGE_ITEMS_PRIVATE,
Self::BURN => BurnNote::NUM_STORAGE_ITEMS,
Self::CONSTANT_FEE_POLICY_CONFIG => ConstantFeePolicyConfigNote::NUM_STORAGE_ITEMS,
Self::FAUCET_POLICY_CONFIG => FaucetPolicyConfigNote::NUM_STORAGE_ITEMS,
// FaucetMetadataConfig storage is variable per action; this returns the upper bound.
Self::FAUCET_METADATA_CONFIG => FaucetMetadataConfigNote::MAX_NUM_STORAGE_ITEMS,
Self::MIN_BURN_AMOUNT_CONFIG => MinBurnAmountConfigNote::NUM_STORAGE_ITEMS,
Self::ALLOWLIST_CONFIG => AllowlistConfigNote::NUM_STORAGE_ITEMS,
Self::BLOCKLIST_CONFIG => BlocklistConfigNote::NUM_STORAGE_ITEMS,
Self::PAUSE_CONFIG => PauseConfigNote::NUM_STORAGE_ITEMS,
// OwnerConfig storage is variable per action; this returns the upper bound.
Self::OWNER_CONFIG => OwnerConfigNote::MAX_NUM_STORAGE_ITEMS,
// RbacConfig storage is variable per action; this returns the upper bound.
Self::RBAC_CONFIG => RbacConfigNote::MAX_NUM_STORAGE_ITEMS,
Self::NETWORK_ACCOUNT_CONFIG => NetworkAccountConfigNote::NUM_STORAGE_ITEMS,
Self::FEE_SPONSORSHIP => FeeSponsorshipNote::NUM_STORAGE_ITEMS,
Self::TX_FEE => TxFeeNote::NUM_STORAGE_ITEMS,
Self::P2ID => num_storage_items == P2idNote::NUM_STORAGE_ITEMS,
Self::P2IDE => num_storage_items == P2ideNote::NUM_STORAGE_ITEMS,
Self::SWAP => num_storage_items == SwapNote::NUM_STORAGE_ITEMS,
Self::PSWAP => num_storage_items == PswapNote::NUM_STORAGE_ITEMS,
// A MINT note creating a private output note holds exactly 13 items, while one
// creating a public output note holds at least 20 and grows with the storage of the
// output note recipient.
Self::MINT => {
num_storage_items == MintNote::NUM_STORAGE_ITEMS_PRIVATE
|| (MintNote::MIN_NUM_STORAGE_ITEMS_PUBLIC..=MAX_NOTE_STORAGE_ITEMS)
.contains(&num_storage_items)
},
Self::BURN => num_storage_items == BurnNote::NUM_STORAGE_ITEMS,
Self::CONSTANT_FEE_POLICY_CONFIG => {
num_storage_items == ConstantFeePolicyConfigNote::NUM_STORAGE_ITEMS
},
Self::FAUCET_POLICY_CONFIG => {
num_storage_items == FaucetPolicyConfigNote::NUM_STORAGE_ITEMS
},
// FaucetMetadataConfig storage is variable per action: `SetMaxSupply` uses the
// minimum, the string-setting actions use the maximum.
Self::FAUCET_METADATA_CONFIG => {
num_storage_items == FaucetMetadataConfigNote::MIN_NUM_STORAGE_ITEMS
|| num_storage_items == FaucetMetadataConfigNote::MAX_NUM_STORAGE_ITEMS
},
Self::MIN_BURN_AMOUNT_CONFIG => {
num_storage_items == MinBurnAmountConfigNote::NUM_STORAGE_ITEMS
},
Self::ALLOWLIST_CONFIG => num_storage_items == AllowlistConfigNote::NUM_STORAGE_ITEMS,
Self::BLOCKLIST_CONFIG => num_storage_items == BlocklistConfigNote::NUM_STORAGE_ITEMS,
Self::PAUSE_CONFIG => num_storage_items == PauseConfigNote::NUM_STORAGE_ITEMS,
// OwnerConfig storage is variable per action: `TransferOwnership` uses the maximum,
// `AcceptOwnership` / `RenounceOwnership` the minimum. No size in between is valid.
Self::OWNER_CONFIG => {
num_storage_items == OwnerConfigNote::MIN_NUM_STORAGE_ITEMS
|| num_storage_items == OwnerConfigNote::MAX_NUM_STORAGE_ITEMS
},
// RbacConfig storage is variable per action, and every size between its bounds is
// used by one of them.
Self::RBAC_CONFIG => (RbacConfigNote::MIN_NUM_STORAGE_ITEMS
..=RbacConfigNote::MAX_NUM_STORAGE_ITEMS)
.contains(&num_storage_items),
Self::NETWORK_ACCOUNT_CONFIG => {
num_storage_items == NetworkAccountConfigNote::NUM_STORAGE_ITEMS
},
Self::FEE_SPONSORSHIP => num_storage_items == FeeSponsorshipNote::NUM_STORAGE_ITEMS,
Self::TX_FEE => num_storage_items == TxFeeNote::NUM_STORAGE_ITEMS,
}
}

Expand Down Expand Up @@ -449,3 +476,77 @@ impl Clone for NoteConsumptionStatus {
}
}
}

// TESTS
// ================================================================================================

#[cfg(test)]
mod tests {
use super::*;

/// A MINT note holds exactly 13 items when it creates a private output note, and 20 or more
/// when it creates a public one, so the sizes in between are the only invalid ones below the
/// protocol limit.
#[test]
fn mint_accepts_both_the_private_and_the_public_storage_sizes() {
for num_items in [MintNote::NUM_STORAGE_ITEMS_PRIVATE, 20, 21, MAX_NOTE_STORAGE_ITEMS] {
assert!(
StandardNote::MINT.accepts_num_storage_items(num_items),
"{num_items} items should be accepted"
);
}

for num_items in [0, 12, 14, 19, MAX_NOTE_STORAGE_ITEMS + 1] {
assert!(
!StandardNote::MINT.accepts_num_storage_items(num_items),
"{num_items} items should be rejected"
);
}
}

/// The config notes size their storage per action, and the sizes no action uses must be
/// rejected even when they fall between the bounds.
#[test]
fn config_notes_accept_only_the_sizes_their_actions_use() {
for (note, accepted, rejected) in [
(StandardNote::OWNER_CONFIG, [1, 3].as_slice(), [0, 2, 4].as_slice()),
(StandardNote::RBAC_CONFIG, [2, 3, 4].as_slice(), [0, 1, 5].as_slice()),
(
StandardNote::FAUCET_METADATA_CONFIG,
[2, 32].as_slice(),
[0, 3, 31, 33].as_slice(),
),
] {
for &num_items in accepted {
assert!(
note.accepts_num_storage_items(num_items),
"{} should accept {num_items} items",
note.name()
);
}

for &num_items in rejected {
assert!(
!note.accepts_num_storage_items(num_items),
"{} should reject {num_items} items",
note.name()
);
}
}
}

/// A note of fixed layout accepts its own size and nothing else.
#[test]
fn fixed_size_notes_accept_only_their_exact_size() {
for note in [StandardNote::P2ID, StandardNote::P2IDE, StandardNote::TX_FEE] {
let num_items = match note {
StandardNote::P2ID => P2idNote::NUM_STORAGE_ITEMS,
StandardNote::P2IDE => P2ideNote::NUM_STORAGE_ITEMS,
_ => TxFeeNote::NUM_STORAGE_ITEMS,
};

assert!(note.accepts_num_storage_items(num_items));
assert!(!note.accepts_num_storage_items(num_items + 1));
}
}
}
Loading