Skip to content

Commit a927035

Browse files
committed
Provide fallback instructions when v1 sender fails
Co-authored-by: DanGould <d@ngould.dev> Explicit fallback commands when either the http request or the psbt processing fails.
1 parent 5f292b1 commit a927035

2 files changed

Lines changed: 53 additions & 32 deletions

File tree

payjoin-cli/src/app/v1.rs

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use std::collections::HashMap;
22
use std::net::SocketAddr;
3-
use std::str::FromStr;
43
use std::sync::Arc;
54

65
use anyhow::{anyhow, Context, Result};
@@ -11,7 +10,7 @@ use hyper::server::conn::http1;
1110
use hyper::service::service_fn;
1211
use hyper::{Method, Request, Response, StatusCode};
1312
use hyper_util::rt::TokioIo;
14-
use payjoin::bitcoin::psbt::Psbt;
13+
use payjoin::bitcoin::consensus::encode::serialize_hex;
1514
use payjoin::bitcoin::{Amount, FeeRate};
1615
use payjoin::receive::v1::{PayjoinProposal, UncheckedOriginalPayload};
1716
use payjoin::receive::Error;
@@ -64,32 +63,44 @@ impl AppTrait for App {
6463
let uri = uri.check_pj_supported().map_err(|_| anyhow!("URI does not support Payjoin"))?;
6564
let amount = uri.amount.ok_or_else(|| anyhow!("please specify the amount in the Uri"))?;
6665
let psbt = self.create_original_psbt(&uri.address, amount, fee_rate)?;
66+
let fallback_tx = psbt.clone().extract_tx()?;
6767
let (req, ctx) = SenderBuilder::new(psbt, uri.clone())
6868
.build_recommended(fee_rate)
6969
.with_context(|| "Failed to build payjoin request")?
7070
.create_v1_post_request();
7171
let http = http_agent(&self.config)?;
7272
let body = String::from_utf8(req.body.clone()).unwrap();
7373
println!("Sending Original PSBT to {}", req.url);
74-
let response = http
74+
let response = match http
7575
.post(req.url)
7676
.header("Content-Type", req.content_type)
7777
.body(body.clone())
7878
.send()
7979
.await
80-
.with_context(|| "HTTP request failed")?;
81-
let fallback_tx = Psbt::from_str(&body)
82-
.map_err(|e| anyhow!("Failed to load PSBT from base64: {}", e))?
83-
.extract_tx()?;
84-
println!("Fallback transaction txid: {}", fallback_tx.compute_txid());
85-
println!(
86-
"Fallback transaction hex: {:#}",
87-
payjoin::bitcoin::consensus::encode::serialize_hex(&fallback_tx)
88-
);
89-
let psbt = ctx.process_response(&response.bytes().await?).map_err(|e| {
90-
tracing::debug!("Error processing response: {e:?}");
91-
anyhow!("Failed to process response {e}")
92-
})?;
80+
{
81+
Ok(response) => response,
82+
Err(e) => {
83+
tracing::error!("HTTP request failed: {e}");
84+
println!("Payjoin failed. To broadcast the fallback transaction, run:");
85+
println!(
86+
" bitcoin-cli -rpcwallet=<wallet> sendrawtransaction {:#}",
87+
serialize_hex(&fallback_tx)
88+
);
89+
return Err(anyhow!("HTTP request failed: {e}"));
90+
}
91+
};
92+
let psbt = match ctx.process_response(&response.bytes().await?) {
93+
Ok(psbt) => psbt,
94+
Err(e) => {
95+
tracing::error!("Error processing response: {e:?}");
96+
println!("Payjoin failed. To broadcast the fallback transaction, run:");
97+
println!(
98+
" bitcoin-cli -rpcwallet=<wallet> sendrawtransaction {:#}",
99+
serialize_hex(&fallback_tx)
100+
);
101+
return Err(anyhow!("Failed to process response {e}"));
102+
}
103+
};
93104

94105
self.process_pj_response(psbt)?;
95106
Ok(())

payjoin-cli/src/app/v2/mod.rs

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -166,9 +166,8 @@ impl AppTrait for App {
166166
match uri.extras.pj_param() {
167167
#[cfg(feature = "v1")]
168168
PjParam::V1(pj_param) => {
169-
use std::str::FromStr;
170-
171169
let psbt = self.create_original_psbt(&address, amount, fee_rate)?;
170+
let fallback_tx = psbt.clone().extract_tx()?;
172171
let (req, ctx) = payjoin::send::v1::SenderBuilder::from_parts(
173172
psbt,
174173
pj_param,
@@ -181,25 +180,36 @@ impl AppTrait for App {
181180
let http = http_agent(&self.config)?;
182181
let body = String::from_utf8(req.body.clone()).unwrap();
183182
println!("Sending Original PSBT to {}", req.url);
184-
let response = http
183+
let response = match http
185184
.post(req.url)
186185
.header("Content-Type", req.content_type)
187186
.body(body.clone())
188187
.send()
189188
.await
190-
.with_context(|| "HTTP request failed")?;
191-
let fallback_tx = payjoin::bitcoin::Psbt::from_str(&body)
192-
.map_err(|e| anyhow!("Failed to load PSBT from base64: {}", e))?
193-
.extract_tx()?;
194-
println!("Fallback transaction txid: {}", fallback_tx.compute_txid());
195-
println!(
196-
"Fallback transaction hex: {:#}",
197-
payjoin::bitcoin::consensus::encode::serialize_hex(&fallback_tx)
198-
);
199-
let psbt = ctx.process_response(&response.bytes().await?).map_err(|e| {
200-
tracing::debug!("Error processing response: {e:?}");
201-
anyhow!("Failed to process response {e}")
202-
})?;
189+
{
190+
Ok(response) => response,
191+
Err(e) => {
192+
tracing::error!("HTTP request failed: {e}");
193+
println!("Payjoin failed. To broadcast the fallback transaction, run:");
194+
println!(
195+
" bitcoin-cli -rpcwallet=<wallet> sendrawtransaction {:#}",
196+
payjoin::bitcoin::consensus::encode::serialize_hex(&fallback_tx)
197+
);
198+
return Err(anyhow!("HTTP request failed: {e}"));
199+
}
200+
};
201+
let psbt = match ctx.process_response(&response.bytes().await?) {
202+
Ok(psbt) => psbt,
203+
Err(e) => {
204+
tracing::error!("Error processing response: {e:?}");
205+
println!("Payjoin failed. To broadcast the fallback transaction, run:");
206+
println!(
207+
" bitcoin-cli -rpcwallet=<wallet> sendrawtransaction {:#}",
208+
payjoin::bitcoin::consensus::encode::serialize_hex(&fallback_tx)
209+
);
210+
return Err(anyhow!("Failed to process response {e}"));
211+
}
212+
};
203213

204214
self.process_pj_response(psbt)?;
205215
Ok(())

0 commit comments

Comments
 (0)