Skip to content

Ownable 2 step feature#2292

Merged
PhilippGackstatter merged 13 commits into0xMiden:nextfrom
afa7789:openzeppelin/ownable_2_step
Mar 9, 2026
Merged

Ownable 2 step feature#2292
PhilippGackstatter merged 13 commits into0xMiden:nextfrom
afa7789:openzeppelin/ownable_2_step

Conversation

@afa7789
Copy link
Copy Markdown
Contributor

@afa7789 afa7789 commented Jan 16, 2026

We want Ownable2Step to override Ownable, as the former is the more secure version.

The core issue with Ownable is that the transferOwnership function immediately sets the new owner in a single transaction. This creates a critical vulnerability:

If you make a typo or pass a wrong address, ownership is permanently lost. There's no recovery mechanism — the contract is effectively locked forever with no owner able to call privileged functions.

How Ownable2Step fixes this
Ownable2Step introduces a two-phase handoff:

Step 1 — Nominate: The current owner calls transferOwnership(newAddress), which only sets a pendingOwner. The current owner remains in control.
Step 2 — Accept: The pendingOwner must call acceptOwnership() from their own address to complete the transfer.

See here:
#2488
#2486

@afa7789 afa7789 changed the title Creation of module "Ownable 2 step" in masm. Add "Ownable 2 step" module Jan 16, 2026
@afa7789 afa7789 force-pushed the openzeppelin/ownable_2_step branch 2 times, most recently from 650e63c to 5754cfa Compare January 19, 2026 16:02
@mmagician mmagician added the pr-from-maintainers PRs that come from internal contributors or integration partners. They should be given priority label Feb 9, 2026
@afa7789 afa7789 force-pushed the openzeppelin/ownable_2_step branch from 7d2ac3e to 397aa5c Compare February 23, 2026 13:18
@afa7789 afa7789 marked this pull request as ready for review February 23, 2026 21:23
@afa7789 afa7789 requested a review from onurinanc February 23, 2026 21:23
Copy link
Copy Markdown
Collaborator

@onurinanc onurinanc left a comment

Choose a reason for hiding this comment

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

Thank you @afa7789! Looks good! I left a few comments, mostly about locals usage, and a couple of suggestions to improve the comments.

@onurinanc
Copy link
Copy Markdown
Collaborator

onurinanc commented Feb 24, 2026

It might be something like this:

# miden::standards::access::ownable
#
# Provides two-step ownership management functionality for account components.
# This module can be imported and used by any component that needs owner controls.
#
# Unlike a single-step ownership transfer, this module requires the new owner to explicitly
# accept the transfer before it takes effect. This prevents accidental transfers to incorrect
# addresses, which would permanently lock the component.
#
# The transfer flow is:
#   1. The current owner calls `transfer_ownership` to nominate a new owner.
#   2. The nominated account calls `accept_ownership` to complete the transfer.
#   3. Optionally, the current owner can call `transfer_ownership` with their own address
#      to cancel the pending transfer.
#
# Storage layout (single slot):
#   Rust Word:  [pending_owner_suffix, pending_owner_prefix, owner_suffix, owner_prefix]
#                word[0]          word[1]         word[2]       word[3]
#
#   After get_item (big-endian load), the stack is:
#               [owner_prefix, owner_suffix, pending_owner_prefix, pending_owner_suffix]
#                (word[3])     (word[2])      (word[1])       (word[0])

use miden::protocol::active_account
use miden::protocol::account_id
use miden::protocol::active_note
use miden::protocol::native_account

# CONSTANTS
# ================================================================================================

# ZERO_ADDRESS word (all zeros) used to represent no owner.
const ZERO_ADDRESS = [0, 0, 0, 0]

# The slot in this component's storage layout where the owner configuration is stored.
# Contains both the current owner and the pending owner in a single word.
const OWNER_CONFIG_SLOT = word("miden::standards::access::ownable::owner_config")

# ERRORS
# ================================================================================================

const ERR_SENDER_NOT_OWNER = "note sender is not the owner"
const ERR_SENDER_NOT_PENDING_OWNER = "note sender is not the pending owner"
const ERR_NO_PENDING_OWNER = "no pending ownership transfer exists"

# LOCAL MEMORY ADDRESSES
# ================================================================================================

# transfer_ownership locals
const NEW_OWNER_PREFIX_LOC = 0
const NEW_OWNER_SUFFIX_LOC = 1
const OWNER_PREFIX_LOC = 2
const OWNER_SUFFIX_LOC = 3

