From a69ab27a629bf42fa7437b5186030a6b3d97660e Mon Sep 17 00:00:00 2001 From: xstoicunicornx Date: Wed, 17 Jun 2026 13:58:41 -0500 Subject: [PATCH] Add comments to FFI interface --- payjoin-ffi/src/receive/mod.rs | 84 ++++++++++++++++++++++++++++++++-- 1 file changed, 81 insertions(+), 3 deletions(-) diff --git a/payjoin-ffi/src/receive/mod.rs b/payjoin-ffi/src/receive/mod.rs index 9daa240d5..d6b3cf272 100644 --- a/payjoin-ffi/src/receive/mod.rs +++ b/payjoin-ffi/src/receive/mod.rs @@ -159,6 +159,9 @@ macro_rules! impl_terminal_cancel_for_receiver { #[uniffi::export] impl $ty { /// Cancel the Payjoin session immediately. + /// + /// Returns an [`CancelTransition`] that, once persisted, closes the + /// the receiver session. pub fn cancel(&self) -> CancelTransition { let transition = self.0.clone().cancel(); CancelTransition { @@ -174,6 +177,9 @@ macro_rules! impl_pending_fallback_cancel_for_receiver { #[uniffi::export] impl $ty { /// Cancel the Payjoin session and return pending fallback handling. + /// + /// Returns an [`CancelTransition`] that, once persisted, yields a + /// [`ReceiverPendingFallback`]. pub fn cancel(&self) -> CancelTransition { let transition = self.0.clone().cancel(); CancelTransition { @@ -201,6 +207,10 @@ impl_pending_fallback_cancel_for_receiver!(Monitor); #[uniffi::export] impl HasReplyableError { /// Cancel the Payjoin session without posting the receiver error response. + /// + /// Returns an [`CancelTransition`] that, once persisted, yields either + /// a [`ReceiverPendingFallback`] if current session has validated fallback tx, + /// or otherwise closes the session. pub fn cancel(&self) -> CancelTransition { let transition = self.0.clone().cancel(); CancelTransition { @@ -699,7 +709,11 @@ impl Initialized { .map_err(Into::into) } - /// The response can either be an UncheckedOriginalPayload or an ACCEPTED message indicating no UncheckedOriginalPayload is available yet. + /// Process the polling response from the directory. + /// + /// Returns an [`InitializedTransition`] that, once persisted, yields either + /// an [`UncheckedOriginalPayload`] if the sender's Original PSBT is available, + /// or [`Initialized`] if no proposal has arrived yet. pub fn process_response(&self, body: &[u8], ctx: &ClientResponse) -> InitializedTransition { InitializedTransition(Arc::new(RwLock::new(Some( self.0.clone().process_response(body, ctx.into()), @@ -781,6 +795,11 @@ pub trait CanBroadcast: Send + Sync { #[uniffi::export] impl UncheckedOriginalPayload { + /// Check that the sender's Original PSBT is suitable for broadcast, ensuring + /// it can be used as a fallback if the payjoin does not complete. + /// + /// Returns an [`UncheckedOriginalPayloadTransition`] that, once persisted, + /// yields a [`MaybeInputsOwned`] to continue validation. pub fn check_broadcast_suitability( &self, min_fee_rate_sat_per_kwu: Option, @@ -846,12 +865,21 @@ pub trait IsScriptOwned: Send + Sync { #[uniffi::export] impl MaybeInputsOwned { - /// The Sender’s Original PSBT + /// Extract the transaction from the Original PSBT for scheduling broadcast as a + /// fallback in case the payjoin does not complete. + /// + /// Returns the consensus-encoded raw transaction bytes. pub fn extract_tx_to_schedule_broadcast(&self) -> Vec { payjoin::bitcoin::consensus::encode::serialize( &self.0.clone().extract_tx_to_schedule_broadcast(), ) } + + /// Check that none of the Original PSBT's inputs belong to the receiver, + /// preventing an attacker from spending the receiver's own inputs. + /// + /// Returns a [`MaybeInputsOwnedTransition`] that, once persisted, + /// yields a [`MaybeInputsSeen`] to continue validation. pub fn check_inputs_not_owned( &self, is_owned: Arc, @@ -902,6 +930,12 @@ pub trait IsOutputKnown: Send + Sync { #[uniffi::export] impl MaybeInputsSeen { + /// Check that none of the inputs have been seen before, preventing input + /// probing and replay attacks (where inputs have been used in a previous + /// payjoin attempt). + /// + /// Returns a [`MaybeInputsSeenTransition`] that, once persisted, + /// yields an [`OutputsUnknown`] to continue validation. pub fn check_no_inputs_seen_before( &self, is_known: Arc, @@ -951,7 +985,11 @@ impl_save_for_transition!(OutputsUnknownTransition, WantsOutputs); #[uniffi::export] impl OutputsUnknown { - /// Find which outputs belong to the receiver + /// Identify which outputs in the original transaction belong to the receiver + /// and ensure at least one output pays the receiver. + /// + /// Returns an [`OutputsUnknownTransition`] that, once persisted, + /// yields a [`WantsOutputs`] to continue the proposal. pub fn identify_receiver_outputs( &self, is_receiver_output: Arc, @@ -994,8 +1032,14 @@ impl_save_for_transition!(WantsOutputsTransition, WantsInputs); #[uniffi::export] impl WantsOutputs { + /// Returns whether output substitution is enabled for this session. pub fn output_substitution(&self) -> OutputSubstitution { self.0.output_substitution() } + /// Replace all receiver outputs with the provided `replacement_outputs`, + /// and set up the `drain_script` as the receiver-owned output whose value + /// may be adjusted based on modifications in subsequent states. + /// + /// Returns an updated [`WantsOutputs`] with the replaced outputs. pub fn replace_receiver_outputs( &self, replacement_outputs: Vec, @@ -1013,6 +1057,9 @@ impl WantsOutputs { .map_err(Into::into) } + /// Substitute the receiver output script with the provided script. + /// + /// Returns an updated [`WantsOutputs`] with the substituted output. pub fn substitute_receiver_script( &self, output_script_pubkey: Vec, @@ -1026,6 +1073,10 @@ impl WantsOutputs { .map_err(Into::into) } + /// Commit the output modifications and proceed to input contribution. + /// + /// Returns a [`WantsOutputsTransition`] that, once persisted, + /// yields a [`WantsInputs`]. pub fn commit_outputs(&self) -> WantsOutputsTransition { WantsOutputsTransition(Arc::new(RwLock::new(Some(self.0.clone().commit_outputs())))) } @@ -1082,6 +1133,9 @@ impl WantsInputs { } } + /// Add the provided inputs to the payjoin proposal. + /// + /// Returns an updated [`WantsInputs`] with the contributed inputs. pub fn contribute_inputs( &self, replacement_inputs: Vec>, @@ -1095,6 +1149,10 @@ impl WantsInputs { .map_err(Into::into) } + /// Commit the input contributions and proceed to fee negotiation. + /// + /// Returns a [`WantsInputsTransition`] that, once persisted, + /// yields a [`WantsFeeRange`]. pub fn commit_inputs(&self) -> WantsInputsTransition { WantsInputsTransition(Arc::new(RwLock::new(Some(self.0.clone().commit_inputs())))) } @@ -1239,6 +1297,10 @@ pub trait ProcessPsbt: Send + Sync { #[uniffi::export] impl ProvisionalProposal { + /// Finalize the proposal by signing the PSBT via the `process_psbt` callback. + /// + /// Returns a [`ProvisionalProposalTransition`] that, once persisted, + /// yields the final [`PayjoinProposal`]. pub fn finalize_proposal( &self, process_psbt: Arc, @@ -1253,6 +1315,7 @@ impl ProvisionalProposal { )))) } + /// Extract the PSBT that needs to be signed by the receiver's wallet. pub fn psbt_to_sign(&self) -> String { self.0.clone().psbt_to_sign().to_string() } } @@ -1297,6 +1360,7 @@ impl_save_for_transition!(PayjoinProposalTransition, Monitor); #[uniffi::export] impl PayjoinProposal { + /// Returns the finalized payjoin proposal PSBT. pub fn psbt(&self) -> String { , @@ -1416,6 +1480,8 @@ impl HasReplyableErrorTransition { #[uniffi::export] impl HasReplyableError { + /// Construct an OHTTP encapsulated POST request to post the receiver + /// error response to the directory so it can be retrieved by the sender. pub fn create_error_request( &self, ohttp_relay: String, @@ -1425,6 +1491,13 @@ impl HasReplyableError { }) } + /// Process the response from the directory after posting the receiver + /// error response. + /// + /// Returns a [`HasReplyableErrorTransition`] that, once persisted, + /// completes the error reporting and either yields a + /// [`ReceiverPendingFallback`] if current session has validated fallback tx, + /// or otherwise closes the session. pub fn process_error_response( &self, body: &[u8], @@ -1505,6 +1578,11 @@ fn try_deserialize_tx( #[uniffi::export] impl Monitor { + /// Check the network for the payjoin or fallback transaction via the + /// `find_transaction` callback. + /// + /// Returns a [`MonitorTransition`] that, once persisted, completes + /// the session if a transaction is found. pub fn check_for_transaction( &self, find_transaction: Arc,