Skip to content

feat: enforce maximum serialized size for output notes#2205

Merged
bobbinth merged 36 commits into0xMiden:nextfrom
Forostovec:feature/output-note-size-limit
Mar 6, 2026
Merged

feat: enforce maximum serialized size for output notes#2205
bobbinth merged 36 commits into0xMiden:nextfrom
Forostovec:feature/output-note-size-limit

Conversation

@Forostovec
Copy link
Copy Markdown
Contributor

@Forostovec Forostovec commented Dec 20, 2025

Introduced NOTE_MAX_SIZE (32 KiB) and enforce it on individual output notes. The limit is applied based on the Serializable::get_size_hint implementation for notes and their components.

  • Added NOTE_MAX_SIZE constant for serialized note size
  • Implemented get_size_hint for Note, PartialNote, and related note types
  • Implemented get_size_hint for OutputNote and OutputNotes
  • Reject output notes larger than NOTE_MAX_SIZE in OutputNotes::new using
    TransactionOutputError::OutputNoteSizeLimitExceeded { note_id, note_size }
  • Added test to ensure OutputNote::get_size_hint matches serialized length
  • Added a regression test that constructs an oversized note and asserts that OutputNotes::new returns OutputNoteSizeLimitExceeded

fixes #2195

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces a maximum serialized size limit of 32 KiB for individual output notes to prevent excessively large notes from being created. The limit is enforced during OutputNotes construction and applies to all output note variants (Full, Partial, and Header).

  • Added NOTE_MAX_SIZE constant (32 KiB) and integrated it into validation logic
  • Implemented get_size_hint methods throughout the note hierarchy to enable size calculation
  • Added validation in OutputNotes::new to reject oversized notes with a descriptive error

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated no comments.

Show a summary per file
File Description
crates/miden-protocol/src/constants.rs Defines NOTE_MAX_SIZE constant (32 KiB)
crates/miden-protocol/src/errors/mod.rs Adds OutputNoteSizeLimitExceeded error variant with note ID and size details
crates/miden-protocol/src/transaction/outputs.rs Implements size validation in OutputNotes::new, adds get_size_hint for OutputNote and OutputNotes, and includes comprehensive tests
crates/miden-protocol/src/note/mod.rs Implements get_size_hint for Note based on metadata and details sizes
crates/miden-protocol/src/note/partial.rs Implements get_size_hint for PartialNote using metadata, recipient digest, and assets sizes
crates/miden-protocol/src/note/header.rs Implements get_size_hint for NoteHeader summing note ID and metadata sizes
crates/miden-protocol/src/note/metadata.rs Implements get_size_hint for NoteMetadata returning Word::SERIALIZED_SIZE
crates/miden-protocol/src/note/details.rs Implements get_size_hint for NoteDetails summing assets and recipient sizes
crates/miden-protocol/src/note/assets.rs Implements get_size_hint for NoteAssets accounting for count prefix and individual asset sizes
crates/miden-protocol/src/note/inputs.rs Implements get_size_hint for NoteInputs accounting for length prefix and individual input sizes
crates/miden-protocol/src/note/recipient.rs Implements get_size_hint for NoteRecipient summing script, inputs, and serial number sizes
crates/miden-protocol/src/note/script.rs Implements get_size_hint for NoteScript summing MAST and entrypoint sizes
crates/miden-protocol/src/note/note_id.rs Implements get_size_hint for NoteId returning Word::SERIALIZED_SIZE
crates/miden-protocol/src/note/nullifier.rs Implements get_size_hint for Nullifier returning Word::SERIALIZED_SIZE

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@bobbinth
Copy link
Copy Markdown
Contributor

@Forostovec - thank you for working on this! Seems like some tests are not passing. Could you make the CI green?

@Forostovec
Copy link
Copy Markdown
Contributor Author

Introduced NOTE_MAX_SIZE (32 KiB) and enforce it on individual output notes

Made changes

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.

Great work! Looks good!

I left a few small comments. Once these are addressed, I think this is good to merge.

