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

Bolt 12 #806

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
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
24 changes: 24 additions & 0 deletions mutiny-core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::esplora::TxSyncError;
use aes::cipher::block_padding::UnpadError;
use bitcoin::Network;
use lightning::ln::peer_handler::PeerHandleError;
use lightning::offers::parse::Bolt12SemanticError;
use lightning_invoice::payment::PaymentError;
use lightning_invoice::ParseOrSemanticError;
use lightning_rapid_gossip_sync::GraphSyncError;
Expand Down Expand Up @@ -446,3 +447,26 @@ impl From<nostr::event::builder::Error> for MutinyError {
Self::NostrError
}
}

impl From<Bolt12SemanticError> for MutinyError {
fn from(e: Bolt12SemanticError) -> Self {
match e {
Bolt12SemanticError::UnsupportedChain => MutinyError::NetworkMismatch,
Bolt12SemanticError::UnexpectedChain => MutinyError::NetworkMismatch,
Bolt12SemanticError::MissingAmount => MutinyError::BadAmountError,
Bolt12SemanticError::InvalidAmount => MutinyError::BadAmountError,
Bolt12SemanticError::InsufficientAmount => MutinyError::BadAmountError,
Bolt12SemanticError::UnexpectedAmount => MutinyError::BadAmountError,
Bolt12SemanticError::UnsupportedCurrency => MutinyError::BadAmountError,
Bolt12SemanticError::MissingSigningPubkey => MutinyError::PubkeyInvalid,
Bolt12SemanticError::InvalidSigningPubkey => MutinyError::PubkeyInvalid,
Bolt12SemanticError::UnexpectedSigningPubkey => MutinyError::PubkeyInvalid,
Bolt12SemanticError::MissingQuantity => MutinyError::BadAmountError,
Bolt12SemanticError::InvalidQuantity => MutinyError::BadAmountError,
Bolt12SemanticError::UnexpectedQuantity => MutinyError::BadAmountError,
Bolt12SemanticError::MissingPaths => MutinyError::RoutingFailed,
Bolt12SemanticError::AlreadyExpired => MutinyError::PaymentTimeout,
_ => MutinyError::LnDecodeError,
}
}
}
31 changes: 26 additions & 5 deletions mutiny-core/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ pub(crate) struct PaymentInfo {
#[serde(skip_serializing_if = "Option::is_none")]
pub preimage: Option<[u8; 32]>,
#[serde(skip_serializing_if = "Option::is_none")]
pub payment_hash: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub secret: Option<[u8; 32]>,
pub status: HTLCStatus,
#[serde(skip_serializing_if = "MillisatAmount::is_none")]
Expand All @@ -37,6 +39,8 @@ pub(crate) struct PaymentInfo {
#[serde(skip_serializing_if = "Option::is_none")]
pub bolt11: Option<Bolt11Invoice>,
#[serde(skip_serializing_if = "Option::is_none")]
pub bolt12: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub payee_pubkey: Option<PublicKey>,
pub last_update: u64,
}
Expand Down Expand Up @@ -266,6 +270,8 @@ impl<S: MutinyStorage> EventHandler<S> {
let payment_secret = payment_secret.map(|p| p.0);
saved_payment_info.status = HTLCStatus::Succeeded;
saved_payment_info.preimage = payment_preimage;
// set payment_hash, we won't have it yet for bolt 12
saved_payment_info.payment_hash = Some(payment_hash.0.to_hex());
saved_payment_info.secret = payment_secret;
saved_payment_info.amt_msat = MillisatAmount(Some(amount_msat));
saved_payment_info.last_update = crate::utils::now().as_secs();
Expand All @@ -288,12 +294,14 @@ impl<S: MutinyStorage> EventHandler<S> {

let payment_info = PaymentInfo {
preimage: payment_preimage,
payment_hash: Some(payment_hash.0.to_hex()),
secret: payment_secret,
status: HTLCStatus::Succeeded,
amt_msat: MillisatAmount(Some(amount_msat)),
fee_paid_msat: None,
payee_pubkey: receiver_node_id,
bolt11: None,
bolt12: None,
last_update,
};
match self.persister.persist_payment_info(
Expand All @@ -311,28 +319,39 @@ impl<S: MutinyStorage> EventHandler<S> {
}
}
Event::PaymentSent {
payment_id,
payment_preimage,
payment_hash,
fee_paid_msat,
..
} => {
log_debug!(
self.logger,
"EVENT: PaymentSent: {}",
payment_hash.0.to_hex()
);

match self
// lookup by payment hash then payment id, we use payment_id for bolt 12
let mut lookup_id = payment_hash.0;
let found_payment_info = self
.persister
.read_payment_info(&payment_hash.0, false, &self.logger)
{
.read_payment_info(&lookup_id, false, &self.logger)
.or_else(|| {
payment_id.and_then(|id| {
lookup_id = id.0;
self.persister.read_payment_info(&id.0, false, &self.logger)
})
});

match found_payment_info {
Some(mut saved_payment_info) => {
saved_payment_info.status = HTLCStatus::Succeeded;
saved_payment_info.preimage = Some(payment_preimage.0);
// bolt 12 won't have the payment hash set yet
saved_payment_info.payment_hash = Some(payment_hash.0.to_hex());
saved_payment_info.fee_paid_msat = fee_paid_msat;
saved_payment_info.last_update = crate::utils::now().as_secs();
match self.persister.persist_payment_info(
&payment_hash.0,
&lookup_id,
&saved_payment_info,
false,
) {
Expand Down Expand Up @@ -666,10 +685,12 @@ mod test {

let payment_info = PaymentInfo {
preimage: Some(preimage),
payment_hash: None,
status: HTLCStatus::Succeeded,
amt_msat: MillisatAmount(Some(420)),
fee_paid_msat: None,
bolt11: None,
bolt12: None,
payee_pubkey: Some(pubkey),
secret: None,
last_update: utils::now().as_secs(),
Expand Down
47 changes: 16 additions & 31 deletions mutiny-core/src/labels.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ use crate::error::MutinyError;
use crate::nodemanager::NodeManager;
use crate::storage::MutinyStorage;
use bitcoin::{Address, XOnlyPublicKey};
use lightning_invoice::Bolt11Invoice;
use lnurl::lightning_address::LightningAddress;
use lnurl::lnurl::LnUrl;
use nostr::Metadata;
Expand All @@ -21,7 +20,7 @@ pub struct LabelItem {
/// List of addresses that have this label
pub addresses: Vec<Address>,
/// List of invoices that have this label
pub invoices: Vec<Bolt11Invoice>,
pub invoices: Vec<String>,
/// Epoch time in seconds when this label was last used
pub last_used_time: u64,
}
Expand Down Expand Up @@ -96,7 +95,7 @@ pub trait LabelStorage {
/// Get a map of addresses to labels. This can be used to get all the labels for an address
fn get_address_labels(&self) -> Result<HashMap<String, Vec<String>>, MutinyError>;
/// Get a map of invoices to labels. This can be used to get all the labels for an invoice
fn get_invoice_labels(&self) -> Result<HashMap<Bolt11Invoice, Vec<String>>, MutinyError>;
fn get_invoice_labels(&self) -> Result<HashMap<String, Vec<String>>, MutinyError>;
/// Get all the existing labels
fn get_labels(&self) -> Result<HashMap<String, LabelItem>, MutinyError>;
/// Get information about a label
Expand All @@ -108,11 +107,7 @@ pub trait LabelStorage {
/// Set the labels for an invoice, replacing any existing labels
/// If you do not want to replace any existing labels, use `get_invoice_labels` to get the existing labels,
/// add the new labels, and then use `set_invoice_labels` to set the new labels
fn set_invoice_labels(
&self,
invoice: Bolt11Invoice,
labels: Vec<String>,
) -> Result<(), MutinyError>;
fn set_invoice_labels(&self, invoice: String, labels: Vec<String>) -> Result<(), MutinyError>;
/// Get all the existing contacts
fn get_contacts(&self) -> Result<HashMap<String, Contact>, MutinyError>;
/// Get a contact by label, the label should be a uuid
Expand All @@ -139,9 +134,8 @@ impl<S: MutinyStorage> LabelStorage for S {
Ok(res.unwrap_or_default()) // if no labels exist, return an empty map
}

fn get_invoice_labels(&self) -> Result<HashMap<Bolt11Invoice, Vec<String>>, MutinyError> {
let res: Option<HashMap<Bolt11Invoice, Vec<String>>> =
self.get_data(INVOICE_LABELS_MAP_KEY)?;
fn get_invoice_labels(&self) -> Result<HashMap<String, Vec<String>>, MutinyError> {
let res: Option<HashMap<String, Vec<String>>> = self.get_data(INVOICE_LABELS_MAP_KEY)?;
Ok(res.unwrap_or_default()) // if no labels exist, return an empty map
}

Expand Down Expand Up @@ -207,11 +201,7 @@ impl<S: MutinyStorage> LabelStorage for S {
Ok(())
}

fn set_invoice_labels(
&self,
invoice: Bolt11Invoice,
labels: Vec<String>,
) -> Result<(), MutinyError> {
fn set_invoice_labels(&self, invoice: String, labels: Vec<String>) -> Result<(), MutinyError> {
// update the labels map
let mut invoice_labels = self.get_invoice_labels()?;
invoice_labels.insert(invoice.clone(), labels.clone());
Expand Down Expand Up @@ -397,7 +387,7 @@ impl<S: MutinyStorage> LabelStorage for NodeManager<S> {
self.storage.get_address_labels()
}

fn get_invoice_labels(&self) -> Result<HashMap<Bolt11Invoice, Vec<String>>, MutinyError> {
fn get_invoice_labels(&self) -> Result<HashMap<String, Vec<String>>, MutinyError> {
self.storage.get_invoice_labels()
}

Expand All @@ -413,11 +403,7 @@ impl<S: MutinyStorage> LabelStorage for NodeManager<S> {
self.storage.set_address_labels(address, labels)
}

fn set_invoice_labels(
&self,
invoice: Bolt11Invoice,
labels: Vec<String>,
) -> Result<(), MutinyError> {
fn set_invoice_labels(&self, invoice: String, labels: Vec<String>) -> Result<(), MutinyError> {
self.storage.set_invoice_labels(invoice, labels)
}

Expand Down Expand Up @@ -459,7 +445,6 @@ mod tests {
use super::*;
use crate::test_utils::*;
use bitcoin::Address;
use lightning_invoice::Bolt11Invoice;
use std::collections::HashMap;
use std::str::FromStr;

Expand Down Expand Up @@ -487,18 +472,18 @@ mod tests {
labels
}

fn create_test_invoice_labels_map() -> HashMap<Bolt11Invoice, Vec<String>> {
fn create_test_invoice_labels_map() -> HashMap<String, Vec<String>> {
let mut labels = HashMap::new();
labels.insert(
Bolt11Invoice::from_str("lnbc923720n1pj9nrefpp5pczykgk37af5388n8dzynljpkzs7sje4melqgazlwv9y3apay8jqhp5rd8saxz3juve3eejq7z5fjttxmpaq88d7l92xv34n4h3mq6kwq2qcqzzsxqzfvsp5z0jwpehkuz9f2kv96h62p8x30nku76aj8yddpcust7g8ad0tr52q9qyyssqfy622q25helv8cj8hyxqltws4rdwz0xx2hw0uh575mn7a76cp3q4jcptmtjkjs4a34dqqxn8uy70d0qlxqleezv4zp84uk30pp5q3nqq4c9gkz").unwrap(),
String::from("lnbc923720n1pj9nrefpp5pczykgk37af5388n8dzynljpkzs7sje4melqgazlwv9y3apay8jqhp5rd8saxz3juve3eejq7z5fjttxmpaq88d7l92xv34n4h3mq6kwq2qcqzzsxqzfvsp5z0jwpehkuz9f2kv96h62p8x30nku76aj8yddpcust7g8ad0tr52q9qyyssqfy622q25helv8cj8hyxqltws4rdwz0xx2hw0uh575mn7a76cp3q4jcptmtjkjs4a34dqqxn8uy70d0qlxqleezv4zp84uk30pp5q3nqq4c9gkz"),
vec!["test1".to_string()],
);
labels.insert(
Bolt11Invoice::from_str("lnbc923720n1pj9nre4pp58zjsgd3xkyj33wv6rfmsshg9hqdpqrh8dyaulzwg62x6h3qs39tqhp5vqcr4c3tnxyxr08rk28n8mkphe6c5gfusmyncpmdh604trq3cafqcqzzsxqzfvsp5un4ey9rh0pl23648xtng2k6gtw7w2p6ldaexl6ylwcuhnsnxnsfs9qyyssqxnhr6jvdqfwr97qk7dtsnqaps78r7fjlpyz5z57r2k70az5tvvss4tpucycqpph8gx0vxxr7xse442zf8wxlskln8n77qkd4kad4t5qp92lvrm").unwrap(),
String::from("lnbc923720n1pj9nre4pp58zjsgd3xkyj33wv6rfmsshg9hqdpqrh8dyaulzwg62x6h3qs39tqhp5vqcr4c3tnxyxr08rk28n8mkphe6c5gfusmyncpmdh604trq3cafqcqzzsxqzfvsp5un4ey9rh0pl23648xtng2k6gtw7w2p6ldaexl6ylwcuhnsnxnsfs9qyyssqxnhr6jvdqfwr97qk7dtsnqaps78r7fjlpyz5z57r2k70az5tvvss4tpucycqpph8gx0vxxr7xse442zf8wxlskln8n77qkd4kad4t5qp92lvrm"),
vec!["test2".to_string()],
);
labels.insert(
Bolt11Invoice::from_str("lnbc923720n1pj9nr6zpp5xmvlq2u5253htn52mflh2e6gn7pk5ht0d4qyhc62fadytccxw7hqhp5l4s6qwh57a7cwr7zrcz706qx0qy4eykcpr8m8dwz08hqf362egfscqzzsxqzfvsp5pr7yjvcn4ggrf6fq090zey0yvf8nqvdh2kq7fue0s0gnm69evy6s9qyyssqjyq0fwjr22eeg08xvmz88307yqu8tqqdjpycmermks822fpqyxgshj8hvnl9mkh6srclnxx0uf4ugfq43d66ak3rrz4dqcqd23vxwpsqf7dmhm").unwrap(),
String::from("lnbc923720n1pj9nr6zpp5xmvlq2u5253htn52mflh2e6gn7pk5ht0d4qyhc62fadytccxw7hqhp5l4s6qwh57a7cwr7zrcz706qx0qy4eykcpr8m8dwz08hqf362egfscqzzsxqzfvsp5pr7yjvcn4ggrf6fq090zey0yvf8nqvdh2kq7fue0s0gnm69evy6s9qyyssqjyq0fwjr22eeg08xvmz88307yqu8tqqdjpycmermks822fpqyxgshj8hvnl9mkh6srclnxx0uf4ugfq43d66ak3rrz4dqcqd23vxwpsqf7dmhm"),
vec!["test3".to_string()],
);
labels
Expand All @@ -517,7 +502,7 @@ mod tests {
"test2".to_string(),
LabelItem {
addresses: vec![Address::from_str("1BitcoinEaterAddressDontSendf59kuE").unwrap()],
invoices: vec![Bolt11Invoice::from_str("lnbc923720n1pj9nr6zpp5xmvlq2u5253htn52mflh2e6gn7pk5ht0d4qyhc62fadytccxw7hqhp5l4s6qwh57a7cwr7zrcz706qx0qy4eykcpr8m8dwz08hqf362egfscqzzsxqzfvsp5pr7yjvcn4ggrf6fq090zey0yvf8nqvdh2kq7fue0s0gnm69evy6s9qyyssqjyq0fwjr22eeg08xvmz88307yqu8tqqdjpycmermks822fpqyxgshj8hvnl9mkh6srclnxx0uf4ugfq43d66ak3rrz4dqcqd23vxwpsqf7dmhm").unwrap()],
invoices: vec![String::from("lnbc923720n1pj9nr6zpp5xmvlq2u5253htn52mflh2e6gn7pk5ht0d4qyhc62fadytccxw7hqhp5l4s6qwh57a7cwr7zrcz706qx0qy4eykcpr8m8dwz08hqf362egfscqzzsxqzfvsp5pr7yjvcn4ggrf6fq090zey0yvf8nqvdh2kq7fue0s0gnm69evy6s9qyyssqjyq0fwjr22eeg08xvmz88307yqu8tqqdjpycmermks822fpqyxgshj8hvnl9mkh6srclnxx0uf4ugfq43d66ak3rrz4dqcqd23vxwpsqf7dmhm")],
..Default::default()
},
);
Expand Down Expand Up @@ -672,7 +657,7 @@ mod tests {

let storage = MemoryStorage::default();

let invoice = Bolt11Invoice::from_str(INVOICE).unwrap();
let invoice = INVOICE.to_string();
let labels = vec!["label1".to_string(), "label2".to_string()];

let result = storage.set_invoice_labels(invoice.clone(), labels.clone());
Expand Down Expand Up @@ -792,7 +777,7 @@ mod tests {
let storage = MemoryStorage::default();

let address = Address::from_str(ADDRESS).unwrap();
let invoice = Bolt11Invoice::from_str(INVOICE).unwrap();
let invoice = INVOICE.to_string();
let label = "test_label".to_string();
let other_label = "other_label".to_string();
let contact = create_test_contacts().iter().next().unwrap().1.to_owned();
Expand Down Expand Up @@ -920,7 +905,7 @@ mod tests {
assert_eq!(contact.last_used, 0);
let id = storage.create_new_contact(contact.clone()).unwrap();

let invoice = Bolt11Invoice::from_str(INVOICE).unwrap();
let invoice = INVOICE.to_string();

storage
.set_invoice_labels(invoice, vec![id.clone()])
Expand Down
Loading
Loading