forked from lightningdevkit/rust-lightning
-
Notifications
You must be signed in to change notification settings - Fork 1
bot12 contacts #79
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
Draft
vincenzopalazzo
wants to merge
2
commits into
main
Choose a base branch
from
macros/contacts-bolt12v2
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
bot12 contacts #79
Changes from 1 commit
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -186,6 +186,10 @@ macro_rules! invoice_request_builder_methods { ( | |||||||||||||||||||||||||||||||||||||||||||||||||
payer: PayerContents(metadata), offer, chain: None, amount_msats: None, | ||||||||||||||||||||||||||||||||||||||||||||||||||
features: InvoiceRequestFeatures::empty(), quantity: None, payer_note: None, | ||||||||||||||||||||||||||||||||||||||||||||||||||
offer_from_hrn: None, | ||||||||||||||||||||||||||||||||||||||||||||||||||
contact_secret: None, | ||||||||||||||||||||||||||||||||||||||||||||||||||
payer_offer: None, | ||||||||||||||||||||||||||||||||||||||||||||||||||
payer_bip353_name: None, | ||||||||||||||||||||||||||||||||||||||||||||||||||
payer_bip353_signature: None, | ||||||||||||||||||||||||||||||||||||||||||||||||||
#[cfg(test)] | ||||||||||||||||||||||||||||||||||||||||||||||||||
experimental_bar: None, | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -254,6 +258,84 @@ macro_rules! invoice_request_builder_methods { ( | |||||||||||||||||||||||||||||||||||||||||||||||||
$return_value | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
/// Sets the contact secret for this payment, as defined in BLIP-0042. | ||||||||||||||||||||||||||||||||||||||||||||||||||
/// | ||||||||||||||||||||||||||||||||||||||||||||||||||
/// The contact secret allows the recipient to identify the payer as a contact. | ||||||||||||||||||||||||||||||||||||||||||||||||||
/// If a contact pair already exists between the payer and recipient, use | ||||||||||||||||||||||||||||||||||||||||||||||||||
/// `with_derived_contact_secret` instead. | ||||||||||||||||||||||||||||||||||||||||||||||||||
/// | ||||||||||||||||||||||||||||||||||||||||||||||||||
/// Successive calls to this method will override the previous setting. | ||||||||||||||||||||||||||||||||||||||||||||||||||
pub fn with_contact_secret($($self_mut)* $self: $self_type, contact_secret: ContactSecret) -> $return_type { | ||||||||||||||||||||||||||||||||||||||||||||||||||
$self.invoice_request.contact_secret = Some(contact_secret); | ||||||||||||||||||||||||||||||||||||||||||||||||||
$return_value | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
/// Derives and sets a contact secret for this payment based on both the payer's and recipient's offers, | ||||||||||||||||||||||||||||||||||||||||||||||||||
/// as defined in BLIP-0042. | ||||||||||||||||||||||||||||||||||||||||||||||||||
/// | ||||||||||||||||||||||||||||||||||||||||||||||||||
/// This method implements the deterministic contact key generation algorithm | ||||||||||||||||||||||||||||||||||||||||||||||||||
/// from BLIP-0042, creating a shared secret that identifies the contact pair. | ||||||||||||||||||||||||||||||||||||||||||||||||||
/// | ||||||||||||||||||||||||||||||||||||||||||||||||||
/// Successive calls to this method will override the previous setting. | ||||||||||||||||||||||||||||||||||||||||||||||||||
pub fn with_derived_contact_secret($($self_mut)* $self: $self_type, payer_offer: &Offer) -> $return_type { | ||||||||||||||||||||||||||||||||||||||||||||||||||
let contact_secret = derive_contact_secret(payer_offer, $self.offer); | ||||||||||||||||||||||||||||||||||||||||||||||||||
$self.invoice_request.contact_secret = Some(contact_secret); | ||||||||||||||||||||||||||||||||||||||||||||||||||
$return_value | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
/// Sets the payer's BOLT12 offer to allow the recipient to pay them back. | ||||||||||||||||||||||||||||||||||||||||||||||||||
/// | ||||||||||||||||||||||||||||||||||||||||||||||||||
/// This allows the recipient to add the payer as a contact and initiate | ||||||||||||||||||||||||||||||||||||||||||||||||||
/// payments back to them in the future. | ||||||||||||||||||||||||||||||||||||||||||||||||||
/// | ||||||||||||||||||||||||||||||||||||||||||||||||||
/// Successive calls to this method will override the previous setting. | ||||||||||||||||||||||||||||||||||||||||||||||||||
pub fn with_payer_offer($($self_mut)* $self: $self_type, payer_offer: &Offer) -> $return_type { | ||||||||||||||||||||||||||||||||||||||||||||||||||
let mut offer_bytes = Vec::new(); | ||||||||||||||||||||||||||||||||||||||||||||||||||
payer_offer.write(&mut offer_bytes).unwrap_or_default(); | ||||||||||||||||||||||||||||||||||||||||||||||||||
$self.invoice_request.payer_offer = Some(offer_bytes); | ||||||||||||||||||||||||||||||||||||||||||||||||||
$return_value | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
/// Sets the payer's BIP-353 name information for contact identification. | ||||||||||||||||||||||||||||||||||||||||||||||||||
/// | ||||||||||||||||||||||||||||||||||||||||||||||||||
/// This allows the payer to reveal their BIP-353 name to allow contacts to pay them back. | ||||||||||||||||||||||||||||||||||||||||||||||||||
/// | ||||||||||||||||||||||||||||||||||||||||||||||||||
/// Successive calls to this method will override the previous setting. | ||||||||||||||||||||||||||||||||||||||||||||||||||
pub fn with_payer_bip353_name($($self_mut)* $self: $self_type, name: String, domain: String) -> $return_type { | ||||||||||||||||||||||||||||||||||||||||||||||||||
$self.invoice_request.payer_bip353_name = Some(Bip353Name { name, domain }); | ||||||||||||||||||||||||||||||||||||||||||||||||||
$return_value | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
/// Signs the payment with a BIP-353 signature proving the payer owns the BIP-353 name. | ||||||||||||||||||||||||||||||||||||||||||||||||||
/// | ||||||||||||||||||||||||||||||||||||||||||||||||||
/// This should only be called if `with_payer_bip353_name` has been used. | ||||||||||||||||||||||||||||||||||||||||||||||||||
/// The signing key must be the private key of the offer issuer ID or blinded path node. | ||||||||||||||||||||||||||||||||||||||||||||||||||
/// | ||||||||||||||||||||||||||||||||||||||||||||||||||
/// Successive calls to this method will override the previous setting. | ||||||||||||||||||||||||||||||||||||||||||||||||||
pub fn sign_with_bip353_key<S: secp256k1::Signing>( | ||||||||||||||||||||||||||||||||||||||||||||||||||
$($self_mut)* $self: $self_type, | ||||||||||||||||||||||||||||||||||||||||||||||||||
signing_key: &Keypair, | ||||||||||||||||||||||||||||||||||||||||||||||||||
secp_ctx: &Secp256k1<S> | ||||||||||||||||||||||||||||||||||||||||||||||||||
) -> $return_type { | ||||||||||||||||||||||||||||||||||||||||||||||||||
if let Some(bip353_name) = &$self.invoice_request.payer_bip353_name { | ||||||||||||||||||||||||||||||||||||||||||||||||||
// Create a tagged hash for BIP-353 signature | ||||||||||||||||||||||||||||||||||||||||||||||||||
use sha2::{Digest, Sha256}; | ||||||||||||||||||||||||||||||||||||||||||||||||||
let tag = "lightning/invoice_request/invreq_payer_bip353_signature"; | ||||||||||||||||||||||||||||||||||||||||||||||||||
let mut message = Vec::new(); | ||||||||||||||||||||||||||||||||||||||||||||||||||
message.extend_from_slice(tag.as_bytes()); | ||||||||||||||||||||||||||||||||||||||||||||||||||
message.extend_from_slice(bip353_name.name.as_bytes()); | ||||||||||||||||||||||||||||||||||||||||||||||||||
message.extend_from_slice(bip353_name.domain.as_bytes()); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
let message_hash = Sha256::digest(&message); | ||||||||||||||||||||||||||||||||||||||||||||||||||
let message = secp256k1::Message::from_slice(&message_hash).unwrap(); | ||||||||||||||||||||||||||||||||||||||||||||||||||
let signature = secp_ctx.sign_schnorr_no_aux_rand(&message, signing_key); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
$self.invoice_request.payer_bip353_signature = Some((signing_key.public_key(), signature)); | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
$return_value | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
fn build_with_checks($($self_mut)* $self: $self_type) -> Result< | ||||||||||||||||||||||||||||||||||||||||||||||||||
(UnsignedInvoiceRequest, Option<Keypair>, Option<&'b Secp256k1<$secp_context>>), | ||||||||||||||||||||||||||||||||||||||||||||||||||
Bolt12SemanticError | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -636,6 +718,11 @@ pub(super) struct InvoiceRequestContentsWithoutPayerSigningPubkey { | |||||||||||||||||||||||||||||||||||||||||||||||||
quantity: Option<u64>, | ||||||||||||||||||||||||||||||||||||||||||||||||||
payer_note: Option<String>, | ||||||||||||||||||||||||||||||||||||||||||||||||||
offer_from_hrn: Option<HumanReadableName>, | ||||||||||||||||||||||||||||||||||||||||||||||||||
// Contact-related fields | ||||||||||||||||||||||||||||||||||||||||||||||||||
contact_secret: Option<ContactSecret>, | ||||||||||||||||||||||||||||||||||||||||||||||||||
payer_offer: Option<Vec<u8>>, | ||||||||||||||||||||||||||||||||||||||||||||||||||
payer_bip353_name: Option<Bip353Name>, | ||||||||||||||||||||||||||||||||||||||||||||||||||
payer_bip353_signature: Option<(PublicKey, Signature)>, | ||||||||||||||||||||||||||||||||||||||||||||||||||
#[cfg(test)] | ||||||||||||||||||||||||||||||||||||||||||||||||||
experimental_bar: Option<u64>, | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -699,9 +786,42 @@ macro_rules! invoice_request_accessors { ($self: ident, $contents: expr) => { | |||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
} } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
macro_rules! invoice_request_contact_accessors { ($self: ident, $contents: expr) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||
/// Returns the contact secret if present, allowing the recipient to identify | ||||||||||||||||||||||||||||||||||||||||||||||||||
/// this payer as a contact based on a shared secret derived from both parties' offers. | ||||||||||||||||||||||||||||||||||||||||||||||||||
/// | ||||||||||||||||||||||||||||||||||||||||||||||||||
/// This is part of the BLIP-0042 contacts implementation. | ||||||||||||||||||||||||||||||||||||||||||||||||||
pub fn contact_secret(&$self) -> Option<&[u8]> { | ||||||||||||||||||||||||||||||||||||||||||||||||||
$contents.contact_secret().map(|s| s.as_bytes()) | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
/// Returns the payer's offer if included, which can be used to pay back to | ||||||||||||||||||||||||||||||||||||||||||||||||||
/// the payer in future payments. | ||||||||||||||||||||||||||||||||||||||||||||||||||
/// | ||||||||||||||||||||||||||||||||||||||||||||||||||
/// This is part of the BLIP-0042 contacts implementation. | ||||||||||||||||||||||||||||||||||||||||||||||||||
pub fn payer_offer(&$self) -> Option<Offer> { | ||||||||||||||||||||||||||||||||||||||||||||||||||
$contents.payer_offer().and_then(|bytes| Offer::try_from(bytes.to_vec()).ok()) | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
/// Returns the payer's BIP-353 name if present. | ||||||||||||||||||||||||||||||||||||||||||||||||||
/// | ||||||||||||||||||||||||||||||||||||||||||||||||||
/// This is part of the BLIP-0042 contacts implementation. | ||||||||||||||||||||||||||||||||||||||||||||||||||
pub fn payer_bip353_name(&$self) -> Option<(&str, &str)> { | ||||||||||||||||||||||||||||||||||||||||||||||||||
$contents.payer_bip353_name().map(|name| (name.name.as_str(), name.domain.as_str())) | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
/// Returns the BIP-353 signature if present, which proves ownership of the BIP-353 name. | ||||||||||||||||||||||||||||||||||||||||||||||||||
/// | ||||||||||||||||||||||||||||||||||||||||||||||||||
/// This is part of the BLIP-0042 contacts implementation. | ||||||||||||||||||||||||||||||||||||||||||||||||||
pub fn payer_bip353_signature(&$self) -> Option<(&PublicKey, &Signature)> { | ||||||||||||||||||||||||||||||||||||||||||||||||||
$contents.payer_bip353_signature() | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
} } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
impl UnsignedInvoiceRequest { | ||||||||||||||||||||||||||||||||||||||||||||||||||
offer_accessors!(self, self.contents.inner.offer); | ||||||||||||||||||||||||||||||||||||||||||||||||||
invoice_request_accessors!(self, self.contents); | ||||||||||||||||||||||||||||||||||||||||||||||||||
invoice_request_contact_accessors!(self, self.contents); | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
macro_rules! invoice_request_respond_with_explicit_signing_pubkey_methods { ( | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -851,6 +971,7 @@ macro_rules! invoice_request_verify_method { | |||||||||||||||||||||||||||||||||||||||||||||||||
impl InvoiceRequest { | ||||||||||||||||||||||||||||||||||||||||||||||||||
offer_accessors!(self, self.contents.inner.offer); | ||||||||||||||||||||||||||||||||||||||||||||||||||
invoice_request_accessors!(self, self.contents); | ||||||||||||||||||||||||||||||||||||||||||||||||||
invoice_request_contact_accessors!(self, self.contents); | ||||||||||||||||||||||||||||||||||||||||||||||||||
invoice_request_respond_with_explicit_signing_pubkey_methods!( | ||||||||||||||||||||||||||||||||||||||||||||||||||
self, | ||||||||||||||||||||||||||||||||||||||||||||||||||
self, | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -868,6 +989,7 @@ impl InvoiceRequest { | |||||||||||||||||||||||||||||||||||||||||||||||||
impl InvoiceRequest { | ||||||||||||||||||||||||||||||||||||||||||||||||||
offer_accessors!(self, self.contents.inner.offer); | ||||||||||||||||||||||||||||||||||||||||||||||||||
invoice_request_accessors!(self, self.contents); | ||||||||||||||||||||||||||||||||||||||||||||||||||
invoice_request_contact_accessors!(self, self.contents); | ||||||||||||||||||||||||||||||||||||||||||||||||||
invoice_request_respond_with_explicit_signing_pubkey_methods!( | ||||||||||||||||||||||||||||||||||||||||||||||||||
self, | ||||||||||||||||||||||||||||||||||||||||||||||||||
self, | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -894,6 +1016,7 @@ impl InvoiceRequest { | |||||||||||||||||||||||||||||||||||||||||||||||||
payer_tlv_stream, | ||||||||||||||||||||||||||||||||||||||||||||||||||
offer_tlv_stream, | ||||||||||||||||||||||||||||||||||||||||||||||||||
invoice_request_tlv_stream, | ||||||||||||||||||||||||||||||||||||||||||||||||||
signature_tlv_stream, | ||||||||||||||||||||||||||||||||||||||||||||||||||
experimental_offer_tlv_stream, | ||||||||||||||||||||||||||||||||||||||||||||||||||
experimental_invoice_request_tlv_stream, | ||||||||||||||||||||||||||||||||||||||||||||||||||
) = self.contents.as_tlv_stream(); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -964,6 +1087,7 @@ macro_rules! invoice_request_respond_with_derived_signing_pubkey_methods { ( | |||||||||||||||||||||||||||||||||||||||||||||||||
impl VerifiedInvoiceRequest { | ||||||||||||||||||||||||||||||||||||||||||||||||||
offer_accessors!(self, self.inner.contents.inner.offer); | ||||||||||||||||||||||||||||||||||||||||||||||||||
invoice_request_accessors!(self, self.inner.contents); | ||||||||||||||||||||||||||||||||||||||||||||||||||
invoice_request_contact_accessors!(self, self.inner.contents); | ||||||||||||||||||||||||||||||||||||||||||||||||||
#[cfg(not(c_bindings))] | ||||||||||||||||||||||||||||||||||||||||||||||||||
invoice_request_respond_with_explicit_signing_pubkey_methods!( | ||||||||||||||||||||||||||||||||||||||||||||||||||
self, | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -1023,7 +1147,6 @@ impl InvoiceRequestContents { | |||||||||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||||||||||
Some(Amount::Currency { .. }) => None, | ||||||||||||||||||||||||||||||||||||||||||||||||||
None => { | ||||||||||||||||||||||||||||||||||||||||||||||||||
debug_assert!(false); | ||||||||||||||||||||||||||||||||||||||||||||||||||
None | ||||||||||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||||||||||
}) | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -1050,13 +1173,78 @@ impl InvoiceRequestContents { | |||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
pub(super) fn offer_from_hrn(&self) -> &Option<HumanReadableName> { | ||||||||||||||||||||||||||||||||||||||||||||||||||
&self.inner.offer_from_hrn | ||||||||||||||||||||||||||||||||||||||||||||||||||
self.inner.offer_from_hrn | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
pub(super) fn contact_secret(&self) -> Option<&ContactSecret> { | ||||||||||||||||||||||||||||||||||||||||||||||||||
self.inner.contact_secret.as_ref() | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
pub(super) fn payer_offer(&self) -> Option<&[u8]> { | ||||||||||||||||||||||||||||||||||||||||||||||||||
self.inner.payer_offer.as_ref().map(|offer| offer.as_slice()) | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
pub(super) fn payer_bip353_name(&self) -> Option<&Bip353Name> { | ||||||||||||||||||||||||||||||||||||||||||||||||||
self.inner.payer_bip353_name.as_ref() | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
pub(super) fn payer_bip353_signature(&self) -> Option<(&PublicKey, &Signature)> { | ||||||||||||||||||||||||||||||||||||||||||||||||||
self.inner.payer_bip353_signature.as_ref() | ||||||||||||||||||||||||||||||||||||||||||||||||||
.map(|(pubkey, sig)| (pubkey, sig)) | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
pub(super) fn as_tlv_stream(&self) -> PartialInvoiceRequestTlvStreamRef { | ||||||||||||||||||||||||||||||||||||||||||||||||||
let (payer, offer, mut invoice_request, experimental_offer, experimental_invoice_request) = | ||||||||||||||||||||||||||||||||||||||||||||||||||
self.inner.as_tlv_stream(); | ||||||||||||||||||||||||||||||||||||||||||||||||||
invoice_request.payer_id = Some(&self.payer_signing_pubkey); | ||||||||||||||||||||||||||||||||||||||||||||||||||
let payer = PayerTlvStreamRef { metadata: self.payer.0.as_bytes() }; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
let (offer, experimental_offer) = self.offer.as_tlv_stream(); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
let features = { | ||||||||||||||||||||||||||||||||||||||||||||||||||
if self.features == InvoiceRequestFeatures::empty() { | ||||||||||||||||||||||||||||||||||||||||||||||||||
None | ||||||||||||||||||||||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||||||||||||||||||||||
Some(&self.features) | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
let invoice_request = InvoiceRequestTlvStreamRef { | ||||||||||||||||||||||||||||||||||||||||||||||||||
chain: self.chain.as_ref(), | ||||||||||||||||||||||||||||||||||||||||||||||||||
amount: self.amount_msats, | ||||||||||||||||||||||||||||||||||||||||||||||||||
features, | ||||||||||||||||||||||||||||||||||||||||||||||||||
quantity: self.quantity, | ||||||||||||||||||||||||||||||||||||||||||||||||||
payer_id: None, | ||||||||||||||||||||||||||||||||||||||||||||||||||
payer_note: self.payer_note.as_ref(), | ||||||||||||||||||||||||||||||||||||||||||||||||||
offer_from_hrn: self.offer_from_hrn.as_ref(), | ||||||||||||||||||||||||||||||||||||||||||||||||||
paths: None, | ||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
// Handle contact-related fields | ||||||||||||||||||||||||||||||||||||||||||||||||||
let mut experimental_invoice_request = ExperimentalInvoiceRequestTlvStreamRef { | ||||||||||||||||||||||||||||||||||||||||||||||||||
contact_secret: self.contact_secret.as_ref().map(|secret| secret.as_bytes()), | ||||||||||||||||||||||||||||||||||||||||||||||||||
payer_offer: self.payer_offer.as_ref().map(|offer| offer.as_slice()), | ||||||||||||||||||||||||||||||||||||||||||||||||||
payer_bip353_name: None, | ||||||||||||||||||||||||||||||||||||||||||||||||||
payer_bip353_signature: None, | ||||||||||||||||||||||||||||||||||||||||||||||||||
#[cfg(test)] | ||||||||||||||||||||||||||||||||||||||||||||||||||
experimental_bar: self.experimental_bar, | ||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
// Serialize BIP-353 name if present | ||||||||||||||||||||||||||||||||||||||||||||||||||
if let Some(bip353_name) = &self.payer_bip353_name { | ||||||||||||||||||||||||||||||||||||||||||||||||||
let mut name_bytes = Vec::new(); | ||||||||||||||||||||||||||||||||||||||||||||||||||
name_bytes.push(bip353_name.name.len() as u8); | ||||||||||||||||||||||||||||||||||||||||||||||||||
name_bytes.extend_from_slice(bip353_name.name.as_bytes()); | ||||||||||||||||||||||||||||||||||||||||||||||||||
name_bytes.push(bip353_name.domain.len() as u8); | ||||||||||||||||||||||||||||||||||||||||||||||||||
name_bytes.extend_from_slice(bip353_name.domain.as_bytes()); | ||||||||||||||||||||||||||||||||||||||||||||||||||
experimental_invoice_request.payer_bip353_name = Some(&name_bytes); | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
// Serialize BIP-353 signature if present | ||||||||||||||||||||||||||||||||||||||||||||||||||
if let Some((pubkey, signature)) = &self.payer_bip353_signature { | ||||||||||||||||||||||||||||||||||||||||||||||||||
let mut sig_bytes = Vec::new(); | ||||||||||||||||||||||||||||||||||||||||||||||||||
sig_bytes.extend_from_slice(&pubkey.serialize()); | ||||||||||||||||||||||||||||||||||||||||||||||||||
sig_bytes.extend_from_slice(&signature.as_ref()); | ||||||||||||||||||||||||||||||||||||||||||||||||||
experimental_invoice_request.payer_bip353_signature = Some(&sig_bytes); | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
(payer, offer, invoice_request, experimental_offer, experimental_invoice_request) | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -1099,8 +1287,12 @@ impl InvoiceRequestContentsWithoutPayerSigningPubkey { | |||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
let experimental_invoice_request = ExperimentalInvoiceRequestTlvStreamRef { | ||||||||||||||||||||||||||||||||||||||||||||||||||
contact_secret: self.contact_secret.as_ref().map(|secret| secret.as_bytes()), | ||||||||||||||||||||||||||||||||||||||||||||||||||
payer_offer: self.payer_offer.as_ref().map(|offer| offer.as_slice()), | ||||||||||||||||||||||||||||||||||||||||||||||||||
payer_bip353_name: None, | ||||||||||||||||||||||||||||||||||||||||||||||||||
payer_bip353_signature: None, | ||||||||||||||||||||||||||||||||||||||||||||||||||
#[cfg(test)] | ||||||||||||||||||||||||||||||||||||||||||||||||||
experimental_bar: self.experimental_bar, | ||||||||||||||||||||||||||||||||||||||||||||||||||
experimental_bar, | ||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
(payer, offer, invoice_request, experimental_offer, experimental_invoice_request) | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -1165,18 +1357,27 @@ pub(super) const EXPERIMENTAL_INVOICE_REQUEST_TYPES: core::ops::Range<u64> = | |||||||||||||||||||||||||||||||||||||||||||||||||
#[cfg(not(test))] | ||||||||||||||||||||||||||||||||||||||||||||||||||
tlv_stream!( | ||||||||||||||||||||||||||||||||||||||||||||||||||
ExperimentalInvoiceRequestTlvStream, | ||||||||||||||||||||||||||||||||||||||||||||||||||
ExperimentalInvoiceRequestTlvStreamRef, | ||||||||||||||||||||||||||||||||||||||||||||||||||
ExperimentalInvoiceRequestTlvStreamRef<'a>, | ||||||||||||||||||||||||||||||||||||||||||||||||||
EXPERIMENTAL_INVOICE_REQUEST_TYPES, | ||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||
// When adding experimental TLVs, update EXPERIMENTAL_TLV_ALLOCATION_SIZE accordingly in | ||||||||||||||||||||||||||||||||||||||||||||||||||
// UnsignedInvoiceRequest::new to avoid unnecessary allocations. | ||||||||||||||||||||||||||||||||||||||||||||||||||
// Contact-related TLV fields defined in BLIP-0042 | ||||||||||||||||||||||||||||||||||||||||||||||||||
(2000001729, contact_secret: [u8; 32]), | ||||||||||||||||||||||||||||||||||||||||||||||||||
(2000001731, payer_offer: (Vec<u8>, WithoutLength)), | ||||||||||||||||||||||||||||||||||||||||||||||||||
(2000001733, payer_bip353_name: (Vec<u8>, WithoutLength)), | ||||||||||||||||||||||||||||||||||||||||||||||||||
(2000001735, payer_bip353_signature: (Vec<u8>, WithoutLength)), | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
#[cfg(test)] | ||||||||||||||||||||||||||||||||||||||||||||||||||
tlv_stream!( | ||||||||||||||||||||||||||||||||||||||||||||||||||
ExperimentalInvoiceRequestTlvStream, ExperimentalInvoiceRequestTlvStreamRef, | ||||||||||||||||||||||||||||||||||||||||||||||||||
ExperimentalInvoiceRequestTlvStream, | ||||||||||||||||||||||||||||||||||||||||||||||||||
ExperimentalInvoiceRequestTlvStreamRef<'a>, | ||||||||||||||||||||||||||||||||||||||||||||||||||
EXPERIMENTAL_INVOICE_REQUEST_TYPES, { | ||||||||||||||||||||||||||||||||||||||||||||||||||
// Contact-related TLV fields defined in BLIP-0042 | ||||||||||||||||||||||||||||||||||||||||||||||||||
(2000001729, contact_secret: [u8; 32]), | ||||||||||||||||||||||||||||||||||||||||||||||||||
(2000001731, payer_offer: (Vec<u8>, WithoutLength)), | ||||||||||||||||||||||||||||||||||||||||||||||||||
(2000001733, payer_bip353_name: (Vec<u8>, WithoutLength)), | ||||||||||||||||||||||||||||||||||||||||||||||||||
(2000001735, payer_bip353_signature: (Vec<u8>, WithoutLength)), | ||||||||||||||||||||||||||||||||||||||||||||||||||
(2_999_999_999, experimental_bar: (u64, HighZeroBytesDroppedBigSize)), | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -1311,6 +1512,10 @@ impl TryFrom<PartialInvoiceRequestTlvStream> for InvoiceRequestContents { | |||||||||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||||||||||
experimental_offer_tlv_stream, | ||||||||||||||||||||||||||||||||||||||||||||||||||
ExperimentalInvoiceRequestTlvStream { | ||||||||||||||||||||||||||||||||||||||||||||||||||
contact_secret, | ||||||||||||||||||||||||||||||||||||||||||||||||||
payer_offer, | ||||||||||||||||||||||||||||||||||||||||||||||||||
payer_bip353_name, | ||||||||||||||||||||||||||||||||||||||||||||||||||
payer_bip353_signature, | ||||||||||||||||||||||||||||||||||||||||||||||||||
#[cfg(test)] | ||||||||||||||||||||||||||||||||||||||||||||||||||
experimental_bar, | ||||||||||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -1354,6 +1559,20 @@ impl TryFrom<PartialInvoiceRequestTlvStream> for InvoiceRequestContents { | |||||||||||||||||||||||||||||||||||||||||||||||||
quantity, | ||||||||||||||||||||||||||||||||||||||||||||||||||
payer_note, | ||||||||||||||||||||||||||||||||||||||||||||||||||
offer_from_hrn, | ||||||||||||||||||||||||||||||||||||||||||||||||||
contact_secret: contact_secret.map(ContactSecret::from), | ||||||||||||||||||||||||||||||||||||||||||||||||||
payer_offer, | ||||||||||||||||||||||||||||||||||||||||||||||||||
payer_bip353_name: payer_bip353_name.map(|bytes| { | ||||||||||||||||||||||||||||||||||||||||||||||||||
let name_len = bytes[0] as usize; | ||||||||||||||||||||||||||||||||||||||||||||||||||
let name = String::from_utf8(bytes[1..1 + name_len].to_vec()).unwrap(); | ||||||||||||||||||||||||||||||||||||||||||||||||||
let domain_len = bytes[1 + name_len] as usize; | ||||||||||||||||||||||||||||||||||||||||||||||||||
let domain = String::from_utf8(bytes[2 + name_len..2 + name_len + domain_len].to_vec()).unwrap(); | ||||||||||||||||||||||||||||||||||||||||||||||||||
Bip353Name { name, domain } | ||||||||||||||||||||||||||||||||||||||||||||||||||
}), | ||||||||||||||||||||||||||||||||||||||||||||||||||
payer_bip353_signature: payer_bip353_signature.map(|bytes| { | ||||||||||||||||||||||||||||||||||||||||||||||||||
let pubkey = PublicKey::from_slice(&bytes[..33]).unwrap(); | ||||||||||||||||||||||||||||||||||||||||||||||||||
let signature = Signature::from_slice(&bytes[33..]).unwrap(); | ||||||||||||||||||||||||||||||||||||||||||||||||||
(pubkey, signature) | ||||||||||||||||||||||||||||||||||||||||||||||||||
}), | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
let name = String::from_utf8(bytes[1..1 + name_len].to_vec()).unwrap(); | |
let domain_len = bytes[1 + name_len] as usize; | |
let domain = String::from_utf8(bytes[2 + name_len..2 + name_len + domain_len].to_vec()).unwrap(); | |
Bip353Name { name, domain } | |
}), | |
payer_bip353_signature: payer_bip353_signature.map(|bytes| { | |
let pubkey = PublicKey::from_slice(&bytes[..33]).unwrap(); | |
let signature = Signature::from_slice(&bytes[33..]).unwrap(); | |
(pubkey, signature) | |
}), | |
let name = String::from_utf8(bytes[1..1 + name_len].to_vec()) | |
.map_err(|_| Bolt12SemanticError::InvalidPayerBip353Name)?; | |
let domain_len = bytes[1 + name_len] as usize; | |
let domain = String::from_utf8(bytes[2 + name_len..2 + name_len + domain_len].to_vec()) | |
.map_err(|_| Bolt12SemanticError::InvalidPayerBip353Name)?; | |
Ok(Bip353Name { name, domain }) | |
}).transpose()?, | |
payer_bip353_signature: payer_bip353_signature.map(|bytes| { | |
let pubkey = PublicKey::from_slice(&bytes[..33]) | |
.map_err(|_| Bolt12SemanticError::InvalidPayerBip353Signature)?; | |
let signature = Signature::from_slice(&bytes[33..]) | |
.map_err(|_| Bolt12SemanticError::InvalidPayerBip353Signature)?; | |
Ok((pubkey, signature)) | |
}).transpose()?, |
Copilot uses AI. Check for mistakes.
Positive FeedbackNegative Feedback
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using unwrap_or_default when writing the payer offer may hide serialization errors. Consider handling the error explicitly to either propagate it or log a meaningful message.
Copilot uses AI. Check for mistakes.