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 invoice encoding and building #1926

Merged
merged 11 commits into from
Jan 23, 2023

Conversation

jkczyz
Copy link
Contributor

@jkczyz jkczyz commented Dec 20, 2022

Define an interface for BOLT 12 invoice messages. The underlying format consists of the original bytes and the parsed contents.

The bytes are later needed for serialization. This is because it must mirror all the offer and invoice_request TLV records, including unknown ones, which aren't represented in the contents.

Invoices may be created for either an Offer (from an InvoiceRequest) or a Refund. As such, the provided InvoiceBuilder can be created for either use case. The primary difference is how the signing pubkey is given -- by the writer of the offer or the reader of the refund.

@jkczyz
Copy link
Contributor Author

jkczyz commented Dec 20, 2022

Still needs tests and some example builder docs, but otherwise should be fully implemented.

@jkczyz
Copy link
Contributor Author

jkczyz commented Dec 20, 2022

@TheBlueMatt @valentinewallace Let me know if you'd like me to split the PR. Could possibly have the first five commits in as a pre-work PR.

@valentinewallace
Copy link
Contributor

@TheBlueMatt @valentinewallace Let me know if you'd like me to split the PR. Could possibly have the first five commits in as a pre-work PR.

Yeah, I'd be in favor of that if you don't mind

@jkczyz
Copy link
Contributor Author

jkczyz commented Dec 20, 2022

Opened #1927, including an additional commit that I moved up.

@jkczyz jkczyz force-pushed the 2022-12-invoice branch 2 times, most recently from 1029c5d to b409b29 Compare December 22, 2022 18:10
@jkczyz jkczyz force-pushed the 2022-12-invoice branch 2 times, most recently from 95b6b69 to 7a558fd Compare January 6, 2023 16:08
Copy link
Collaborator

@TheBlueMatt TheBlueMatt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basically LGTM.

&self.contents.fields().paths[..]
}

/// Information for each hop in [`Invoice::paths`] needed for routing payments across it.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is confusing - a BlindedPath is a path encompassing multiple hops. I assume this really means information for each path?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, right. Was confused for a second, but yeah this information is for all hops on the path, in aggregate, IIUC.

lightning/src/offers/invoice.rs Show resolved Hide resolved
lightning/src/offers/invoice.rs Show resolved Hide resolved
lightning/src/offers/invoice.rs Outdated Show resolved Hide resolved
Copy link
Contributor

@valentinewallace valentinewallace left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still going through a first pass. I assume tests are imminent

