Skip to content

Use fixed P2ID recipients for private PSWAP paybacks - #3918

Open
partylikeits1983 wants to merge 21 commits into
nextfrom
ajl-pswap-private-paybacks
Open

partylikeits1983 wants to merge 21 commits into
nextfrom
ajl-pswap-private-paybacks

Conversation

@partylikeits1983

@partylikeits1983 partylikeits1983 commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor

Private P2ID paybacks currently derive their serial number from the PSWAP serial number. For a public PSWAP, this lets observers reconstruct the paybacks and compute their nullifiers.

This implements the private-payback part of the updated proposal. Each order uses a fixed P2ID recipient commitment and discovery tag, with a fresh secret serial independent of the PSWAP serial. The recipient stays the same across fills and remainders; fillers need only its commitment. The owner retains the full recipient to reconstruct paybacks from their public fill attachments.

Public paybacks retain their existing recipient derivation and creator reclaim. Paybacks and remainders preserve the output sealing already merged in #3923 and #3927, including public attachments added by callbacks before sealing. Reconstruction must include the complete attachment list.

Private paybacks should first be committed on chain, then consumed as authenticated inputs. Unauthenticated consumption exposes the NoteId and links the payback to the consuming account. Discovery tags should not identify the target account.

Cancellation follows in #3911. Both PRs should land before releasing the new script. This changes the Rust APIs, private storage layout, and PSWAP script root.

Separate the private-payback foundation from cancellation. Keep the
storage and Rust/MASM changes, authenticated retained-note input support,
fill and malformed-storage coverage, public creator reclaim, and updated
benchmarks. Explicitly reject unsupported note actions in this layer.

Private cancellation and its refund, fee-funding, and router-flow tests
are provided by the dependent PSWAP cancellation branch.
@partylikeits1983
partylikeits1983 force-pushed the ajl-pswap-private-paybacks branch from 33b722d to 2e678bd Compare September 22, 2026 14:15
Merge next at 701b6e1 to pick up the latest release workflow updates.

Link the sealing changelog entry to PR #3923, align the seal procedure table row, clarify the sealed-flag offset documentation, and rename the mixed test helper section to HELPERS.

Validation: 77 output-note, callback, and foreign-account tests passed; workspace formatting, targeted spelling, and diff checks passed.
Integrate PR #3923 and its latest next merge so PSWAP can compile and enforce immutable paybacks and remainders.
partylikeits1983 added a commit that referenced this pull request Sep 23, 2026
Propagate the output-note sealing dependency from PR #3918 so private cancellation can compile and enforce immutable refunds.
Use the output-note sealing implementation merged on next, including public
attachments added before sealing. Preserve the fixed private recipient flow
and verify authenticated reconstruction after issuer callbacks.

Remove superseded callback tests and duplicate kernel changes, retain current
release history, and refresh the four PSWAP fill benchmarks.
@partylikeits1983
partylikeits1983 marked this pull request as ready for review October 6, 2026 11:52

@zeapoz zeapoz left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Looks good to me, left some suggestions!

Comment thread crates/miden-standards/asm/standards/notes/pswap.masm Outdated
Comment thread crates/miden-standards/asm/standards/notes/pswap.masm Outdated
Comment thread crates/miden-standards/asm/standards/notes/pswap.masm Outdated

@PhilippGackstatter PhilippGackstatter left a comment

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.

Not a full review, just left a few comments/suggestions.

Comment thread bin/bench-transaction/src/context_setups/network_wallet.rs Outdated
Comment thread crates/miden-standards/asm/standards/notes/pswap.masm Outdated
Comment thread crates/miden-standards/asm/standards/notes/pswap.masm Outdated
Comment thread crates/miden-standards/asm/standards/notes/pswap.masm Outdated
Comment thread crates/miden-standards/asm/standards/notes/pswap.masm Outdated
Comment on lines 548 to +553
pub fn execute(
&self,
consumer_account_id: AccountId,
account_fill_asset: Option<FungibleAsset>,
note_fill_asset: Option<FungibleAsset>,
) -> Result<(Note, Option<PswapNote>), NoteError> {
) -> Result<(RawOutputNote, Option<PswapNote>), NoteError> {

@PhilippGackstatter PhilippGackstatter Oct 6, 2026 •

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.

May be worth double-checking whether it's fine to return notes without the recipient here (i.e. RawOutputNote which contains private note as PartialNote), cc @VAIBHAVJINDAL3012.

If not, we would need to modify the PSwapPayback type to hold the full recipient, but I haven't though through all implications of that.

Comment thread crates/miden-standards/src/note/pswap.rs Outdated
Comment thread crates/miden-standards/src/note/pswap.rs Outdated
Comment thread crates/miden-standards/src/note/pswap.rs Outdated
Comment on lines +707 to +721
let recipient = match self.storage.payback {
PswapPayback::Public { creator_account_id } => {
let serial = Word::new([
self.serial_number[0] + ONE,
self.serial_number[1],
self.serial_number[2],
self.serial_number[3] + Felt::from(rounds - 1),
]);
P2idNoteStorage::new(creator_account_id).into_recipient(serial)
},
PswapPayback::Private { .. } => {
let recipient = private_recipient.ok_or_else(|| {
NoteError::other("private payback requires its recipient opening")
})?;
self.validate_private_payback_recipient(recipient)?;

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.

Question: IIUC, for public notes we derive the correct serial from the PSWAP serial.
For private notes, validate_private_payback_recipient checks that the recipient, and therefore the serial number matches. Is that convenient? Would it be more convenient for callers to provide just P2idNoteStorage and the function computes the recipient, checks it matches, and constructs the note?

partylikeits1983 and others added 3 commits October 6, 2026 19:37
Co-authored-by: Philipp Gackstatter <PhilippGackstatter@users.noreply.github.com>
Co-authored-by: Philipp Gackstatter <PhilippGackstatter@users.noreply.github.com>

This branch has not been deployed

No deployments
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.

3 participants