Skip to content

Tracking PR for v0.15.0 release#2674

Draft
bobbinth wants to merge 28 commits intomainfrom
next
Draft

Tracking PR for v0.15.0 release#2674
bobbinth wants to merge 28 commits intomainfrom
next

Conversation

@bobbinth
Copy link
Copy Markdown
Contributor

This is a tracking PR for v0.15.0 release.

bobbinth and others added 28 commits March 23, 2026 20:41
* fix(ci): install Rust 1.91 for cargo-msrv compatibility

cargo-msrv 0.19.3 requires rustc 1.91.1 or newer, but the stable
toolchain currently ships 1.90.0. Install the 1.91 toolchain and
set RUSTUP_TOOLCHAIN=1.91 on the cargo-msrv install and MSRV
check steps so the tool can build and run.

* Revert "fix(ci): install Rust 1.91 for cargo-msrv compatibility"

This reverts commit 4485176.

* chore: bump toolchain

* chore: clippy
Add scheme-specific constructors that accept typed public keys instead
of a generic PublicKeyCommitment, preventing mismatches between key type
and authentication scheme:

- falcon512_poseidon2(pub_key): creates component from a Falcon512 key
- ecdsa_k256_keccak(pub_key): creates component from an ECDSA K256 key
- from_public_key(pub_key): creates component from a PublicKey enum

Also add From<ecdsa_k256_keccak::PublicKey> for PublicKeyCommitment for
parity with the existing Falcon512 conversion.
* chore: rename from PSM to Guardian

* fix: prefer state guardian in comments

* fix: rename guardian multisig to guarded multisig

* fix: restore changelog note and apply nightly formatting

* fix: move guardian rename changelog notes to v0.15

* style: clarify guardian masm comments

* fix: comment
…#2736)

* refactor: lowercase note script filenames in miden-agglayer

* docs(agglayer): update SPEC.md note script filenames to lowercase

Agent-Logs-Url: https://github.com/0xMiden/protocol/sessions/4f7eb749-9710-4a36-8caf-486f12ae21d7

Co-authored-by: partylikeits1983 <77119221+partylikeits1983@users.noreply.github.com>

* fix: update SPEC.md to reference .masm

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
* feat(asset): add AssetAmount wrapper type for fungible amounts

Introduce a validated AssetAmount(u64) newtype that encapsulates the
max-amount check (2^63 - 2^31) at construction time.

- Add AssetAmount with MAX constant, new(), as_u64(), From<u8/u16/u32>,
  TryFrom<u64>, Display, Serializable, Deserializable
- Integrate into FungibleAsset: replace raw u64 amount field
- Delegate validation in FungibleAsset::new() to AssetAmount::new()
- Keep FungibleAsset::MAX_AMOUNT as backward-compatible alias
- Keep amount() returning u64 to minimize downstream churn

Closes #2532

* refactor: remove as_u64, delegate add/sub to AssetAmount

Remove the redundant as_u64() accessor since From<AssetAmount> for u64
already provides the same conversion. Replace all call sites with
u64::from().

Add add() and sub() methods to AssetAmount so that FungibleAsset can
delegate arithmetic to the inner type instead of performing it inline.
Add unit tests for both new methods.

* style: fix rustfmt and clippy lints

Expand import list to one-per-line, merge duplicate use super
statements, collapse single-line assert_eq, and suppress
should_implement_trait on AssetAmount::add/sub.

* docs: add changelog entry for AssetAmount wrapper

* refactor: make AssetAmount::MAX a u64 constant, use .into() in tests

* fix: add type annotations for .into() in tests to resolve ambiguity

* refactor: implement Add and Sub traits for AssetAmount

---------

Co-authored-by: Bobbin Threadbare <43513081+bobbinth@users.noreply.github.com>
Co-authored-by: Marti <marti@miden.team>
* feat: add `metadata_into_note_type` procedure to note.masm

Add a procedure to extract the note type from the metadata header
and unit tests for both public and private note types.

* refactor: address PR review — move stack layout to Where docs, consolidate tests with rstest

* chore: add CHANGELOG entry for metadata_into_note_type

* refactor: address review — clarify bit position, add stack layout docs, use NoteType comparison

* refactoring changes

Co-authored-by: Alexander John Lee <77119221+partylikeits1983@users.noreply.github.com>

* docs: link CHANGELOG entry for metadata_into_note_type to PR #2738

---------

Co-authored-by: Alexander John Lee <77119221+partylikeits1983@users.noreply.github.com>
…er build scripts (#2538)

* refactor: avoid unnecessary asm/ tree copy in standards build script

miden-standards: Remove the full asm/ directory copy to OUT_DIR entirely.
This crate never mutates its source tree, so the assembler and error
extractor can read directly from the crate's asm/ directory.

miden-protocol: Replace the bulk copy of the entire asm/ tree with
targeted staging of only the two directories that need modification
(kernels/transaction/ and protocol/). The assembler requires shared
modules to be physically present alongside these source files, but
shared_utils/ and shared_modules/ themselves don't need to be copied.
Event extraction now reads directly from the original source.

Also simplifies copy_dir_recursive (replacing the old copy_directory
with its awkward prefix-stripping API) and removes dead code.

https://claude.ai/code/session_01HDd5o3XxcgZiGrvBDFsUr1

refactor: scope change to standards build only, leave protocol as-is

The protocol crate needs source-level staging because the assembler's
`$kernel::` import resolution requires shared modules to be physically
co-located with kernel source. This cannot be avoided without assembler
changes, so revert the protocol build.rs to the base branch version.

https://claude.ai/code/session_01HDd5o3XxcgZiGrvBDFsUr1

* refactor: remove copy_directory from agglayer build.rs

Read MASM sources directly from the crate's asm/ directory instead of
copying them to OUT_DIR first. The copy is unnecessary because agglayer
doesn't mutate the directory structure during compilation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: generate agglayer error constants into OUT_DIR

Write error constants to OUT_DIR instead of src/errors/agglayer.rs, matching
the pattern used by miden-standards. The include now uses
concat!(env!("OUT_DIR"), ...) so error generation always runs regardless of
BUILD_GENERATED_FILES_IN_SRC.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: validate canonical_zeros.masm instead of generating it

The canonical zeros are deterministic Keccak256 values that never change.
Instead of generating the file at build time (guarded by BUILD_GENERATED_FILES_IN_SRC),
the committed file is now validated against the expected content. To regenerate,
run with REGENERATE_CANONICAL_ZEROS=1.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: remove BUILD_GENERATED_FILES_IN_SRC from agglayer

Both error constants and canonical zeros no longer write to src/, so
BUILD_GENERATED_FILES_IN_SRC is unused and can be removed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: fix trailing empty doc comment line

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
* refactor: reduce max assets per note from 255 to 64

* feat: add add_assets_exceeding_max_per_note_fails test

* Update crates/miden-protocol/asm/kernels/transaction/lib/note.masm

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update crates/miden-protocol/asm/kernels/transaction/lib/memory.masm

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* fix: correct CHANGELOG issue link from miden-base to protocol repo

Agent-Logs-Url: https://github.com/0xMiden/protocol/sessions/7e834550-7f38-4d90-9c05-d46e3a7e37fa

Co-authored-by: partylikeits1983 <77119221+partylikeits1983@users.noreply.github.com>

* refactor: address PR comments: add max assets success test, fix memory layout table

* chore: fix changelog

* Update crates/miden-protocol/src/transaction/kernel/memory.rs

Co-authored-by: Philipp Gackstatter <PhilippGackstatter@users.noreply.github.com>

* Update crates/miden-testing/src/kernel_tests/tx/test_output_note.rs

Co-authored-by: Philipp Gackstatter <PhilippGackstatter@users.noreply.github.com>

* refactor: use rstest for tests & update changelog

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Philipp Gackstatter <PhilippGackstatter@users.noreply.github.com>
…onstruction (#2758)

* chore: make note procedure names consistent

* chore: add changelog

* chore: update swap tag construction
* Add cycle counts to successful and failed notes

* Changelog

* Cycle count order comment

* Make fields private
* fix: improve deserialization error handling in AccountCode

* chore: update changelog

* refactor: removed AccountCode::from_bytes()
* refactor: always enable concurrent on std in miden-tx

* chore: delete miden-protocol-macros crate

* chore: update changelog
* Added the pswap masm contract and its supportive functions

* Added the pswap masm contract and its supportive functions

* refactor: rename swapp_tag to pswap_tag, NUM_ITEMS to NUM_STORAGE_ITEMS, remove create_ wrappers, add builder finish_fn

- Rename swapp_tag -> pswap_tag and SWAPp -> PSWAP throughout
- Rename NUM_ITEMS -> NUM_STORAGE_ITEMS for clarity
- Remove create_p2id_payback_note and create_remainder_note wrappers,
  make build_ functions public instead
- Compute p2id_tag inside build_p2id_payback_note from self.storage
- Add #[builder(finish_fn(vis = "", name = build_internal))] to PswapNote

* refactor: remove redundant test helpers, use pswap lib functions directly

Replace all test helper wrappers with direct calls to library functions:
- create_pswap_note -> PswapNote::create()
- create_expected_pswap_p2id_note + create_expected_pswap_remainder_note -> pswap.execute()
- build_pswap_storage -> PswapNoteStorage::from_parts()
- Remove make_pswap_tag, make_note_assets, make_note_args, compute_p2id_tag_*
- Inline calculate_output_amount as PswapNote::calculate_output_amount()

* docs: polish pswap doc comments to match sibling note style

- Replace storage layout list with markdown table
- Remove trivial "Returns the X" docs on simple getters
- Add # Errors sections where relevant
- Rewrite method docs to describe intent, not implementation
- Add one-line docs on From/TryFrom conversion impls
- Tighten PswapNote struct doc

* refactor: make assets a required field on PswapNote builder

* fix: adapt pswap to upstream API changes after rebase

- Rename RpoRandomCoin to RandomCoin (miden-crypto 0.23 rename)
- Store full ASSET_KEY instead of prefix/suffix to preserve callback
  metadata in faucet_id_suffix_and_metadata
- Replace create_fungible_asset calls with direct ASSET_KEY + manual
  ASSET_VALUE construction, avoiding the new enable_callbacks parameter
- Update hardcoded P2ID script root to match current P2idNote::script_root()

* refactor: use procref for P2ID script root, clean up pswap MASM

- Replace hardcoded P2ID script root with procref.p2id::main for
  compile-time resolution
- Default to full fill when both input and inflight amounts are zero
- Replace magic address 4000 with named P2ID_RECIPIENT_STORAGE constant
- Remove step numbering from comments, fix memory layout docs

* refactor: address PR review comments on pswap MASM

- Rename P2ID_RECIPIENT_STORAGE to P2ID_RECIPIENT_SUFFIX/PREFIX
- Add METADATA_HEADER word layout docs with source reference
- Document attachment_scheme parameter in set_word_attachment call
- Add stack views after condition checks

* refactor: rename pswap storage accessors, add offered_asset_amount helper, simplify build_p2id_payback_note

Rename requested_key/requested_value/requested_amount to
requested_asset_key/requested_asset_value/requested_asset_amount for
clarity. Extract offered_asset_amount() helper on PswapNote to
deduplicate offered asset extraction. Simplify build_p2id_payback_note
to take fill_amount: u64 instead of aux_word: Word, constructing the
aux word internally.

* refactor: address PR review comments on pswap Rust module

- Use NoteTag::default() instead of NoteTag::new(0)
- Change swap_count from u64 to u16 with safe try_into conversion
- Rename p2id_tag to payback_note_tag, p2id_payback_note to payback_note
- Rename build_ prefix to create_ for consistency
- Rename aux_word to attachment_word
- Replace Felt::new with Felt::from where possible
- Rename inputs to note_storage in TryFrom impl
- Make create_payback_note and create_remainder_pswap_note private
- Add offered_faucet_id helper to replace unreachable!()

* refactor: use builder pattern for PswapNote and PswapNoteStorage

- Remove PswapNote::create, add public build() with validation on builder
- Add bon::Builder to PswapNoteStorage, remove new() and from_parts()
- Remove payback_note_tag field, compute from creator_account_id on the fly
- Fix clippy warnings (useless conversions, needless borrows)
- Update all unit and integration tests to use builder pattern

* refactor: clean up pswap tests and fix clippy warnings

- Compare full faucet_id instead of just prefix in build validation
- Rename swap_note to pswap_note in integration tests for consistency
- Extract PswapNoteStorage builder into separate let bindings
- Replace manual current_swap_count counter with enumerate index
- Fix clippy useless_conversion and needless_borrow warnings

* refactor: rename inputs to storage_items in pswap_note_storage_try_from test

* chore: fix Cargo.toml dependency ordering and add CHANGELOG entry for PSWAP

* refactor: address PR review comments — use FungibleAsset types, improve safety and docs

- PswapNoteStorage: store FungibleAsset instead of raw key/value Words
- PswapNote: store offered_asset as FungibleAsset instead of NoteAssets
- execute(): take Option<FungibleAsset> for input/inflight assets
- Split execute into execute() and execute_full_fill_network() for clarity
- create_tag(): accept &FungibleAsset instead of &Asset
- Eliminate all Felt::new usage (use Felt::from, Felt::try_from, + ONE)
- Add swap_count overflow protection via checked_add
- Add script root verification in TryFrom<&Note>
- Add MASM fill validation (fill_amount <= requested)
- Rename MASM constants: _INPUT -> _ITEM, SWAPP_ -> PSWAP_
- Make calculate_output_amount private, add precision doc comment
- Add doc comments on all public accessors and methods
- Add network account full fill test
- Document network transaction compatibility throughout

* refactor: pass creator ID via stack to is_consumer_creator and use account_id::is_equal

* refactor: optimize requested asset storage to 4 felts and add NUM_STORAGE_ITEMS constant

Store requested asset as individual felts [enable_callbacks, faucet_suffix,
faucet_prefix, amount] instead of full ASSET_KEY + ASSET_VALUE (8 felts).
Use create_fungible_asset to reconstruct when needed. Reduces note storage
items from 18 to 14. Add NUM_STORAGE_ITEMS constant and execute_pswap doc comment.

* refactor: use p2id::new, pass inputs via stack, unify serial derivation

- Replace manual recipient building with p2id::new in create_p2id_note
- Pass all inputs via stack, store intermediates in @Locals
- Remove build_p2id_recipient_hash, P2ID_RECIPIENT_*, P2ID_NOTE_IDX
- Payback serial: serial[0] + 1, remainder serial: serial[3] + 1
- Update Rust side to match new serial derivation, remove Hasher import

* refactor: pass all inputs via stack to create_remainder_note, eliminate global memory reads

- create_remainder_note takes 8 stack inputs, stores to @Locals(6)
- Extract offered faucet info from ASSET_KEY in execute_pswap
- Use create_fungible_asset for consumer receive_asset and remainder asset
- Remove OFFERED_ASSET_KEY, PSWAP_NOTE_IDX constants

* feat: add P2ID reconstruction test, overfill docs, and stack comments

- Add test verifying Alice can reconstruct and consume P2ID payback note
  from her PSWAP data after Bob's partial fill
- Document why overfill is not allowed (likely unintentional error)
- Add missing stack comment on partial fill check

* fix: use Rust-predicted note for aux data in P2ID reconstruction test

The test framework doesn't preserve word attachment content in executed
transaction outputs. Use the Rust-predicted note to read the fill amount
from aux data, which mirrors production behavior where aux is visible.

* refactor: rename input to account fill, extract load_offered_asset proc

- Rename AMT_REQUESTED_IN to AMT_REQUESTED_ACCT_FILL and related variables
- Extract load_offered_asset proc centralizing asset loading and validation
- Remove OFFERED_ASSET_WORD global memory constant

* refactor: extract test helpers and reduce verbosity in pswap unit tests

* refactor: guard zero-amount receive_asset in MASM, add NUM_STORAGE_ITEMS alias on PswapNote

* refactor: rename ERR_PSWAP_WRONG_NUMBER_OF_INPUTS to ERR_PSWAP_WRONG_NUMBER_OF_STORAGE_ITEMS

* refactor: replace global memory with @Locals in execute_pswap and pass fill amounts via stack

* refactor(pswap): apply PR review nits from #2636

- Drop duplicated u64 stdlib docs inside calculate_tokens_offered_for_requested
- Replace `swap push.MAX_U32 mul add` with `swap mul.MAX_U32 add`
- Fold redundant empty-input guard in PswapNote::execute into the match arm

* feat(pswap): make payback note type configurable via PswapNoteStorage

Adds a `payback_note_type: NoteType` field on `PswapNoteStorage` (default
`Private`) so the payback note produced on fill can be private even when
the pswap note itself is public. Private payback notes are cheaper in
fees and bandwidth and don't lose information — the fill amount is
already recorded in the executed transaction's output.

Stored in the previously-reserved storage slot [6]. MASM now loads the
type from that slot when building the payback p2id note instead of using
the active note's own type. Remainder pswap note continues to inherit
the parent pswap's note type (unchanged).

Addresses PhilippGackstatter's review comment on PR #2636.

* perf(pswap): compress note storage layout from 14 to 10 items

Drops four reserved/zero padding slots ([7] and [9-11] in the old layout)
that served no purpose. The compressed layout is contiguous:

  [0-3] requested asset (callbacks, faucet suffix/prefix, amount)
  [4]   pswap_tag
  [5]   payback_note_tag
  [6]   payback_note_type
  [7]   swap_count
  [8-9] creator account id (prefix, suffix)

Saves 4 felts per pswap note on-chain, reducing fees and bandwidth for
every pswap created. No information is lost. Addresses PR #2636 review
comment from PhilippGackstatter on compressing the storage layout.

* test(pswap): cover combined input+inflight partial and full fill paths

Adds two unit tests against `PswapNote::execute` for the input+inflight
code path flagged in PR #2636 review:

- partial fill: account fill 10 + inflight 20 of a 50-requested pswap.
  Asserts payback carries 30 of the requested asset and a remainder
  pswap is produced with 20 requested / 40 offered remaining.
- full fill: account fill 30 + inflight 20 of a 50-requested pswap.
  Asserts payback carries 50 of the requested asset and no remainder.

Also factors out a `dummy_consumer_id()` helper alongside
`dummy_creator_id()` to keep the new tests readable.

* refactor(pswap): rename input/inflight to account_fill/note_fill

Addresses the naming feedback on PR #2636. The old `input` / `inflight`
terminology was ambiguous — "input" could mean the total fill amount,
the note_args input word, or the account-side fill, while "inflight"
conflated the transport mechanism with its role. The new names are
explicit about where the fill comes from:

  - offered_out             -> payout_amount
  - input / input_amount    -> fill_amount (when referring to the total
                               fill amount) or account_fill_amount
                               (when referring to the native-account
                               portion specifically)
  - inflight_amount         -> note_fill_amount
  - input_asset             -> account_fill_asset
  - inflight_asset          -> note_fill_asset

Touches the public `PswapNote::execute` parameters, all internal Rust
locals and doc comments, the MASM stack annotations / doc blocks /
local-var labels, and the integration test callers.

* refactor(pswap): fetch active note_type inline, drop global NOTE_TYPE

Upstream PR #2738 added `miden::protocol::note::metadata_into_note_type`
which does the bit-correct extraction against the new 1-bit NoteType
encoding. The pswap script no longer needs its own `extract_note_type`
proc or the `NOTE_TYPE` global slot.

Changes:
- Drop the `extract_note_type` proc and the `NOTE_TYPE_MASK` / `NOTE_TYPE`
  consts.
- The payback note already reads its type from `PAYBACK_NOTE_TYPE_ITEM`
  in storage (no active-note fetch needed).
- The remainder branch inlines `active_note::get_metadata dropw
  exec.note::metadata_into_note_type` where the type is actually used,
  instead of eagerly computing it once and stashing it in global memory.
- Update `pswap_note_alice_reconstructs_and_consumes_p2id` to set
  `payback_note_type(NoteType::Public)` so the reconstructed P2ID payback
  matches — the previous test implicitly relied on the payback inheriting
  the pswap's Public type (broken since the bit-encoding change).

This also fixes the previously-failing
`pswap_note_alice_reconstructs_and_consumes_p2id` test, which was hitting
the broken extract_note_type mask (always returning 0 under the new
encoding).

* refactor(pswap): switch storage consts to STORAGE_PTR base pattern

Matches the convention used in p2id.masm: declare a single
`STORAGE_PTR` base and derive every note-storage item offset relative
to it. This makes the storage layout easier to read and relocate.
Also use `push.STORAGE_PTR` at the load site instead of a bare `0`.

* docs(pswap): add #! doc block to @note_script main procedure

Closes PR #2636 review comment: `pub proc main` lacked a proper `#!`
doc block with `Inputs`/`Outputs`/storage layout/panic conditions.
Adds one modelled after the canonical style in p2id.masm / swap.masm.

* refactor(pswap): scrub last inflight/input references in tests

Rename `pswap_note_inflight_cross_swap_test` to
`pswap_note_note_fill_cross_swap_test` and update two stale comments
that still said "input" / "inflight" to match the new account_fill /
note_fill terminology used throughout the module.

* style(pswap): readability nits across pswap.masm

Minor cleanups surfaced during review, no behavior changes:
- Sort imports alphabetically by path.
- Reshape storage layout doc block to `[i..j] : N felts` + `Where:` form
  and drop the duplicate listing in the main proc docstring.
- Drop the stale "Memory Addresses" meta-block.
- Name local slots via top-of-file consts (e.g. CALC_FILL_AMOUNT,
  P2ID_NOTE_IDX, REMAINDER_AMT_OFFERED, EXEC_AMT_OFFERED, ...) so
  loc_store/loc_load sites read like variables rather than loc.N.
- Expand the rationale for the FACTOR=1e5 choice (precision vs u64
  overflow headroom) and switch the literal to decimal.
- Rename boolean stack-comment markers from operator syntax
  (`amt > 0`, `requested > offered`, `requested == fill_amount`) to
  readable predicates (`has_account_fill`, `has_note_fill`,
  `has_account_fill_payout`, `requested_exceeds_offered`,
  `is_full_fill`).
- Drop obvious/redundant comments (error-const headers, `gt pops…`
  primitive explanation, proc-start stack echoes, validate/calculate
  rationale moved into the execute_pswap docstring).
- Split `push.NUM_STORAGE_ITEMS.0` into two pushes using the
  STORAGE_PTR constant to match the `bridge_out.masm` pattern.
- Add converged stack markers (`# => [...]`) after every if/else `end`
  and before every proc return.

* test(pswap): dedupe integration tests via helpers + rstest

- Add shared helpers at the top of the test file: `build_pswap_note`,
  `note_args`, `assert_fungible_asset`, `assert_vault_added_removed`,
  `assert_vault_single_added`. Removes the repeated 13-line pswap-note
  setup and the `if let Asset::Fungible(f) = …` / vault-delta
  boilerplate.
- Consolidate variant-heavy tests with `#[rstest]`:
  - `pswap_fill_test` merges full/private/partial/network fill cases
    into one parameterized async test (4 cases).
  - `pswap_multiple_partial_fills_test` replaces the inner for-loop
    with 9 `#[case]`s, one per fill amount.
  - `pswap_partial_fill_ratio_test` merges the non-exact-ratio and
    hand-listed fuzz suites into one rstest with 27 regression cases,
    delegating to a new `run_partial_fill_ratio_case` helper.
  - `pswap_chained_partial_fills_test` parameterizes the outer chain
    loop with 10 `#[case]`s; the stateful per-chain inner fill loop
    is preserved intact.
- Add `pswap_partial_fill_ratio_fuzz` as a seeded-random coverage
  sibling: two `#[case]`s (seed 42, seed 1337) × 30 random
  `(offered, requested, fill)` triples each, drawn from `SmallRng`.
  Failure message includes seed, iter, and the failing triple so any
  regression is reproducible from the rstest case name alone. The
  regression `#[case]` block stays as a permanent edge-case suite.
- Leave `alice_reconstructs`, `cross_swap`, `creator_reclaim`,
  `invalid_input`, `compare`, and `parse_inputs` as standalone tests
  but rewrite their bodies to use the shared helpers.

Net: 1325 → 956 non-helper lines (-369), test count 14 → 58.

* style(pswap): apply nightly rustfmt

* fix(pswap): guard user-provided fill sum against u64 overflow

Switch `execute_pswap`'s `account_fill + note_fill` check to
`u64::overflowing_add` + `ERR_PSWAP_FILL_SUM_OVERFLOW`, so a malicious
consumer cannot pick operands whose felt sum wraps past u64 and
spuriously satisfies the `lte requested` guard. Also document why the
matching sums/subs in `create_p2id_note` and `create_remainder_note`
cannot over/underflow, and drop a stray trailing space on `FACTOR`.

* fix(pswap): correctness + layout cleanups from PR review theme 2

- create_p2id_note: drop three stray zero pads that shoved the real
  `note_idx` to stack depth 11 in the `has_account_fill` branch, so
  `move_asset_to_note` was reading a pad zero as the note index. The
  bug was masked in every existing test because `note_idx == 0`; add a
  SPAWN-note regression test that forces P2ID to `note_idx == 1`.
- load_offered_asset: bump `@locals(2)` to `@locals(8)` — we store two
  words (8 felts), not two felts.
- Drop `pswap_tag` from note storage (10 -> 9 items). The remainder
  path now lifts the tag out of the active note's metadata via
  `dup.2 movdn.4` + `metadata_into_note_type`, since the asset pair
  is unchanged across remainder creation. `PswapNoteStorage` loses
  the field, `with_pswap_tag`, and the getter; storage offsets and
  `NUM_STORAGE_ITEMS` shift accordingly.
- Document why `create_p2id_note`'s add (P2ID attachment), why
  `create_remainder_note`'s sub (amt_offered - amt_payout), and why
  `create_p2id_note` itself are reachable but safe (invariants come
  from the caller, not local checks).
- Correct the `payback_note_type` storage-layout docstring (only P2ID
  payback uses it; remainder inherits from active-note metadata).
- Note in a comment why `procref.main` cannot replace the runtime
  `active_note::get_script_root` call in `create_remainder_note`
  (compile-time call-graph cycle via main -> execute_pswap ->
  create_remainder_note -> procref.main).

* fix(pswap): align attachment layout + drop dead swap_count slot

- Attachment layout: MASM now emits `[fill, 0, 0, 0]` (Word[0] = fill)
  for both the P2ID and remainder attachments, matching the Rust-side
  `Word::from([fill, 0, 0, 0])` convention. Previously the two sides
  disagreed (Rust wrote Word[0], MASM wrote Word[3]), and the
  alice-reconstructs test was silently compensating by reading
  `aux_word[3]` against a Rust prediction it never compared to.
- alice-reconstructs test: read from the executed transaction's output
  (was already), read `aux_word[0]`, and add a direct Rust <-> MASM
  attachment-word parity `assert_eq!` so a future drift actually fails.
- Extend alice-reconstructs to also reconstruct the remainder PSWAP
  from her original pswap data + the remainder's on-chain attachment,
  verifying recipient + attachment parity against the executed output.
- Drop `pswap_count` from note storage (9 -> 8 items). The field was
  never read or written by pswap.masm — a dead pass-through slot. The
  Rust side was silently bumping it on remainder creation, so the
  Rust-predicted remainder recipient diverged from MASM's (caught only
  by extending the alice test). Remove the field, getter, builder arg,
  storage offsets, and all callers; shift creator ID to slots [6-7].

* refactor(pswap): masm perf + style cleanups from PR review theme 4

- Replace three `push.0 gt` (~16 cycles) zero-checks with `neq.0`
  (~3 cycles) at the has_account_fill, has_note_fill, and
  has_account_fill_payout branches.
- Use `dup.1 dup.1 neq` instead of `lt` for the `is_partial` check;
  the `lte requested` assertion above guarantees `total_in <=
  total_requested`, so `!=` is equivalent and cheaper.
- Flip the faucet-ID limb order at the create_remainder_note boundary
  from prefix-then-suffix to suffix-then-prefix (standard miden
  convention), updating the caller in execute_pswap, the proc
  docstring, local-slot comment, and the store block.
- Drop the redundant `movdn.2` / `movup.2` pair in create_remainder_note
  — `remaining_requested` is already on top, so `mem_store` can take it
  directly.
- Drop the `(4)` suffix on NOTE_ATTACHMENT / METADATA_HEADER stack
  comments; uppercase identifiers already imply a full word.
- Remove `exec.sys::truncate_stack` and its stale `use` import from
  execute_pswap. Tracing the proc end-to-end (including every syscall
  wrapper, call frame, and proc-local store) shows the stack is
  already balanced at exit, and all 59 pswap integration tests pass
  without it.

* refactor(pswap/tests): PR review theme 5 test quality cleanups

- `pswap_note_invalid_input_test` asserts the specific error kind via
  `assert_transaction_executor_error!(result, ERR_PSWAP_FILL_EXCEEDS_REQUESTED)`
  instead of a bare `.is_err()`. A future bug that fails the tx for the
  wrong reason will now surface.
- Drop the `rng: &mut RandomCoin` parameter from `build_pswap_note` —
  the helper draws its serial number from `builder.rng_mut()` directly.
  Remove all the `let mut rng = RandomCoin::new(Word::default())`
  declarations at call sites that only existed to feed this param.
- Rename local helper `note_args` -> `pswap_args` so it reads clearly
  next to the (unrelated) `TransactionContextBuilder::extend_note_args`
  method it gets passed into.
- Switch `assert_vault_added_removed` / `assert_vault_single_added` /
  `assert_fungible_asset_eq` helpers to take `FungibleAsset` instead of
  `(AccountId, u64)` tuples, and update every call site.
- `pswap_note_note_fill_cross_swap_test`: replace the flag-and-for-loop
  "which note contains which asset" check with `iter().any()` against
  two locally-built `Asset::Fungible(FungibleAsset::new(...))` values.
- `pswap_note_alice_reconstructs_and_consumes_p2id`: propagate the
  `prove_next_block()` error via `?` instead of swallowing it with
  `let _ =`.

Still outstanding from theme 5:
- realistic large amounts (20 * 10^18) in the happy-path tests
- an integration test where both account_fill and note_fill are
  non-zero on the same note (the Rust unit tests cover the predictor
  path, but the integration-level cross-flow needs a cleaner two-note
  setup than a simple adaption of the existing cross-swap test).
- skipping `Note -> PswapNote::try_from` roundtrips by having
  `build_pswap_note` return the `PswapNote` directly — a broader
  signature churn that is orthogonal to the items above.

* test(pswap): finish theme 5 — realistic amounts, combined fill, skip try_from

- `pswap_fill_test`: scale every amount by `AMOUNT_SCALE = 10^12` so
  the happy path exercises 12-decimal token values instead of single
  digits. The scale cap is chosen so `requested * FACTOR` stays under
  `u64::MAX`, matching the current MASM assumption.
- `build_pswap_note` now returns `(PswapNote, Note)` instead of just
  the `Note`, so call sites can execute the PswapNote directly and
  skip the `PswapNote::try_from(&note)?` round-trip the reviewer
  flagged. Every caller updated.
- New `pswap_note_combined_account_fill_and_note_fill_test`: exercises
  a PSWAP fill where the consumer supplies BOTH `account_fill` and
  `note_fill` on the same note in the same transaction. Alice offers
  100 USDC for 50 ETH, Bob offers 30 ETH for 60 USDC, Charlie consumes
  both — Alice's pswap uses combined fill (20 ETH from Charlie's vault
  + 30 ETH sourced from inflight via Bob's pswap payout), Bob's pswap
  uses pure note_fill. Asserts output notes, recipient parity, and
  Charlie's vault delta (-20 ETH / +40 USDC; the note_fill legs flow
  through inflight and never touch his vault).

* refactor(pswap): rename execute_full_fill_network -> execute_full_fill

The method has nothing network-specific about it — a "network
transaction" is just one where the kernel defaults note_args to
`[0, 0, 0, 0]` and the MASM script falls back to a full fill, which
any caller can trigger by calling this method directly. Rename to
reflect that, update the sole caller in the pswap_fill_test, and
rewrite the doc comment to describe the behavior (full fill producing
only the payback note, no remainder) rather than the network use case.

Also scrub stale doc references left over from theme 3's `swap_count`
removal:
- drop the "swap count overflows u16::MAX" bullet from `execute`'s
  errors section
- drop "swap count overflow" from `execute_full_fill`'s errors section
  (the whole section is gone — no error paths remain)
- update the PswapNote struct doc: the remainder carries "an updated
  serial number", not "an incremented swap count"
- update `create_remainder_pswap_note`'s doc for the same reason

* refactor(pswap): drop FACTOR, use u128 math for payout calculation

`calculate_tokens_offered_for_requested` previously used a fixed-point
1e5 precision factor with two separate branches (offered >= requested
vs requested > offered) plus a full-fill early return, to approximate
`offered * fill / requested` without overflowing u64 intermediates.

That design had three problems:

- Four `u64::wrapping_mul` calls silently produced wrong results for
  inputs where `x * FACTOR >= 2^64`, capping each operand at about
  1.84e14 — roughly 0.000184 whole tokens for any 18-decimal asset.
- The two-branch structure was fragile and harder to reason about than
  a single linear formula.
- The FACTOR-scaled intermediate introduced rounding error in `ratio`
  that then got amplified by `fill`.

Replace the whole proc with one linear path:

  product = u64::widening_mul(offered, fill_amount)   # -> u128
  quot    = u128::div(product, requested_u128)        # -> u128
  assert  q.hi_limbs == 0                              # payout fits in u64
  return  q.lo_limbs combined back into a felt

miden-core-lib 0.22 already exposes `u128::div` (advice-provider
assisted, same host-compute-and-verify pattern as `u64::div`), so no
custom division implementation is needed.

Properties of the new version:
- Exact integer precision (one floor division at the end, no FACTOR
  rounding).
- Each operand can go up to `FungibleAsset::MAX ≈ 2^63`, so 18-decimal
  tokens now work at realistic volumes (~9.2 billion whole tokens
  per swap) — a ~50,000x improvement in dynamic range.
- No branching on the offered/requested relationship. No full-fill
  early return. One generic formula that also handles the
  `fill_amount == requested` case trivially.
- All wrapping_mul references gone, closing the last deferred theme-1
  item from the PR review.

Also mirror the same change on the Rust side: `calculate_output_amount`
now just does `(offered as u128) * (fill as u128) / (requested as u128)`
and `try_from`s back to u64, matching MASM exactly. Scrub the old
PRECISION_FACTOR constant, the two-branch Rust logic, and the FACTOR
doc block.