# accept_ownership locals
const PENDING_OWNER_PREFIX_LOC = 0
const PENDING_OWNER_SUFFIX_LOC = 1
const SENDER_PREFIX_LOC = 2
const SENDER_SUFFIX_LOC = 3

# INTERNAL PROCEDURES
# ================================================================================================

#! Returns the full ownership word from storage.
#!
#! Inputs:  []
#! Outputs: [owner_prefix, owner_suffix, pending_owner_prefix, pending_owner_suffix]
#!
#! Where:
#! - owner_prefix and owner_suffix are the prefix and suffix felts of the current owner AccountId.
#! - pending_owner_prefix and pending_owner_suffix are the prefix and suffix felts of the pending owner AccountId.
proc _ownership_word
    push.OWNER_CONFIG_SLOT[0..2] exec.active_account::get_item
    # => [owner_prefix, owner_suffix, pending_owner_prefix, pending_owner_suffix]
end

#! Returns the owner AccountId from storage.
#!
#! Inputs:  []
#! Outputs: [owner_prefix, owner_suffix]
#!
#! Where:
#! - owner_prefix and owner_suffix are the prefix and suffix felts of the owner AccountId.
proc _owner
    exec._ownership_word
    # => [owner_prefix, owner_suffix, pending_owner_prefix, pending_owner_suffix]

    # Drop the pending owner fields
    movup.2 drop
    # => [owner_prefix, owner_suffix, pending_owner_suffix]

    movup.2 drop
    # => [owner_prefix, owner_suffix]
end

#! Returns the pending owner AccountId from storage.
#!
#! Inputs:  []
#! Outputs: [pending_owner_prefix, pending_owner_suffix]
#!
#! Where:
#! - pending_owner_prefix and pending_owner_suffix are the prefix and suffix felts of the pending owner AccountId.
proc _pending_owner
    exec._ownership_word
    # => [owner_prefix, owner_suffix, pending_owner_prefix, pending_owner_suffix]

    drop drop
    # => [pending_owner_prefix, pending_owner_suffix]
end

#! Checks if the given account ID is the owner of this component.
#!
#! Inputs:  [account_id_prefix, account_id_suffix]
#! Outputs: [is_owner]
#!
#! Where:
#! - is_owner is 1 if the account is the owner, 0 otherwise.
proc _is_owner
    exec._owner
    # => [owner_prefix, owner_suffix, account_id_prefix, account_id_suffix]

    exec.account_id::is_equal
    # => [is_owner]
end

#! Checks if the given account ID is the pending owner of this component.
#!
#! Inputs:  [account_id_prefix, account_id_suffix]
#! Outputs: [is_pending_owner]
#!
#! Where:
#! - account_id_prefix and account_id_suffix are the prefix and suffix felts of the AccountId to check.
#! - is_pending_owner is 1 if the account is the pending owner, 0 otherwise.
proc _is_pending_owner
    exec._pending_owner
    # => [pending_owner_prefix, pending_owner_suffix, account_id_prefix, account_id_suffix]

    exec.account_id::is_equal
    # => [is_pending_owner]
end

# PUBLIC INTERFACE
# ================================================================================================

#! Checks if the note sender is the owner and panics if not.
#!
#! Inputs:  []
#! Outputs: []
#!
#! Panics if:
#! - the note sender is not the owner.
pub proc verify_owner
    exec.active_note::get_sender
    # => [sender_prefix, sender_suffix]

    exec._is_owner
    # => [is_owner]

    assert.err=ERR_SENDER_NOT_OWNER
    # => []
end

#! Returns the owner AccountId.
#!
#! Inputs:  [pad(16)]
#! Outputs: [owner_prefix, owner_suffix, pad(14)]
#!
#! Where:
#! - owner_prefix and owner_suffix are the prefix and suffix felts of the owner AccountId.
#!
#! Invocation: call
pub proc owner
    exec._owner
    # => [owner_prefix, owner_suffix, pad(16)]

    # _owner introduces a net +2 stack delta, for call-friendly ABI shape we keep 16 outputs.
    movup.2 drop movup.2 drop
    # => [owner_prefix, owner_suffix, pad(14)]
end

