Skip to content

Commit b879ba6

Browse files
Add FFI bindings for async receive interface
1 parent 0c2988c commit b879ba6

1 file changed

Lines changed: 155 additions & 0 deletions

File tree

payjoin-ffi/src/receive/mod.rs

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -711,6 +711,12 @@ pub trait CanBroadcast: Send + Sync {
711711
fn callback(&self, tx: Vec<u8>) -> Result<bool, ForeignError>;
712712
}
713713

714+
#[uniffi::export(with_foreign)]
715+
#[async_trait::async_trait]
716+
pub trait CanBroadcastAsync: Send + Sync {
717+
async fn callback(&self, tx: Vec<u8>) -> Result<bool, ForeignError>;
718+
}
719+
714720
#[uniffi::export]
715721
impl UncheckedOriginalPayload {
716722
pub fn check_broadcast_suitability(
@@ -728,6 +734,28 @@ impl UncheckedOriginalPayload {
728734
)))))
729735
}
730736

737+
pub async fn check_broadcast_suitability_async(
738+
&self,
739+
min_fee_rate: Option<u64>,
740+
can_broadcast: Arc<dyn CanBroadcastAsync>,
741+
) -> Result<UncheckedOriginalPayloadTransition, FfiValidationError> {
742+
let min_fee_rate = validate_fee_rate_sat_per_kwu_opt(min_fee_rate)?;
743+
Ok(UncheckedOriginalPayloadTransition(Arc::new(RwLock::new(Some(
744+
self.0
745+
.clone()
746+
.check_broadcast_suitability_async(min_fee_rate, |transaction| {
747+
let bytes = payjoin::bitcoin::consensus::encode::serialize(transaction);
748+
async {
749+
can_broadcast
750+
.callback(bytes)
751+
.await
752+
.map_err(|e| ImplementationError::new(e).into())
753+
}
754+
})
755+
.await,
756+
)))))
757+
}
758+
731759
/// Call this method if the only way to initiate a Payjoin with this receiver
732760
/// requires manual intervention, as in most consumer wallets.
733761
///
@@ -775,6 +803,12 @@ pub trait IsScriptOwned: Send + Sync {
775803
fn callback(&self, script: Vec<u8>) -> Result<bool, ForeignError>;
776804
}
777805

806+
#[uniffi::export(with_foreign)]
807+
#[async_trait::async_trait]
808+
pub trait IsScriptOwnedAsync: Send + Sync {
809+
async fn callback(&self, script: Vec<u8>) -> Result<bool, ForeignError>;
810+
}
811+
778812
#[uniffi::export]
779813
impl MaybeInputsOwned {
780814
///The Sender’s Original PSBT
@@ -783,6 +817,7 @@ impl MaybeInputsOwned {
783817
&self.0.clone().extract_tx_to_schedule_broadcast(),
784818
)
785819
}
820+
786821
pub fn check_inputs_not_owned(
787822
&self,
788823
is_owned: Arc<dyn IsScriptOwned>,
@@ -793,6 +828,26 @@ impl MaybeInputsOwned {
793828
}),
794829
))))
795830
}
831+
832+
pub async fn check_inputs_not_owned_async(
833+
&self,
834+
is_owned: Arc<dyn IsScriptOwnedAsync>,
835+
) -> MaybeInputsOwnedTransition {
836+
MaybeInputsOwnedTransition(Arc::new(RwLock::new(Some(
837+
self.0
838+
.clone()
839+
.check_inputs_not_owned_async(&mut |input| {
840+
let bytes = input.to_bytes();
841+
async {
842+
is_owned
843+
.callback(bytes)
844+
.await
845+
.map_err(|e| ImplementationError::new(e).into())
846+
}
847+
})
848+
.await,
849+
))))
850+
}
796851
}
797852

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

