Skip to content
Draft
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 @@ -8,6 +8,7 @@
- [BREAKING] Allowed account components to share identical account code procedures ([#2164](https://github.com/0xMiden/miden-base/pull/2164)).
- Add `From<&ExecutedTransaction> for TransactionHeader` implementation ([#2178](https://github.com/0xMiden/miden-base/pull/2178)).
- Add `AccountId::parse()` helper function to parse both hex and bech32 formats ([#2223](https://github.com/0xMiden/miden-base/pull/2223)).
- Added `note::build_note_tag_for_local_account` procedure ([#2235](https://github.com/0xMiden/miden-base/pull/2235)).

### Changes

Expand Down
59 changes: 59 additions & 0 deletions crates/miden-protocol/asm/protocol/note.masm
Original file line number Diff line number Diff line change
Expand Up @@ -252,3 +252,62 @@ pub proc build_note_tag_for_network_account
drop
# => [network_account_tag]
end

#! Constructs a LocalAny note tag from the given account_id.
#!
#! This procedure implements the same logic as the Rust NoteTag::from_local_account_id()
#! but assumes tag_len to be 14 bits.
#!
#! The tag is constructed as follows:
#! - The two most significant bits are set to `0b11` to indicate a LOCAL_ANY tag.
#! - The next 14 bits are set to the most significant bits of the account ID prefix.
#! - The remaining bits are set to zero.
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think it makes sense to build on the NoteTag format from next that will be outdated as soon as #2219 is merged, since that PR completely refactors the current format. This PR should rather build on top of #2219.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Makes sense! What is the timeline to merge #2219?

Once 2219 is merged I will update this PR

Copy link
Contributor

Choose a reason for hiding this comment

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

Sounds good.

What is the timeline to merge #2219?

Sometime early next week if everything goes well.

#!
#! Inputs: [account_id_prefix, account_id_suffix]
#! Outputs: [local_account_tag]
#!
#! Invocation: exec
pub proc build_note_tag_for_local_account
Copy link
Contributor

Choose a reason for hiding this comment

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

As for the structure/naming, maybe it makes sense to introduce a note_tag.masm module and name the procedure new_account_target so we can use this as note_tag::new_account_target, and for completeness it seems it would make sense to have a new_custom_account_target as well (I think with_ would also work for consistency).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good idea!

# => [account_id_prefix, account_id_suffix]

# Drop the suffix as we only need the prefix
swap drop
# => [account_id_prefix]

# Convert prefix to u64 by splitting into high and low parts
u32split
# => [prefix_hi, prefix_lo]

# Shift the high bits of the account ID such that they are laid out as:
# [34 zero bits | remaining high bits (30 bits)]
push.2
# => [2, prefix_hi, prefix_lo]

exec.u64::shr
# => [shifted_hi, shifted_lo]

# This is equivalent to the following layout, interpreted as a u32:
# [2 zero bits | remaining high bits (30 bits)]
swap drop
# => [high_bits]

# Select the top 14 bits of the account ID, i.e.:
# [2 zero bits | remaining high bits (14 bits) | (30 - 14) zero bits]
# Create mask: u32::MAX << (32 - 2 - 14) = u32::MAX << 16
push.4294967295
push.16
# => [16, u32::MAX, high_bits]

u32shl
# => [mask, high_bits]

u32and
# => [masked_high_bits]

# Set the local execution tag in the two most significant bits
push.3221225472 # 0xc0000000 = LOCAL_ANY
# => [LOCAL_ANY, masked_high_bits]

u32or
# => [local_account_tag]
end
37 changes: 37 additions & 0 deletions crates/miden-testing/src/kernel_tests/tx/test_note.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ use miden_protocol::note::{
};
use miden_protocol::testing::account_id::{
ACCOUNT_ID_NETWORK_FUNGIBLE_FAUCET,
ACCOUNT_ID_PRIVATE_SENDER,
ACCOUNT_ID_REGULAR_PRIVATE_ACCOUNT_UPDATABLE_CODE,
ACCOUNT_ID_SENDER,
};
Expand Down Expand Up @@ -594,3 +595,39 @@ async fn test_build_note_tag_for_network_account() -> anyhow::Result<()> {

Ok(())
}

#[tokio::test]
async fn test_build_note_tag_for_local_account() -> anyhow::Result<()> {
let tx_context = TransactionContextBuilder::with_existing_mock_account().build()?;

let account_id = AccountId::try_from(ACCOUNT_ID_PRIVATE_SENDER)?;
let expected_tag = NoteTag::from_account_id(account_id).as_u32();

let prefix: u64 = account_id.prefix().into();
let suffix: u64 = account_id.suffix().into();

let code = format!(
"
use miden::core::sys
use miden::protocol::note

begin
push.{suffix}.{prefix}

exec.note::build_note_tag_for_local_account
# => [local_account_tag]

exec.sys::truncate_stack
end
",
suffix = suffix,
prefix = prefix,
);

let exec_output = tx_context.execute_code(&code).await?;
let actual_tag = exec_output.stack[0].as_int();

assert_eq!(expected_tag, actual_tag as u32,);

Ok(())
}