#! Returns the pending owner AccountId.
#!
#! Inputs:  [pad(16)]
#! Outputs: [pending_owner_prefix, pending_owner_suffix, pad(14)]
#!
#! Where:
#! - pending_owner_prefix and pending_owner_suffix are the prefix and suffix felts of the pending owner AccountId.
#!   Both are zero if no transfer is pending.
#!
#! Invocation: call
pub proc pending_owner
    exec._pending_owner
    # => [pending_owner_prefix, pending_owner_suffix, pad(16)]

    # _pending_owner introduces a net +2 stack delta, for call-friendly ABI shape we keep 16 outputs.
    movup.2 drop movup.2 drop
    # => [pending_owner_prefix, pending_owner_suffix, pad(14)]
end

#! Initiates a two-step ownership transfer by setting the pending owner.
#!
#! The current owner remains in control until the pending owner calls `accept_ownership`.
#! Can only be called by the current owner.
#!
#! If the new owner is the current owner, any pending transfer is cancelled and the
#! pending owner field is cleared.
#!
#! Inputs:  [new_owner_prefix, new_owner_suffix, pad(14)]
#! Outputs: [pad(16)]
#!
#! Panics if:
#! - the note sender is not the owner.
#!
#! Locals:
#! 0: new_owner_prefix
#! 1: new_owner_suffix
#! 2: owner_prefix
#! 3: owner_suffix
#!
#! Invocation: call
@locals(4)
pub proc transfer_ownership
    exec.verify_owner
    # => [new_owner_prefix, new_owner_suffix, pad(14)]

    # Persist new owner to avoid deep-stack duplication.
    loc_store.NEW_OWNER_PREFIX_LOC
    # => [new_owner_suffix, pad(14)]

    loc_store.NEW_OWNER_SUFFIX_LOC
    # => [pad(14)]

    # Retrieve and persist the current owner.
    exec._owner
    # => [owner_prefix, owner_suffix, pad(14)]

    loc_store.OWNER_PREFIX_LOC
    # => [owner_suffix, pad(14)]

    loc_store.OWNER_SUFFIX_LOC
    # => [pad(14)]

    # Check if new_owner == owner (cancel case).
    loc_load.NEW_OWNER_SUFFIX_LOC loc_load.NEW_OWNER_PREFIX_LOC
    # => [new_owner_prefix, new_owner_suffix, pad(14)]

    loc_load.OWNER_SUFFIX_LOC loc_load.OWNER_PREFIX_LOC
    # => [owner_prefix, owner_suffix, new_owner_prefix, new_owner_suffix, pad(14)]

    exec.account_id::is_equal
    # => [is_self_transfer, pad(14)]

    if.true
        # Cancel ownership transfer and clear pending owner.
        loc_load.OWNER_SUFFIX_LOC loc_load.OWNER_PREFIX_LOC
        # => [owner_prefix, owner_suffix, pad(14)]

        push.0.0
        # => [0, 0, owner_prefix, owner_suffix, pad(14)]

        movup.3 movup.3
        # => [owner_prefix, owner_suffix, 0, 0, pad(14)]
    else
        # Transfer ownership by setting pending = new_owner.
        loc_load.NEW_OWNER_SUFFIX_LOC loc_load.NEW_OWNER_PREFIX_LOC
        # => [new_owner_prefix, new_owner_suffix, pad(14)]

        loc_load.OWNER_SUFFIX_LOC loc_load.OWNER_PREFIX_LOC
        # => [owner_prefix, owner_suffix, new_owner_prefix, new_owner_suffix, pad(14)]
    end

    push.OWNER_CONFIG_SLOT[0..2]
    # => [slot_prefix, slot_suffix, owner_prefix, owner_suffix, pending_owner_prefix, pending_owner_suffix, pad(14)]

    exec.native_account::set_item
    # => [OLD_OWNERSHIP_WORD, pad(14)]

    dropw
    # => [pad(16)]
end

