Skip to content

Conversation

@arminsabouri
Copy link
Collaborator

This allows the refrence to find a resumable session without having to replay each session. And removes a misuse of pj endpoint when trying to filter resumable sessions.

Related ticket: #336


Could also do something similar for the receiver where you first generate an ephemeral session id, generate your HPKE keypair then find and replace the session id -- but that is ugly. A much better approach would be a receiver builder model where you first generate the key pair and other parts of the session context and then build(session_persister(short_id)). Builder model is also more attractive bc we are also expanding the session context with optional fields (#933 #920 )

Pull Request Checklist

Please confirm the following before requesting review:

  • A human has reviewed every single line of this code before opening the PR (no auto-generated, unreviewed LLM/robot submissions).
  • I have read CONTRIBUTING.md and rebased my branch to produce hygienic commits.

@coveralls
Copy link
Collaborator

coveralls commented Aug 26, 2025

Pull Request Test Coverage Report for Build 17298830437

Details

  • 23 of 24 (95.83%) changed or added relevant lines in 3 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage increased (+0.01%) to 85.785%

Changes Missing Coverage Covered Lines Changed/Added Lines %
payjoin-cli/src/app/v2/mod.rs 9 10 90.0%
Totals Coverage Status
Change from base Build 17297590316: 0.01%
Covered Lines: 8026
Relevant Lines: 9356

💛 - Coveralls

@arminsabouri
Copy link
Collaborator Author

arminsabouri commented Aug 26, 2025

impl'd the builder id in 8503e2f

TODO: before this can be undrafted

  • Remove UnInitialized
  • Update FFI

@nothingmuch
Copy link
Collaborator

nothingmuch commented Aug 26, 2025

Hmm, i'm a bit ambivalent about the approach, since short IDs are not guaranteed to be collision free just very unlikely to. In a 24 hour window that probability is very low, but for a user who makes potentially thousands of historical sessions, the birthday bound can conceivably make this non-negligible (but still very unlikely) in the future, and there is no clear way to handle this. This is probably not an issue in practice for payjoin-cli, but as a reference implementation I think it's best we discourage people from doing that, not because this failure case is likely but because in the very unlikely event that it does crop up there's no clean way to handle it.

A more robust approach would be to add the short ID as a column without a unique constraint, and fetch all of the sessions that match it (which should almost always only have 0 or 1 rows), and in the very unlikely event of a collision, compare the full receiver key.

@DanGould
Copy link
Contributor

Even if someone were searching through potentially thousands of historical sessions, the use case we have in mind here is for resumption. In the case of resumption couldn't you just use the most recent, or only look within a pre-defined expiration period?

@arminsabouri arminsabouri force-pushed the short-id-session-id-replacment branch from 8503e2f to 4cc43a4 Compare August 27, 2025 12:37
@nothingmuch
Copy link
Collaborator

nothingmuch commented Aug 27, 2025

Even if someone were searching through potentially thousands of historical sessions, the use case we have in mind here is for resumption. In the case of resumption couldn't you just use the most recent, or only look within a pre-defined expiration period?

For querying yes, but if the session ID is the primary key, then that would have to be unique accounting for for completed sessions as well. Our suggestions are basically equivalent.

@DanGould
Copy link
Contributor

in the very unlikely event of a collision, compare the full receiver key

Why not just use the full receiver key instead? @nothingmuch. I feel like we're still not settled on a solution here.

@arminsabouri
Copy link
Collaborator Author

Can we satify #336 by just recommending to the integrations to use a auto incrementing id? We do this in Liana and save the short id as metadata in the sessions table for fast look ups.

@nothingmuch
Copy link
Collaborator

Can we satify #336 by just recommending to the integrations to use a auto incrementing id? We do this in Liana and save the short id as metadata in the sessions table for fast look ups.

I think that's the preferred approach. The arguments for this are that it's very widely understood as a pattern, with sqlite or other SQL databases. More importantly application developers don't need a surrogate key to link back to their information. For example, a wallet with a contacts feature might associate payjoin sessions with specific contacts and therefore choose to store them differently. Demonstrating sequential IDs in payjoin-cli just makes this point but if we eventually also ship this code as a library that would make a material difference.

The argument in favor of just using the receiver key as a primary key is, again assuming the tradeoff is mainly about readability (if it's shipped as a library this may have portability should we choose to support more than just sqlite, debatable), this can simplify session creation somewhat, but would also require rejecting BIP 21 URIs with an RK that matches a previously seen one, but which may differ in pjos or the amount parameters, which is essentially the same kind of error condition as using RK or the short ID as merely a surrogate key.

@arminsabouri
Copy link
Collaborator Author

I agree with the above. With all that said the contents of this PR are still independently useful for two reasons.

  1. Receiver builder model which can be cherrypicked into its own PR
  2. Session Id as metadata so we dont have to replay each session to determine if we have a resumable session

@arminsabouri arminsabouri mentioned this pull request Aug 27, 2025
2 tasks
@arminsabouri arminsabouri force-pushed the short-id-session-id-replacment branch 2 times, most recently from 6dd5a64 to eee865a Compare August 28, 2025 14:17
@arminsabouri arminsabouri changed the title Use short id as session id for pj sender Add receiver pk to session metadata Aug 28, 2025
This allows the refrence to find a resumable session without having to
replay each session. And removes a misuse of pj endpoint when trying to
filter resumable sessions.
@arminsabouri arminsabouri force-pushed the short-id-session-id-replacment branch from eee865a to 9180edd Compare August 28, 2025 14:24
@arminsabouri arminsabouri marked this pull request as ready for review August 28, 2025 14:26
@arminsabouri arminsabouri requested a review from DanGould August 28, 2025 14:26
Copy link
Contributor

@DanGould DanGould left a comment

Choose a reason for hiding this comment

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

ACK 9180edd [edited: my initial commit has was for the parent since I git reset to check the diff`. fixed]

  • Keep session_id as primary key with autoincrement
  • Similar to Yuval's suggestion, add the receiver public key as a column without a unique constraint, and fetch all of the sessions that match it (which[, unlike ShortID,] should almost always only have 0 or 1 rows), and in the very unlikely event of a collision, compare the full receiver key.

let mut stmt =
conn.prepare("SELECT receiver_pubkey FROM send_sessions WHERE session_id = ?1")?;
let receiver_pubkey: Vec<u8> = stmt.query_row(params![session_id.0], |row| row.get(0))?;
Ok(HpkePublicKey::from_compressed_bytes(&receiver_pubkey).expect("Valid receiver pubkey"))
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this is a Deserialize Error. Should that db::Error::Deserialize variant be a unit type so that it's appropriate here as well?

Or do we just need a new variant? I can see how this wouldn't be the end of the world to panic because there's nothing else to do as long as get_send_session_receiver_pk is only being used in the one place it currently is

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

yeah I was kinda ambivalent about this expect. If its truly undeserializable you got bigger problems and a panic is appropriate. But we don't really do that anywhere else in db.rs. db::Error::Deserialize makes the most sense if rust is not mad at the internal type

Copy link
Contributor

@DanGould DanGould Aug 28, 2025

Choose a reason for hiding this comment

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

if its truly undeserializable you got bigger problems

Ack and merging

this is tiny techdebt outside of payjoin crate.

let session_receiver_pubkey = self
.db
.get_send_session_receiver_pk(&session_id)
.expect("Receiver pubkey should exist if session id exists");
Copy link
Contributor

Choose a reason for hiding this comment

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

This expect seems OK since it's looking for exactly one session to send. The other one seems less ok because the context does not restrict it to exactly one use.

@DanGould DanGould requested a review from nothingmuch August 28, 2025 15:21
@DanGould DanGould merged commit 0849df8 into payjoin:master Aug 28, 2025
10 checks passed
// TODO: perhaps we should store pj uri in the session wrapper as to not replay the event log for each session
let receiver_pubkey = pj_param.receiver_pubkey();
let sender_state =
self.db.get_send_session_ids()?.into_iter().find_map(|session_id| {
Copy link
Collaborator

Choose a reason for hiding this comment

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

wouldn't it be simpler to add a variant of get_sens_session_ids that can use the expected RK in a WHERE clause?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

yes it would

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This PR was written with sled in mind and I totally forgot I could do this after I rebased.

@arminsabouri arminsabouri deleted the short-id-session-id-replacment branch August 28, 2025 18:11
arminsabouri added a commit to arminsabouri/rust-payjoin that referenced this pull request Aug 28, 2025
Follow up from payjoin#995.
We can replace a loop over all active session ids with a WHERE clause.
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.

5 participants