888+
#[uniffi::export(with_foreign)]
889+
#[async_trait::async_trait]
890+
pub trait IsOutputKnownAsync: Send + Sync {
891+
async fn callback(&self, outpoint: OutPoint) -> Result<bool, ForeignError>;
892+
}
893+
833894
#[uniffi::export]
834895
impl MaybeInputsSeen {
835896
pub fn check_no_inputs_seen_before(
@@ -844,6 +905,26 @@ impl MaybeInputsSeen {
844905
}),
845906
))))
846907
}
908+
909+
pub async fn check_no_inputs_seen_before_async(
910+
&self,
911+
is_known: Arc<dyn IsOutputKnownAsync>,
912+
) -> MaybeInputsSeenTransition {
913+
MaybeInputsSeenTransition(Arc::new(RwLock::new(Some(
914+
self.0
915+
.clone()
916+
.check_no_inputs_seen_before_async(&mut |outpoint| {
917+
let plain_outpoint = OutPoint::from(*outpoint);
918+
async {
919+
is_known
920+
.callback(plain_outpoint)
921+
.await
922+
.map_err(|e| ImplementationError::new(e).into())
923+
}
924+
})
925+
.await,
926+
))))
927+
}
847928
}
848929

849930
/// The receiver has not yet identified which outputs belong to the receiver.
@@ -893,6 +974,26 @@ impl OutputsUnknown {
893974
}),
894975
))))
895976
}
977+
978+
pub async fn identify_receiver_outputs_async(
979+
&self,
980+
is_receiver_output: Arc<dyn IsScriptOwnedAsync>,
981+
) -> OutputsUnknownTransition {
982+
OutputsUnknownTransition(Arc::new(RwLock::new(Some(
983+
self.0
984+
.clone()
985+
.identify_receiver_outputs_async(&mut |input| {
986+
let bytes = input.to_bytes();
987+
async {
988+
is_receiver_output
989+
.callback(bytes)
990+
.await
991+
.map_err(|e| ImplementationError::new(e).into())
992+
}
993+
})
994+
.await,
995+
))))
996+
}
896997
}
897998

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

1264+
#[uniffi::export(with_foreign)]
1265+
#[async_trait::async_trait]
1266+
pub trait ProcessPsbtAsync: Send + Sync {
1267+
async fn callback(&self, psbt: String) -> Result<String, ForeignError>;
1268+
}
1269+
11631270
#[uniffi::export]
11641271
impl ProvisionalProposal {
11651272
pub fn finalize_proposal(
@@ -1176,6 +1283,27 @@ impl ProvisionalProposal {
11761283
))))
11771284
}
11781285

1286+
pub async fn finalize_proposal_async(
1287+
&self,
1288+
process_psbt: Arc<dyn ProcessPsbtAsync>,
1289+
) -> ProvisionalProposalTransition {
1290+
ProvisionalProposalTransition(Arc::new(RwLock::new(Some(
1291+
self.0
1292+
.clone()
1293+
.finalize_proposal_async(|pre_processed| {
1294+
let string = pre_processed.to_string();
1295+
async {
1296+
let psbt = process_psbt
1297+
.callback(string)
1298+
.await
1299+
.map_err(ImplementationError::new)?;
1300+
Ok(Psbt::from_str(&psbt).map_err(ImplementationError::new)?)
1301+
}
1302+
})
1303+
.await,
1304+
))))
1305+
}
1306+
11791307
pub fn psbt_to_sign(&self) -> String { self.0.clone().psbt_to_sign().to_string() }
11801308
}
11811309

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

1489+
#[uniffi::export(with_foreign)]
1490+
#[async_trait::async_trait]
1491+
pub trait TransactionExistsAsync: Send + Sync {
1492+
async fn callback(&self, txid: String) -> Result<Option<Vec<u8>>, ForeignError>;
1493+
}
1494+
13611495
#[allow(clippy::type_complexity)]
13621496
#[derive(uniffi::Object)]
13631497
pub struct MonitorTransition(
@@ -1433,6 +1567,27 @@ impl Monitor {
14331567
.map_err(|e| ImplementationError::new(e).into())
14341568
})))))
14351569
}
1570+
1571+
pub async fn monitor_async(
1572+
&self,
1573+
transaction_exists: Arc<dyn TransactionExistsAsync>,
1574+
) -> MonitorTransition {
1575+
MonitorTransition(Arc::new(RwLock::new(Some(
1576+
self.0
1577+
.clone()
1578+
.check_payment_async(|txid| {
1579+
let string = txid.to_string();
1580+
async {
1581+
transaction_exists
1582+
.callback(string)
1583+
.await
1584+
.and_then(|buf| buf.map(try_deserialize_tx).transpose())
1585+
.map_err(|e| ImplementationError::new(e).into())
1586+
}
1587+
})
1588+
.await,
1589+
))))
1590+
}
14361591
}
14371592

14381593
/// Session persister that should save and load events as JSON strings.

0 commit comments

Comments
 (0)