Comment on lines +407 to +412
let mock_note = Note::mock_noop(Word::empty());
let output_note = OutputNote::Full(mock_note);

let bytes = output_note.to_bytes();

assert_eq!(bytes.len(), output_note.get_size_hint());
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 think we should construct a more complex note to make sure everything is correctly covered. The note returned by mock_noop is a bit too simple, I think. E.g. I would add at least two assets and at least two note inputs.

Comment on lines +204 to +209
fn get_size_hint(&self) -> usize {
let u16_size = 0u16.get_size_hint();
let notes_size: usize = self.notes.iter().map(|note| note.get_size_hint()).sum();

u16_size + notes_size
}
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 think we don't technically need this method for the purpose of this issue, right? If so, I'd remove it. Especially since it isn't covered by a test.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think we don't technically need this method for the purpose of this issue, right? If so, I'd remove it. Especially since it isn't covered by a test.

Did it!

Forostovec and others added 3 commits December 29, 2025 19:51
Co-authored-by: Philipp Gackstatter <PhilippGackstatter@users.noreply.github.com>
Co-authored-by: Philipp Gackstatter <PhilippGackstatter@users.noreply.github.com>
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.

One thing that I'm still trying to think through is at what level do we want to enforce this limit. The approach taken in this PR is to enforce this at OutputNotes level - but there are other options:

  • Enforce it at Note level. This would imply that we'd need to make Note::new() fallible - which is annoying. And I'm also not 100% sure this is the right approach.
  • Enforce it at OutputNote level. This would probably be ideal because this would also imply that the limit is automatically imposed for OutputNoteBatch as well, but since OutputNote is an enum, I'm not sure how to do this.

Also, I wonder if we should impose a separate limit on NoteScript. And maybe an alternative approach could be to impose limits on individual note components - though, this has its own downsides.

// Size of the serialized inputs length prefix.
let u16_size = 0u16.get_size_hint();

let values_size: usize = self.values.iter().map(|value| value.get_size_hint()).sum();
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.

Why do we need to iterate over inputs to compute the size? Each input is just one Felt and so we should be able to just multiple the number of inputs by felt size, right?

@PhilippGackstatter
Copy link
Copy Markdown
Contributor

Good points, thank you!

Also, I wonder if we should impose a separate limit on NoteScript.

Having a limit on the note script may allow omitting any checks at the output note level, since then all note components are size limited, e.g:

  • note attachments: 16 KiB
  • note assets: ~8 KiB
  • note inputs: 8 KiB

If we now impose a note script limit of, say, 16 KiB, then the maximum possible note size is 48 KiB. This is higher than the 32 KiB we want to impose as an overall limit here, but this could be okay because fees will already disincentivize constructing needlessly large notes.

Not for this PR, but one separate question is whether we will be able to impose a note script limit in the tx kernel (and similarly for account code). I believe we can't really get the script size from within the VM, so it seems to me we can't enforce this at that level, short of hashing the entire script of a note in the VM.

So, if we simply add validation to NoteScript::new, then this would solve the issue fairly nicely, I think.

The alternatives are:

  • Make OutputNoteBatch a proper struct and enforce the size limit same as in OutputNotes::new.
  • Rename OutputNote to OutputNoteKind and introduce struct OutputNote(OutputNoteKind) for the sole purpose of enforcing the size limit.

Out of these, the second one is annoying but probably cleanest to enforce the limit only in one place.

Overall, I would prefer the NoteScript::new approach. We can start with 16 KiB and if that's too low we can always increase it. The main downside is that the max output note size is fairly large, but with fees this should not really occur in practice.

@bobbinth
Copy link
Copy Markdown
Contributor

Overall, I would prefer the NoteScript::new approach. We can start with 16 KiB and if that's too low we can always increase it. The main downside is that the max output note size is fairly large, but with fees this should not really occur in practice.

I agree with this. Though, I wonder if we should start with 32KB - mostly because the way we serialize MAST code now is not super efficient (and I'm actually not sure if we strip out all the decorator info). There are a few additional considerations here:

  1. I'm curious how big P2ID note script is for hand-written vs. compiler-generated code. Just want to sanity-check the 32 KB limit.
  2. We've had discussions about putting not just the note script, but also note constructor code into the note package. When we build a note script from note package, we'll probably need to prune all constructor-related code to make sure we don't serialize some irrelevant code. Doing this if the programs are static is not difficult - but if there are any dynamic calls, this task would be non-trivial.

Also cc @greenhat and @huitseeker as some of this is relevant to the topics you are working on.

@greenhat
Copy link
Copy Markdown
Collaborator

Overall, I would prefer the NoteScript::new approach. We can start with 16 KiB and if that's too low we can always increase it. The main downside is that the max output note size is fairly large, but with fees this should not really occur in practice.

I agree with this. Though, I wonder if we should start with 32KB - mostly because the way we serialize MAST code now is not super efficient (and I'm actually not sure if we strip out all the decorator info). There are a few additional considerations here:

  1. I'm curious how big P2ID note script is for hand-written vs. compiler-generated code. Just want to sanity-check the 32 KB limit.

If I serialize the MastForest of the P2ID note it's 72480 bytes. I think we discussed before, how we want to strip the debug decorators, but I don't know if it was implemented. The ~70KB is the size without stripping.

  1. We've had discussions about putting not just the note script, but also note constructor code into the note package. When we build a note script from note package, we'll probably need to prune all constructor-related code to make sure we don't serialize some irrelevant code. Doing this if the programs are static is not difficult - but if there are any dynamic calls, this task would be non-trivial.

FWIW, we don't use dynexec. But I don't think I fully understand how the static code removal would work. Pinging @bitwalker

@bobbinth
Copy link
Copy Markdown
Contributor

If I serialize the MastForest of the P2ID note it's 72480 bytes. I think we discussed before, how we want to strip the debug decorators, but I don't know if it was implemented. The ~70KB is the size without stripping.

Oh wow - that's pretty large. For comparison:

  • Hand-written P2ID note is about 5 KB with decorators.
  • Without decorators it is 1.4 KB.
  • I think with better serialization we should be able to get it to be under 500 bytes.

Not directly related to this issue, but would be good to understand where the 70KB size is coming from:

  • Assuming the same decorator/code ratio as for hand-written note, the version w/o decorators should be under 20KB.
  • It may also be possible that compiler-generated code inserts more decorators - would be good to double check this.
  • Beyond decorators, I wonder if there are some low-hanging fruit for optimizing the code that could get the size down.

@greenhat - could you create an issue in the compiler repo to investigate these?

For this issue, I think we should definitely set the NoteScript size limit at 32KB or above. But this does bring us back what's the right place to set this limit at. Specifically, we frequently serialize NoteScript with decorators to disc for debug purposes - which should be fine. We want to strip decorators and compact the code at the time when we send it over the wire (i.e., to the node). Maybe the best place for doing this is at the time when we create an OutputNote? But there could be other options too.

@bitwalker
Copy link
Copy Markdown
Collaborator

FWIW, we don't use dynexec. But I don't think I fully understand how the static code removal would work. Pinging @bitwalker

Essentially we'd be doing link-time dead code elimination, but starting by explicitly marking certain procedures as dead (e.g. the note script constructor).

I'm not sure dynexec/dyncall cause any particular problems here though - we can know statically whether there are any references to a procedure (dynamic or static) within the same package. So if we were going to try and strip out the note script constructor (for example), then any reference to that procedure would need to raise an error (or simply not remove that procedure and its callees, it sort of depends on how strict we want to be about it).

It may also be possible that compiler-generated code inserts more decorators - would be good to double check this.

It definitely does, such as the traces for call frame tracking. It also has to include procedures for anything from libcore/liballoc that are referenced and used.

We may also need to play with optimization levels and such to see if that makes any meaningful difference.

Now that we have some practical example programs, we should do some statistical analysis of the MASM to determine where the bulk of the instruction count comes from. My suspicion is that it is primarily stack management operations, but if we see a lot of common sequences, we could look into factoring those out into intrinsic procedures to collapse those sequences.

I'm sure there is fat to be trimmed, but we are also inherently fighting a bit of an uphill battle here, because we are effectively emulating Wasm on Miden, so there will always be some impedance mismatch that results in more code than would otherwise be necessary. Luckily, there aren't too many places where that mismatch is particularly noticeable, but its still something we have to take into account.

@greenhat
Copy link
Copy Markdown
Collaborator

greenhat commented Jan 1, 2026

Not directly related to this issue, but would be good to understand where the 70KB size is coming from:

  • Assuming the same decorator/code ratio as for hand-written note, the version w/o decorators should be under 20KB.
  • It may also be possible that compiler-generated code inserts more decorators - would be good to double check this.
  • Beyond decorators, I wonder if there are some low-hanging fruit for optimizing the code that could get the size down.

@greenhat - could you create an issue in the compiler repo to investigate these?

0xMiden/compiler#852

@PhilippGackstatter
Copy link
Copy Markdown
Contributor

But this does bring us back what's the right place to set this limit at. Specifically, we frequently serialize NoteScript with decorators to disc for debug purposes - which should be fine.

I think there could be two points in time where enforcing the size limit could make sense:

  • At the time we build an ExecutedTransaction. This could be done at the OutputNote or OutputNotes level as you suggested.
  • At the time we build a ProvenTransaction. This would be done in ProvenTransactionBuilder::build, which would be the same time at which we currently enforce AccountUpdateDetails size restrictions.

As @bobbinth mentioned before, we also want to make sure that these limits continue to be enforced at the batch and block level:

  • Since the limit of AccountUpdateDetails is enforced in TxAccountUpdate and not as part of the type itself, we don't have that limit enforced in batch or block currently. The simplest solution is probably to enforce this at the BatchAccountUpdate and BlockAccountUpdate level. It would be possible to do it at the AccountUpdateDetails level, but since is is an enum, this is a bit harder to do. Also, this allows us to have different account update limits for transactions and batches/blocks. (This would be a separate PR, but wanted to mention it here for completeness).
  • For notes, I think the same logic applies.
    • If we enforce size limits in OutputNote, then this also applies to batch and block. Enforcing it in OutputNotes only applies at the transaction level and changing batches or blocks to use it isn't really easily possible, because we have to retain the batch note index.
      • This looks pretty clean, but could be problematic for testing. For instance, say we want to test a flow where two transactions A and B build on top of each other. A creates a compiler-generated (and therefore large) OutputNote and B consumes that note. When the note is created as part of A, this fails the transaction because it exceeds the limit, which would be annoying. At least that's how I understand this would work, but if that's correct, I think we should avoid that situation.
    • If we enforce the size limit in ProvenTransaction, then we'd also have to separately enforce it in batch and block.
      • This seems less desirable because of code duplication, and also unnecessary, because we want the same note size limit across tx, batch and block, from what I can tell.
      • This avoids the testing problems of the other approach, at least for executing transactions, but not for proving transactions. My assumption is that this could be fine as proving a transaction means preparing it for submission to the network. Perhaps it ends up making sense to strip decorators (from both note scripts and account code) when building a ProvenTransaction if some option on the prover is set (which could address Note should not include debug decorators info #1812).

So, maybe we should enforce the output note size limits separately to not make testing harder at the cost of enforcing the limit in a few places. This would be somewhere in:

  • ProvenTransactionBuilder::build
  • ProvenBatch::new
  • In ProvenBlock or BlockBody or OutputNoteBatch.

I'm hoping there's a better solution still, but I don't see it.

@bobbinth
Copy link
Copy Markdown
Contributor

bobbinth commented Jan 4, 2026

The more I think about this, the more it seems like this and #1812 are related and should be tackled together. Specifically:

  • We don't really care about Note size. We may want to execute notes locally in debug mode, and so note scripts there should have decorators which would blow up the size significantly.
  • What we do want to prevent is ProvenTransaction, ProvenBatch, and BlockBody having notes that are too large and notes there should be stripped of decorators (and other debug info). Thankfully, these all represent notes as OutputNote structs.

This makes me think that: we want to enforce note size limits for OutputNote and also, whenever we construct OutputNote::Full variant, we need to make sure that we strip all decorators from the NoteScript of the underlying note. Basically, we don't just want to compute note size, we want to compute note size for a note with all debug info removed.

The only annoying thing here is that OutputNote is an enum - here, we could probably use a newtype (e.g., FullOutputNote) to wrap the Note when it is added to the OutputNote::Full variant. Then, in the constructor of FullOutputNote we could strip the decorators off the note script and enforce the size on the resulting note.

Another way to do this is to rethink the OutputNote enum. The main thing I don't like about it is that it is not really "type-safe":

  1. It has Partial variant that is never supposed to end up in ProvenTransaction, ProvenBatch, or BlockBody.
  2. The Full variant could contain private notes.

Both of these are adjusted via the OutputNote::shrink() method, but this kind of suggest that were are maybe trying to do too much with the OutputNote and maybe we should have two separate structs for it. For example, maybe OutputNote::shrink() should return some other type and this would be the type that gets stored in ProvenTransaction, ProvenBatch, or BlockBody.

@Forostovec
Copy link
Copy Markdown
Contributor Author

Looks good! Thank you! I left a couple of comments inline.

One thing that I'm still trying to think through is at what level do we want to enforce this limit. The approach taken in this PR is to enforce this at OutputNotes level - but there are other options:

  • Enforce it at Note level. This would imply that we'd need to make Note::new() fallible - which is annoying. And I'm also not 100% sure this is the right approach.
  • Enforce it at OutputNote level. This would probably be ideal because this would also imply that the limit is automatically imposed for OutputNoteBatch as well, but since OutputNote is an enum, I'm not sure how to do this.

Also, I wonder if we should impose a separate limit on NoteScript. And maybe an alternative approach could be to impose limits on individual note components - though, this has its own downsides.

Hi , sorry for the delay. Todo, simplification of noteinputs size hint was done, and fixed failling ci because of duplicate fungible asset

@PhilippGackstatter
Copy link
Copy Markdown
Contributor

For example, maybe OutputNote::shrink() should return some other type and this would be the type that gets stored in ProvenTransaction, ProvenBatch, or BlockBody.

I like this approach as it is more type safe, achieves the note size enforcement and the removal of decorators.

One small challenge is that we'll want to enforce the constraints from OutputNotes in both ExecutedTransaction and ProvenTransaction, but if we introduce separate types, we'll need to let OutputNotes abstract over them. This could overall look like this:

// Previously OutputNote
pub enum UnprovenOutputNote {
    Full(Note),
    Partial(PartialNote),
    Header(NoteHeader),
}

// New type that is created during proving from `UnprovenOutputNote::shrink` (which may be worth
// renaming at this point)
pub enum ProvenOutputNote {
    Public(PublicOutputNote),
    Header(NoteHeader),
}

// Thin wrapper around `Note` that enforces that the contained Note is public, does not contain
// decorators and has a maximum size.
pub struct PublicOutputNote(Note);

// Used in ExecutedTransaction
pub type UnprovenOutputNotes = OutputNotes<UnprovenOutputNote>;
// Used in ProvenTransaction
pub type ProvenOutputNotes = OutputNotes<ProvenOutputNote>;

// Structurally unchanged; generic N parameter added.
pub struct OutputNotes<N> {
    notes: Vec<N>,
    commitment: Word,
}

// Unless we introduce an OutputNote trait, which seems unnecessary, we'll need to abstract
// over Unproven and Proven output notes like this:
impl<N> OutputNotes<N>
where
    for<'a> NoteHeader: From<&'a N>,
    for<'a> NoteId: From<&'a N>,
{ ... }

I'm not sure about the Unproven/Proven naming. An alternative may be RawOutputNote and OutputNote.

Structurally this should work pretty nicely. During output note "shrinking", any public note needs to be converted into a PublicOutputNote which strips decorators and enforces the max size limit.

This gives us the chance to either:

  • enforce an overall note size limit in PublicOutputNote
  • or a note script limit, which, together with the other limits, will imply a max note size.

Since we have the ability to enforce a max note size explicitly, this seems slightly better, but not a strong opinion.

@bobbinth
Copy link
Copy Markdown
Contributor

bobbinth commented Jan 5, 2026

I really like how this is shaping up. @Forostovec would you be able to take a stab at this new approach?

A few comments:

I'm not sure about the Unproven/Proven naming. An alternative may be RawOutputNote and OutputNote.

Yeah, I don't love any of these (maybe the "raw" approach is a bit better than "proven"/"unproven" approach) - but also don't have a better suggestion yet.

This gives us the chance to either:

  • enforce an overall note size limit in PublicOutputNote
  • or a note script limit, which, together with the other limits, will imply a max note size.

I'd go with the first option - i.e., enforce the limit at PublicOutputNote creation.

// Unless we introduce an OutputNote trait, which seems unnecessary, we'll need to abstract
// over Unproven and Proven output notes like this:
impl<N> OutputNotes<N>
where
    for<'a> NoteHeader: From<&'a N>,
    for<'a> NoteId: From<&'a N>,
{ ... }

I wonder if we could use the same approach for InputNotes as well (currently, we have to use ToInputNoteCommitments trait because of a similar reason).

@Forostovec
Copy link
Copy Markdown
Contributor Author

Forostovec commented Jan 5, 2026

I really like how this is shaping up. @Forostovec would you be able to take a stab at this new approach?

A few comments:

I'm not sure about the Unproven/Proven naming. An alternative may be RawOutputNote and OutputNote.

Yeah, I don't love any of these (maybe the "raw" approach is a bit better than "proven"/"unproven" approach) - but also don't have a better suggestion yet.

This gives us the chance to either:

  • enforce an overall note size limit in PublicOutputNote
  • or a note script limit, which, together with the other limits, will imply a max note size.

I'd go with the first option - i.e., enforce the limit at PublicOutputNote creation.

// Unless we introduce an OutputNote trait, which seems unnecessary, we'll need to abstract
// over Unproven and Proven output notes like this:
impl<N> OutputNotes<N>
where
    for<'a> NoteHeader: From<&'a N>,
    for<'a> NoteId: From<&'a N>,
{ ... }

I wonder if we could use the same approach for InputNotes as well (currently, we have to use ToInputNoteCommitments trait because of a similar reason).

Yeah, I'm gonna get into this tomorrow morning as soon as possible (now it's 01:26 on my time sorry)

@Forostovec
Copy link
Copy Markdown
Contributor Author

I really like how this is shaping up. @Forostovec would you be able to take a stab at this new approach?

A few comments:

I'm not sure about the Unproven/Proven naming. An alternative may be RawOutputNote and OutputNote.

Yeah, I don't love any of these (maybe the "raw" approach is a bit better than "proven"/"unproven" approach) - but also don't have a better suggestion yet.

This gives us the chance to either:

  • enforce an overall note size limit in PublicOutputNote
  • or a note script limit, which, together with the other limits, will imply a max note size.

I'd go with the first option - i.e., enforce the limit at PublicOutputNote creation.

// Unless we introduce an OutputNote trait, which seems unnecessary, we'll need to abstract
// over Unproven and Proven output notes like this:
impl<N> OutputNotes<N>
where
    for<'a> NoteHeader: From<&'a N>,
    for<'a> NoteId: From<&'a N>,
{ ... }

I wonder if we could use the same approach for InputNotes as well (currently, we have to use ToInputNoteCommitments trait because of a similar reason).

I hope I didn't miss anything - refactored OutputNote into RawOutputNote / ProvenOutputNote with PublicOutputNote enforcing NOTE_MAX_SIZE at shrink-time, updated tx/batch/block types, and in miden-testing MockChain keep full genesis notes so tests can still create InputNotes.

Comment on lines +442 to +447
pub enum ProvenOutputNote {
/// A public note with full details, size-validated.
Public(PublicOutputNote),
/// A note header (for private notes or notes without full details).
Header(NoteHeader),
}
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.

For a separate PR, I think the Header variant can be renamed to Private, since this should only ever be constructed from private notes. To disallow construction of a ProvenOutputNote::Header with a NoteHeader whose metadata is public, maybe this should be a PrivateNoteHeader newtype that enforces this, so:

pub enum ProvenOutputNote {
    /// A public note with full details, size-validated.
    Public(PublicOutputNote),
    /// A note header (for private notes or notes without full details).
    Private(PrivateNoteHeader),
}

Next to this, I think OutputNote::Partial and OutputNote::Header should also only ever be constructed with metadata that is private, so I think we should consider enforcing private metadata in PartialNote and also use PrivateNoteHeader in OutputNote::Header. Or in other words, only OutputNote::Full can contain both public and private notes. cc @bobbinth in case I'm saying something wrong.

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.

Agreed - this structure would enforce all relevant invariants for both public and private output notes.

Public(PublicOutputNote),
/// A note header (for private notes or notes without full details).
Header(NoteHeader),
}
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.

Generally, this PR looks great - the only thing I'm not sure about is the naming. Specifically, ProvenOutputNote sounds a bit off since the note itself is not proven. What it is is a more "final form" of the output note. I don't have great suggestions here, FinalizedOutputNote is an option, but I'm not sure it is any better.

Another option is to rename the current OutputNote into something like RawOutputNote and then ProvenOutputNote could be renamed into just OutputNote.

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.

Another option is to rename the current OutputNote into something like RawOutputNote and then ProvenOutputNote could be renamed into just OutputNote.

I think that's probably the best option I can think of.

I would suggest merging this PR and do the rename in a small follow-up PR to make reviewing that easier. We could also rename ProvenOutputNote::Header to Private in that PR, and I can open an issue for the remaining things mentioned above.

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! As mentioned in the comments, I think we should rename some types/variants - but I agree with @PhilippGackstatter that we can do this in a follow up PR. So, I'll merge this PR as is.

@PhilippGackstatter - could you create an issue with the remaining follow-up items?

Comment on lines +210 to +220
/// Output notes produced during transaction execution (before proving).
///
/// Contains [`OutputNote`] instances which represent notes as they exist immediately after
/// transaction execution.
pub type OutputNotes = RawOutputNotes<OutputNote>;

/// Output notes in a proven transaction.
///
/// Contains [`ProvenOutputNote`] instances which have been processed for inclusion in
/// proven transactions, with size limits enforced on public notes.
pub type ProvenOutputNotes = RawOutputNotes<ProvenOutputNote>;
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.

In a follow-up PR, this would switch as:

  • OutputNotes -> RawOutputNotes.
  • ProvenOutputNotes -> OutputNotes.

This means that we'll need to come up with a new name for the current RawOutputNotes type, though I don't have great suggestions for this yet.

@bobbinth bobbinth merged commit 17ff028 into 0xMiden:next Mar 6, 2026
15 checks passed
@PhilippGackstatter
Copy link
Copy Markdown
Contributor

@Forostovec Would you be interested in doing a small follow-up PR that renames OutputNote to RawOutputNote and ProvenOutputNote to OutputNote? We can also rename what is currently ProvenOutputNote::Header to ProvenOutputNote::Private.

afa7789 pushed a commit to afa7789/miden-base that referenced this pull request Mar 9, 2026
afa7789 pushed a commit to afa7789/miden-base that referenced this pull request Mar 9, 2026
afa7789 pushed a commit to afa7789/miden-base that referenced this pull request Mar 9, 2026
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

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Restrict maximum OutputNote size

7 participants