Skip to content

Commit

Permalink
Enable payjoin expiration
Browse files Browse the repository at this point in the history
  • Loading branch information
DanGould committed Dec 20, 2023
1 parent 6c58885 commit 4a681f2
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 21 deletions.
29 changes: 21 additions & 8 deletions mutiny-core/src/nodemanager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -809,8 +809,11 @@ impl<S: MutinyStorage> NodeManager<S> {
for payjoin in all {
let wallet = nm.wallet.clone();
let stop = nm.stop.clone();
let storage = Arc::new(nm.storage.clone());
utils::spawn(async move {
let pj_txid = Self::receive_payjoin(wallet, stop, payjoin).await.unwrap();
let pj_txid = Self::receive_payjoin(wallet, stop, storage, payjoin)
.await
.unwrap();
log::info!("Received payjoin txid: {}", pj_txid);
});
}
Expand Down Expand Up @@ -1057,14 +1060,17 @@ impl<S: MutinyStorage> NodeManager<S> {
.process_res(ohttp_response.as_ref(), context)
.map_err(|e| anyhow!("parse error {}", e))
.unwrap();
self.storage.persist_payjoin(enrolled.clone()).unwrap();
let session = self.storage.persist_payjoin(enrolled.clone()).unwrap();
let pj_uri = enrolled.fallback_target();
log_debug!(self.logger, "{pj_uri}");
// run await payjoin task in the background as it'll keep polling the relay
let wallet = self.wallet.clone();
let stop = self.stop.clone();
let storage = Arc::new(self.storage.clone());
utils::spawn(async move {
let pj_txid = Self::receive_payjoin(wallet, stop, enrolled).await.unwrap();
let pj_txid = Self::receive_payjoin(wallet, stop, storage, session)
.await
.unwrap();
log::info!("Received payjoin txid: {}", pj_txid);
});
Some(pj_uri)
Expand Down Expand Up @@ -1155,14 +1161,15 @@ impl<S: MutinyStorage> NodeManager<S> {
pub async fn receive_payjoin(
wallet: Arc<OnChainWallet<S>>,
stop: Arc<AtomicBool>,
mut enrolled: payjoin::receive::v2::Enrolled,
storage: Arc<S>,
mut session: crate::payjoin::Session,
) -> Result<Txid, MutinyError> {
let http_client = reqwest::Client::builder()
//.danger_accept_invalid_certs(true) ? is tls unchecked :O
.build()
.unwrap();
let proposal: payjoin::receive::v2::UncheckedProposal =
Self::poll_for_fallback_psbt(stop, &http_client, &mut enrolled)
Self::poll_for_fallback_psbt(stop, storage, &http_client, &mut session)
.await
.unwrap();
let payjoin_proposal = wallet.process_payjoin_proposal(proposal).unwrap();
Expand All @@ -1187,17 +1194,23 @@ impl<S: MutinyStorage> NodeManager<S> {

async fn poll_for_fallback_psbt(
stop: Arc<AtomicBool>,
storage: Arc<S>,
client: &reqwest::Client,
enroller: &mut payjoin::receive::v2::Enrolled,
session: &mut crate::payjoin::Session,
) -> Result<payjoin::receive::v2::UncheckedProposal, ()> {
loop {
if stop.load(Ordering::Relaxed) {
return Err(()); // stopped
}
let (req, context) = enroller.extract_req().unwrap();
if session.expiry < utils::now() {
let _ = storage.delete_payjoin(&session.enrolled.pubkey());
return Err(()); // expired
}
let (req, context) = session.enrolled.extract_req().unwrap();
let ohttp_response = client.post(req.url).body(req.body).send().await.unwrap();
let ohttp_response = ohttp_response.bytes().await.unwrap();
let proposal = enroller
let proposal = session
.enrolled
.process_res(ohttp_response.as_ref(), context)
.map_err(|e| anyhow!("parse error {}", e))
.unwrap();
Expand Down
45 changes: 32 additions & 13 deletions mutiny-core/src/payjoin.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,28 @@
use crate::error::MutinyError;
use crate::storage::MutinyStorage;
use bitcoin::hashes::hex::ToHex;
use core::time::Duration;
use payjoin::receive::v2::Enrolled;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

// #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
// pub struct Session {
// pub id: [u8; 16],
// pub enrolled: Enrolled,
// }
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Session {
pub enrolled: Enrolled,
pub expiry: Duration,
}

impl Session {
pub fn pubkey(&self) -> [u8; 33] {
self.enrolled.pubkey()
}
}

pub trait PayjoinStorage {
fn get_payjoin(&self, id: &[u8; 33]) -> Result<Option<Enrolled>, MutinyError>;
fn get_payjoins(&self) -> Result<Vec<Enrolled>, MutinyError>;
fn persist_payjoin(&self, session: Enrolled) -> Result<(), MutinyError>;
fn get_payjoin(&self, id: &[u8; 33]) -> Result<Option<Session>, MutinyError>;
fn get_payjoins(&self) -> Result<Vec<Session>, MutinyError>;
fn persist_payjoin(&self, session: Enrolled) -> Result<Session, MutinyError>;
fn delete_payjoin(&self, id: &[u8; 33]) -> Result<(), MutinyError>;
}

const PAYJOIN_KEY_PREFIX: &str = "payjoin/";
Expand All @@ -23,17 +32,27 @@ fn get_payjoin_key(id: &[u8; 33]) -> String {
}

impl<S: MutinyStorage> PayjoinStorage for S {
fn get_payjoin(&self, id: &[u8; 33]) -> Result<Option<Enrolled>, MutinyError> {
fn get_payjoin(&self, id: &[u8; 33]) -> Result<Option<Session>, MutinyError> {
let sessions = self.get_data(get_payjoin_key(id))?;
Ok(sessions)
}

fn get_payjoins(&self) -> Result<Vec<Enrolled>, MutinyError> {
let map: HashMap<String, Enrolled> = self.scan(PAYJOIN_KEY_PREFIX, None)?;
fn get_payjoins(&self) -> Result<Vec<Session>, MutinyError> {
let map: HashMap<String, Session> = self.scan(PAYJOIN_KEY_PREFIX, None)?;
Ok(map.values().map(|v| v.to_owned()).collect())
}

fn persist_payjoin(&self, session: Enrolled) -> Result<(), MutinyError> {
self.set_data(get_payjoin_key(&session.pubkey()), session, None)
fn persist_payjoin(&self, enrolled: Enrolled) -> Result<Session, MutinyError> {
let in_24_hours = crate::utils::now() + Duration::from_secs(60 * 60 * 24);
let session = Session {
enrolled,
expiry: in_24_hours,
};
self.set_data(get_payjoin_key(&session.pubkey()), session.clone(), None)
.map(|_| session)
}

fn delete_payjoin(&self, id: &[u8; 33]) -> Result<(), MutinyError> {
self.delete(&[get_payjoin_key(id)])
}
}

0 comments on commit 4a681f2

Please sign in to comment.