lightning/src/offers/merkle.rs Outdated Show resolved Hide resolved
lightning/src/offers/merkle.rs Outdated Show resolved Hide resolved
lightning/src/offers/invoice.rs Outdated Show resolved Hide resolved
lightning/src/offers/invoice.rs Outdated Show resolved Hide resolved
invoice_request: &'a InvoiceRequest, paths: Vec<BlindedPath>, payinfo: Vec<BlindedPayInfo>,
created_at: Duration, payment_hash: PaymentHash
) -> Result<Self, SemanticError> {
let amount_msats = match invoice_request.amount_msats() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Weird that invoice_request.amount_msats() doesn't return the offer's amount in msats if it's available, since that is its amount

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is largely to avoid using unreachable!(). Technically, the two errors below are unreachable, but if we move the code into InvoiceRequest::amount_msats(), then they could return None instead, I suppose? But if we add currency conversion, we'd likely want to do it at the builder-level, I think.

Related discussion from the last PR: #1927 (comment)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think invoice_request.amount_msats() should return an Option to avoid an unreachable, just think it should return self.contents.amount_msats.or(offer.contents.amount_msats()) (i.e. None if the offer has a Currency amount), because the offer's amount is its amount in that case IIUC. Anyway, can be punted

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There was some confusion over if we should support sending to a non-bitcoin-denominated offer, which was my fault, we can/should revisit this when we add that support.

///
/// [`Invoice`]: crate::offers::invoice::Invoice
pub fn respond_with(
&self, paths: Vec<BlindedPath>, payinfo: Vec<BlindedPayInfo>, created_at: Duration,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we have std-only versions that automatically supplies created_at?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

End up making the parameter no-std only and otherwise automated as suggested.

@codecov-commenter
Copy link

codecov-commenter commented Jan 13, 2023

Codecov Report

Base: 90.73% // Head: 91.39% // Increases project coverage by +0.66% 🎉

Coverage data is based on head (15f1295) compared to base (ad40573).
Patch coverage: 93.80% of modified lines in pull request are covered.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1926      +/-   ##
==========================================
+ Coverage   90.73%   91.39%   +0.66%     
==========================================
  Files          97       98       +1     
  Lines       50604    57080    +6476     
  Branches    50604    57080    +6476     
==========================================
+ Hits        45914    52167    +6253     
- Misses       4690     4913     +223     
Impacted Files Coverage Δ
lightning/src/offers/parse.rs 94.00% <ø> (ø)
lightning/src/routing/gossip.rs 92.15% <ø> (ø)
lightning/src/util/ser_macros.rs 86.73% <50.00%> (-0.30%) ⬇️
lightning/src/offers/invoice_request.rs 96.12% <62.50%> (+1.12%) ⬆️
lightning/src/offers/refund.rs 95.89% <68.75%> (+2.15%) ⬆️
lightning/src/util/ser.rs 91.41% <82.35%> (-0.30%) ⬇️
lightning/src/offers/offer.rs 93.33% <91.66%> (+0.06%) ⬆️
lightning/src/offers/invoice.rs 94.79% <94.79%> (ø)
lightning/src/offers/merkle.rs 100.00% <100.00%> (ø)
lightning/src/util/enforcing_trait_impls.rs 81.15% <0.00%> (-1.77%) ⬇️
... and 21 more

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

☔ View full report at Codecov.
📢 Do you have feedback about the report comment? Let us know in this issue.

@TheBlueMatt
Copy link
Collaborator

This all basically LGTM, feel free to squash and fixup the CI failures and I'm ready to land.

@jkczyz
Copy link
Contributor Author

jkczyz commented Jan 16, 2023

Added tests, but still need to add an example.

Also, see 8c76a69 for small clean-up such that Iterable is not special-cased. Open to a better name for that as well.

lightning/src/offers/invoice.rs Outdated Show resolved Hide resolved
lightning/src/offers/invoice.rs Outdated Show resolved Hide resolved
lightning/src/offers/invoice.rs Show resolved Hide resolved
@jkczyz jkczyz force-pushed the 2022-12-invoice branch 3 times, most recently from d7ec2d5 to 70a9967 Compare January 18, 2023 16:06
@jkczyz
Copy link
Contributor Author

jkczyz commented Jan 18, 2023

Added examples in the module docs and missing cross-linking. PTAL.

@@ -184,6 +184,9 @@ macro_rules! decode_tlv {
($reader: expr, $field: ident, (option: $trait: ident $(, $read_arg: expr)?)) => {{
$field = Some($trait::read(&mut $reader $(, $read_arg)*)?);
}};
($reader: expr, $field: ident, (option, encoding: ($fieldty: ty, $encoding: ident, $encoder:ty))) => {{
decode_tlv!($reader, $field, (option, encoding: ($fieldty, $encoding)));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like I'll need to rebase to account for #1823. Let me know when it's good to squash and rebase.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Go for it, IMO.

Explicitly using TlvRecord::record_bytes makes reading the code more
obvious than hiding which bytes are used in AsRef<[u8]>::as_ref.
Provide a helper for skipping signature TLV records from a TLV stream.
This prevents needing to duplicate the check for signature TLV records
when writing a TLV stream without signatures in an upcoming commit.
When using bytes from an InvoiceRequest to constructing bytes for an
Invoice, any signature TLV records in the bytes must be excluded. Define
a wrapper for encoding such pre-serialized bytes in this manner. This
will allow the forthcoming InvoiceBuilder to construct bytes for an
Invoice properly.
@jkczyz
Copy link
Contributor Author

jkczyz commented Jan 18, 2023

Rebased and squashed though added a missed parsing check on the signing pubkey after reviewing the spec. It's already handled correctly in the builder.

diff --git a/lightning/src/offers/invoice.rs b/lightning/src/offers/invoice.rs
index 9b5fe3fd..2e6fcf05 100644
--- a/lightning/src/offers/invoice.rs
+++ b/lightning/src/offers/invoice.rs
@@ -705,7 +705,11 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
 		};
 
 		match offer_tlv_stream.node_id {
-			Some(_) => {
+			Some(expected_signing_pubkey) => {
+				if fields.signing_pubkey != expected_signing_pubkey {
+					return Err(SemanticError::InvalidSigningPubkey);
+				}
+
 				let invoice_request = InvoiceRequestContents::try_from(
 					(payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream)
 				)?;
@@ -1455,6 +1459,17 @@ mod tests {
 				assert_eq!(e, ParseError::InvalidSemantics(SemanticError::MissingSigningPubkey));
 			},
 		}
+
+		let invalid_pubkey = payer_pubkey();
+		let mut tlv_stream = invoice.as_tlv_stream();
+		tlv_stream.3.node_id = Some(&invalid_pubkey);
+
+		match Invoice::try_from(tlv_stream.to_bytes()) {
+			Ok(_) => panic!("expected error"),
+			Err(e) => {
+				assert_eq!(e, ParseError::InvalidSemantics(SemanticError::InvalidSigningPubkey));
+			},
+		}
 	}
 
 	#[test]
diff --git a/lightning/src/offers/parse.rs b/lightning/src/offers/parse.rs
index 904ce403..a7d13e57 100644
--- a/lightning/src/offers/parse.rs
+++ b/lightning/src/offers/parse.rs
@@ -147,6 +147,8 @@ pub enum SemanticError {
 	MissingDescription,
 	/// A signing pubkey was not provided.
 	MissingSigningPubkey,
+	/// A signing pubkey was provided but a different one was expected.
+	InvalidSigningPubkey,
 	/// A signing pubkey was provided but was not expected.
 	UnexpectedSigningPubkey,
 	/// A quantity was expected but was missing.

TheBlueMatt
TheBlueMatt previously approved these changes Jan 19, 2023
network,
};

if !address.is_standard() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, this is probably too restrictive - we want to send to it even if its a v3 witness program or whatever, its not our job to enforce forwards compat. rust-bitcoin/rust-bitcoin#1558

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that does make sense. I may have been reading too much into this part of the spec:

  • MUST ignore any fallback_address for which address does not meet known requirements for the given version

Since there wouldn't be any known requirements for, say, version 3, we shouldn't ignore them, I guess?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought there were requirements around v0 scripts being only exactly the "correct" length but otherwise none. I could be wrong, but I'd love for rust-bitcoin upstream to fix this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, length of V0 and V1 scripts are covered by is_standard but other versions are considered non-standard. Best to have it fixed upstream, I agree, but we'll have to explicitly allow for other versions now, it seems?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, I think we should just backport the v0 length check, note that there is no length restriction on v1 - see https://github.com/bitcoin/bitcoin/blob/e9262ea32a6e1d364fb7974844fadc36f931f8c6/src/script/standard.cpp#L183-L201

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like V1 must be WITNESS_V1_TAPROOT_SIZE (32), which is what Address::is_standard checks.

Copy link
Contributor Author

@jkczyz jkczyz Jan 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, nevermind. I see the fall-through case is fine with it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, now we only skip V0 addresses that aren't the right size.

&self, payment_paths: Vec<(BlindedPath, BlindedPayInfo)>, payment_hash: PaymentHash,
signing_pubkey: PublicKey,
#[cfg(any(test, not(feature = "std")))]
created_at: Duration
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any idea of docs.rs will document this in a sane way?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm... it should be using std according to our Cargo.toml file:

[package.metadata.docs.rs]
features = ["std"]
rustdoc-args = ["--cfg", "docsrs"]

So I think that means it should leave the parameter out.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, but isn't that confusing? The docs will only be correct for std users and no-std users will be left wondering why their code doesn't compile?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, would be best to note in the docs then. Not sure if there's a better way. Even if we made separate methods, the docs will only list the std one.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC docsrs is smart and magic and will list all features with this is only available with feature X tags, but maybe we'd have to enable both no-std and std for that?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, we'd have to enable both. Not sure how much time I want to spend getting that to work at the moment. Updated the docs for now.

@TheBlueMatt
Copy link
Collaborator

Oops, no-std builds are failing on the new serialization iterable things.

@jkczyz
Copy link
Contributor Author

jkczyz commented Jan 19, 2023

New push fixes a problem with no-std:

diff --git a/lightning/src/offers/invoice.rs b/lightning/src/offers/invoice.rs
index 2e6fcf05..fbf193a5 100644
--- a/lightning/src/offers/invoice.rs
+++ b/lightning/src/offers/invoice.rs
@@ -550,12 +550,12 @@ tlv_stream!(InvoiceTlvStream, InvoiceTlvStreamRef, 160..240, {
 	(176, node_id: PublicKey),
 });
 
-type BlindedPathIter<'a> = std::iter::Map<
+type BlindedPathIter<'a> = core::iter::Map<
 	core::slice::Iter<'a, (BlindedPath, BlindedPayInfo)>,
 	for<'r> fn(&'r (BlindedPath, BlindedPayInfo)) -> &'r BlindedPath,
 >;
 
-type BlindedPayInfoIter<'a> = std::iter::Map<
+type BlindedPayInfoIter<'a> = core::iter::Map<
 	core::slice::Iter<'a, (BlindedPath, BlindedPayInfo)>,
 	for<'r> fn(&'r (BlindedPath, BlindedPayInfo)) -> &'r BlindedPayInfo,
 >;

@TheBlueMatt
Copy link
Collaborator

Looks like no-std is still sad.

Copy link
Contributor

@valentinewallace valentinewallace left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basically LGTM

self
}

/// Adds a P2WSH address to [`Invoice::fallbacks`].
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any reason users would want one type of fallback address over another? May be nice to document

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, sorry, I don't know if the current additions add much (am I missing something?). I was thinking more of the tradeoffs/use cases between p2wsh vs p2wpkh vs taproot, or a link to a reference for something like that. May be out of our purview.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, seems more like a wallet concern.

lightning/src/offers/invoice.rs Outdated Show resolved Hide resolved
return None;
}

let version = address.version;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may have been discussed already, but the spec also specifies MUST ignore any fallback_address for which version is greater than 16.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, right. We were failing to parse instead. I've updated FallbackAddress to use u8 and now ignore any address with version greater than 16. Should be fine given FallbackAddress is not public.

Define an interface for BOLT 12 `invoice` messages. The underlying
format consists of the original bytes and the parsed contents.

The bytes are later needed for serialization. This is because it must
mirror all the `offer` and `invoice_request` TLV records, including
unknown ones, which aren't represented in the contents.

Invoices may be created for an Offer (from an InvoiceRequest) or for a
Refund. The primary difference is how the signing pubkey is given -- by
the writer of the offer or the reader of the refund.
Add a builder for creating invoices for an offer from a given request
and required fields. Other settings are optional and duplicative
settings will override previous settings. Building produces a
semantically valid `invoice` message for the offer, which then can be
signed with the key associated with the offer's signing pubkey.
Add a builder for creating invoices for a refund and required fields.
Other settings are optional and duplicative settings will override
previous settings. Building produces a semantically valid `invoice`
message for the refund, which then can be signed with the key associated
with the provided signing pubkey.
For std builds, Invoice::created_at can be automatically set upon
construction using SystemTime::now() offset by SystemTime::UNIX_EPOCH.
Change InvoiceRequest::respond_with and Refund::respond_with to only
take a created_at parameter in no-std builds.
Tests for checking invoice message semantics when building an invoice as
defined by BOLT 12.
Tests for checking invoice semantics when parsing invoice bytes as
defined by BOLT 12.
Copy link
Contributor

@valentinewallace valentinewallace left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Went through the spec again and garnered some more feedback, but I think it can all be saved for follow-up

invoice_request: &'a InvoiceRequest, paths: Vec<BlindedPath>, payinfo: Vec<BlindedPayInfo>,
created_at: Duration, payment_hash: PaymentHash
) -> Result<Self, SemanticError> {
let amount_msats = match invoice_request.amount_msats() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think invoice_request.amount_msats() should return an Option to avoid an unreachable, just think it should return self.contents.amount_msats.or(offer.contents.amount_msats()) (i.e. None if the offer has a Currency amount), because the offer's amount is its amount in that case IIUC. Anyway, can be punted

self
}

