|
1 | 1 | use crate::event::HTLCStatus; |
2 | 2 | use crate::labels::LabelStorage; |
3 | 3 | use crate::logging::LOGGING_KEY; |
4 | | -use crate::payjoin::PayjoinStorage; |
| 4 | +use crate::payjoin::{Error as PayjoinError, PayjoinStorage}; |
5 | 5 | use crate::utils::{sleep, spawn}; |
6 | 6 | use crate::ActivityItem; |
7 | 7 | use crate::MutinyInvoice; |
@@ -48,6 +48,7 @@ use lightning::util::logger::*; |
48 | 48 | use lightning::{log_debug, log_error, log_info, log_warn}; |
49 | 49 | use lightning_invoice::Bolt11Invoice; |
50 | 50 | use lightning_transaction_sync::EsploraSyncClient; |
| 51 | +use payjoin::receive::v2::Enrolled; |
51 | 52 | use payjoin::Uri; |
52 | 53 | use reqwest::Client; |
53 | 54 | use serde::{Deserialize, Serialize}; |
@@ -580,15 +581,7 @@ impl<S: MutinyStorage> NodeManager<S> { |
580 | 581 | pub(crate) fn resume_payjoins(nm: Arc<NodeManager<S>>) { |
581 | 582 | let all = nm.storage.get_payjoins().unwrap_or_default(); |
582 | 583 | for payjoin in all { |
583 | | - let wallet = nm.wallet.clone(); |
584 | | - let stop = nm.stop.clone(); |
585 | | - let storage = Arc::new(nm.storage.clone()); |
586 | | - utils::spawn(async move { |
587 | | - let pj_txid = Self::receive_payjoin(wallet, stop, storage, payjoin) |
588 | | - .await |
589 | | - .unwrap(); |
590 | | - log::info!("Received payjoin txid: {}", pj_txid); |
591 | | - }); |
| 584 | + nm.clone().spawn_payjoin_receiver(payjoin); |
592 | 585 | } |
593 | 586 | } |
594 | 587 |
|
@@ -680,6 +673,31 @@ impl<S: MutinyStorage> NodeManager<S> { |
680 | 673 | Err(MutinyError::WalletOperationFailed) |
681 | 674 | } |
682 | 675 |
|
| 676 | + pub async fn start_payjoin_session(&self) -> Result<Enrolled, PayjoinError> { |
| 677 | + // DANGER! TODO get from &self config, do not get config directly from PAYJOIN_DIR ohttp-gateway |
| 678 | + // That would reveal IP address |
| 679 | + |
| 680 | + let http_client = reqwest::Client::builder().build()?; |
| 681 | + |
| 682 | + let ohttp_config_base64 = http_client |
| 683 | + .get(format!("{}/ohttp-config", crate::payjoin::PAYJOIN_DIR)) |
| 684 | + .send() |
| 685 | + .await? |
| 686 | + .text() |
| 687 | + .await?; |
| 688 | + |
| 689 | + let mut enroller = payjoin::receive::v2::Enroller::from_relay_config( |
| 690 | + crate::payjoin::PAYJOIN_DIR, |
| 691 | + &ohttp_config_base64, |
| 692 | + crate::payjoin::OHTTP_RELAYS[0], // TODO pick ohttp relay at random |
| 693 | + ); |
| 694 | + // enroll client |
| 695 | + let (req, context) = enroller.extract_req()?; |
| 696 | + let ohttp_response = http_client.post(req.url).body(req.body).send().await?; |
| 697 | + let ohttp_response = ohttp_response.bytes().await?; |
| 698 | + Ok(enroller.process_res(ohttp_response.as_ref(), context)?) |
| 699 | + } |
| 700 | + |
683 | 701 | // Send v1 payjoin request |
684 | 702 | pub async fn send_payjoin( |
685 | 703 | &self, |
@@ -753,38 +771,45 @@ impl<S: MutinyStorage> NodeManager<S> { |
753 | 771 | Ok(txid) |
754 | 772 | } |
755 | 773 |
|
| 774 | + pub fn spawn_payjoin_receiver(&self, session: crate::payjoin::Session) { |
| 775 | + let logger = self.logger.clone(); |
| 776 | + let wallet = self.wallet.clone(); |
| 777 | + let stop = self.stop.clone(); |
| 778 | + let storage = Arc::new(self.storage.clone()); |
| 779 | + utils::spawn(async move { |
| 780 | + match Self::receive_payjoin(wallet, stop, storage, session).await { |
| 781 | + Ok(txid) => log_info!(logger, "Received payjoin txid: {txid}"), |
| 782 | + Err(e) => log_error!(logger, "Error receiving payjoin: {e}"), |
| 783 | + }; |
| 784 | + }); |
| 785 | + } |
| 786 | + |
756 | 787 | /// Poll the payjoin relay to maintain a payjoin session and create a payjoin proposal. |
757 | | - pub async fn receive_payjoin( |
| 788 | + async fn receive_payjoin( |
758 | 789 | wallet: Arc<OnChainWallet<S>>, |
759 | 790 | stop: Arc<AtomicBool>, |
760 | 791 | storage: Arc<S>, |
761 | 792 | mut session: crate::payjoin::Session, |
762 | | - ) -> Result<Txid, MutinyError> { |
| 793 | + ) -> Result<Txid, PayjoinError> { |
763 | 794 | let http_client = reqwest::Client::builder() |
764 | 795 | //.danger_accept_invalid_certs(true) ? is tls unchecked :O |
765 | | - .build() |
766 | | - .unwrap(); |
| 796 | + .build()?; |
767 | 797 | let proposal: payjoin::receive::v2::UncheckedProposal = |
768 | 798 | Self::poll_for_fallback_psbt(stop, storage, &http_client, &mut session) |
769 | 799 | .await |
770 | 800 | .unwrap(); |
771 | | - let payjoin_proposal = wallet.process_payjoin_proposal(proposal).unwrap(); |
| 801 | + let payjoin_proposal = wallet |
| 802 | + .process_payjoin_proposal(proposal) |
| 803 | + .map_err(PayjoinError::Wallet)?; |
772 | 804 |
|
773 | | - let (req, ohttp_ctx) = payjoin_proposal.extract_v2_req().unwrap(); // extraction failed |
774 | | - let res = http_client |
775 | | - .post(req.url) |
776 | | - .body(req.body) |
777 | | - .send() |
778 | | - .await |
779 | | - .unwrap(); |
780 | | - let res = res.bytes().await.unwrap(); |
| 805 | + let (req, ohttp_ctx) = payjoin_proposal.extract_v2_req()?; // extraction failed |
| 806 | + let res = http_client.post(req.url).body(req.body).send().await?; |
| 807 | + let res = res.bytes().await?; |
781 | 808 | // enroll must succeed |
782 | | - let _res = payjoin_proposal |
783 | | - .deserialize_res(res.to_vec(), ohttp_ctx) |
784 | | - .unwrap(); |
| 809 | + let _res = payjoin_proposal.deserialize_res(res.to_vec(), ohttp_ctx)?; |
785 | 810 | // convert from bitcoin 29 to 30 |
786 | 811 | let txid = payjoin_proposal.psbt().clone().extract_tx().txid(); |
787 | | - let txid = Txid::from_str(&txid.to_string()).unwrap(); |
| 812 | + let txid = Txid::from_str(&txid.to_string()).map_err(PayjoinError::Txid)?; |
788 | 813 | Ok(txid) |
789 | 814 | } |
790 | 815 |
|
|
0 commit comments