#! Accepts a pending ownership transfer. The pending owner becomes the new owner
#! and the pending owner field is cleared.
#!
#! Can only be called by the pending owner.
#!
#! Inputs:  [pad(16)]
#! Outputs: [pad(16)]
#!
#! Panics if:
#! - there is no pending ownership transfer (pending owner is zero).
#! - the note sender is not the pending owner.
#!
#! Locals:
#! 0: pending_owner_prefix
#! 1: pending_owner_suffix
#! 2: sender_prefix
#! 3: sender_suffix
#!
#! Invocation: call
@locals(4)
pub proc accept_ownership
    exec.active_note::get_sender
    # => [sender_prefix, sender_suffix, pad(16)]

    loc_store.SENDER_PREFIX_LOC
    # => [sender_suffix, pad(16)]

    loc_store.SENDER_SUFFIX_LOC
    # => [pad(16)]

    exec._ownership_word
    # => [owner_prefix, owner_suffix, pending_owner_prefix, pending_owner_suffix, pad(16)]

    # Drop owner and persist pending owner.
    drop drop
    # => [pending_owner_prefix, pending_owner_suffix, pad(16)]

    loc_store.PENDING_OWNER_PREFIX_LOC
    # => [pending_owner_suffix, pad(16)]

    loc_store.PENDING_OWNER_SUFFIX_LOC
    # => [pad(16)]

    # Check that a pending transfer exists (pending owner is not zero).
    loc_load.PENDING_OWNER_SUFFIX_LOC
    loc_load.PENDING_OWNER_PREFIX_LOC
    # => [pending_owner_prefix, pending_owner_suffix, pad(16)]

    push.0.0
    # => [0, 0, pending_owner_prefix, pending_owner_suffix, pad(16)]

    exec.account_id::is_equal
    # => [is_zero, pad(16)]

    assertz.err=ERR_NO_PENDING_OWNER
    # => [pad(16)]

    # Verify sender == pending owner without another storage read.
    loc_load.SENDER_SUFFIX_LOC
    loc_load.SENDER_PREFIX_LOC
    loc_load.PENDING_OWNER_SUFFIX_LOC
    loc_load.PENDING_OWNER_PREFIX_LOC
    # => [pending_owner_prefix, pending_owner_suffix, sender_prefix, sender_suffix, pad(16)]

    exec.account_id::is_equal
    # => [is_pending, pad(16)]

    assert.err=ERR_SENDER_NOT_PENDING_OWNER
    # => [pad(16)]

    # Build new ownership word: pending becomes owner, clear pending.
    # Target: [new_owner_prefix, new_owner_suffix, 0, 0] where new_owner = pending.
    loc_load.PENDING_OWNER_SUFFIX_LOC
    loc_load.PENDING_OWNER_PREFIX_LOC
    # => [pending_owner_prefix, pending_owner_suffix, pad(16)]

    push.0.0
    # => [0, 0, pending_owner_prefix, pending_owner_suffix, pad(16)]

    movup.3 movup.3
    # => [pending_owner_prefix, pending_owner_suffix, 0, 0, pad(16)]

    push.OWNER_CONFIG_SLOT[0..2]
    # => [slot_prefix, slot_suffix, pending_owner_prefix, pending_owner_suffix, 0, 0, pad(16)]

    exec.native_account::set_item
    # => [OLD_OWNERSHIP_WORD, pad(16)]

    dropw
    # => [pad(16)]
end

#! Renounces ownership, leaving the component without an owner.
#!
#! Can only be called by the current owner. Clears both the owner and any pending owner.
#!
#! Inputs:  [pad(16)]
#! Outputs: [pad(16)]
#!
#! Panics if:
#! - the note sender is not the owner.
#!
#! Invocation: call
#!
#! Important Note!
#! This feature allows the owner to relinquish administrative privileges, a common pattern
#! after an initial stage with centralized administration is over. Once ownership is renounced,
#! the component becomes permanently ownerless and cannot be managed by any account.
pub proc renounce_ownership
    exec.verify_owner
    # => [pad(16)]

    # Set ownership word to all zeros, clearing both owner and pending.
    push.ZERO_ADDRESS
    # => [0, 0, 0, 0, pad(16)]

    push.OWNER_CONFIG_SLOT[0..2]
    # => [slot_prefix, slot_suffix, 0, 0, 0, 0, pad(16)]

    exec.native_account::set_item
    # => [OLD_OWNERSHIP_WORD, pad(16)]

    dropw
    # => [pad(16)]
end

Thanks for adding this!

@onurinanc
Copy link
Copy Markdown
Collaborator

@afa7789 Could you rename ownable.masm to ownable2step.masm as Ownable2Step is a naming convention for the well-known access control mechanism.

@onurinanc
Copy link
Copy Markdown
Collaborator

Hi @PhilippGackstatter, @bobbinth, and @mmagician Ownable2Step related to this issue is ready for review! #2497

@onurinanc
Copy link
Copy Markdown
Collaborator

