Skip to content

Commit

Permalink
Bolt 12
Browse files Browse the repository at this point in the history
  • Loading branch information
benthecarman committed Nov 22, 2023
1 parent c0ce5b5 commit 15ecc36
Show file tree
Hide file tree
Showing 12 changed files with 583 additions and 90 deletions.
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

0 comments on commit 15ecc36

Please sign in to comment.