The 8 in-crate unit tests and all 60 pswap script tests (including
the 27-case hand-picked ratio regression suite and two seeded
30-iteration fuzzers) still pass.

* test(pswap): second pass on PR review follow-ups

- `calculate_output_amount` doc: fix a mismatched paren and the empty
  blank line between doc and fn signature that triggered clippy's
  `empty_line_after_doc_comments` (caught by CI after the last push).
- `pswap_note_alice_reconstructs_and_consumes_p2id`: build the
  `PswapNote` via the builder, call `pswap.clone().into()` to get the
  protocol `Note`, and keep `pswap` in scope for `.execute(...)`. Drops
  the `PswapNote::try_from(&pswap_note)?` roundtrip the reviewer flagged
  on this exact test as a nit.
- `pswap_chained_partial_fills_test`: same pattern — replace the manual
  `Note::new(NoteAssets / NoteMetadata / NoteRecipient)` construction
  with `PswapNote::builder().serial_number(current_serial)...build()?`
  + `pswap.clone().into()`. The raw-Note construction was only there to
  inject a specific serial per chain position, which the builder
  already supports. Also drops the dependent `PswapNote::try_from`
  roundtrip.
- `pswap_fill_test`: bump `AMOUNT_SCALE` from 10^12 to 10^18 so the
  happy-path test exercises the MASM u128 calculation at realistic
  18-decimal token magnitudes. Base values adjusted (offered=8,
  requested=4, fills=3/4) so every amount stays under AssetAmount::MAX
  ≈ 9.22 × 10^18. Previously the 10^12 scale was chosen to stay under
  the old FACTOR=1e5 cap; now that the u128 rewrite lifted that cap,
  the test can stress the calculation at the actual wei-equivalent
  scale the reviewer asked for.
- New `pswap_attachment_layout_matches_masm_test`: dedicated
  regression test for the shared P2ID and remainder attachment-word
  layout. Does a partial fill, then explicitly asserts the executed
  transaction's P2ID attachment equals `[fill_amount, 0, 0, 0]` and
  the remainder attachment equals `[amt_payout, 0, 0, 0]`, and
  cross-checks both against the Rust-predicted attachments. Fires if
  either MASM or Rust drifts the load-bearing felt off `Word[0]`.
- Apply the "blank line after every `# => [...]` stack comment"
  convention across pswap.masm where it was missing — one reviewer
  tends to leave this as a nit so sweep the whole file for
  consistency rather than wait for the comments.

* style(pswap/tests): apply rustfmt

* refactor(pswap): expose `PswapNote::create_args` as public helper

Add `PswapNote::create_args(account_fill: u64, note_fill: u64) ->
Result<Word, NoteError>` so downstream consumers building PSWAP
`NOTE_ARGS` words don't need to know the `[account_fill, note_fill,
0, 0]` layout by hand. Returns a `Result` instead of panicking so the
underlying `Felt::try_from` conversion errors propagate cleanly
through the standard `NoteError` path — although for any amount that
fits in `FungibleAsset::MAX_AMOUNT` this cannot actually fail, the
conversion is surfaced explicitly rather than hidden behind an
`.expect(...)` in a public API.

Drop the local `pswap_args` test helper — every call site now uses
`PswapNote::create_args(...)?` directly, matching the reviewer's
suggestion that the layout helper live on `PswapNote` rather than
being reimplemented in each consumer.

All 61 pswap script tests pass with the new signature (twelve call
sites updated to propagate the Result via `?`).

* Update crates/miden-standards/asm/standards/notes/pswap.masm

Co-authored-by: Alexander John Lee <77119221+partylikeits1983@users.noreply.github.com>

* Update crates/miden-standards/asm/standards/notes/pswap.masm

Co-authored-by: Alexander John Lee <77119221+partylikeits1983@users.noreply.github.com>

* Update crates/miden-standards/asm/standards/notes/pswap.masm

Co-authored-by: Alexander John Lee <77119221+partylikeits1983@users.noreply.github.com>

* Update crates/miden-standards/asm/standards/notes/pswap.masm

Co-authored-by: Alexander John Lee <77119221+partylikeits1983@users.noreply.github.com>

* Update crates/miden-standards/asm/standards/notes/pswap.masm

Co-authored-by: Alexander John Lee <77119221+partylikeits1983@users.noreply.github.com>

* Update crates/miden-standards/asm/standards/notes/pswap.masm

Co-authored-by: Alexander John Lee <77119221+partylikeits1983@users.noreply.github.com>

* Update crates/miden-standards/asm/standards/notes/pswap.masm

Co-authored-by: Alexander John Lee <77119221+partylikeits1983@users.noreply.github.com>

* fix(pswap): CI green — unused imports + duplicated exec

Two fixes triggered by `make check-features` / `clippy -D warnings`
and the full pswap test suite:

- `tests/scripts/pswap.rs`: drop `NoteAssets`, `NoteMetadata`,
  `NoteRecipient`, `NoteStorage` from the `miden_protocol::note`
  import list. Leftovers from the theme-5 chained-fills refactor that
  switched from low-level `Note::new(NoteAssets, NoteMetadata,
  NoteRecipient)` construction to `PswapNote::builder()`. CI compiles
  with `-D warnings` so the unused-imports warning was fatal.

- `asm/standards/notes/pswap.masm`: remove a duplicate
  `exec.calculate_tokens_offered_for_requested` line that had crept
  into the note-fill payout path in one of the upstream edits, plus a
  duplicate stack comment above the account-fill payout block. With
  the duplicate `exec`, the second call read garbage off a stack that
  only held the first call's payout — specifically a zero divisor,
  which surfaced as `u128::div` "division by zero" and failed 57/61
  pswap tests.

All 61 pswap script tests pass after the fix, and
`cargo check --all-targets --all-features` on miden-testing is clean.

* refactoring changes

* fix(pswap): address PR review — storage, attachments, asset validation

- Derive payback note tag from creator ID in MASM via
  note_tag::create_account_target instead of storing in note storage,
  reducing storage from 8 to 7 items.
- Add dedicated attachment constructors (payback_attachment,
  remainder_attachment) on PswapNote for clarity and reuse.
- Validate computed amounts as valid fungible asset amounts:
  - calculate_output_amount now returns Result and checks via AssetAmount.
  - MASM assert_valid_asset_amount proc checks u64 values against
    FUNGIBLE_ASSET_MAX_AMOUNT before felt conversion to prevent silent
    wrapping, applied to both fill sum and payout quotient.
- Fix P2ID comment nit per reviewer suggestion.
- Remove unused NoteTag import from p2id test.

* style: apply nightly rustfmt

* fix(pswap): simplify assert_valid_asset_amount and rename procedure

- Replace manual hi/lo branch logic with exec.u64::lte
- Rename calculate_tokens_offered_for_requested to calculate_output_amount (matches Rust)
- Simplify tag derivation in create_p2id_note: swap+dup+swap+movup.2 → dup.1+movdn.2

* Update crates/miden-standards/asm/standards/notes/pswap.masm

Co-authored-by: Alexander John Lee <77119221+partylikeits1983@users.noreply.github.com>

* Update crates/miden-standards/asm/standards/notes/pswap.masm

Co-authored-by: Alexander John Lee <77119221+partylikeits1983@users.noreply.github.com>

* Update crates/miden-standards/asm/standards/notes/pswap.masm

Co-authored-by: Alexander John Lee <77119221+partylikeits1983@users.noreply.github.com>

* test(pswap): add rstest cases for fill-sum overflow and max-asset-amount validation

Converts pswap_note_invalid_input_test to rstest with three named cases:
- fill_exceeds_requested (existing)
- fill_sum_u64_overflow: both fills at 2^63, sum overflows u64 → ERR_PSWAP_FILL_SUM_OVERFLOW
- fill_sum_exceeds_max_asset_amount: both fills at MAX_AMOUNT, sum > MAX_AMOUNT → ERR_PSWAP_NOT_VALID_ASSET_AMOUNT

---------

Co-authored-by: Vaibhav Jindal <vaibhavjindal@Vaibhavs-MacBook-Pro.local>
Co-authored-by: Alexander John Lee <77119221+partylikeits1983@users.noreply.github.com>
* chore: remove attachment parameters from SWAP and MINT

* chore: add changelog

* chore: remove outdated comment
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.