Let's consider this comment by @PhilippGackstatter #2486 (comment) and add an ID validation using account_id::validate

Copy link
Copy Markdown
Contributor

@bobbinth bobbinth left a comment

Choose a reason for hiding this comment

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

Looks great! Thank you! I've reviewed mostly non-test code so far and left some comments inline (most of them are small nits related to procedure naming convention).

Copy link
Copy Markdown
Contributor

@bobbinth bobbinth left a 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! The MASM code is basically done - the main comment is about the Rust code.

Also, would be good for @PhilippGackstatter to also take a look as I mostly focused on non-test code.

Copy link
Copy Markdown
Contributor

@bobbinth bobbinth left a 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! I left a couple of comments inline - but these are just about naming.

Would be good to get a review from @PhilippGackstatter as well.

Overall, it may make sense to merge this PR after #2512 is merged.

@afa7789
Copy link
Copy Markdown
Contributor Author

afa7789 commented Mar 2, 2026

Overall, it may make sense to merge this PR after #2512 is merged.

Great!

Copy link
Copy Markdown
Contributor

@PhilippGackstatter PhilippGackstatter left a comment

Choose a reason for hiding this comment

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

Looks good to me!

I left a few comments, but nothing too important (maybe except for exec/call verify_owner).

Will take a look at the rest asap.

Copy link
Copy Markdown
Contributor

@PhilippGackstatter PhilippGackstatter left a comment

Choose a reason for hiding this comment

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

Looks good to me! Thanks for the comprehensive test coverage!

I left a few comments. The important one is regarding AccountId::new_unchecked and owner potentially being renounced. The other comments are mostly about reducing the size of the test module, feel free to skip that.

@afa7789
Copy link
Copy Markdown
Contributor Author

afa7789 commented Mar 4, 2026

@PhilippGackstatter
@bobbinth
@onurinanc
should I resolve conflicts and we can merge this ?

@onurinanc
Copy link
Copy Markdown
Collaborator

@afa7789 I think #2512 is merged and this should be ready to merge after resolving the conflicts, I'm not sure if anything left from @PhilippGackstatter

@PhilippGackstatter
Copy link
Copy Markdown
Contributor

PhilippGackstatter commented Mar 4, 2026