/// Adds a P2WSH address to [`Invoice::fallbacks`].
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, sorry, I don't know if the current additions add much (am I missing something?). I was thinking more of the tradeoffs/use cases between p2wsh vs p2wpkh vs taproot, or a link to a reference for something like that. May be out of our purview.

/// The caller is expected to remember the preimage of `payment_hash` in order to claim a payment
/// for the invoice.
///
/// The `payment_paths` parameter is useful for maintaining the payment recipient's privacy. It
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this could use more details about why it's required even when you're using a public node id. Maybe could live on Invoice::payment_paths with a docs link here

/// must be given, which is used to set [`Invoice::created_at`] since [`std::time::SystemTime`]
/// is not available.
///
/// The caller is expected to remember the preimage of `payment_hash` in order to claim a payment
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lightning-invoice has links to ChannelManager::create_inbound_payment*, may want some here too

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll wait until the code is used in onion message handling before documenting. For Offers that code will construct the payment hash, though responding to a Refund may need some utility.


impl<'a> UnsignedInvoice<'a> {
/// Signs the invoice using the given function.
pub fn sign<F, E>(self, sign: F) -> Result<Invoice, SignError<E>>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will we need a new method on NodeSigner for this, or does NodeSigner::sign_invoice work? May want to add some documentation for how users will likely want to practically sign this using LDK

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, will need a dedicated method in a later PR. Was wondering what the interface should be given a the merkle root is what needs to be signed and would need to be calculated from the bytes as formed below (i.e., invoice_request_bytes which excludes the payer's signature and includes unknown fields).

cc: @TheBlueMatt

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, not sure what the right API is exactly but I think our target should basically be an unsigned list of fields and a util method to construct the merkle root.

lightning/src/offers/invoice.rs Show resolved Hide resolved
/// for the invoice.
///
/// The `payment_paths` parameter is useful for maintaining the payment recipient's privacy. It
/// must contain one or more elements.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should also note that these should be specified in order of most preferred to least preferred

lightning/src/offers/invoice.rs Show resolved Hide resolved
Copy link
Contributor Author

@jkczyz jkczyz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Went through the spec again and garnered some more feedback, but I think it can all be saved for follow-up

Let's leave remaining comments to a follow-up (maybe in #1972) to avoid needing to rebase #1977.

self
}

/// Adds a P2WSH address to [`Invoice::fallbacks`].
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, seems more like a wallet concern.

lightning/src/offers/invoice.rs Show resolved Hide resolved
lightning/src/offers/invoice.rs Show resolved Hide resolved
/// must be given, which is used to set [`Invoice::created_at`] since [`std::time::SystemTime`]
/// is not available.
///
/// The caller is expected to remember the preimage of `payment_hash` in order to claim a payment
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll wait until the code is used in onion message handling before documenting. For Offers that code will construct the payment hash, though responding to a Refund may need some utility.


impl<'a> UnsignedInvoice<'a> {
/// Signs the invoice using the given function.
pub fn sign<F, E>(self, sign: F) -> Result<Invoice, SignError<E>>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, will need a dedicated method in a later PR. Was wondering what the interface should be given a the merkle root is what needs to be signed and would need to be calculated from the bytes as formed below (i.e., invoice_request_bytes which excludes the payer's signature and includes unknown fields).

cc: @TheBlueMatt

@TheBlueMatt TheBlueMatt merged commit e0a0add into lightningdevkit:main Jan 23, 2023
@jkczyz jkczyz mentioned this pull request May 10, 2023
59 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants