Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace Configuration with ergonomic RequestBuilder #106

Merged
merged 4 commits into from
Oct 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 16 additions & 29 deletions payjoin-cli/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ use bitcoincore_rpc::jsonrpc::serde_json;
use bitcoincore_rpc::RpcApi;
use clap::ArgMatches;
use config::{Config, File, FileFormat};
use payjoin::bitcoin;
use payjoin::bitcoin::psbt::Psbt;
use payjoin::receive::{Error, ProvisionalProposal};
use payjoin::{bitcoin, PjUriExt, UriExt};
use rouille::{Request, Response};
use serde::{Deserialize, Serialize};

Expand Down Expand Up @@ -42,21 +42,15 @@ impl App {
}

pub fn send_payjoin(&self, bip21: &str) -> Result<()> {
use payjoin::send::Configuration;

let link = payjoin::Uri::try_from(bip21)
.map_err(|e| anyhow!("Failed to create URI from BIP21: {}", e))?;

let link = link
.assume_checked()
.check_pj_supported()
.map_err(|e| anyhow!("The provided URI doesn't support payjoin (BIP78): {}", e))?;
let uri = payjoin::Uri::try_from(bip21)
.map_err(|e| anyhow!("Failed to create URI from BIP21: {}", e))?
.assume_checked();

let amount = link.amount.ok_or_else(|| anyhow!("please specify the amount in the Uri"))?;
let amount = uri.amount.ok_or_else(|| anyhow!("please specify the amount in the Uri"))?;

// wallet_create_funded_psbt requires a HashMap<address: String, Amount>
let mut outputs = HashMap::with_capacity(1);
outputs.insert(link.address.to_string(), amount);
outputs.insert(uri.address.to_string(), amount);

// TODO: make payjoin-cli send feerate configurable
// 2.1 sat/vB == 525 sat/kwu for testing purposes.
Expand Down Expand Up @@ -89,14 +83,11 @@ impl App {
let psbt = Psbt::from_str(&psbt).with_context(|| "Failed to load PSBT from base64")?;
log::debug!("Original psbt: {:#?}", psbt);

let payout_scripts = std::iter::once(link.address.script_pubkey());
// recommendation or bust for this simple reference implementation
let pj_params = Configuration::recommended(&psbt, payout_scripts, fee_rate)
.unwrap_or_else(|_| Configuration::non_incentivizing());
let (req, ctx) = payjoin::send::RequestBuilder::from_psbt_and_uri(psbt, uri)
.with_context(|| "Failed to build payjoin request")?
.build_recommended(fee_rate)
.with_context(|| "Failed to build payjoin request")?;

let (req, ctx) = link
.create_pj_request(psbt, pj_params)
.with_context(|| "Failed to create payjoin request")?;
let client = reqwest::blocking::Client::builder()
.danger_accept_invalid_certs(self.config.danger_accept_invalid_certs)
.build()
Expand Down Expand Up @@ -141,12 +132,10 @@ impl App {
amount.to_btc(),
self.config.pj_endpoint
);
let pj_uri = Uri::from_str(&pj_uri_string)
.map_err(|e| anyhow!("Constructed a bad URI string from args: {}", e))?;
let _pj_uri = pj_uri
.assume_checked()
.check_pj_supported()
.map_err(|e| anyhow!("Constructed URI does not support payjoin: {}", e))?;
// check that the URI is corrctly formatted
let _pj_uri = Uri::from_str(&pj_uri_string)
.map_err(|e| anyhow!("Constructed a bad URI string from args: {}", e))?
.assume_checked();

println!(
"Listening at {}. Configured to accept payjoin at BIP 21 Payjoin Uri:",
Expand Down Expand Up @@ -231,10 +220,8 @@ impl App {
};
let uri = payjoin::Uri::try_from(uri_string.clone())
.map_err(|_| Error::Server(anyhow!("Could not parse payjoin URI string.").into()))?;
let _ = uri
.assume_checked() // we just got it from bitcoind above
.check_pj_supported()
.map_err(|_| Error::Server(anyhow!("Created bip21 with invalid &pj=.").into()))?;
let _ = uri.assume_checked(); // we just got it from bitcoind above

Ok(Response::text(uri_string))
}

Expand Down
2 changes: 1 addition & 1 deletion payjoin/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,4 @@ pub(crate) mod weight;

#[cfg(feature = "base64")]
pub use bitcoin::base64;
pub use uri::{PjParseError, PjUri, PjUriExt, Uri, UriExt};
pub use uri::{PjParseError, PjUri, Uri};
23 changes: 9 additions & 14 deletions payjoin/src/send/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,20 +127,6 @@ impl std::error::Error for ValidationError {
}
}

#[derive(Debug)]
pub struct ConfigurationError(InternalConfigurationError);

#[derive(Debug)]
pub(crate) enum InternalConfigurationError {
PrevTxOut(crate::psbt::PrevTxOutError),
InputType(crate::input_type::InputTypeError),
NoInputs,
}

impl From<InternalConfigurationError> for ConfigurationError {
fn from(value: InternalConfigurationError) -> Self { ConfigurationError(value) }
}

/// Error returned when request could not be created.
///
/// This error can currently only happen due to programmer mistake.
Expand All @@ -163,6 +149,9 @@ pub(crate) enum InternalCreateRequestError {
ChangeIndexOutOfBounds,
ChangeIndexPointsAtPayee,
Url(url::ParseError),
UriDoesNotSupportPayjoin,
PrevTxOut(crate::psbt::PrevTxOutError),
InputType(crate::input_type::InputTypeError),
}

impl fmt::Display for CreateRequestError {
Expand All @@ -182,6 +171,9 @@ impl fmt::Display for CreateRequestError {
ChangeIndexOutOfBounds => write!(f, "fee output index is points out of bounds"),
ChangeIndexPointsAtPayee => write!(f, "fee output index is points at output belonging to the payee"),
Url(e) => write!(f, "cannot parse endpoint url: {:#?}", e),
UriDoesNotSupportPayjoin => write!(f, "the URI does not support payjoin"),
PrevTxOut(e) => write!(f, "invalid previous transaction output: {}", e),
InputType(e) => write!(f, "invalid input type: {}", e),
}
}
}
Expand All @@ -203,6 +195,9 @@ impl std::error::Error for CreateRequestError {
ChangeIndexOutOfBounds => None,
ChangeIndexPointsAtPayee => None,
Url(error) => Some(error),
UriDoesNotSupportPayjoin => None,
PrevTxOut(error) => Some(error),
InputType(error) => Some(error),
}
}
}
Expand Down
Loading
Loading