Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 155 additions & 0 deletions payjoin-ffi/src/receive/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -711,6 +711,12 @@ pub trait CanBroadcast: Send + Sync {
fn callback(&self, tx: Vec<u8>) -> Result<bool, ForeignError>;
}

#[uniffi::export(with_foreign)]
#[async_trait::async_trait]
pub trait CanBroadcastAsync: Send + Sync {
async fn callback(&self, tx: Vec<u8>) -> Result<bool, ForeignError>;
}

#[uniffi::export]
impl UncheckedOriginalPayload {
pub fn check_broadcast_suitability(
Expand All @@ -728,6 +734,28 @@ impl UncheckedOriginalPayload {
)))))
}

pub async fn check_broadcast_suitability_async(
&self,
min_fee_rate: Option<u64>,
can_broadcast: Arc<dyn CanBroadcastAsync>,
) -> Result<UncheckedOriginalPayloadTransition, FfiValidationError> {
let min_fee_rate = validate_fee_rate_sat_per_kwu_opt(min_fee_rate)?;
Ok(UncheckedOriginalPayloadTransition(Arc::new(RwLock::new(Some(
self.0
.clone()
.check_broadcast_suitability_async(min_fee_rate, |transaction| {
let bytes = payjoin::bitcoin::consensus::encode::serialize(transaction);
async {
can_broadcast
.callback(bytes)
.await
.map_err(|e| ImplementationError::new(e).into())
}
})
.await,
)))))
}

/// Call this method if the only way to initiate a Payjoin with this receiver
/// requires manual intervention, as in most consumer wallets.
///
Expand Down Expand Up @@ -775,6 +803,12 @@ pub trait IsScriptOwned: Send + Sync {
fn callback(&self, script: Vec<u8>) -> Result<bool, ForeignError>;
}

#[uniffi::export(with_foreign)]
#[async_trait::async_trait]
pub trait IsScriptOwnedAsync: Send + Sync {
async fn callback(&self, script: Vec<u8>) -> Result<bool, ForeignError>;
}

#[uniffi::export]
impl MaybeInputsOwned {
///The Sender’s Original PSBT
Expand All @@ -783,6 +817,7 @@ impl MaybeInputsOwned {
&self.0.clone().extract_tx_to_schedule_broadcast(),
)
}

pub fn check_inputs_not_owned(
&self,
is_owned: Arc<dyn IsScriptOwned>,
Expand All @@ -793,6 +828,26 @@ impl MaybeInputsOwned {
}),
))))
}

pub async fn check_inputs_not_owned_async(
&self,
is_owned: Arc<dyn IsScriptOwnedAsync>,
) -> MaybeInputsOwnedTransition {
MaybeInputsOwnedTransition(Arc::new(RwLock::new(Some(
self.0
.clone()
.check_inputs_not_owned_async(&mut |input| {
let bytes = input.to_bytes();
async {
is_owned
.callback(bytes)
.await
.map_err(|e| ImplementationError::new(e).into())
}
})
.await,
))))
}
}

#[derive(Clone, uniffi::Object)]
Expand Down Expand Up @@ -830,6 +885,12 @@ pub trait IsOutputKnown: Send + Sync {
fn callback(&self, outpoint: OutPoint) -> Result<bool, ForeignError>;
}

#[uniffi::export(with_foreign)]
#[async_trait::async_trait]
pub trait IsOutputKnownAsync: Send + Sync {
async fn callback(&self, outpoint: OutPoint) -> Result<bool, ForeignError>;
}

#[uniffi::export]
impl MaybeInputsSeen {
pub fn check_no_inputs_seen_before(
Expand All @@ -844,6 +905,26 @@ impl MaybeInputsSeen {
}),
))))
}

pub async fn check_no_inputs_seen_before_async(
&self,
is_known: Arc<dyn IsOutputKnownAsync>,
) -> MaybeInputsSeenTransition {
MaybeInputsSeenTransition(Arc::new(RwLock::new(Some(
self.0
.clone()
.check_no_inputs_seen_before_async(&mut |outpoint| {
let plain_outpoint = OutPoint::from(*outpoint);
async {
is_known
.callback(plain_outpoint)
.await
.map_err(|e| ImplementationError::new(e).into())
}
})
.await,
))))
}
}

/// The receiver has not yet identified which outputs belong to the receiver.
Expand Down Expand Up @@ -893,6 +974,26 @@ impl OutputsUnknown {
}),
))))
}

pub async fn identify_receiver_outputs_async(
&self,
is_receiver_output: Arc<dyn IsScriptOwnedAsync>,
) -> OutputsUnknownTransition {
OutputsUnknownTransition(Arc::new(RwLock::new(Some(
self.0
.clone()
.identify_receiver_outputs_async(&mut |input| {
let bytes = input.to_bytes();
async {
is_receiver_output
.callback(bytes)
.await
.map_err(|e| ImplementationError::new(e).into())
}
})
.await,
))))
}
}

#[derive(uniffi::Object)]
Expand Down Expand Up @@ -1160,6 +1261,12 @@ pub trait ProcessPsbt: Send + Sync {
fn callback(&self, psbt: String) -> Result<String, ForeignError>;
}

#[uniffi::export(with_foreign)]
#[async_trait::async_trait]
pub trait ProcessPsbtAsync: Send + Sync {
async fn callback(&self, psbt: String) -> Result<String, ForeignError>;
}

#[uniffi::export]
impl ProvisionalProposal {
pub fn finalize_proposal(
Expand All @@ -1176,6 +1283,27 @@ impl ProvisionalProposal {
))))
}

pub async fn finalize_proposal_async(
&self,
process_psbt: Arc<dyn ProcessPsbtAsync>,
) -> ProvisionalProposalTransition {
ProvisionalProposalTransition(Arc::new(RwLock::new(Some(
self.0
.clone()
.finalize_proposal_async(|pre_processed| {
let string = pre_processed.to_string();
async {
let psbt = process_psbt
.callback(string)
.await
.map_err(ImplementationError::new)?;
Ok(Psbt::from_str(&psbt).map_err(ImplementationError::new)?)
}
})
.await,
))))
}

pub fn psbt_to_sign(&self) -> String { self.0.clone().psbt_to_sign().to_string() }
}

Expand Down Expand Up @@ -1358,6 +1486,12 @@ pub trait TransactionExists: Send + Sync {
fn callback(&self, txid: String) -> Result<Option<Vec<u8>>, ForeignError>;
}

#[uniffi::export(with_foreign)]
#[async_trait::async_trait]
pub trait TransactionExistsAsync: Send + Sync {
async fn callback(&self, txid: String) -> Result<Option<Vec<u8>>, ForeignError>;
}

#[allow(clippy::type_complexity)]
#[derive(uniffi::Object)]
pub struct MonitorTransition(
Expand Down Expand Up @@ -1433,6 +1567,27 @@ impl Monitor {
.map_err(|e| ImplementationError::new(e).into())
})))))
}

pub async fn monitor_async(
&self,
transaction_exists: Arc<dyn TransactionExistsAsync>,
) -> MonitorTransition {
MonitorTransition(Arc::new(RwLock::new(Some(
self.0
.clone()
.check_payment_async(|txid| {
let string = txid.to_string();
async {
transaction_exists
.callback(string)
.await
.and_then(|buf| buf.map(try_deserialize_tx).transpose())
.map_err(|e| ImplementationError::new(e).into())
}
})
.await,
))))
}
}

/// Session persister that should save and load events as JSON strings.
Expand Down
4 changes: 2 additions & 2 deletions payjoin/src/core/psbt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ pub(crate) trait PsbtExt: Sized {
) -> &mut BTreeMap<bip32::Xpub, (bip32::Fingerprint, bip32::DerivationPath)>;
fn proprietary_mut(&mut self) -> &mut BTreeMap<psbt::raw::ProprietaryKey, Vec<u8>>;
fn unknown_mut(&mut self) -> &mut BTreeMap<psbt::raw::Key, Vec<u8>>;
fn input_pairs(&self) -> Box<dyn Iterator<Item = InternalInputPair<'_>> + '_>;
fn input_pairs(&self) -> Box<dyn Iterator<Item = InternalInputPair<'_>> + Send + '_>;
// guarantees that length of psbt input matches that of unsigned_tx inputs and same
/// thing for outputs.
fn validate(self) -> Result<Self, InconsistentPsbt>;
Expand All @@ -63,7 +63,7 @@ impl PsbtExt for Psbt {

fn unknown_mut(&mut self) -> &mut BTreeMap<psbt::raw::Key, Vec<u8>> { &mut self.unknown }

fn input_pairs(&self) -> Box<dyn Iterator<Item = InternalInputPair<'_>> + '_> {
fn input_pairs(&self) -> Box<dyn Iterator<Item = InternalInputPair<'_>> + Send + '_> {
Box::new(
self.unsigned_tx
.input
Expand Down
Loading