@afa7789 Yes, would be great to resolve conflicts. Note that we have merged the VM migration PR (#2508) that affects MASM semantics (word layouts are reversed compare to before), so I think we should review the PR again after conflicts were resolved. See that PR's migration section for some help during the migration of ownable.masm.

@afa7789 afa7789 force-pushed the openzeppelin/ownable_2_step branch 2 times, most recently from f0d56c7 to 09baed8 Compare March 4, 2026 16:54
@onurinanc
Copy link
Copy Markdown
Collaborator

@PhilippGackstatter Please review!

@afa7789 afa7789 force-pushed the openzeppelin/ownable_2_step branch from 09baed8 to f438ee2 Compare March 5, 2026 14:17
Copy link
Copy Markdown
Contributor

@bobbinth bobbinth left a comment

Choose a reason for hiding this comment

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

@afa7789 - seems like a bunch of files got included in this PR by mistake. Could you lean up the changeset?

afa7789 added 3 commits March 6, 2026 08:42
- 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`.
@afa7789 afa7789 force-pushed the openzeppelin/ownable_2_step branch from 49e1cb2 to aa38409 Compare March 6, 2026 11:43
- 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.
Copy link
Copy Markdown
Contributor

@PhilippGackstatter PhilippGackstatter left a comment

Choose a reason for hiding this comment

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

I may be missing something, but when I look at the first commit in this PR, its parent is the commit from PR #2441. It would be good to update this PR to latest next to include the changes from #2508 (which changed word layouts in MASM) because otherwise we'll need to re-review that part again later.

I left some comments thinking the state was already updated to include #2508, but I'll leave these comments since they'll be relevant when updating.

For future PRs, it would be nice to avoid force pushing (after reviews have started) as that breaks GitHub's "what has changed since your last review" and so makes reviewing harder. Thank you 🙏!

Comment on lines +16 to +22
# Storage layout (single slot):
# Rust Word: [nominated_owner_suffix, nominated_owner_prefix, owner_suffix, owner_prefix]
# word[0] word[1] word[2] word[3]
#
# After get_item (reversed onto stack), the stack is:
# [owner_prefix, owner_suffix, nominated_owner_prefix, nominated_owner_suffix]
# (word[3]) (word[2]) (word[1]) (word[0])
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The second part should no longer be true after the recent VM migration, so I'd remove it.

#! Returns the full ownership word from storage.
#!
#! Inputs: []
#! Outputs: [owner_prefix, owner_suffix, nominated_owner_prefix, nominated_owner_suffix]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
#! Outputs: [owner_prefix, owner_suffix, nominated_owner_prefix, nominated_owner_suffix]
#! Outputs: [owner_suffix, owner_prefix, nominated_owner_suffix, nominated_owner_prefix]

After the migration to the new VM version (#2508) the word layouts and "half-word" (e.g. account IDs, slot IDs) layouts have been reversed. So we should now return these in suffix, prefix order.

Comment on lines +27 to +32
/// After `get_item` (which reverses the word onto the MASM stack), the stack is:
///
/// ```text
/// Stack: [owner_prefix, owner_suffix, nominated_owner_prefix, nominated_owner_suffix]
/// (word[3]) (word[2]) (word[1]) (word[0])
/// ```
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'd remove this as well from here. This is low-level information one the one hand and now also no longer necessary.

afa7789 and others added 4 commits March 6, 2026 11:15
…e_2_step

# Conflicts:
#	crates/miden-standards/asm/standards/faucets/network_fungible.masm
#	crates/miden-standards/src/account/faucets/network_fungible.rs
#	crates/miden-standards/src/errors/standards.rs
#	crates/miden-testing/tests/scripts/faucet.rs
Copy link
Copy Markdown
Contributor

@bobbinth bobbinth left a 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! I left a few small comments inline.

Copy link
Copy Markdown
Contributor

@PhilippGackstatter PhilippGackstatter left a comment

Choose a reason for hiding this comment

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

Looks great!

Thank you for refactoring the tests, they are much easier to follow now!

Opened #2570 as a potential follow-up.

#! Invocation: call
pub proc get_owner
exec.get_owner_internal
# => [owner_suffix, owner_prefix, pad(16)]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Just a thought: It might eventually be useful to have a get_initial_owner that an auth procedure can query to check if the owner has changed (get_initial_owner != get_owner) to apply certain auth logic, but we can add this when it's actually needed.

Comment on lines +56 to +62
let ownership_word: Word = [
owner.suffix(), // word[0] = owner_suffix
owner.prefix().as_felt(), // word[1] = owner_prefix
Felt::new(0), // word[2] = nominated_suffix
Felt::new(0), // word[3] = nominated_prefix
]
.into();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nit: Can we instantiate a Ownable2Step here and call to_word to avoid duplicating the word construction?

Comment on lines +78 to +98
fn get_owner_from_storage(account: &Account) -> anyhow::Result<Option<AccountId>> {
let word = account.storage().get_item(&OWNER_CONFIG_SLOT_NAME)?;
let suffix = word[0];
let prefix = word[1];
if suffix == Felt::new(0) && prefix == Felt::new(0) {
Ok(None)
} else {
Ok(Some(AccountId::try_from_elements(suffix, prefix)?))
}
}

fn get_nominated_owner_from_storage(account: &Account) -> anyhow::Result<Option<AccountId>> {
let word = account.storage().get_item(&OWNER_CONFIG_SLOT_NAME)?;
let suffix = word[2];
let prefix = word[3];
if suffix == Felt::new(0) && prefix == Felt::new(0) {
Ok(None)
} else {
Ok(Some(AccountId::try_from_elements(suffix, prefix)?))
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nit: These would also be nice to replace with Ownable2Step::try_from_storage(account.storage())? and then returning ownable.owner(), etc.

@PhilippGackstatter PhilippGackstatter enabled auto-merge (squash) March 9, 2026 12:04
@PhilippGackstatter PhilippGackstatter merged commit 698fa6f into 0xMiden:next Mar 9, 2026
15 checks passed
afa7789 added a commit to afa7789/miden-base that referenced this pull request Mar 9, 2026
* 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>
afa7789 added a commit to afa7789/miden-base that referenced this pull request Mar 9, 2026
* 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>
afa7789 added a commit to afa7789/miden-base that referenced this pull request Mar 9, 2026
* 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>
mmagician added a commit that referenced this pull request Mar 19, 2026
* 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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr-from-maintainers PRs that come from internal contributors or integration partners. They should be given priority

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants