mirror of
https://github.com/lndk-org/lndk.git
synced 2026-08-13 12:33:05 +02:00
Merge pull request #238 from IgnacioPorte/master
Improve CLI error messages
This commit is contained in:
commit
933d68bfa8
7 changed files with 174 additions and 238 deletions
277
src/cli.rs
277
src/cli.rs
|
|
@ -15,6 +15,36 @@ use std::path::PathBuf;
|
|||
use std::process::exit;
|
||||
use tonic::transport::{Certificate, Channel, ClientTlsConfig};
|
||||
use tonic::Request;
|
||||
use tonic_types::StatusExt;
|
||||
|
||||
trait ExitGracefully {
|
||||
fn exit_gracefully(self) -> !;
|
||||
}
|
||||
|
||||
impl ExitGracefully for &str {
|
||||
fn exit_gracefully(self) -> ! {
|
||||
println!("{self}");
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
impl ExitGracefully for tonic::Status {
|
||||
fn exit_gracefully(self) -> ! {
|
||||
let details = self.get_error_details();
|
||||
let error_msg = if let Some(error_info) = details.error_info() {
|
||||
format!("ERROR ({}): {}", error_info.reason, self.message())
|
||||
} else {
|
||||
format!("ERROR: {}", self.message())
|
||||
};
|
||||
error_msg.exit_gracefully();
|
||||
}
|
||||
}
|
||||
|
||||
impl ExitGracefully for lndk::offers::OfferError {
|
||||
fn exit_gracefully(self) -> ! {
|
||||
format!("ERROR ({}): {}", self.code(), self).exit_gracefully();
|
||||
}
|
||||
}
|
||||
|
||||
fn get_macaroon_path_default(network: &str) -> PathBuf {
|
||||
home::home_dir()
|
||||
|
|
@ -174,17 +204,8 @@ async fn main() {
|
|||
Commands::DecodeOffer { offer_string } => {
|
||||
println!("Decoding offer: {offer_string}.");
|
||||
match decode(offer_string) {
|
||||
Ok(offer) => {
|
||||
println!("Decoded offer: {:?}.", offer)
|
||||
}
|
||||
Err(e) => {
|
||||
println!(
|
||||
"ERROR please provide offer starting with lno. Provided offer is \
|
||||
invalid, failed to decode with error: {:?}.",
|
||||
e
|
||||
);
|
||||
exit(1)
|
||||
}
|
||||
Ok(offer) => println!("Decoded offer: {:?}.", offer),
|
||||
Err(e) => e.exit_gracefully(),
|
||||
}
|
||||
}
|
||||
Commands::DecodeInvoice { invoice_string } => {
|
||||
|
|
@ -195,14 +216,7 @@ async fn main() {
|
|||
Ok(invoice) => {
|
||||
println!("Decoded invoice: {:?}.", invoice);
|
||||
}
|
||||
Err(e) => {
|
||||
println!(
|
||||
"ERROR please provide hex-encoded invoice string. Provided invoice is \
|
||||
invalid, failed to decode with error: {:?}.",
|
||||
e
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
Err(e) => e.exit_gracefully(),
|
||||
}
|
||||
}
|
||||
Commands::PayOffer {
|
||||
|
|
@ -214,41 +228,19 @@ async fn main() {
|
|||
fee_limit_percent,
|
||||
} => {
|
||||
let tls = read_cert_from_args_or_exit(args.cert_pem, args.cert_path);
|
||||
let grpc_host = args.grpc_host;
|
||||
let grpc_port = args.grpc_port;
|
||||
let channel = Channel::from_shared(format!("{grpc_host}:{grpc_port}"))
|
||||
.unwrap_or_else(|e| {
|
||||
println!("ERROR creating endpoint: {e:?}");
|
||||
exit(1)
|
||||
})
|
||||
.tls_config(tls)
|
||||
.unwrap_or_else(|e| {
|
||||
println!("ERROR tls config: {e:?}");
|
||||
exit(1)
|
||||
})
|
||||
.connect()
|
||||
.await
|
||||
.unwrap_or_else(|e| {
|
||||
println!("ERROR connecting: {e:?}");
|
||||
exit(1)
|
||||
});
|
||||
let channel = create_grpc_channel(args.grpc_host, args.grpc_port, tls).await;
|
||||
let (mut client, macaroon) = create_authenticated_client(
|
||||
channel,
|
||||
args.macaroon_path,
|
||||
args.macaroon_hex,
|
||||
&args.network,
|
||||
);
|
||||
|
||||
let mut client = OffersClient::new(channel);
|
||||
|
||||
let offer = match decode(offer_string.to_owned()) {
|
||||
let offer = match decode(offer_string.to_string()) {
|
||||
Ok(offer) => offer,
|
||||
Err(e) => {
|
||||
println!(
|
||||
"ERROR: please provide offer starting with lno. Provided offer is \
|
||||
invalid, failed to decode with error: {:?}.",
|
||||
e
|
||||
);
|
||||
exit(1)
|
||||
}
|
||||
Err(e) => e.exit_gracefully(),
|
||||
};
|
||||
|
||||
let macaroon =
|
||||
read_macaroon_from_args(args.macaroon_path, args.macaroon_hex, &args.network);
|
||||
let mut request = Request::new(PayOfferRequest {
|
||||
offer: offer.to_string(),
|
||||
amount,
|
||||
|
|
@ -257,14 +249,11 @@ async fn main() {
|
|||
fee_limit,
|
||||
fee_limit_percent,
|
||||
});
|
||||
add_metadata(&mut request, macaroon).unwrap_or_else(|_| exit(1));
|
||||
add_metadata(&mut request, macaroon);
|
||||
|
||||
match client.pay_offer(request).await {
|
||||
Ok(_) => println!("Successfully paid for offer!"),
|
||||
Err(err) => {
|
||||
println!("Error paying for offer: {err:?}");
|
||||
exit(1)
|
||||
}
|
||||
Err(err) => err.exit_gracefully(),
|
||||
};
|
||||
}
|
||||
Commands::GetInvoice {
|
||||
|
|
@ -274,55 +263,29 @@ async fn main() {
|
|||
response_invoice_timeout,
|
||||
} => {
|
||||
let tls = read_cert_from_args_or_exit(args.cert_pem, args.cert_path);
|
||||
let grpc_host = args.grpc_host;
|
||||
let grpc_port = args.grpc_port;
|
||||
let channel = Channel::from_shared(format!("{grpc_host}:{grpc_port}"))
|
||||
.unwrap_or_else(|e| {
|
||||
println!("ERROR creating endpoint: {e:?}");
|
||||
exit(1)
|
||||
})
|
||||
.tls_config(tls)
|
||||
.unwrap_or_else(|e| {
|
||||
println!("ERROR tls config: {e:?}");
|
||||
exit(1)
|
||||
})
|
||||
.connect()
|
||||
.await
|
||||
.unwrap_or_else(|e| {
|
||||
println!("ERROR connecting: {e:?}");
|
||||
exit(1)
|
||||
});
|
||||
let channel = create_grpc_channel(args.grpc_host, args.grpc_port, tls).await;
|
||||
let (mut client, macaroon) = create_authenticated_client(
|
||||
channel,
|
||||
args.macaroon_path,
|
||||
args.macaroon_hex,
|
||||
&args.network,
|
||||
);
|
||||
|
||||
let mut client = OffersClient::new(channel);
|
||||
let offer = match decode(offer_string.to_owned()) {
|
||||
let offer = match decode(offer_string.to_string()) {
|
||||
Ok(offer) => offer,
|
||||
Err(e) => {
|
||||
println!(
|
||||
"ERROR: please provide offer starting with lno. Provided offer is \
|
||||
invalid, failed to decode with error: {:?}.",
|
||||
e
|
||||
);
|
||||
exit(1)
|
||||
}
|
||||
Err(e) => e.exit_gracefully(),
|
||||
};
|
||||
|
||||
let macaroon =
|
||||
read_macaroon_from_args(args.macaroon_path, args.macaroon_hex, &args.network);
|
||||
let mut request = Request::new(GetInvoiceRequest {
|
||||
offer: offer.to_string(),
|
||||
amount,
|
||||
payer_note,
|
||||
response_invoice_timeout,
|
||||
});
|
||||
add_metadata(&mut request, macaroon).unwrap_or_else(|_| exit(1));
|
||||
add_metadata(&mut request, macaroon);
|
||||
match client.get_invoice(request).await {
|
||||
Ok(response) => {
|
||||
println!("Invoice: {:?}.", response.get_ref())
|
||||
}
|
||||
Err(err) => {
|
||||
println!("Error getting invoice for offer: {err:?}");
|
||||
exit(1)
|
||||
}
|
||||
Ok(response) => println!("Invoice: {:?}.", response.get_ref()),
|
||||
Err(err) => err.exit_gracefully(),
|
||||
}
|
||||
}
|
||||
Commands::PayInvoice {
|
||||
|
|
@ -332,41 +295,24 @@ async fn main() {
|
|||
fee_limit_percent,
|
||||
} => {
|
||||
let tls = read_cert_from_args_or_exit(args.cert_pem, args.cert_path);
|
||||
let grpc_host = args.grpc_host.clone();
|
||||
let grpc_port = args.grpc_port;
|
||||
let channel = Channel::from_shared(format!("{grpc_host}:{grpc_port}"))
|
||||
.unwrap_or_else(|e| {
|
||||
println!("ERROR creating endpoint: {e:?}");
|
||||
exit(1)
|
||||
})
|
||||
.tls_config(tls)
|
||||
.unwrap_or_else(|e| {
|
||||
println!("ERROR tls config: {e:?}");
|
||||
exit(1)
|
||||
})
|
||||
.connect()
|
||||
.await
|
||||
.unwrap_or_else(|e| {
|
||||
println!("ERROR connecting: {e:?}");
|
||||
exit(1)
|
||||
});
|
||||
let channel = create_grpc_channel(args.grpc_host, args.grpc_port, tls).await;
|
||||
let (mut client, macaroon) = create_authenticated_client(
|
||||
channel,
|
||||
args.macaroon_path,
|
||||
args.macaroon_hex,
|
||||
&args.network,
|
||||
);
|
||||
|
||||
let mut client = OffersClient::new(channel);
|
||||
let macaroon =
|
||||
read_macaroon_from_args(args.macaroon_path, args.macaroon_hex, &args.network);
|
||||
let mut request = Request::new(PayInvoiceRequest {
|
||||
invoice: invoice_string.to_owned(),
|
||||
amount,
|
||||
fee_limit,
|
||||
fee_limit_percent,
|
||||
});
|
||||
add_metadata(&mut request, macaroon).unwrap_or_else(|_| exit(1));
|
||||
add_metadata(&mut request, macaroon);
|
||||
match client.pay_invoice(request).await {
|
||||
Ok(_) => println!("Successfully paid for offer!"),
|
||||
Err(err) => {
|
||||
println!("Error paying invoice: {err:?}");
|
||||
exit(1)
|
||||
}
|
||||
Err(err) => err.exit_gracefully(),
|
||||
}
|
||||
}
|
||||
Commands::CreateOffer {
|
||||
|
|
@ -377,28 +323,14 @@ async fn main() {
|
|||
quantity,
|
||||
} => {
|
||||
let tls = read_cert_from_args_or_exit(args.cert_pem, args.cert_path);
|
||||
let grpc_host = args.grpc_host.clone();
|
||||
let grpc_port = args.grpc_port;
|
||||
let channel = Channel::from_shared(format!("{grpc_host}:{grpc_port}"))
|
||||
.unwrap_or_else(|e| {
|
||||
println!("ERROR creating endpoint: {e:?}");
|
||||
exit(1)
|
||||
})
|
||||
.tls_config(tls)
|
||||
.unwrap_or_else(|e| {
|
||||
println!("ERROR tls config: {e:?}");
|
||||
exit(1)
|
||||
})
|
||||
.connect()
|
||||
.await
|
||||
.unwrap_or_else(|e| {
|
||||
println!("ERROR connecting: {e:?}");
|
||||
exit(1)
|
||||
});
|
||||
let channel = create_grpc_channel(args.grpc_host, args.grpc_port, tls).await;
|
||||
let (mut client, macaroon) = create_authenticated_client(
|
||||
channel,
|
||||
args.macaroon_path,
|
||||
args.macaroon_hex,
|
||||
&args.network,
|
||||
);
|
||||
|
||||
let mut client = OffersClient::new(channel);
|
||||
let macaroon =
|
||||
read_macaroon_from_args(args.macaroon_path, args.macaroon_hex, &args.network);
|
||||
let mut request = Request::new(CreateOfferRequest {
|
||||
amount,
|
||||
quantity,
|
||||
|
|
@ -406,27 +338,42 @@ async fn main() {
|
|||
issuer,
|
||||
expiry,
|
||||
});
|
||||
add_metadata(&mut request, macaroon).unwrap_or_else(|_| exit(1));
|
||||
add_metadata(&mut request, macaroon);
|
||||
match client.create_offer(request).await {
|
||||
Ok(response) => {
|
||||
println!("Offer: {:?}.", response.get_ref())
|
||||
}
|
||||
Err(err) => {
|
||||
println!("Error creating offer: {err:?}");
|
||||
exit(1)
|
||||
}
|
||||
Ok(response) => println!("Offer: {:?}.", response.get_ref()),
|
||||
Err(err) => err.exit_gracefully(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn add_metadata<R>(request: &mut Request<R>, macaroon: String) -> Result<(), ()> {
|
||||
let macaroon = macaroon.parse().map_err(|e| {
|
||||
println!("Error parsing provided macaroon string into tonic metadata {e:?}")
|
||||
})?;
|
||||
request.metadata_mut().insert("macaroon", macaroon);
|
||||
async fn create_grpc_channel(grpc_host: String, grpc_port: u16, tls: ClientTlsConfig) -> Channel {
|
||||
Channel::from_shared(format!("{grpc_host}:{grpc_port}"))
|
||||
.unwrap_or_else(|e| format!("ERROR: failed to create endpoint {e:?}").exit_gracefully())
|
||||
.tls_config(tls)
|
||||
.unwrap_or_else(|e| format!("ERROR: failed to configure tls {e:?}").exit_gracefully())
|
||||
.connect()
|
||||
.await
|
||||
.unwrap_or_else(|e| format!("ERROR: failed to connect {e:?}").exit_gracefully())
|
||||
}
|
||||
|
||||
Ok(())
|
||||
fn create_authenticated_client(
|
||||
channel: Channel,
|
||||
macaroon_path: Option<PathBuf>,
|
||||
macaroon_hex: Option<String>,
|
||||
network: &str,
|
||||
) -> (OffersClient<Channel>, String) {
|
||||
let client = OffersClient::new(channel);
|
||||
let macaroon = read_macaroon_from_args(macaroon_path, macaroon_hex, network);
|
||||
(client, macaroon)
|
||||
}
|
||||
|
||||
fn add_metadata<R>(request: &mut Request<R>, macaroon: String) {
|
||||
let macaroon = macaroon.parse().unwrap_or_else(|e| {
|
||||
format!("ERROR: failed to parse provided macaroon string into tonic metadata {e:?}")
|
||||
.exit_gracefully()
|
||||
});
|
||||
request.metadata_mut().insert("macaroon", macaroon);
|
||||
}
|
||||
|
||||
fn read_macaroon_from_file(path: PathBuf) -> Result<String, std::io::Error> {
|
||||
|
|
@ -450,7 +397,7 @@ fn read_cert_from_args(
|
|||
let pem = match (&cert_pem, &cert_path) {
|
||||
(Some(pem), _) => pem.clone(),
|
||||
(None, Some(cert_path)) => std::fs::read_to_string(cert_path)
|
||||
.map_err(|e| format!("ERROR reading cert: {:?}", e))?,
|
||||
.map_err(|e| format!("ERROR: failed to read cert: {:?}", e))?,
|
||||
(None, None) => {
|
||||
// If no cert pem string is provided, we'll look for the tls certificate in the
|
||||
// default location.
|
||||
|
|
@ -459,7 +406,7 @@ fn read_cert_from_args(
|
|||
.join(DEFAULT_LNDK_DIR)
|
||||
.join(DEFAULT_DATA_DIR);
|
||||
std::fs::read_to_string(data_dir.join(TLS_CERT_FILENAME))
|
||||
.map_err(|e| format!("ERROR reading cert: {:?}", e))?
|
||||
.map_err(|e| format!("ERROR: failed to read cert: {:?}", e))?
|
||||
}
|
||||
};
|
||||
let cert = Certificate::from_pem(pem);
|
||||
|
|
@ -474,10 +421,7 @@ fn read_cert_from_args_or_exit(
|
|||
) -> ClientTlsConfig {
|
||||
match read_cert_from_args(cert_pem, cert_path) {
|
||||
Ok(config) => config,
|
||||
Err(err) => {
|
||||
println!("{}", err);
|
||||
exit(1);
|
||||
}
|
||||
Err(err) => err.exit_gracefully(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -488,24 +432,21 @@ fn read_macaroon_from_args(
|
|||
) -> String {
|
||||
// Make sure both macaroon options are not set.
|
||||
if macaroon_path.is_some() && macaroon_hex.is_some() {
|
||||
println!("ERROR: Only one of `macaroon_path` or `macaroon_hex` should be set.");
|
||||
exit(1)
|
||||
"ERROR: Only one of `macaroon_path` or `macaroon_hex` should be set.".exit_gracefully();
|
||||
}
|
||||
|
||||
// Let's grab the macaroon string now. If neither macaroon_path nor macaroon_hex are
|
||||
// set, use the default macaroon path.
|
||||
match macaroon_path {
|
||||
Some(path) => read_macaroon_from_file(path.clone()).unwrap_or_else(|e| {
|
||||
println!("ERROR reading macaroon from file {e:?}");
|
||||
exit(1)
|
||||
format!("ERROR: failed to read macaroon from file {e:?}").exit_gracefully()
|
||||
}),
|
||||
None => match &macaroon_hex {
|
||||
Some(macaroon) => macaroon.clone(),
|
||||
None => {
|
||||
let path = get_macaroon_path_default(network);
|
||||
read_macaroon_from_file(path).unwrap_or_else(|e| {
|
||||
println!("ERROR reading macaroon from file {e:?}");
|
||||
exit(1)
|
||||
format!("ERROR: failed to read macaroon from file {e:?}").exit_gracefully()
|
||||
})
|
||||
}
|
||||
},
|
||||
|
|
@ -568,7 +509,9 @@ mod tests {
|
|||
let result = read_cert_from_args(None, Some(invalid_path));
|
||||
|
||||
assert!(result.is_err());
|
||||
assert!(result.unwrap_err().starts_with("ERROR reading cert:"));
|
||||
assert!(result
|
||||
.unwrap_err()
|
||||
.starts_with("ERROR: failed to read cert:"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -373,7 +373,7 @@ pub enum LndError {
|
|||
/// LND node is not connected to bitcoin network.
|
||||
NetworkNotConnected,
|
||||
/// LND service is unavailable or not responding.
|
||||
ServiceUnavailable(LndStatus),
|
||||
ServiceUnavailable(String),
|
||||
}
|
||||
|
||||
impl LndError {
|
||||
|
|
|
|||
|
|
@ -189,11 +189,15 @@ impl InvoicePayer for Client {
|
|||
.router()
|
||||
.track_payment_v2(req)
|
||||
.await
|
||||
.map_err(OfferError::TrackFailure)?
|
||||
.map_err(|e| OfferError::TrackFailure(e.message().to_string()))?
|
||||
.into_inner();
|
||||
|
||||
// Wait for a failed or successful payment.
|
||||
while let Some(payment) = stream.message().await.map_err(OfferError::TrackFailure)? {
|
||||
while let Some(payment) = stream
|
||||
.message()
|
||||
.await
|
||||
.map_err(|e| OfferError::TrackFailure(e.message().to_string()))?
|
||||
{
|
||||
if payment.status() == tonic_lnd::lnrpc::payment::PaymentStatus::Succeeded {
|
||||
return Ok(payment);
|
||||
} else if payment.status() == tonic_lnd::lnrpc::payment::PaymentStatus::Failed {
|
||||
|
|
|
|||
|
|
@ -122,13 +122,13 @@ pub(crate) async fn send_payment(
|
|||
params.fee_limit,
|
||||
)
|
||||
.await
|
||||
.map_err(OfferError::RouteFailure)?;
|
||||
.map_err(|e| OfferError::RouteFailure(e.message().to_string()))?;
|
||||
|
||||
trace!("Routes found {}...", resp.routes.len());
|
||||
let resp = payer
|
||||
.send_to_route(params.payment_hash, resp.routes[0].clone())
|
||||
.await
|
||||
.map_err(OfferError::RouteFailure)?;
|
||||
.map_err(|e| OfferError::RouteFailure(e.message().to_string()))?;
|
||||
|
||||
trace!(
|
||||
"Sent payment using preimage {} using attempt_id {} with status {}. {}",
|
||||
|
|
@ -253,13 +253,13 @@ pub(super) async fn create_invoice_info_from_request(
|
|||
let invoice_response = creator
|
||||
.add_invoice(invoice_request)
|
||||
.await
|
||||
.map_err(OfferError::AddInvoiceFailure)?;
|
||||
.map_err(|e| OfferError::AddInvoiceFailure(e.message().to_string()))?;
|
||||
let payment_request = invoice_response.payment_request;
|
||||
log::trace!("Payment request: {:?}", payment_request);
|
||||
let payreq = creator
|
||||
.decode_payment_request(payment_request)
|
||||
.await
|
||||
.map_err(OfferError::DecodePaymentRequestFailure)?;
|
||||
.map_err(|e| OfferError::DecodePaymentRequestFailure(e.message().to_string()))?;
|
||||
log::trace!("Decoded payment request: {:?}", payreq);
|
||||
let payment_hash =
|
||||
bitcoin::hashes::sha256::Hash::from_str(&payreq.payment_hash).map_err(|e| {
|
||||
|
|
@ -290,7 +290,7 @@ pub async fn create_reply_path_for_offer_creation(
|
|||
// Find introduction channels for our blinded paths.
|
||||
let current_channels = connector.list_active_public_channels().await.map_err(|e| {
|
||||
error!("Could not lookup current channels: {e}.");
|
||||
OfferError::ListChannelsFailure(e)
|
||||
OfferError::ListChannelsFailure(e.message().to_string())
|
||||
})?;
|
||||
|
||||
let mut intro_channels = HashSet::new();
|
||||
|
|
@ -376,7 +376,7 @@ pub async fn create_reply_path_for_outgoing_payments(
|
|||
// Find an introduction node for our blinded path.
|
||||
let current_peers = connector.list_peers().await.map_err(|e| {
|
||||
error!("Could not lookup current peers: {e}.");
|
||||
OfferError::ListPeersFailure(e)
|
||||
OfferError::ListPeersFailure(e.message().to_string())
|
||||
})?;
|
||||
|
||||
let mut intro_node = None;
|
||||
|
|
@ -535,7 +535,7 @@ pub(crate) async fn connect_to_peer(
|
|||
let resp = connector
|
||||
.list_peers()
|
||||
.await
|
||||
.map_err(OfferError::PeerConnectError)?;
|
||||
.map_err(|e| OfferError::PeerConnectError(e.message().to_string()))?;
|
||||
|
||||
let node_id_str = node_id.to_string();
|
||||
for peer in resp.peers.iter() {
|
||||
|
|
@ -551,7 +551,7 @@ pub(crate) async fn connect_to_peer(
|
|||
let node = connector
|
||||
.get_node_info(node_id_str.clone(), false)
|
||||
.await
|
||||
.map_err(OfferError::PeerConnectError)?;
|
||||
.map_err(|e| OfferError::PeerConnectError(e.message().to_string()))?;
|
||||
|
||||
let node = match node.node {
|
||||
Some(node) => node,
|
||||
|
|
@ -565,7 +565,7 @@ pub(crate) async fn connect_to_peer(
|
|||
connector
|
||||
.connect_peer(node_id_str, node.addresses[0].clone().addr)
|
||||
.await
|
||||
.map_err(OfferError::PeerConnectError)?;
|
||||
.map_err(|e| OfferError::PeerConnectError(e.message().to_string()))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -596,7 +596,7 @@ pub(super) async fn get_node_id_from_scid(
|
|||
.lightning_read_only()
|
||||
.get_chan_info(get_info_request)
|
||||
.await
|
||||
.map_err(OfferError::GetChannelInfo)?
|
||||
.map_err(|e| OfferError::GetChannelInfo(e.message().to_string()))?
|
||||
.into_inner();
|
||||
match direction {
|
||||
Direction::NodeOne => Ok(channel_info.node1_pub),
|
||||
|
|
|
|||
|
|
@ -6,13 +6,12 @@ use lightning::{
|
|||
offers::{merkle::SignError, parse::Bolt12ParseError, parse::Bolt12SemanticError},
|
||||
};
|
||||
use tonic::{Code, Status};
|
||||
use tonic_lnd::tonic::Status as LndStatus;
|
||||
use tonic_types::{ErrorDetails, StatusExt};
|
||||
|
||||
mod client_impls;
|
||||
pub mod handler;
|
||||
mod lnd_requests;
|
||||
mod parse;
|
||||
pub mod parse;
|
||||
|
||||
pub(crate) use lnd_requests::connect_to_peer_with_retry;
|
||||
pub use lnd_requests::create_reply_path_for_offer_creation;
|
||||
|
|
@ -29,23 +28,23 @@ pub enum OfferError {
|
|||
/// SignError indicates a failure to sign the invoice request.
|
||||
SignError(SignError),
|
||||
/// DeriveKeyFailure indicates a failure to derive key for signing the invoice request.
|
||||
DeriveKeyFailure(LndStatus),
|
||||
DeriveKeyFailure(String),
|
||||
/// User provided an invalid amount.
|
||||
InvalidAmount(String),
|
||||
/// Invalid currency contained in the offer.
|
||||
InvalidCurrency,
|
||||
/// Unable to connect to peer.
|
||||
PeerConnectError(LndStatus),
|
||||
PeerConnectError(String),
|
||||
/// No node address.
|
||||
NodeAddressNotFound,
|
||||
/// Cannot list peers.
|
||||
ListPeersFailure(LndStatus),
|
||||
ListPeersFailure(String),
|
||||
/// Failure to build a reply path.
|
||||
BuildBlindedPathFailure,
|
||||
/// Unable to find or send to payment route.
|
||||
RouteFailure(LndStatus),
|
||||
RouteFailure(String),
|
||||
/// Failed to track payment.
|
||||
TrackFailure(LndStatus),
|
||||
TrackFailure(String),
|
||||
/// Failed to send payment.
|
||||
PaymentFailure,
|
||||
/// Failed to receive an invoice back from offer creator before the timeout.
|
||||
|
|
@ -53,15 +52,15 @@ pub enum OfferError {
|
|||
/// Failed to find introduction node for blinded path.
|
||||
IntroductionNodeNotFound,
|
||||
/// Cannot fetch channel info.
|
||||
GetChannelInfo(LndStatus),
|
||||
GetChannelInfo(String),
|
||||
/// Failed to create offer.
|
||||
CreateOfferFailure(Bolt12SemanticError),
|
||||
/// Failed to create offer with expiry time given system clock.
|
||||
CreateOfferTimeFailure,
|
||||
/// Failed to add invoice.
|
||||
AddInvoiceFailure(LndStatus),
|
||||
AddInvoiceFailure(String),
|
||||
/// Failed to decode payment request.
|
||||
DecodePaymentRequestFailure(LndStatus),
|
||||
DecodePaymentRequestFailure(String),
|
||||
/// Failed to parse payment hash.
|
||||
ParsePaymentHashFailure(String),
|
||||
/// Failed to parse offer.
|
||||
|
|
@ -71,7 +70,7 @@ pub enum OfferError {
|
|||
/// Failed to encode invoice.
|
||||
EncodeInvoiceFailure(BitcoinIoError),
|
||||
/// Cannot list channels.
|
||||
ListChannelsFailure(LndStatus),
|
||||
ListChannelsFailure(String),
|
||||
}
|
||||
|
||||
impl OfferError {
|
||||
|
|
@ -131,53 +130,44 @@ impl Display for OfferError {
|
|||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
OfferError::AlreadyProcessing(id) => {
|
||||
write!(
|
||||
f,
|
||||
"We're already trying to pay for a payment with this id {id}"
|
||||
)
|
||||
write!(f, "Payment with id {id} already in progress")
|
||||
}
|
||||
OfferError::BuildUIRFailure(e) => write!(f, "Error building invoice request: {e:?}"),
|
||||
OfferError::SignError(e) => write!(f, "Error signing invoice request: {e:?}"),
|
||||
OfferError::DeriveKeyFailure(e) => write!(f, "Error signing invoice request: {e:?}"),
|
||||
OfferError::InvalidAmount(e) => write!(f, "User provided an invalid amount: {e:?}"),
|
||||
OfferError::InvalidCurrency => write!(
|
||||
f,
|
||||
"LNDK doesn't yet support offer currencies other than bitcoin"
|
||||
),
|
||||
OfferError::PeerConnectError(e) => write!(f, "Error connecting to peer: {e:?}"),
|
||||
OfferError::NodeAddressNotFound => write!(f, "Couldn't get node address"),
|
||||
OfferError::ListPeersFailure(e) => write!(f, "Error listing peers: {e:?}"),
|
||||
OfferError::BuildBlindedPathFailure => write!(f, "Error building blinded path"),
|
||||
OfferError::RouteFailure(e) => write!(f, "Error routing payment: {e:?}"),
|
||||
OfferError::TrackFailure(e) => write!(f, "Error tracking payment: {e:?}"),
|
||||
OfferError::PaymentFailure => write!(f, "Failed to send payment"),
|
||||
OfferError::InvoiceTimeout(e) => write!(f, "Did not receive invoice in {e:?} seconds."),
|
||||
OfferError::IntroductionNodeNotFound => write!(f, "Could not find introduction node."),
|
||||
OfferError::GetChannelInfo(e) => write!(f, "Could not fetch channel info: {e:?}"),
|
||||
OfferError::CreateOfferFailure(e) => write!(f, "Could not create offer: {e:?}"),
|
||||
OfferError::CreateOfferTimeFailure => write!(
|
||||
f,
|
||||
"Could not create offer with expiry time given system clock"
|
||||
),
|
||||
OfferError::BuildUIRFailure(e) => write!(f, "Failed to build invoice request: {e:?}"),
|
||||
OfferError::SignError(e) => write!(f, "Failed to sign invoice request: {e:?}"),
|
||||
OfferError::DeriveKeyFailure(e) => write!(f, "Failed to derive key: {e}"),
|
||||
OfferError::InvalidAmount(e) => write!(f, "Invalid amount: {e}"),
|
||||
OfferError::InvalidCurrency => write!(f, "Only bitcoin currency is supported"),
|
||||
OfferError::PeerConnectError(e) => write!(f, "Failed to connect to peer: {e}"),
|
||||
OfferError::NodeAddressNotFound => write!(f, "Node address not found"),
|
||||
OfferError::ListPeersFailure(e) => write!(f, "Failed to list peers: {e}"),
|
||||
OfferError::BuildBlindedPathFailure => write!(f, "Failed to build blinded path"),
|
||||
OfferError::RouteFailure(e) => write!(f, "Failed to route payment: {e}"),
|
||||
OfferError::TrackFailure(e) => write!(f, "Failed to track payment: {e}"),
|
||||
OfferError::PaymentFailure => write!(f, "Payment failed"),
|
||||
OfferError::InvoiceTimeout(e) => {
|
||||
write!(f, "Invoice request timed out after {e} seconds")
|
||||
}
|
||||
OfferError::IntroductionNodeNotFound => write!(f, "Introduction node not found"),
|
||||
OfferError::GetChannelInfo(e) => write!(f, "Failed to get channel info: {e}"),
|
||||
OfferError::CreateOfferFailure(e) => write!(f, "Failed to create offer: {e:?}"),
|
||||
OfferError::CreateOfferTimeFailure => write!(f, "Invalid offer expiry time"),
|
||||
OfferError::AddInvoiceFailure(e) => {
|
||||
write!(f, "Could not add invoice to lnd node: {e:?}")
|
||||
write!(f, "Failed to add invoice to lnd node: {e}")
|
||||
}
|
||||
OfferError::DecodePaymentRequestFailure(e) => {
|
||||
write!(f, "Could not decode payment request: {e:?}")
|
||||
write!(f, "Failed to decode payment request: {e}")
|
||||
}
|
||||
OfferError::ParsePaymentHashFailure(e) => {
|
||||
write!(f, "Could not parse payment hash: {e:?}")
|
||||
write!(f, "Failed to parse payment hash: {e:?}")
|
||||
}
|
||||
OfferError::ParseOfferFailure(e) => {
|
||||
write!(f, "The provided offer was invalid. Please provide a valid offer in bech32 format, i.e. starting with 'lno'. Error: {e:?}")
|
||||
write!(f, "Invalid offer: must start with 'lno'. Error: {e:?}")
|
||||
}
|
||||
OfferError::ParseInvoiceFailure(e) => {
|
||||
write!(f, "The provided invoice was invalid. Please provide a valid invoice in hex format. Error: {e:?}")
|
||||
}
|
||||
OfferError::EncodeInvoiceFailure(e) => {
|
||||
write!(f, "Failed to encode invoice to hex format. Error: {e:?}")
|
||||
write!(f, "Invalid invoice: must be hex format. Error: {e:?}")
|
||||
}
|
||||
OfferError::ListChannelsFailure(e) => write!(f, "Error listing channels: {e:?}"),
|
||||
OfferError::EncodeInvoiceFailure(e) => write!(f, "Failed to encode invoice: {e:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,14 @@
|
|||
use lightning::{
|
||||
offers::{
|
||||
offer::{Amount, Offer},
|
||||
parse::Bolt12ParseError,
|
||||
},
|
||||
offers::offer::{Amount, Offer},
|
||||
onion_message::messenger::Destination,
|
||||
};
|
||||
use std::str::FromStr;
|
||||
|
||||
use super::OfferError;
|
||||
|
||||
/// Decodes a bech32 offer string into an LDK offer.
|
||||
pub fn decode(offer_str: String) -> Result<Offer, Bolt12ParseError> {
|
||||
offer_str.parse::<Offer>()
|
||||
pub fn decode(offer_str: String) -> Result<Offer, OfferError> {
|
||||
Offer::from_str(&offer_str).map_err(OfferError::ParseOfferFailure)
|
||||
}
|
||||
|
||||
/// Get the destination of an offer.
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ use crate::lnd::{get_lnd_client, get_network, Creds, LndCfg, LndError};
|
|||
use crate::lndkrpc::{CreateOfferRequest, CreateOfferResponse};
|
||||
use crate::offers::get_destination;
|
||||
use crate::offers::handler::{CreateOfferParams, PayOfferParams};
|
||||
use crate::offers::parse::decode;
|
||||
use crate::offers::validate_amount;
|
||||
use crate::offers::OfferError;
|
||||
use crate::{lndkrpc, Bolt12InvoiceString, OfferHandler};
|
||||
|
|
@ -10,7 +11,7 @@ use lightning::blinded_path::payment::BlindedPaymentPath;
|
|||
use lightning::blinded_path::{Direction, IntroductionNode};
|
||||
use lightning::ln::channelmanager::PaymentId;
|
||||
use lightning::offers::invoice::Bolt12Invoice;
|
||||
use lightning::offers::offer::{Offer, Quantity};
|
||||
use lightning::offers::offer::Quantity;
|
||||
use lightning::sign::EntropySource;
|
||||
use lightning::util::ser::Writeable;
|
||||
use lndkrpc::offers_server::Offers;
|
||||
|
|
@ -71,7 +72,7 @@ impl Offers for LNDKServer {
|
|||
let mut client = get_lnd_client(lnd_cfg)?;
|
||||
|
||||
let inner_request = request.get_ref();
|
||||
let offer = Offer::from_str(&inner_request.offer).map_err(OfferError::ParseOfferFailure)?;
|
||||
let offer = decode(inner_request.offer.clone())?;
|
||||
|
||||
let destination = get_destination(&offer).await?;
|
||||
let reply_path = None;
|
||||
|
|
@ -79,7 +80,7 @@ impl Offers for LNDKServer {
|
|||
.lightning()
|
||||
.get_info(GetInfoRequest {})
|
||||
.await
|
||||
.map_err(LndError::ServiceUnavailable)?
|
||||
.map_err(|e| LndError::ServiceUnavailable(e.message().to_string()))?
|
||||
.into_inner();
|
||||
let network = get_network(info).await?;
|
||||
|
||||
|
|
@ -139,7 +140,7 @@ impl Offers for LNDKServer {
|
|||
let mut client = get_lnd_client(lnd_cfg)?;
|
||||
|
||||
let inner_request = request.get_ref();
|
||||
let offer = Offer::from_str(&inner_request.offer).map_err(OfferError::ParseOfferFailure)?;
|
||||
let offer = decode(inner_request.offer.clone())?;
|
||||
|
||||
let destination = get_destination(&offer).await?;
|
||||
let reply_path = None;
|
||||
|
|
@ -148,7 +149,7 @@ impl Offers for LNDKServer {
|
|||
.lightning()
|
||||
.get_info(GetInfoRequest {})
|
||||
.await
|
||||
.map_err(LndError::ServiceUnavailable)?
|
||||
.map_err(|e| LndError::ServiceUnavailable(e.message().to_string()))?
|
||||
.into_inner();
|
||||
let network = get_network(info).await?;
|
||||
|
||||
|
|
@ -238,7 +239,7 @@ impl Offers for LNDKServer {
|
|||
.lightning()
|
||||
.get_info(GetInfoRequest {})
|
||||
.await
|
||||
.map_err(LndError::ServiceUnavailable)?
|
||||
.map_err(|e| LndError::ServiceUnavailable(e.message().to_string()))?
|
||||
.into_inner();
|
||||
let network = get_network(info).await?;
|
||||
let quantity = parse_quantity(inner_request.quantity);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue