Skip to content

Commit

Permalink
Merge pull request #948 from getlipa/feature/set-personal-note
Browse files Browse the repository at this point in the history
Implement payment personal notes
  • Loading branch information
danielgranhao authored Feb 23, 2024
2 parents 3943e5a + 0e5fb68 commit 222f930
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 0 deletions.
20 changes: 20 additions & 0 deletions examples/node/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,11 @@ pub(crate) fn poll_for_user_input(node: &LightningNode, log_file_path: &str) {
Ok(uuid) => println!("{uuid}"),
Err(message) => eprintln!("{}", format!("{message:#}").red()),
},
"personalnote" => {
if let Err(message) = set_personal_note(node, &mut words) {
println!("{}", format!("{message:#}").red());
}
}
"swaponchaintolightning" => {
if let Err(message) = swap_onchain_to_lightning(node) {
println!("{}", format!("{message:#}").red());
Expand Down Expand Up @@ -366,6 +371,10 @@ fn setup_editor(history_path: &Path) -> Editor<CommandHinter, DefaultHistory> {
"paymentuuid <payment hash>",
"paymentuuid ",
));
hints.insert(CommandHint::new(
"personalnote <payment hash> [note]",
"personalnote ",
));
hints.insert(CommandHint::new("sweep <address>", "sweep "));
hints.insert(CommandHint::new("clearwalletinfo", "clearwalletinfo"));
hints.insert(CommandHint::new("clearwallet <address>", "clearwallet "));
Expand Down Expand Up @@ -428,6 +437,7 @@ fn help() {
println!(" l | listactivities [number of activities = 2]");
println!(" listlightningaddresses");
println!(" paymentuuid <payment hash>");
println!(" personalnote <payment hash> [note]");
println!();
println!(" getchannelcloseresolvingfees");
println!(" sweep <address>");
Expand Down Expand Up @@ -1135,6 +1145,7 @@ fn print_payment(payment: Payment) -> Result<()> {
println!(" Offer: {}", offer_to_string(payment.offer));
println!(" Swap: {:?}", payment.swap);
println!(" Recipient: {:?}", payment.recipient);
println!(" Personal note: {:?}", payment.personal_note);
Ok(())
}

Expand Down Expand Up @@ -1164,6 +1175,15 @@ fn payment_uuid(node: &LightningNode, words: &mut dyn Iterator<Item = &str>) ->
Ok(node.get_payment_uuid(payment_hash.to_string())?)
}

fn set_personal_note(node: &LightningNode, words: &mut dyn Iterator<Item = &str>) -> Result<()> {
let payment_hash = words.next().ok_or(anyhow!("Payment Hash is required"))?;
let note = words.collect::<Vec<_>>().join(" ");

node.set_payment_personal_note(payment_hash.to_string(), note.to_string())?;

Ok(())
}

fn sweep(node: &LightningNode, address: String) -> Result<String> {
let fee_rate = node.query_onchain_fee_rate()?;
let sweep_info = node.prepare_sweep(address, fee_rate)?;
Expand Down
4 changes: 4 additions & 0 deletions mock/breez-sdk/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -581,4 +581,8 @@ impl BreezServices {
pub async fn register_webhook(&self, _webhook_url: String) -> SdkResult<()> {
Ok(())
}

pub async fn set_payment_metadata(&self, _hash: String, _metadata: String) -> SdkResult<()> {
todo!("set_payment_metadata");
}
}
24 changes: 24 additions & 0 deletions src/activity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::{Amount, InvoiceDetails, OfferKind, PayErrorCode, SwapInfo, TzTime};
use std::time::SystemTime;

use breez_sdk_core::{LnPaymentDetails, PaymentStatus};
use serde::{Deserialize, Serialize};

#[derive(PartialEq, Eq, Debug, Clone)]
#[repr(u8)]
Expand Down Expand Up @@ -68,6 +69,8 @@ pub struct Payment {
pub swap: Option<SwapInfo>,
/// Information about a payment's recipient. Will only be present for outgoing payments.
pub recipient: Option<Recipient>,
/// A personal note previously added to this payment through [`LightningNode::set_payment_personal_note`](crate::LightningNode::set_payment_personal_note)
pub personal_note: Option<String>,
}

/// User-friendly representation of an outgoing payment's recipient.
Expand Down Expand Up @@ -149,3 +152,24 @@ pub enum ChannelCloseState {
Pending,
Confirmed,
}

#[derive(Serialize, Deserialize, PartialEq, Debug)]
pub(crate) struct BreezPaymentMetadata {
pub personal_note: Option<String>,
}

#[cfg(test)]
mod tests {
use crate::activity::BreezPaymentMetadata;

#[test]
fn test_payment_metadata_serde() {
let metadata = BreezPaymentMetadata {
personal_note: None,
};

let json = "{}";

assert_eq!(metadata, serde_json::from_str(json).unwrap());
}
}
55 changes: 55 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ use crate::util::{
};
pub use notification_handling::{handle_notification, Notification, RecommendedAction};

use crate::activity::BreezPaymentMetadata;
use crate::symmetric_encryption::encrypt;
pub use breez_sdk_core::error::ReceiveOnchainError as SwapError;
use breez_sdk_core::error::{LnUrlWithdrawError, ReceiveOnchainError, SendPaymentError};
Expand Down Expand Up @@ -1035,6 +1036,49 @@ impl LightningNode {
}
}

/// Set a personal note on a specific payment.
///
/// Parameters:
/// * `payment_hash` - The hash of the payment for which a personal note will be set.
/// * `note` - The personal note.
pub fn set_payment_personal_note(&self, payment_hash: String, note: String) -> Result<()> {
let note = Some(note.trim().to_string()).filter(|s| !s.is_empty());

let previous_metadata = self
.rt
.handle()
.block_on(self.sdk.payment_by_hash(payment_hash.clone()))
.map_to_runtime_error(
RuntimeErrorCode::NodeUnavailable,
"Failed to fetch payment by hash",
)?
.ok_or_invalid_input("Payment not found")?
.metadata;

let new_metadata = match previous_metadata {
None => BreezPaymentMetadata {
personal_note: note,
},
Some(m) => {
let mut m: BreezPaymentMetadata = serde_json::from_str(&m)
.map_to_permanent_failure("Payment metadata got corrupted")?;
m.personal_note = note;
m
}
};
let new_metadata = serde_json::to_string(&new_metadata)
.map_to_permanent_failure("Failed to serialize BreezPaymentMetadata")?;

self.rt
.handle()
.block_on(self.sdk.set_payment_metadata(payment_hash, new_metadata))
.map_to_runtime_error(
RuntimeErrorCode::NodeUnavailable,
"Failed to set payment metadata",
)?;
Ok(())
}

fn activity_from_breez_payment(
&self,
breez_payment: breez_sdk_core::Payment,
Expand Down Expand Up @@ -1189,6 +1233,12 @@ impl LightningNode {
_ => description,
};

let payment_metadata: Option<BreezPaymentMetadata> = breez_payment
.metadata
.as_ref()
.and_then(|m| serde_json::from_str(m).unwrap_or(None));
let personal_note = payment_metadata.and_then(|p| p.personal_note);

Ok(Activity::PaymentActivity {
payment: Payment {
payment_type,
Expand All @@ -1210,6 +1260,7 @@ impl LightningNode {
offer,
swap,
recipient,
personal_note,
},
})
}
Expand Down Expand Up @@ -1329,6 +1380,7 @@ impl LightningNode {
offer: None,
swap: None,
recipient: None,
personal_note: None,
})
}

Expand Down Expand Up @@ -2599,6 +2651,7 @@ mod tests {
offer: None,
swap: None,
recipient: None,
personal_note: None,
},
Payment {
payment_type: PaymentType::Receiving,
Expand Down Expand Up @@ -2648,6 +2701,7 @@ mod tests {
}),
swap: None,
recipient: None,
personal_note: None,
},
Payment {
payment_type: PaymentType::Receiving,
Expand Down Expand Up @@ -2697,6 +2751,7 @@ mod tests {
}),
swap: None,
recipient: None,
personal_note: None,
},
];

Expand Down
4 changes: 4 additions & 0 deletions src/lipalightninglib.udl
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ interface LightningNode {
[Throws=LnError]
Payment get_payment(string hash);

[Throws=LnError]
void set_payment_personal_note(string payment_hash, string note);

[Throws=LnError]
sequence<string> list_lightning_addresses();

Expand Down Expand Up @@ -332,6 +335,7 @@ dictionary Payment {
OfferKind? offer;
SwapInfo? swap;
Recipient? recipient;
string? personal_note;
};

enum PaymentType {
Expand Down

0 comments on commit 222f930

Please sign in to comment.