Skip to content

Commit c5a0f3c

Browse files
committed
f e2e working
1 parent 1edab3b commit c5a0f3c

11 files changed

+356
-340
lines changed

src/builder.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use crate::liquidity::LiquiditySource;
1212
use crate::logger::{log_error, log_info, FilesystemLogger, Logger};
1313
use crate::message_handler::NodeCustomMessageHandler;
1414
use crate::payjoin_receiver::PayjoinReceiver;
15-
use crate::payment::payjoin::send::PayjoinSender;
15+
use crate::payment::payjoin::handler::PayjoinHandler;
1616
use crate::payment::store::PaymentStore;
1717
use crate::peer_store::PeerStore;
1818
use crate::tx_broadcaster::TransactionBroadcaster;
@@ -1022,7 +1022,7 @@ fn build_with_store_internal(
10221022
let mut payjoin_sender = None;
10231023
let mut payjoin_receiver = None;
10241024
if let Some(pj_config) = payjoin_config {
1025-
payjoin_sender = Some(Arc::new(PayjoinSender::new(
1025+
payjoin_sender = Some(Arc::new(PayjoinHandler::new(
10261026
Arc::clone(&logger),
10271027
pj_config.payjoin_relay.clone(),
10281028
Arc::clone(&tx_sync),

src/error.rs

+6
Original file line numberDiff line numberDiff line change
@@ -222,3 +222,9 @@ impl From<lightning_transaction_sync::TxSyncError> for Error {
222222
Self::TxSyncFailed
223223
}
224224
}
225+
226+
impl From<reqwest::Error> for Error {
227+
fn from(e: reqwest::Error) -> Self {
228+
Self::PayjoinRequestCreationFailed
229+
}
230+
}

src/event.rs

+33-15
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ use lightning_liquidity::lsps2::utils::compute_opening_fee;
3434

3535
use bitcoin::blockdata::locktime::absolute::LockTime;
3636
use bitcoin::secp256k1::PublicKey;
37-
use bitcoin::OutPoint;
37+
use bitcoin::{OutPoint, ScriptBuf};
3838

3939
use rand::{thread_rng, Rng};
4040

@@ -144,28 +144,40 @@ pub enum Event {
144144
/// This will be `None` for events serialized by LDK Node v0.2.1 and prior.
145145
reason: Option<ClosureReason>,
146146
},
147+
/// Failed to send Payjoin transaction.
148+
///
149+
/// This event is emitted when our attempt to send Payjoin transaction fail.
150+
PayjoinPaymentPending {
151+
/// Transaction ID of the successfully sent Payjoin transaction.
152+
txid: bitcoin::Txid,
153+
/// docs
154+
amount: u64,
155+
/// docs
156+
receipient: ScriptBuf,
157+
},
147158
/// A Payjoin transaction has been successfully sent.
148159
///
149160
/// This event is emitted when we send a Payjoin transaction and it was accepted by the
150161
/// receiver, and then finalised and broadcasted by us.
151-
PayjoinTxSendSuccess {
162+
PayjoinPaymentSuccess {
152163
/// Transaction ID of the successfully sent Payjoin transaction.
153164
txid: bitcoin::Txid,
165+
/// docs
166+
amount: u64,
167+
/// docs
168+
receipient: ScriptBuf,
154169
},
155170
/// Failed to send Payjoin transaction.
156171
///
157172
/// This event is emitted when our attempt to send Payjoin transaction fail.
158-
PayjoinTxSendFailed {
173+
PayjoinPaymentFailed {
174+
/// docs
175+
amount: u64,
176+
/// docs
177+
receipient: ScriptBuf,
159178
/// Reason for the failure.
160179
reason: String,
161180
},
162-
/// Failed to send Payjoin transaction.
163-
///
164-
/// This event is emitted when our attempt to send Payjoin transaction fail.
165-
PayjoinPaymentPending {
166-
/// Transaction ID of the successfully sent Payjoin transaction.
167-
txid: bitcoin::Txid,
168-
},
169181
}
170182

171183
impl_writeable_tlv_based_enum!(Event,
@@ -208,14 +220,20 @@ impl_writeable_tlv_based_enum!(Event,
208220
(4, claimable_amount_msat, required),
209221
(6, claim_deadline, option),
210222
},
211-
(7, PayjoinTxSendSuccess) => {
223+
(7, PayjoinPaymentPending) => {
212224
(0, txid, required),
225+
(2, amount, required),
226+
(4, receipient, required),
213227
},
214-
(8, PayjoinTxSendFailed) => {
215-
(0, reason, required),
216-
},
217-
(9, PayjoinPaymentPending) => {
228+
(8, PayjoinPaymentSuccess) => {
218229
(0, txid, required),
230+
(2, amount, required),
231+
(4, receipient, required),
232+
},
233+
(9, PayjoinPaymentFailed) => {
234+
(0, amount, required),
235+
(2, receipient, required),
236+
(4, reason, required),
219237
};
220238
);
221239

src/lib.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ use error::Error;
112112

113113
pub use event::Event;
114114
use payjoin_receiver::PayjoinReceiver;
115-
use payment::payjoin::send::PayjoinSender;
115+
use payment::payjoin::handler::PayjoinHandler;
116116
pub use types::ChannelConfig;
117117

118118
pub use io::utils::generate_entropy_mnemonic;
@@ -192,7 +192,7 @@ pub struct Node {
192192
output_sweeper: Arc<Sweeper>,
193193
peer_manager: Arc<PeerManager>,
194194
connection_manager: Arc<ConnectionManager<Arc<FilesystemLogger>>>,
195-
payjoin_sender: Option<Arc<PayjoinSender>>,
195+
payjoin_sender: Option<Arc<PayjoinHandler>>,
196196
payjoin_receiver: Option<Arc<PayjoinReceiver>>,
197197
keys_manager: Arc<KeysManager>,
198198
network_graph: Arc<Graph>,

src/payment/payjoin/handler.rs

+239
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
use lightning::util::logger::Logger;
2+
use crate::config::PAYJOIN_REQUEST_TIMEOUT;
3+
use crate::error::Error;
4+
use crate::io::utils::ohttp_headers;
5+
use crate::logger::FilesystemLogger;
6+
use crate::types::{ChainSource, EventQueue, Wallet};
7+
use crate::Event;
8+
9+
use bitcoin::address::NetworkChecked;
10+
use bitcoin::block::Header;
11+
use bitcoin::psbt::Psbt;
12+
use bitcoin::{Address, Amount, BlockHash, Script, Transaction, Txid};
13+
use lightning::chain::channelmonitor::ANTI_REORG_DELAY;
14+
use lightning::chain::transaction::TransactionData;
15+
use lightning::chain::{BestBlock, Filter, WatchedOutput};
16+
use lightning::log_info;
17+
18+
use std::sync::{Arc, RwLock};
19+
20+
#[derive(Clone, Debug)]
21+
enum PayjoinTransaction {
22+
PendingFirstConfirmation {
23+
tx: Transaction,
24+
receiver: Address,
25+
amount: Amount,
26+
first_broadcast_height: u32,
27+
first_broadcast_hash: BlockHash,
28+
},
29+
PendingThresholdConfirmations {
30+
tx: Transaction,
31+
receiver: Address,
32+
amount: Amount,
33+
first_broadcast_height: u32,
34+
first_broadcast_hash: BlockHash,
35+
first_confirmation_height: u32,
36+
first_confirmation_hash: BlockHash,
37+
},
38+
}
39+
40+
impl PayjoinTransaction {
41+
fn txid(&self) -> Option<Txid> {
42+
match self {
43+
PayjoinTransaction::PendingFirstConfirmation { tx, .. } => Some(tx.txid()),
44+
PayjoinTransaction::PendingThresholdConfirmations { tx, .. } => Some(tx.txid()),
45+
}
46+
}
47+
fn first_confirmation_height(&self) -> Option<u32> {
48+
match self {
49+
PayjoinTransaction::PendingFirstConfirmation { .. } => None,
50+
PayjoinTransaction::PendingThresholdConfirmations {
51+
first_confirmation_height, ..
52+
} => Some(*first_confirmation_height),
53+
}
54+
}
55+
fn amount(&self) -> Amount {
56+
match self {
57+
PayjoinTransaction::PendingFirstConfirmation { amount, .. } => *amount,
58+
PayjoinTransaction::PendingThresholdConfirmations { amount, .. } => *amount,
59+
}
60+
}
61+
fn receiver(&self) -> Address {
62+
match self {
63+
PayjoinTransaction::PendingFirstConfirmation { receiver, .. } => receiver.clone(),
64+
PayjoinTransaction::PendingThresholdConfirmations { receiver, .. } => receiver.clone(),
65+
}
66+
}
67+
}
68+
69+
pub(crate) struct PayjoinHandler {
70+
logger: Arc<FilesystemLogger>,
71+
payjoin_relay: payjoin::Url,
72+
chain_source: Arc<ChainSource>,
73+
best_known_block: RwLock<Option<BestBlock>>,
74+
transactions: RwLock<Vec<PayjoinTransaction>>,
75+
event_queue: Arc<EventQueue>,
76+
wallet: Arc<Wallet>,
77+
}
78+
79+
impl PayjoinHandler {
80+
pub(crate) fn new(
81+
logger: Arc<FilesystemLogger>, payjoin_relay: payjoin::Url, chain_source: Arc<ChainSource>,
82+
event_queue: Arc<EventQueue>, wallet: Arc<Wallet>,
83+
) -> Self {
84+
Self {
85+
logger,
86+
payjoin_relay,
87+
transactions: RwLock::new(Vec::new()),
88+
best_known_block: RwLock::new(None),
89+
chain_source,
90+
event_queue,
91+
wallet,
92+
}
93+
}
94+
95+
pub(crate) fn payjoin_relay(&self) -> &payjoin::Url {
96+
&self.payjoin_relay
97+
}
98+
99+
pub(crate) async fn send_request(&self, request: &payjoin::Request) -> Result<Vec<u8>, Error> {
100+
let response = reqwest::Client::new()
101+
.post(request.url.clone())
102+
.body(request.body.clone())
103+
.timeout(PAYJOIN_REQUEST_TIMEOUT)
104+
.headers(ohttp_headers())
105+
.send()
106+
.await?;
107+
let response = response.error_for_status()?;
108+
let response = response.bytes().await?;
109+
let response = response.to_vec();
110+
Ok(response)
111+
}
112+
113+
pub(crate) fn finalise_payjoin_transaction(
114+
&self, payjoin_proposal: &mut Psbt, original_psbt: &mut Psbt,
115+
payjoin_uri: payjoin::Uri<NetworkChecked>,
116+
) -> Result<Transaction, Error> {
117+
let wallet = self.wallet.clone();
118+
wallet.sign_payjoin_proposal(payjoin_proposal, original_psbt)?;
119+
let tx = payjoin_proposal.clone().extract_tx();
120+
let our_input =
121+
tx.output.iter().find(|output| wallet.is_mine(&output.script_pubkey).unwrap_or(false));
122+
if let Some(our_input) = our_input {
123+
let best_known_block = self.best_known_block.read().unwrap().clone();
124+
let (current_height, current_hash) = match best_known_block {
125+
Some(b) => (b.height, b.block_hash),
126+
None => return Err(Error::PayjoinReceiverRequestValidationFailed), // fixeror
127+
};
128+
self.transactions.write().unwrap().push(PayjoinTransaction::PendingFirstConfirmation {
129+
tx: tx.clone(),
130+
receiver: payjoin_uri.address,
131+
amount: payjoin_uri.amount.unwrap_or_default(),
132+
first_broadcast_height: current_height,
133+
first_broadcast_hash: current_hash,
134+
});
135+
self.register_tx(&tx.txid(), &our_input.script_pubkey);
136+
Ok(tx)
137+
} else {
138+
Err(Error::PayjoinReceiverRequestValidationFailed) // fixeror
139+
}
140+
}
141+
142+
fn internal_transactions_confirmed(
143+
&self, header: &Header, txdata: &TransactionData, height: u32,
144+
) {
145+
let (_, tx) = txdata[0];
146+
let confirmed_tx_txid = tx.txid();
147+
let mut transactions = self.transactions.write().unwrap();
148+
let position = match transactions.iter().position(|o| o.txid() == Some(confirmed_tx_txid)) {
149+
Some(position) => position,
150+
None => {
151+
log_info!(self.logger, "Confirmed transaction {} not found in payjoin transactions", confirmed_tx_txid);
152+
return
153+
},
154+
};
155+
let pj_tx = transactions.remove(position);
156+
match pj_tx {
157+
PayjoinTransaction::PendingFirstConfirmation {
158+
ref tx,
159+
receiver,
160+
amount,
161+
first_broadcast_height,
162+
first_broadcast_hash,
163+
} => {
164+
transactions.push(PayjoinTransaction::PendingThresholdConfirmations {
165+
tx: tx.clone(),
166+
receiver,
167+
amount,
168+
first_broadcast_height,
169+
first_broadcast_hash,
170+
first_confirmation_height: height,
171+
first_confirmation_hash: header.block_hash(),
172+
});
173+
},
174+
_ => {
175+
unreachable!()
176+
},
177+
};
178+
}
179+
}
180+
181+
impl Filter for PayjoinHandler {
182+
fn register_tx(&self, txid: &Txid, script_pubkey: &Script) {
183+
self.chain_source.register_tx(txid, script_pubkey);
184+
}
185+
186+
fn register_output(&self, output: WatchedOutput) {
187+
self.chain_source.register_output(output);
188+
}
189+
}
190+
191+
impl lightning::chain::Confirm for PayjoinHandler {
192+
fn transactions_confirmed(&self, header: &Header, txdata: &TransactionData, height: u32) {
193+
self.internal_transactions_confirmed(header, txdata, height);
194+
}
195+
196+
fn transaction_unconfirmed(&self, _txid: &Txid) {}
197+
198+
fn best_block_updated(&self, header: &Header, height: u32) {
199+
*self.best_known_block.write().unwrap() =
200+
Some(BestBlock { height, block_hash: header.block_hash() });
201+
let mut transactions = self.transactions.write().unwrap();
202+
transactions.retain(|tx| {
203+
if let (Some(first_conf), Some(txid)) = (tx.first_confirmation_height(), tx.txid()) {
204+
if height - first_conf >= ANTI_REORG_DELAY {
205+
let _ = self.event_queue.add_event(Event::PayjoinPaymentSuccess {
206+
txid,
207+
amount: tx.amount().to_sat(),
208+
receipient: tx.receiver().into(),
209+
});
210+
false
211+
} else {
212+
true
213+
}
214+
} else {
215+
true
216+
}
217+
});
218+
}
219+
220+
fn get_relevant_txids(&self) -> Vec<(Txid, u32, Option<BlockHash>)> {
221+
let state_lock = self.transactions.read().unwrap();
222+
state_lock
223+
.iter()
224+
.filter_map(|o| match o {
225+
PayjoinTransaction::PendingThresholdConfirmations {
226+
tx,
227+
first_confirmation_height,
228+
first_confirmation_hash,
229+
..
230+
} => Some((
231+
tx.clone().txid(),
232+
first_confirmation_height.clone(),
233+
Some(first_confirmation_hash.clone()),
234+
)),
235+
_ => None,
236+
})
237+
.collect::<Vec<_>>()
238+
}
239+
}

0 commit comments

Comments
 (0)