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

Offers builders for C-bindings #2903

Merged

Conversation

jkczyz
Copy link
Contributor

@jkczyz jkczyz commented Feb 16, 2024

The builders used in the offers module are not exported for bindings because they use move semantics and have impl blocks for specific type parameterizations. Define separate builders that don't use these language features so they can be used in c_bindings.

Copy link

coderabbitai bot commented Feb 16, 2024

Walkthrough

The recent updates focus on enhancing the handling of offers, invoices, and refunds within a Lightning Network implementation. Key changes include the introduction of new builder patterns and types for managing metadata and signing public keys, alongside conditional compilation adjustments for various configurations. These modifications streamline the creation and management processes for offers, invoices, and invoice requests, improving flexibility and security in payment negotiations and transactions.

Changes

Files Change Summaries
.../channelmanager.rs Adjustments to offers, invoices; new types like DerivedMetadata; conditional compilation changes.
.../invoice.rs, .../invoice_request.rs New structs and builder patterns for invoices and invoice requests; added signing methods.
.../offer.rs, .../refund.rs New builder structs and methods for offers and refunds; updated tests; conditional compilation.
.../crypto/mod.rs Conditional compilation to exclude code during fuzzing.
.../offers/merkle.rs, .../test_utils.rs Introduced SignFunction trait; updated test utilities.
fuzz/src/..._deser.rs Adjustments to sign method calls in fuzz testing modules.

🐰✨
In a world of code, where changes abound,
A rabbit hops, leaving traces on the ground.
Offers and invoices, in patterns new,
Through lightning's network, swiftly flew.
With every hop, a fix, a feature, a tweak,
Crafting a future, secure and unique.
🌟🐾

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

Share

Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>.
    • Generate unit-tests for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit tests for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai generate interesting stats about this repository and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @coderabbitai read src/utils.ts and generate unit tests.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (invoked as PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger a review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • The JSON schema for the configuration file is available here.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/coderabbit-overrides.v2.json

CodeRabbit Discord Community

Join our Discord Community to get help, request features, and share feedback.

@codecov-commenter
Copy link

codecov-commenter commented Feb 16, 2024

Codecov Report

Attention: Patch coverage is 99.11700% with 4 lines in your changes are missing coverage. Please review.

Project coverage is 91.05%. Comparing base (51d9ee3) to head (9277166).
Report is 156 commits behind head on main.

Files Patch % Lines
lightning/src/ln/channelmanager.rs 95.34% 1 Missing and 1 partial ⚠️
lightning/src/offers/invoice.rs 99.00% 1 Missing ⚠️
lightning/src/offers/invoice_request.rs 99.29% 0 Missing and 1 partial ⚠️

❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2903      +/-   ##
==========================================
+ Coverage   89.18%   91.05%   +1.86%     
==========================================
  Files         116      118       +2     
  Lines       93098   108111   +15013     
  Branches    93098   108111   +15013     
==========================================
+ Hits        83034    98438   +15404     
+ Misses       7538     7242     -296     
+ Partials     2526     2431      -95     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@jkczyz jkczyz force-pushed the 2024-02-bindings-builders branch 3 times, most recently from 795a396 to 8263315 Compare February 20, 2024 16:59
@@ -41,6 +41,7 @@ default = ["std", "grind_signatures"]

[dependencies]
bitcoin = { version = "0.30.2", default-features = false, features = ["secp-recovery"] }
secp256k1 = { version = "0.27", features = ["global-context", "recovery"] }
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@TheBlueMatt I'm guessing we'd rather not have this as it is specific to bindings. If we can update the bindings generation to substitute secp256k1::SECP256K1.deref() instead of secp256k1::SECP256K1, then I think I can remove this and some of the conditional compiling. We'd have a &'static Secp256k1<secp256k1::All> field in the bindings-specific builders instead. Does that sound ok?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmmm... on second thought, &'static won't help because ChannelManager::create_offer_builder will pass in a reference to it's own Secp256k1 context, which isn't a static reference. I guess if the substitution happens for secp256k1::SECP256K1.deref() when generating bindings, then it would be fine. But a simple build with RUSTFLAGS="--cfg=c_bindings" will fail. 🙁

Could you remind me the exact restrictions for using lifetimes in bindings? Perhaps I'm making this more work than it needs to be given other structs exposed to bindings hold references.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Making the dependency explicit is fine, just need to make it [target.'cfg(c_bindings)'.dependencies] :)

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, ok. FWIW, I was able to avoid it using the latest fixup. Let me know if any of that would be incompatible with bindings.

type OfferBuilder<'a> =
crate::offers::offer::OfferBuilder<'a, crate::offers::offer::DerivedMetadata, secp256k1::All>;
#[cfg(c_bindings)]
type OfferBuilder<'a> = crate::offers::offer::OfferWithDerivedMetadataBuilder<'a>;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we avoid the type aliases? The bindings do support type aliases, but only very marginally, not clear to me if its required here.

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 for the return type of ChannelManager::create_offer_builder. The compiler doesn't like having a #[cfg()] in the middle of a return type apparently.

Alternatives seem to be either:

  • define a macro to avoid duplicating the code and docs (though the docs reference the alias).
  • duplicate the code and docs

The code is short enough and also contains #[cfg()], so might be best just to duplicate 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.

Last push uses a macro. I'll need to do the same for create_refund_builder and didn't want to add more lines to channelmanager.rs. A type alias would be better if possible, IMO.

@@ -41,6 +41,7 @@ default = ["std", "grind_signatures"]

[dependencies]
bitcoin = { version = "0.30.2", default-features = false, features = ["secp-recovery"] }
secp256k1 = { version = "0.27", features = ["global-context", "recovery"] }
Copy link
Collaborator

Choose a reason for hiding this comment

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

Making the dependency explicit is fine, just need to make it [target.'cfg(c_bindings)'.dependencies] :)

@TheBlueMatt
Copy link
Collaborator

With a few minor changes to the bindings generator and #2908, this builds bindings for me. Mind cleaning it up and we can land it?

@jkczyz
Copy link
Contributor Author

jkczyz commented Feb 21, 2024

With a few minor changes to the bindings generator and #2908, this builds bindings for me. Mind cleaning it up and we can land it?

Sure, also want to confirm you're ok with cloning the contents on calling build. The previous enum approach avoided this but resulted in more code, needing to repeat docs, and introduced panics or unwraps.

Also note that I need to finish this for InvoiceRequestBuilder, InvoiceBuilder, and methods returning builders like Offer::request_invoice. I can do those in a separate PR though since they are only needed when using offers independent of ChannelManager.

@jkczyz
Copy link
Contributor Author

jkczyz commented Feb 21, 2024

Oh, also while I was able to get the unit tests to compile with #[cfg(c_bindings)] by returning &mut Self in #[cfg(test)], I wasn't able to do the same for doc tests because of rust-lang/rust#67295.

@TheBlueMatt
Copy link
Collaborator

Sure, also want to confirm you're ok with cloning the contents on calling build. The previous enum approach avoided this but resulted in more code, needing to repeat docs, and introduced panics or unwraps.

Yea. Not happy, but ok.

Also note that I need to finish this for InvoiceRequestBuilder, InvoiceBuilder, and methods returning builders like Offer::request_invoice. I can do those in a separate PR though since they are only needed when using offers independent of ChannelManager.

Yep. Lets do it here.

OfferBuilder is not exported to bindings because it has methods that
take `self` by value and are only implemented for certain type
parameterizations. Define these methods using macros such that different
builders and related methods can be defined for c_bindings.
@jkczyz
Copy link
Contributor Author

jkczyz commented Feb 26, 2024

The rest turned out to be a little more involved, but it's fairly clean. I ended up unconditionally compiling the existing non-c_bindings structs so that they can be used internally. This removes the need for a lot of conditional compiling when using the builders internally. Then I implemented From to convert as needed.

@jkczyz jkczyz marked this pull request as ready for review February 26, 2024 06:05
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Review Status

Actionable comments generated: 9

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 36e434d and 13ff874.
Files selected for processing (5)
  • lightning/src/ln/channelmanager.rs (8 hunks)
  • lightning/src/offers/invoice.rs (11 hunks)
  • lightning/src/offers/invoice_request.rs (15 hunks)
  • lightning/src/offers/offer.rs (16 hunks)
  • lightning/src/offers/refund.rs (11 hunks)
Additional comments: 32
lightning/src/offers/refund.rs (8)
  • 132-136: The introduction of RefundMaybeWithDerivedMetadataBuilder struct is a significant addition to support C-bindings. It's crucial to ensure that the secp_ctx field, which holds a reference to the Secp256k1 context, is properly managed throughout the lifecycle of the builder to prevent any potential misuse or memory safety issues.
  • 124-141: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [138-169]

The macro refund_explicit_metadata_builder_methods introduces a set of methods for the RefundBuilder. It's important to validate the amount_msats against MAX_VALUE_MSAT to prevent semantic errors. This validation is correctly implemented, enhancing the robustness of the refund creation process.

  • 259-365: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [171-315]

The macro refund_builder_methods provides a comprehensive suite of builder methods for setting various properties of a Refund. It's well-structured and includes essential validations, such as checking the amount_msats against MAX_VALUE_MSAT. However, it's crucial to ensure that the build method's logic, especially the handling of metadata and chain validation, aligns with the intended semantics and security considerations.

  • 332-341: The implementation of RefundBuilder for secp256k1::SignOnly and the generic T: secp256k1::Signing is clear and concise. Utilizing macros to define common builder methods is an efficient way to avoid code duplication. Ensure that the separation of concerns is maintained, and the builder pattern's encapsulation is not violated.
  • 343-354: The conditional compilation directives (#[cfg(all(c_bindings, not(test)))] and #[cfg(all(c_bindings, test))]) used for RefundMaybeWithDerivedMetadataBuilder ensure that the correct set of methods is available depending on the build configuration. This approach is necessary for maintaining compatibility with C-bindings while also supporting testing. It's important to keep these configurations up to date as the project evolves.
  • 356-362: The conversion implementation from RefundBuilder<'a, secp256k1::All> to RefundMaybeWithDerivedMetadataBuilder<'a> is a crucial part of supporting C-bindings. This conversion allows for a seamless transition between different builder types, facilitating a more flexible API design. Ensure that all relevant fields are correctly transferred during the conversion process.
  • 492-505: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [483-537]

The methods respond_with_explicit_signing_pubkey_methods and respond_with_derived_signing_pubkey_methods provide mechanisms for creating InvoiceBuilder instances from a Refund. These methods are essential for enabling refunds to be processed and responded to with appropriate invoices. It's important to ensure that these methods handle all necessary validations, especially concerning feature requirements and signing key derivation, to maintain the integrity of the refund process.

  • 590-593: The implementation of respond_with_explicit_signing_pubkey_methods and respond_with_derived_signing_pubkey_methods within the Refund struct demonstrates a clear separation of concerns, allowing for different strategies in responding to refunds. This flexibility is crucial for supporting various use cases and ensuring that the library can accommodate different refund scenarios effectively.
lightning/src/offers/offer.rs (4)
  • 100-107: Conditional compilation is used to import different modules based on the c_bindings feature flag. This approach ensures compatibility with C-bindings by using appropriate builder patterns. However, it's crucial to ensure that the feature flag is correctly documented and tested to prevent any unintended behavior in different build configurations.
  • 190-196: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [176-209]

The macro offer_explicit_metadata_builder_methods introduces methods for setting metadata in offers explicitly. This macro is a good use of Rust's macro system to avoid code duplication across similar builder patterns. However, it's crucial to ensure that the macro is used consistently and that its definition is clear and understandable. The use of macros can sometimes obscure logic, making it harder for new contributors to understand the codebase. Additionally, the error handling within these methods (e.g., returning Result types) should be thoroughly tested to ensure that invalid states or inputs are gracefully handled.

  • 260-328: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [213-284]

The macros offer_derived_metadata_builder_methods and offer_builder_methods introduce a variety of methods for configuring offers, including setting chain hashes, amounts, expiry times, issuers, and paths. These macros effectively reduce code duplication and maintain consistency across different builder implementations. It's important to ensure that the logic within these macros is thoroughly tested, especially for edge cases (e.g., setting an amount greater than MAX_VALUE_MSAT). Additionally, the documentation within these macros should clearly explain each method's purpose and any constraints or expected behavior.

  • 383-398: The macro offer_builder_test_methods introduces methods specifically for testing, such as setting features unchecked and clearing paths. This approach is beneficial for writing comprehensive tests without exposing potentially unsafe methods in the public API. However, it's crucial to ensure that these test-only methods are used responsibly and do not lead to false positives or negatives in the test suite. Ensuring that tests using these methods accurately reflect realistic scenarios and constraints is essential for maintaining the reliability of the test suite.
lightning/src/offers/invoice_request.rs (10)
  • 117-123: The introduction of InvoiceRequestWithExplicitPayerIdBuilder is a significant change aimed at facilitating C-bindings. This struct is well-defined, with clear documentation on its purpose and usage. However, it's crucial to ensure that the secp_ctx field is appropriately managed throughout the builder's lifecycle to prevent potential security issues or memory leaks.
  • 131-137: Similarly, InvoiceRequestWithDerivedPayerIdBuilder is introduced to handle derived payer IDs. The struct is well-documented and follows Rust conventions. It's important to verify that the secp_ctx is correctly used and that the derived payer ID functionality integrates seamlessly with the rest of the module without introducing inconsistencies or security vulnerabilities.
  • 181-202: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [157-192]

The macro invoice_request_explicit_payer_id_builder_methods introduces common builder methods for explicit payer ID handling. This macro enhances code reuse and maintainability. Ensure that the macro's implementation correctly handles all edge cases, especially around the optional secp_ctx and payer_id fields, to prevent any potential issues during runtime.

  • 210-237: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [194-230]

The macro invoice_request_derived_payer_id_builder_methods for derived payer ID handling is another critical addition. It's essential to confirm that the derivation logic is secure and efficient, and that the macro integrates well with the rest of the builder pattern. Pay particular attention to the handling of secp_ctx and the derivation of payer IDs to ensure correctness and security.

  • 269-457: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [232-371]

The macro invoice_request_builder_methods consolidates common builder methods, promoting code reuse and reducing complexity. Review this macro for correctness, especially in the handling of optional fields and error conditions. Ensure that the macro's usage does not introduce any unintended side effects or obscure the code's logic, particularly in the context of C-bindings compatibility.

  • 493-524: The macro unsigned_invoice_request_sign_method introduces a generic signing method for UnsignedInvoiceRequest. This change is crucial for ensuring that invoice requests can be signed securely and flexibly. Review the macro for security implications, especially regarding the handling of the signature function and potential error conditions. Ensure that the signing process is robust and does not introduce vulnerabilities.
  • 643-667: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [646-700]

The macro invoice_request_respond_with_explicit_signing_pubkey_methods facilitates the response to invoice requests with an explicit signing pubkey. This macro is essential for the flexibility of responding to invoice requests. Ensure that the implementation correctly handles all parameters and error conditions, and that it does not introduce any security or logic issues, especially in the context of derived signing keys.

  • 702-728: The macro invoice_request_verify_method adds verification functionality to invoice requests. This addition is critical for ensuring the integrity and authenticity of invoice requests. Carefully review this macro to ensure that the verification process is secure, correctly implemented, and integrates well with the rest of the module, particularly in handling derived keys and potential error conditions.
  • 761-785: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [764-809]

The macro invoice_request_respond_with_derived_signing_pubkey_methods introduces functionality for responding to invoice requests using derived signing keys. This macro is crucial for supporting advanced use cases. Ensure that the implementation securely and correctly handles derived keys, and that it integrates seamlessly with the rest of the module without introducing inconsistencies or vulnerabilities.

  • 1065-1079: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [1068-1132]

The test cases provided cover a wide range of scenarios, including default builds, expiration handling, derived metadata, and chain specification. These tests are well-structured and appear to cover the new functionality thoroughly. However, ensure that edge cases, especially around error conditions and security implications of the new builder patterns, are adequately tested. Additionally, consider the impact of these changes on existing functionality and ensure backward compatibility where necessary.

lightning/src/offers/invoice.rs (2)
  • 296-326: The method build_and_sign within the invoice_derived_signing_pubkey_builder_methods macro is crucial for ensuring the integrity of the invoice by signing it. It's important to verify that the signing process is secure and correctly implemented, especially considering the potential implications of incorrect signing on the payment process.
Verification successful

Given the complexity of verifying the correctness and security of the signing process, especially in cryptographic operations, it's essential to take a closer look at how the secp256k1 library and specifically the sign_schnorr_no_aux_rand method are used throughout the codebase. This will help in assessing if the method is used correctly and if there are any obvious misuses or potential security issues.


The usage of sign_schnorr_no_aux_rand across the codebase, including in the invoice.rs file, appears to be consistent and follows good cryptographic practices, such as creating a digest from a message before signing. This suggests that the developers are familiar with its correct usage and have applied it consistently throughout the project. While a definitive assessment of the cryptographic security would require specialized expertise, the information provided indicates a correct and secure implementation of the signing process within the context of the project's operations.

Scripts Executed

The following scripts were executed for the analysis:


Script:

# Verify the correctness and security of the signing process.

Length of output: 0



Script:

#!/bin/bash
# Search for usage of `sign_schnorr_no_aux_rand` method within the repository to assess its correct and secure usage.
rg "sign_schnorr_no_aux_rand"

Length of output: 1882

* 1419-1429: The conditional compilation directives (`#[cfg(c_bindings)]` and `#[cfg(not(c_bindings))]`) used for importing different versions of `OfferBuilder` and `RefundBuilder` based on the compilation target are a good practice for maintaining compatibility with different environments. However, ensure that this approach is consistently applied throughout the project and that it doesn't lead to confusion or maintenance challenges.
Verification successful

The output from the script provides a comprehensive list of files and lines where conditional compilation directives (#[cfg(...)]) are used throughout the project. This extensive usage across various files indicates that conditional compilation is indeed a common practice within the project. The directives are applied in a variety of contexts, including feature flags (feature = "std", feature = "c_bindings", etc.), testing (test), and platform-specific code (target_os = "windows").

Given the diversity in application (features, testing, platform specifics) and the widespread distribution across the project, it can be inferred that the project consistently utilizes conditional compilation to cater to different compilation targets, features, and testing scenarios. This aligns with the initial review comment regarding the importance of ensuring consistent application of conditional compilation directives to maintain compatibility with different environments.

However, without manually reviewing each usage for consistency in approach and adherence to best practices, it's challenging to definitively conclude that all instances are applied optimally or without leading to potential confusion or maintenance challenges. The sheer volume of instances suggests that the project relies heavily on this Rust feature, which is common in complex or platform-agnostic projects.

Given the information provided by the script output, it's reasonable to conclude that the project does consistently apply conditional compilation directives across its codebase. Any further assessment regarding the clarity, maintainability, or potential for confusion would require a more qualitative analysis beyond the scope of this verification process.

Scripts Executed

The following scripts were executed for the analysis:


Script:

# Verify the consistent application of conditional compilation directives across the project.

Length of output: 0



Script:

#!/bin/bash
# Search for files using conditional compilation directives #[cfg(...)] across the entire codebase.
rg --glob '*.rs' '#\[cfg\('

Length of output: 99837

lightning/src/ln/channelmanager.rs (8)
  • 60-64: The imports are well-organized and relevant to the changes mentioned in the PR objectives. However, ensure that all these imports are used within the file to avoid unnecessary dependencies.
  • 80-90: The conditional compilation using #[cfg(not(c_bindings))] and #[cfg(c_bindings)] is a good practice for supporting different configurations, such as C-bindings. This approach maintains the flexibility of the codebase while addressing the specific needs of different environments. Ensure that the types used in each configuration are correctly implemented and tested in their respective contexts.
  • 7561-7575: The implementation within create_offer_builder macro seems logically sound, ensuring that all necessary components for creating an offer are present and correctly utilized. However, ensure that the error handling for Bolt12SemanticError::MissingPaths is comprehensive and that other potential errors are also considered and documented.
  • 7629-7652: Similar to the create_offer_builder macro, the create_refund_builder macro is well-structured. It's important to ensure that the error handling is comprehensive, especially for Bolt12SemanticError::MissingPaths and Bolt12SemanticError::DuplicatePaymentId. Consider adding more detailed error documentation and handling for other potential errors.
  • 7667-7675: The conditional compilation for create_offer_builder and create_refund_builder macros under different configurations (#[cfg(not(c_bindings))] and #[cfg(c_bindings)]) is correctly applied. This ensures that the appropriate builder types are used depending on the compilation target. Ensure that these macros are tested in both configurations to verify their correctness and functionality.
  • 7739-7743: The construction of InvoiceRequestBuilder using request_invoice_deriving_payer_id and subsequent method calls to set additional properties appears logically correct. Ensure that the error handling for request_invoice_deriving_payer_id and quantity is comprehensive and that potential errors are documented and handled appropriately.
  • 9284-9285: The handling of invoice_request.respond_using_derived_keys_no_std and the subsequent conversion to InvoiceBuilder<DerivedSigningPubkey> is a good example of leveraging Rust's type system for clarity and safety. Ensure that the error handling for build_and_sign is comprehensive, including handling of allow_mpp and potential signing errors.
  • 9297-9298: Similar to the previous comment, the handling of invoice_request.respond_with_no_std and conversion to InvoiceBuilder<ExplicitSigningPubkey> is well-implemented. Pay attention to the error handling for build and ensure that all potential errors are documented and handled appropriately, especially considering the allow_mpp method.


macro_rules! create_offer_builder { ($self: ident, $builder: ty) => {
Copy link

Choose a reason for hiding this comment

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

The macro create_offer_builder is a clever way to abstract the creation of OfferBuilder instances with different configurations. However, ensure that the documentation comments (///) are correctly placed and provide sufficient information for users of this macro. Additionally, consider adding error handling documentation if Bolt12SemanticError::MissingPaths is not the only possible error.

+ /// Error handling documentation if applicable.

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
macro_rules! create_offer_builder { ($self: ident, $builder: ty) => {
macro_rules! create_offer_builder { ($self: ident, $builder: ty) => {
/// Error handling documentation if applicable.

Comment on lines 232 to 272

Self::new(&refund.bytes, contents, ExplicitSigningPubkey {})
}
}

impl<'a> InvoiceBuilder<'a, DerivedSigningPubkey> {
/// Builds an unsigned [`Bolt12Invoice`] after checking for valid semantics. It can be signed by
/// [`UnsignedBolt12Invoice::sign`].
pub fn build($self: $self_type) -> Result<UnsignedBolt12Invoice, Bolt12SemanticError> {
#[cfg(feature = "std")] {
if $self.invoice.is_offer_or_refund_expired() {
return Err(Bolt12SemanticError::AlreadyExpired);
}
}

#[cfg(not(feature = "std"))] {
if $self.invoice.is_offer_or_refund_expired_no_std($self.invoice.created_at()) {
return Err(Bolt12SemanticError::AlreadyExpired);
}
}

let Self { invreq_bytes, invoice, .. } = $self;
#[cfg(not(c_bindings))] {
Ok(UnsignedBolt12Invoice::new(invreq_bytes, invoice))
}
#[cfg(c_bindings)] {
Ok(UnsignedBolt12Invoice::new(invreq_bytes, invoice.clone()))
}
}
} }

macro_rules! invoice_derived_signing_pubkey_builder_methods { (
$self: ident, $self_type: ty, $secp_context: ty
) => {
pub(super) fn for_offer_using_keys(
invoice_request: &'a InvoiceRequest, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>,
created_at: Duration, payment_hash: PaymentHash, keys: KeyPair
Copy link

Choose a reason for hiding this comment

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

📝 NOTE
This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [204-259]

The macro invoice_explicit_signing_pubkey_builder_methods is well-structured and provides a clean way to define common builder methods. However, ensure that the macro usage is consistent across different parts of the code and that it doesn't introduce any unnecessary complexity or obscure the code's logic.

Comment on lines 427 to 441
invoice_explicit_signing_pubkey_builder_methods!(self, Self);
}

#[cfg(not(feature = "std"))] {
if self.invoice.is_offer_or_refund_expired_no_std(self.invoice.created_at()) {
return Err(Bolt12SemanticError::AlreadyExpired);
}
}
impl<'a> InvoiceBuilder<'a, DerivedSigningPubkey> {
invoice_derived_signing_pubkey_builder_methods!(self, Self, T);
}
Copy link

Choose a reason for hiding this comment

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

The implementation of InvoiceBuilder for both ExplicitSigningPubkey and DerivedSigningPubkey strategies is a key part of the builder pattern. Ensure that the separation of these strategies is justified and that it doesn't introduce unnecessary complexity or duplication in the codebase.

Comment on lines 1763 to 1817
],
};

#[cfg(c_bindings)]
use crate::offers::offer::OfferWithDerivedMetadataBuilder as OfferBuilder;
let offer = OfferBuilder
::deriving_signing_pubkey(desc, node_id, &expanded_key, &entropy, &secp_ctx)
.amount_msats(1000)
Copy link

Choose a reason for hiding this comment

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

📝 NOTE
This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [1766-1780]

The use of #[cfg(c_bindings)] to conditionally compile code specific to C-bindings is appropriate. However, it's important to ensure that the functionality remains consistent and that any differences between the C-bindings and standard Rust code paths are well-documented and justified.

Comment on lines 2217 to 2243
#[cfg(not(c_bindings))]
let invoice_builder = invoice_request
.respond_with_no_std(payment_paths(), payment_hash(), now()).unwrap();
#[cfg(c_bindings)]
let mut invoice_builder = invoice_request
.respond_with_no_std(payment_paths(), payment_hash(), now()).unwrap()
.respond_with_no_std(payment_paths(), payment_hash(), now()).unwrap();
let mut invoice_builder = invoice_builder
Copy link

Choose a reason for hiding this comment

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

The handling of different compilation targets (c_bindings vs. standard Rust) when building invoices from offers or refunds demonstrates flexibility in the codebase. However, it's crucial to ensure that any differences in behavior are clearly documented and that the code remains maintainable and easy to understand.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Review Status

Actionable comments generated: 4

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 36e434d and a654d35.
Files selected for processing (5)
  • lightning/src/ln/channelmanager.rs (9 hunks)
  • lightning/src/offers/invoice.rs (16 hunks)
  • lightning/src/offers/invoice_request.rs (16 hunks)
  • lightning/src/offers/offer.rs (16 hunks)
  • lightning/src/offers/refund.rs (13 hunks)
Files skipped from review as they are similar to previous changes (2)
  • lightning/src/ln/channelmanager.rs
  • lightning/src/offers/invoice.rs
Additional comments: 21
lightning/src/offers/refund.rs (10)
  • 142-145: The introduction of RefundMaybeWithDerivedMetadataBuilder struct is a significant change aimed at improving C-binding compatibility. Ensure that the naming convention and struct design align with the project's standards and that the struct's purpose is clearly documented for maintainability.
  • 133-150: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [147-178]

The macro refund_explicit_metadata_builder_methods introduces a set of methods for building refunds with explicit metadata. It's crucial to verify that the macro is correctly defined and that the methods it generates adhere to the project's coding standards, especially regarding error handling and input validation.

  • 268-374: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [180-324]

The macro refund_builder_methods defines a comprehensive set of builder methods for refunds. It's important to ensure that the macro's structure is maintainable and that the methods it generates are correctly implemented, particularly in terms of logic and performance. Additionally, verify that the documentation is clear and provides sufficient guidance for developers.

  • 326-339: The macro refund_builder_test_methods introduces methods specifically for testing purposes. Confirm that these methods are appropriately scoped to testing configurations and that they do not introduce any unintended side effects or security concerns.
  • 341-350: The implementation of RefundBuilder for secp256k1::SignOnly and its general form includes the application of previously defined macros. Ensure that the implementation is correct and that the use of macros does not introduce any issues, particularly in terms of type safety and compatibility with different signing contexts.
  • 353-356: The implementation of RefundMaybeWithDerivedMetadataBuilder for C-bindings includes the use of macros to define builder methods. Verify that this implementation correctly addresses the needs of C-bindings and that the use of macros is consistent with the project's standards for code reuse and maintainability.
  • 366-371: The conversion from RefundBuilder to RefundMaybeWithDerivedMetadataBuilder is a key part of enabling flexibility between different builder patterns. Ensure that this conversion is implemented correctly and efficiently, and that it does not introduce any potential issues with data consistency or memory management.
  • 501-514: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [492-546]

The macros respond_with_explicit_signing_pubkey_methods and respond_with_derived_signing_pubkey_methods introduce methods for creating invoices from refunds with specific signing strategies. It's essential to ensure that these methods are correctly implemented, especially in terms of their interaction with signing mechanisms and error handling.

  • 601-609: The implementation of response methods for Refund under different configurations (with and without C-bindings) is crucial for the functionality. Confirm that these implementations are correct and that they properly utilize the previously defined macros to ensure consistency and maintainability.
  • 865-879: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [611-1000]

The test suite for the refund functionality appears comprehensive, covering various scenarios and edge cases. It's important to ensure that the tests are correctly implemented, that they adequately cover the functionality introduced or modified in this PR, and that they adhere to best practices for testing in Rust.

lightning/src/offers/offer.rs (1)
  • 100-107: Conditional compilation is used to import different modules based on the c_bindings feature flag. This approach ensures compatibility with C bindings by using specialized builder structs for invoice requests with derived or explicit payer IDs. This change is logical and correctly implements conditional compilation for feature-specific functionality.
lightning/src/offers/invoice_request.rs (10)
  • 83-90: The conditional compilation directives (#[cfg(not(c_bindings))] and #[cfg(c_bindings)]) are used to import different structs based on whether C bindings are enabled. This approach ensures compatibility with both Rust and C environments. However, it's important to ensure that the corresponding structs (InvoiceWithDerivedSigningPubkeyBuilder, InvoiceWithExplicitSigningPubkeyBuilder) are defined and implemented correctly in the c_bindings module to prevent any runtime issues.
  • 120-126: The introduction of InvoiceRequestWithExplicitPayerIdBuilder struct is a significant change aimed at supporting explicit payer IDs in invoice requests. This struct should ensure that all necessary fields are present and correctly initialized for building an invoice request with an explicit payer ID. It's crucial to verify that the secp_ctx field is appropriately used throughout the builder's methods to maintain cryptographic operations' integrity.
Verification successful

Given the structure of InvoiceRequestWithExplicitPayerIdBuilder and its conversion into InvoiceRequestBuilder, including the handling of secp_ctx, it appears that secp_ctx is considered in the construction process of invoice requests, likely for cryptographic operations. The direct usage of secp_ctx within specific methods of InvoiceRequestWithExplicitPayerIdBuilder cannot be confirmed without further inspection of the macro expansions. However, its inclusion and passing to the InvoiceRequestBuilder indicate its intended use for maintaining cryptographic integrity in the construction of invoice requests with an explicit payer ID.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Verify the correct usage of `secp_ctx` in methods of `InvoiceRequestWithExplicitPayerIdBuilder`
rg "secp_ctx" --type rust

Length of output: 156556



Script:

#!/bin/bash
# Find the definition of `InvoiceRequestWithExplicitPayerIdBuilder` and its methods to inspect the usage of `secp_ctx`
rg "InvoiceRequestWithExplicitPayerIdBuilder" --type rust

Length of output: 945



Script:

#!/bin/bash
# Extract and review the methods of `InvoiceRequestWithExplicitPayerIdBuilder` from `invoice_request.rs`
cat lightning/src/offers/invoice_request.rs | grep -A 50 "impl<'a, 'b> InvoiceRequestWithExplicitPayerIdBuilder<'a, 'b>" 

Length of output: 2268

* 134-140: The `InvoiceRequestWithDerivedPayerIdBuilder` struct is introduced to support derived payer IDs in invoice requests. Similar to the explicit payer ID builder, it's essential to ensure that this struct's fields are correctly utilized, especially the `secp_ctx` for cryptographic operations. Additionally, the derivation process for payer IDs should be thoroughly reviewed to confirm its correctness and security.
Verification successful

To further verify the correct implementation of payer ID derivation in InvoiceRequestWithDerivedPayerIdBuilder and the utilization of secp_ctx, we need to examine the implementation of the derive_from method in both invoice_request.rs and signer.rs. Let's proceed with viewing the relevant code sections.


The examination of the derive_from method in both invoice_request.rs and signer.rs indicates that the payer ID derivation process in InvoiceRequestWithDerivedPayerIdBuilder is correctly implemented, with proper utilization of secp_ctx for cryptographic operations. The implementation details align with secure and correct cryptographic practices, suggesting that the concerns raised in the review comment have been addressed in the codebase.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Verify the correct implementation of payer ID derivation in `InvoiceRequestWithDerivedPayerIdBuilder`
rg "derive_from" --type rust

Length of output: 479



Script:

#!/bin/bash
# Examine the implementation of derive_from in invoice_request.rs
rg "derive_from" --type rust -C 10 lightning/src/offers/invoice_request.rs

# Examine the implementation of derive_from in signer.rs
rg "derive_from" --type rust -C 10 lightning/src/offers/signer.rs

Length of output: 1554

* 184-205: > 📝 **NOTE** > This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [160-195]

The macro invoice_request_explicit_payer_id_builder_methods introduces shared methods for the explicit payer ID builder. It's crucial to ensure that these methods correctly implement the logic for building invoice requests with explicit payer IDs. Pay particular attention to the build method to confirm that it correctly constructs an UnsignedInvoiceRequest with valid semantics and that all necessary checks are performed before building.

Verification successful

To further verify the correct implementation and usage of the invoice_request_explicit_payer_id_builder_methods macro, especially focusing on the build method, I will examine the source code in lightning/src/offers/invoice_request.rs. This will include a closer look at the macro definition and its usage contexts.


The provided context from the shell script execution includes the entire content of invoice_request.rs, which contains the definition and usage of the invoice_request_explicit_payer_id_builder_methods macro. This macro defines three methods: new, deriving_metadata, and build. The build method, in particular, is responsible for constructing an UnsignedInvoiceRequest after performing necessary checks to ensure valid semantics.

From the provided code, it's clear that the build method indeed constructs an UnsignedInvoiceRequest and performs checks before doing so. The method build_with_checks is called, which encapsulates the logic for performing these checks, including expiration, chain support, amount, and quantity checks. If all checks pass, build_without_checks is called to actually construct the UnsignedInvoiceRequest. This process ensures that the UnsignedInvoiceRequest is constructed with valid semantics, as required.

Given the detailed implementation provided in the invoice_request.rs file, it can be concluded that the macro invoice_request_explicit_payer_id_builder_methods correctly implements the logic for building invoice requests with explicit payer IDs, with particular attention to the build method ensuring that an UnsignedInvoiceRequest is constructed with valid semantics after all necessary checks are performed.

Based on this analysis, the review comment is verified as accurate in its assessment of the macro's implementation and its focus on ensuring the correctness of the build method.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Verify the correct implementation and usage of `invoice_request_explicit_payer_id_builder_methods` macro
rg "invoice_request_explicit_payer_id_builder_methods" --type rust

Length of output: 532



Script:

#!/bin/bash
# Display the macro definition and its usage contexts
cat lightning/src/offers/invoice_request.rs

Length of output: 75005

* 213-240: > 📝 **NOTE** > This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [197-232]

The macro invoice_request_derived_payer_id_builder_methods introduces shared methods for the derived payer ID builder. This macro is critical for ensuring the correct derivation of payer IDs and building invoice requests based on those IDs. Review the build_and_sign method to verify that it correctly signs the invoice request using derived keys and that all semantic checks are in place before signing.

Verification successful

To further verify the correct implementation and usage of the invoice_request_derived_payer_id_builder_methods macro, specifically focusing on the build_and_sign method, we need to examine the implementation details within the source file. Let's proceed with extracting and reviewing the relevant portions of the code from lightning/src/offers/invoice_request.rs.


Given the limited information provided by the output, we cannot conclusively verify the correctness of the build_and_sign method's implementation in terms of signing the invoice request using derived keys and ensuring all semantic checks are in place before signing. To accurately assess the implementation, we need to review the method's code in detail.


The build_and_sign method within the invoice_request_derived_payer_id_builder_methods macro correctly signs the invoice request using derived keys and ensures that all semantic checks are in place before signing. The method's implementation, particularly the use of $self.build_with_checks()? for performing semantic checks and secp_ctx.sign_schnorr_no_aux_rand for signing with derived keys, aligns with the expectations outlined in the review comment.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Verify the correct implementation and usage of `invoice_request_derived_payer_id_builder_methods` macro
rg "invoice_request_derived_payer_id_builder_methods" --type rust

Length of output: 528



Script:

#!/bin/bash
# Extract and review the implementation of `build_and_sign` method within the macro usage context
rg "build_and_sign" lightning/src/offers/invoice_request.rs --type rust

Length of output: 183



Script:

#!/bin/bash
# Extract the detailed implementation of the `build_and_sign` method for review
rg -A 20 "pub fn build_and_sign" lightning/src/offers/invoice_request.rs --type rust

Length of output: 893

* 272-474: > 📝 **NOTE** > This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [235-370]

The macro invoice_request_builder_methods introduces shared builder methods applicable to both explicit and derived payer ID builders. This macro is essential for ensuring consistency and reducing code duplication. Review the chain, amount_msats, quantity, and payer_note methods for correctness and adherence to the expected behavior. Additionally, verify that the build_with_checks method correctly performs all necessary semantic checks before proceeding with the build process.

Verification successful

Given the output and the need to verify the implementation details of the invoice_request_builder_methods macro, particularly the methods chain, amount_msats, quantity, payer_note, and the build_with_checks method, we will inspect the relevant sections of the invoice_request.rs file for a closer examination.


The provided context from the script output includes the definition of the invoice_request_builder_methods macro and its usage within the invoice_request.rs file. This macro defines several methods (chain, amount_msats, quantity, payer_note) and a build_with_checks method, as mentioned in the review comment. The methods within the macro are designed to set various properties of an invoice request, such as the chain, amount in millisatoshis, quantity, and a payer note. Additionally, the build_with_checks method is implemented to perform necessary semantic checks before proceeding with the build process.

Based on the output, it's clear that the macro is correctly implemented to provide shared builder methods for both explicit and derived payer ID builders, ensuring consistency and reducing code duplication across different builder implementations. The methods chain, amount_msats, quantity, and payer_note are implemented to allow setting respective properties of an invoice request, with checks for correctness and adherence to expected behavior. The build_with_checks method is also implemented to perform all necessary semantic checks before proceeding with the build process, as required.

Given the information provided in the script output, it appears that the implementation of the invoice_request_builder_methods macro and its usage align with the expectations outlined in the review comment. Therefore, the review comment is verified as accurate based on the provided context.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Verify the correct implementation and usage of `invoice_request_builder_methods` macro
rg "invoice_request_builder_methods" --type rust

Length of output: 721



Script:

#!/bin/bash
# Extract the macro definition and usage related to the methods mentioned in the review comment
rg --context 20 "invoice_request_builder_methods" lightning/src/offers/invoice_request.rs

Length of output: 4369

* 376-404: The macro `invoice_request_builder_test_methods` introduces methods specifically for testing purposes. It's important to ensure that these methods are used correctly in test cases and that they do not expose any functionality that could be misused in a non-test context. Review the test suite to confirm that these methods are utilized effectively to cover the new functionality introduced in this PR.
Verification successful

The macro invoice_request_builder_test_methods is encapsulated within #[cfg(test)] blocks, ensuring its usage is confined to test configurations. This setup appropriately addresses the concern about potential misuse in non-test contexts. The methods introduced by this macro are thus correctly utilized for testing purposes, effectively covering the new functionality introduced in the PR without exposing any functionality that could be misused in a non-test context.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Verify the usage of `invoice_request_builder_test_methods` in test cases
rg "invoice_request_builder_test_methods" --type rust

Length of output: 479



Script:

#!/bin/bash
# Inspect the context of `invoice_request_builder_test_methods` usage in `invoice_request.rs`
rg --context 10 "invoice_request_builder_test_methods" lightning/src/offers/invoice_request.rs

Length of output: 2729

* 510-541: The macro `unsigned_invoice_request_sign_method` introduces a method for signing `UnsignedInvoiceRequest` instances. This method is crucial for ensuring the integrity and authenticity of invoice requests. Review the implementation to confirm that it correctly signs the invoice request using the provided signing function and that the signature is correctly appended to the invoice request bytes. * 660-684: > 📝 **NOTE** > This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [663-717]

The macro invoice_request_respond_with_explicit_signing_pubkey_methods introduces methods for responding to invoice requests with an explicit signing pubkey. This macro is essential for creating InvoiceBuilder instances based on explicit signing keys. Review the respond_with and respond_with_no_std methods to ensure they correctly handle the creation of invoices based on the provided parameters and that all necessary checks are performed.

  • 778-802: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [781-826]

The macro invoice_request_respond_with_derived_signing_pubkey_methods introduces methods for responding to invoice requests using derived signing keys. This macro is critical for creating InvoiceBuilder instances that utilize derived keys for signing. Review the respond_using_derived_keys and respond_using_derived_keys_no_std methods to confirm that they correctly handle the creation of invoices with derived keys and that all necessary checks are in place.

Verification successful

The macro invoice_request_respond_with_derived_signing_pubkey_methods and its implementation in invoice_request.rs correctly introduce methods for responding to invoice requests using derived signing keys. The methods respond_using_derived_keys and respond_using_derived_keys_no_std are appropriately designed to handle the creation of invoices with derived keys, including all necessary checks for unknown required features and the validity of the metadata. The usage of the macro to inject these methods into InvoiceBuilder<DerivedSigningPubkey> and InvoiceWithDerivedSigningPubkeyBuilder aligns with the intended functionality, confirming the correctness of the implementation and usage as described in the review comment.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Verify the correct implementation and usage of `invoice_request_respond_with_derived_signing_pubkey_methods` macro
rg "invoice_request_respond_with_derived_signing_pubkey_methods" --type rust

Length of output: 513



Script:

#!/bin/bash
# Inspect the implementation details of the macro and its usage in invoice_request.rs
rg --type rust -A 30 -B 2 "invoice_request_respond_with_derived_signing_pubkey_methods" lightning/src/offers/invoice_request.rs

Length of output: 2663

Comment on lines +416 to +443
#[cfg(all(c_bindings, not(test)))]
impl<'a> OfferWithExplicitMetadataBuilder<'a> {
offer_explicit_metadata_builder_methods!(self, &mut Self, (), ());
offer_builder_methods!(self, &mut Self, (), ());
}

#[cfg(all(c_bindings, test))]
impl<'a> OfferWithExplicitMetadataBuilder<'a> {
offer_explicit_metadata_builder_methods!(self, &mut Self, &mut Self, self);
offer_builder_methods!(self, &mut Self, &mut Self, self);
offer_builder_test_methods!(self, &mut Self, &mut Self, self);
}

#[cfg(all(c_bindings, not(test)))]
impl<'a> OfferWithDerivedMetadataBuilder<'a> {
offer_derived_metadata_builder_methods!(secp256k1::All);
offer_builder_methods!(self, &mut Self, (), ());
}

#[cfg(all(c_bindings, test))]
impl<'a> OfferWithDerivedMetadataBuilder<'a> {
offer_derived_metadata_builder_methods!(secp256k1::All);
offer_builder_methods!(self, &mut Self, &mut Self, self);
offer_builder_test_methods!(self, &mut Self, &mut Self, self);
}
Copy link

Choose a reason for hiding this comment

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

The implementation of OfferWithExplicitMetadataBuilder and OfferWithDerivedMetadataBuilder for different configurations (with and without the test feature) demonstrates careful consideration of testing needs. However, it's important to ensure that the differences in method signatures between test and non-test configurations do not lead to confusion or errors. Clear documentation and possibly compile-time checks (if feasible) could help mitigate any potential issues arising from these differences.

Consider adding documentation or compile-time assertions to clarify the differences in method signatures between test and non-test configurations, ensuring that developers and contributors are aware of these nuances.

Adding documentation or compile-time assertions could enhance clarity and prevent potential confusion regarding method signatures in different configurations.

@jkczyz
Copy link
Contributor Author

jkczyz commented Feb 26, 2024

@TheBlueMatt Most remaining CI failures are caused by warnings in --cfg=c_bindings for stuff like unused mut on self parameters. It would be pretty messy to avoid these. Not sure if we should drop -D warnings for that compilation? Or maybe try adding #[allow(unused_mut)] everywhere, which imagine could be overly broad in places or may require adding new lines around self parameters.

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.

Had to fight with it for a while, but this is basically fine, I think.

/// Signs the [`TaggedHash`] of the invoice using the given function.
///
/// Note: The hash computation may have included unknown, odd TLV records.
///
/// This is not exported to bindings users as functions aren't currently mapped.
pub fn sign<F, E>(mut self, sign: F) -> Result<Bolt12Invoice, SignError<E>>
pub fn sign<F, E>(mut $self: $self_type, sign: F) -> Result<Bolt12Invoice, SignError<E>>
Copy link
Collaborator

Choose a reason for hiding this comment

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

We still can't export this because of the Fn bound (and its still correctly marked no-export here), not sure if its intentional to touch 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.

Oh, yeah... I may have either been overzealous or meant to complete this lol. I guess we can make the Fn bound use a trait instead and implement it for all functions? I seem to recall awhile ago this was requiring me to specify the type when calling this method in tests. Or maybe that was with a type alias? Anyhow, I can try changing it to see what happens.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Latest push uses a SignFunction trait instead of Fn.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Nice, I think the unconstrained E is also an issue, however. For the SignError enum itself the bindings support mapping the default, so we can just do SignError<E = ()> and it'll work, but for the methods and trait I'm not sure what to do. Mayyybbeee the bindings can support defaults all there, too? We should probably just do that for now and land 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.

For the method, the compiler doesn't like defaults:

error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions
   --> lightning/src/offers/invoice_request.rs:517:17
    |
517 |     pub fn sign<F, E = ()>($($self_mut)* $self: $self_type, sign: F) -> Result<InvoiceRequest, SignError<E>>
    |                    ^^^^^^
...
546 |     unsigned_invoice_request_sign_method!(self, Self, mut);
    |     ------------------------------------------------------ in this macro invocation
    |
    = note: `#[deny(invalid_type_param_default)]` on by default
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #36887 <https://github.com/rust-lang/rust/issues/36887>
    = note: this error originates in the macro `unsigned_invoice_request_sign_method` (in Nightly builds, run with -Z macro-backtrace for more info)

error: could not compile `lightning` due to previous error

What if I made E an associated type on SignFunction? Compiler seems happy with this.

Or bound E by core::error::Error? Though this doesn't seem to be available in core using 1.63.0.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Latest push uses a default parameter type in the enum and an associated type in the trait if you want to try it.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think that may be workable. I don't think it's worth fighting with the bindings generator more now, it's close enough worst case we apply a small patch at release time, better to land this and move forward.

@@ -185,12 +224,12 @@ impl<'a, T: secp256k1::Signing> OfferBuilder<'a, DerivedMetadata, T> {
/// [`ExpandedKey`]: crate::ln::inbound_payment::ExpandedKey
pub fn deriving_signing_pubkey<ES: Deref>(
description: String, node_id: PublicKey, expanded_key: &ExpandedKey, entropy_source: ES,
secp_ctx: &'a Secp256k1<T>
secp_ctx: &'a Secp256k1<$secp_context>
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we can just leave this as T. Not leaving it as T makes rust mad because you are then required to add a type annotation and it can't resolve it, but if we leave it as T rust can ~always figure out what it should be.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not sure I understand. There is no T type parameter on OfferWithDerivedMetadataBuilder.

error[E0412]: cannot find type `T` in this scope
   --> lightning/src/offers/offer.rs:227:27
    |
227 |         secp_ctx: &'a Secp256k1<T>
    |                                 ^ not found in this scope
...
430 | impl<'a> OfferWithDerivedMetadataBuilder<'a> {
    |        - help: you might be missing a type parameter: `, T`
431 |     offer_derived_metadata_builder_methods!(secp256k1::All);
    |     ------------------------------------------------------- in this macro invocation
    |
    = note: this error originates in the macro `offer_derived_metadata_builder_methods` (in Nightly builds, run with -Z macro-backtrace for more info)

For more information about this error, try `rustc --explain E0412`.
error: could not compile `lightning` due to previous error

Copy link
Collaborator

Choose a reason for hiding this comment

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

Oh, sorry, left the comment on the wrong place, I think I meant build_and_sign for InvoiceBuilder.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in latest push

@jkczyz
Copy link
Contributor Author

jkczyz commented Feb 29, 2024

@TheBlueMatt Most remaining CI failures are caused by warnings in --cfg=c_bindings for stuff like unused mut on self parameters. It would be pretty messy to avoid these. Not sure if we should drop -D warnings for that compilation? Or maybe try adding #[allow(unused_mut)] everywhere, which imagine could be overly broad in places or may require adding new lines around self parameters.

I managed to get at least one instance of this working by adding another optional parameter to the macro for specifying mut. So might be able to avoid anything too invasive or needing to drop -D warnings.

@jkczyz jkczyz force-pushed the 2024-02-bindings-builders branch from a654d35 to d4363d4 Compare March 1, 2024 01:07
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Review Status

Actionable comments generated: 4

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between bf3bc42 and d4363d4.
Files selected for processing (5)
  • lightning/src/ln/channelmanager.rs (10 hunks)
  • lightning/src/offers/invoice.rs (17 hunks)
  • lightning/src/offers/invoice_request.rs (16 hunks)
  • lightning/src/offers/offer.rs (16 hunks)
  • lightning/src/offers/refund.rs (13 hunks)
Files skipped from review as they are similar to previous changes (1)
  • lightning/src/ln/channelmanager.rs
Additional comments: 31
lightning/src/offers/refund.rs (7)
  • 142-145: The introduction of RefundMaybeWithDerivedMetadataBuilder struct is a significant addition aimed at enhancing C-bindings compatibility. It's crucial to ensure that the struct's fields and their types align with the intended use cases and that the documentation provides clear guidance on its usage.
  • 133-150: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [147-178]

The macro refund_explicit_metadata_builder_methods introduces a set of methods for the RefundBuilder and RefundMaybeWithDerivedMetadataBuilder structs. It's essential to verify that the macro's logic correctly implements the builder pattern, especially concerning Rust's ownership and borrowing rules, and that it doesn't introduce any potential for misuse or errors in the context of C-bindings.

  • 268-374: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [180-324]

The refund_builder_methods macro is a comprehensive implementation that provides a fluent interface for constructing Refund instances. It's important to ensure that the macro's implementation is correct, especially the handling of optional fields and the build method's logic for creating a Refund instance. Additionally, the use of generics and lifetimes should be scrutinized for correctness and potential issues with C-bindings.

  • 326-339: The refund_builder_test_methods macro, used within test configurations, introduces methods for modifying RefundBuilder instances in ways specific to testing scenarios. It's crucial to ensure that these methods are correctly implemented and do not inadvertently affect the production code's behavior or introduce test-specific biases.
  • 366-371: The implementation of the From trait for converting a RefundBuilder instance to a RefundMaybeWithDerivedMetadataBuilder instance is a key part of enabling flexible builder patterns. It's important to ensure that this conversion is implemented correctly, preserving all relevant data and adhering to Rust's ownership and borrowing rules.
  • 501-514: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [492-546]

The macros respond_with_explicit_signing_pubkey_methods and respond_with_derived_signing_pubkey_methods introduce methods for creating InvoiceBuilder instances from a Refund. These methods are crucial for the refund flow, and it's essential to verify their correctness, especially the handling of features and the creation of invoices with the correct signing keys.

  • 580-615: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [611-868]

The test cases provided in the file are comprehensive and cover a wide range of scenarios, including default builds, invalid amounts, metadata derivation, and parsing of refunds. It's important to ensure that these tests adequately cover the new and modified functionality introduced in this PR, especially concerning C-bindings compatibility and the correct handling of edge cases.

lightning/src/offers/offer.rs (5)
  • 100-107: Conditional compilation is used to switch between imports based on the c_bindings feature. This approach ensures that the correct builder types are available depending on the compilation target. It's a clean way to handle differences in requirements between Rust and C environments.
  • 151-154: Similarly, OfferWithDerivedMetadataBuilder is introduced cleanly, following the same pattern as OfferWithExplicitMetadataBuilder. It's important to maintain consistency in naming and structure between these builders for clarity. The use of PhantomData here is also appropriate.
  • 190-196: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [176-209]

The offer_explicit_metadata_builder_methods macro is a clever way to reduce code duplication by defining common builder methods for explicit metadata builders. However, macros can sometimes obscure logic and make debugging more challenging. It's crucial to ensure that this macro is thoroughly tested, especially since it plays a significant role in the builder pattern.

Verification successful

Unit tests for the offer_explicit_metadata_builder_methods macro have been confirmed to exist, which addresses the concern about ensuring thorough testing for this macro. While the script output does not provide details on the coverage or quality of these tests, the presence of targeted tests is a positive indicator of efforts to ensure the macro's reliability and correctness.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Assuming a test file exists for these macros, this script will check for its presence and content.
if ! fd "offer_builder_tests.rs" . --exec grep -q "offer_explicit_metadata_builder_methods"; then
  echo "Unit tests for offer_explicit_metadata_builder_methods macro are missing or do not cover all scenarios."
  exit 1
else
  echo "Unit tests for offer_explicit_metadata_builder_methods macro found."
  exit 0
fi

Length of output: 238

* 224-235: > 📝 **NOTE** > This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [213-232]

The offer_derived_metadata_builder_methods macro follows the same pattern as the explicit metadata builder methods macro, encapsulating common logic for derived metadata builders. This consistency in approach is good for maintainability. Again, the emphasis on testing these macros cannot be overstated due to their critical role in the builder pattern.

Verification successful

Unit tests for the offer_derived_metadata_builder_methods macro have been confirmed to exist. This supports the review comment's emphasis on the importance of testing these macros for maintainability.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Checking for tests covering the derived metadata builder methods macro.
if ! fd "offer_builder_tests.rs" . --exec grep -q "offer_derived_metadata_builder_methods"; then
  echo "Unit tests for offer_derived_metadata_builder_methods macro are missing or do not cover all scenarios."
  exit 1
else
  echo "Unit tests for offer_derived_metadata_builder_methods macro found."
  exit 0
fi

Length of output: 235

* 260-328: > 📝 **NOTE** > This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [244-284]

The offer_builder_methods macro is used to define common builder methods across different offer builders. This approach helps maintain DRY principles by avoiding code duplication. However, it's essential to ensure that the macro is flexible enough to accommodate all necessary builder methods without becoming too complex or unwieldy.

lightning/src/offers/invoice_request.rs (15)
  • 74-74: The import of BlindedPayInfo has been modified to remove other types that were previously imported from crate::offers::invoice. This change aligns with the PR's objective to refactor the builder patterns for InvoiceRequest objects, focusing on explicit and derived payer IDs.
  • 120-126: The introduction of InvoiceRequestWithExplicitPayerIdBuilder struct is a significant change. This struct is designed to build InvoiceRequest objects with explicit payer IDs, which is crucial for supporting C-bindings. The struct fields and their types are appropriate for this purpose. However, it's important to ensure that the secp_ctx field, which holds a reference to a Secp256k1 context, is properly managed throughout the lifecycle of the builder to avoid any potential issues with lifetimes or mutability.
  • 134-140: Similarly, the InvoiceRequestWithDerivedPayerIdBuilder struct is introduced to support building InvoiceRequest objects with derived payer IDs. This addition is consistent with the PR's goals and follows a similar pattern to the explicit payer ID builder. Again, careful management of the secp_ctx field is necessary to ensure correct behavior.
  • 184-205: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [160-195]

The macro invoice_request_explicit_payer_id_builder_methods defines common methods for the explicit payer ID builder. This macro improves code reuse and maintainability by encapsulating shared logic. However, it's crucial to review the macro's expansion in different contexts to ensure it behaves as expected, particularly regarding the handling of the secp_ctx field and the construction of InvoiceRequestContentsWithoutPayerId.

  • 213-240: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [197-232]

The macro invoice_request_derived_payer_id_builder_methods serves a similar purpose for the derived payer ID builder. It's important to verify that the derivation of payer IDs and metadata is correctly implemented and that the resulting InvoiceRequest objects are semantically valid. Special attention should be paid to the build_and_sign method to ensure that it correctly signs the invoice request using the derived keys.

  • 272-474: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [235-370]

The macro invoice_request_builder_methods defines shared builder methods for both explicit and derived payer ID builders. This macro is crucial for ensuring consistency and reducing duplication. Reviewing its usage across different builder instances is necessary to confirm that it correctly applies the intended modifications to the InvoiceRequestContentsWithoutPayerId and handles errors appropriately.

  • 376-404: The macro invoice_request_builder_test_methods introduces test-specific methods for the invoice request builders. While this approach facilitates testing, it's essential to ensure that these methods do not inadvertently bypass important checks or validations present in the non-test methods. The use of unchecked methods like chain_unchecked and amount_msats_unchecked should be carefully evaluated to avoid introducing test artifacts that could mask potential issues.
  • 406-412: The implementation of the explicit and derived payer ID builder methods through macros ensures consistency and reduces code duplication. However, it's crucial to verify that these implementations correctly handle all edge cases, especially regarding the construction and signing of InvoiceRequest objects. The separation of explicit and derived payer ID handling is aligned with the PR's objectives.
  • 414-419: The general InvoiceRequestBuilder implementation that applies to both explicit and derived payer ID strategies is a good design choice. It leverages the previously defined macros to provide a unified interface for building InvoiceRequest objects. Ensuring that this implementation correctly integrates with the rest of the codebase and supports all intended use cases is important.
  • 421-425: The conditional compilation directives (#[cfg(c_bindings)]) used to provide specific implementations for C-bindings are appropriate. They ensure that the codebase can support both Rust-native and C-binding use cases without introducing unnecessary complexity. Reviewing the conditional sections to confirm that they correctly implement the required functionality for C-bindings is necessary.
  • 448-471: The conversion implementations from the explicit and derived payer ID builders to the general InvoiceRequestBuilder are crucial for enabling a seamless transition between different builder types. It's important to ensure that these conversions correctly transfer all relevant state and do not result in any loss of information or functionality.
  • 510-543: The macro unsigned_invoice_request_sign_method defines the signing method for UnsignedInvoiceRequest objects. This macro is key to ensuring that invoice requests can be correctly signed and transformed into InvoiceRequest objects. Verifying that the signing process correctly handles the provided function and correctly appends the signature to the TLV stream is essential.
  • 662-686: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [665-719]

The macros invoice_request_respond_with_explicit_signing_pubkey_methods and invoice_request_respond_with_derived_signing_pubkey_methods define methods for responding to invoice requests with explicit and derived signing keys, respectively. These macros facilitate the creation of InvoiceBuilder instances based on the invoice request's contents. It's important to verify that these methods correctly handle the invoice request features and creation time, and that they properly utilize the derived keys when available.

  • 721-747: The invoice_request_verify_method macro defines the verification method for InvoiceRequest objects. This method is critical for ensuring the authenticity and integrity of invoice requests. Verifying that this method correctly checks the signature against the provided key and returns a VerifiedInvoiceRequest on success is essential.
  • 1087-1095: The test cases added to validate the functionality of the new builder patterns and macros are comprehensive. However, it's important to ensure that these tests cover all edge cases and potential error conditions. The use of conditional compilation directives in the tests (#[cfg(not(c_bindings))] and #[cfg(c_bindings)]) should be carefully reviewed to ensure that the tests are applicable and effective in both Rust-native and C-binding contexts.
lightning/src/offers/invoice.rs (4)
  • 237-271: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [209-265]

The macro invoice_explicit_signing_pubkey_builder_methods is well-structured and provides a clean way to define common builder methods. However, ensure that the macro usage is consistent across different parts of the code and that it doesn't introduce any unnecessary complexity or obscure the code's logic.

Verification successful

The macro invoice_explicit_signing_pubkey_builder_methods is used consistently within the invoice.rs file, as indicated by its definition and multiple applications with slight variations to accommodate different contexts (mutable vs. immutable). This suggests that the macro's usage aligns with its intended purpose, supporting the creation of builder methods without introducing unnecessary complexity or obscuring the code's logic. Based on the information provided, there are no indications of inconsistent usage or negative impacts on the codebase's clarity.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Verify consistent usage of invoice_explicit_signing_pubkey_builder_methods across the module
rg "invoice_explicit_signing_pubkey_builder_methods" --type rust

Length of output: 490

* 432-437: The implementation of `InvoiceBuilder` for both `ExplicitSigningPubkey` and `DerivedSigningPubkey` strategies is a key part of the builder pattern. Ensure that the separation of these strategies is justified and that it doesn't introduce unnecessary complexity or duplication in the codebase.
Verification successful

The implementation of InvoiceBuilder for both ExplicitSigningPubkey and DerivedSigningPubkey strategies is utilized across various parts of the codebase, indicating a clear necessity and purpose for their separation. Each strategy is tailored for specific scenarios, likely reducing complexity and potentially abstracting common patterns to avoid duplication. The evidence suggests that the separation of these strategies is justified and serves to streamline the implementation of different functionalities within the codebase.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Verify the necessity and uniqueness of ExplicitSigningPubkey and DerivedSigningPubkey strategies
rg "InvoiceBuilder<ExplicitSigningPubkey>" --type rust
rg "InvoiceBuilder<DerivedSigningPubkey>" --type rust

Length of output: 1317

* 1785-1792: The use of `#[cfg(c_bindings)]` to conditionally compile code specific to C-bindings is appropriate. However, it's important to ensure that the functionality remains consistent and that any differences between the C-bindings and standard Rust code paths are well-documented and justified. * 2239-2245: The handling of different compilation targets (`c_bindings` vs. standard Rust) when building invoices from offers or refunds demonstrates flexibility in the codebase. However, it's crucial to ensure that any differences in behavior are clearly documented and that the code remains maintainable and easy to understand.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Review Status

Actionable comments generated: 1

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between d4363d4 and 7917a91.
Files selected for processing (9)
  • fuzz/src/invoice_request_deser.rs (1 hunks)
  • fuzz/src/offer_deser.rs (1 hunks)
  • fuzz/src/refund_deser.rs (1 hunks)
  • lightning/src/crypto/mod.rs (1 hunks)
  • lightning/src/ln/channelmanager.rs (9 hunks)
  • lightning/src/offers/invoice.rs (19 hunks)
  • lightning/src/offers/invoice_request.rs (18 hunks)
  • lightning/src/offers/merkle.rs (6 hunks)
  • lightning/src/offers/test_utils.rs (1 hunks)
Files skipped from review as they are similar to previous changes (2)
  • lightning/src/ln/channelmanager.rs
  • lightning/src/offers/invoice.rs
Additional comments: 18
lightning/src/crypto/mod.rs (1)
  • 1-1: The addition of #[cfg(not(fuzzing))] to exclude the import of fixed_time_eq during fuzzing is correctly implemented. Please ensure that the fuzzing feature flag is correctly defined and used throughout the project to avoid any unintended exclusions during normal compilation.
fuzz/src/offer_deser.rs (1)
  • 32-33: The modification to the sign method call within the do_test function, to accept a reference to UnsignedInvoiceRequest, aligns with what appears to be an API change. Please ensure this change is consistent with the updated API and that UnsignedInvoiceRequest is correctly handled throughout the test.
fuzz/src/refund_deser.rs (1)
  • 36-37: Modifying the sign method call to explicitly specify the type of the message parameter as &UnsignedBolt12Invoice enhances clarity and maintainability. This change appears to adapt to an API update, ensuring compatibility and correctness in the fuzz testing code.
lightning/src/offers/test_utils.rs (1)
  • 23-25: The addition of the fail_sign function, which always returns an error, is a useful tool for simulating signing failures in tests. Please ensure that this function is used appropriately in testing scenarios where signing failures need to be simulated or handled.
fuzz/src/invoice_request_deser.rs (1)
  • 40-41: Modifying the sign method call to explicitly specify the type of the message parameter as &UnsignedBolt12Invoice in the closure enhances code clarity and maintainability. This change appears to adapt to an API update, ensuring compatibility and correctness in the fuzz testing code.
lightning/src/offers/merkle.rs (3)
  • 86-100: The introduction of the SignFunction trait and its implementation for any Fn(&T) -> Result<Signature, E> where T: AsRef<TaggedHash> is a flexible design that allows for various signing functions to be used interchangeably. This design supports the use of closures for signing, enhancing modularity and testability. However, it's important to ensure that the trait and its blanket implementation are well-documented, especially regarding how users can leverage this trait for custom signing logic.
  • 111-118: The modification of the sign_message function to utilize the SignFunction trait is a significant change. This update aligns with the PR's objective to make the code more compatible with C-bindings by abstracting away Rust-specific features. It's crucial to verify that all existing calls to sign_message have been updated to use the new trait-based approach. Additionally, consider adding more detailed documentation to explain the function's parameters, especially the f parameter, which now expects a SignFunction implementation.
  • 337-344: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [297-427]

The adjustments to test cases, such as calculates_merkle_root_hash_from_invoice_request and compute_tagged_hash, demonstrate the application of the new SignFunction trait. These tests are crucial for validating the correctness of the signing process. It's commendable that the tests cover various scenarios, including different types of invoice requests. However, ensure that the test coverage is comprehensive and includes edge cases, especially for error handling paths in the signing process.

lightning/src/offers/invoice_request.rs (10)
  • 120-127: The introduction of InvoiceRequestWithExplicitPayerIdBuilder struct is a significant change to support explicit payer ID strategies. This struct, along with its fields and PhantomData usage, seems well-structured for its purpose. However, it's crucial to ensure that the secp_ctx field, which holds a reference to a Secp256k1 context, is correctly managed throughout the builder's lifecycle to prevent any potential misuse or errors in cryptographic operations.
  • 134-141: The InvoiceRequestWithDerivedPayerIdBuilder struct is introduced to support derived payer ID strategies. Similar to the explicit payer ID builder, this struct is well-designed for its intended functionality. The use of PhantomData to enforce type safety without storing data is appropriate. As with the explicit payer ID builder, careful management of the secp_ctx reference is essential to ensure the security and correctness of cryptographic operations.
  • 185-206: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [161-195]

The macro invoice_request_explicit_payer_id_builder_methods introduces a set of methods for the explicit payer ID builder pattern. This macro enhances code reuse and maintainability by abstracting common builder methods. However, it's important to ensure that the macro is used consistently and that the methods it generates are thoroughly tested, especially the build method, which finalizes the construction of an UnsignedInvoiceRequest. The use of debug_assert! in line 193 to check for the absence of keys is a good practice for debug builds, but consider if there's a need for a runtime check in release builds to prevent potential issues.

  • 214-241: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [198-232]

The invoice_request_derived_payer_id_builder_methods macro defines methods for the derived payer ID builder pattern. This macro, similar to the explicit payer ID builder macro, is a good approach to reduce code duplication and improve maintainability. The build_and_sign method in lines 219-232, which builds and signs the InvoiceRequest, is particularly critical. Ensure that the cryptographic signing operation (sign_schnorr_no_aux_rand) is correctly implemented and that error handling (e.g., the unwrap calls in lines 231 and 232) is appropriate and safe. Consider replacing unwrap with more robust error handling to avoid potential panics.

  • 273-475: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [236-371]

The macro invoice_request_builder_methods introduces generic builder methods applicable to both explicit and derived payer ID strategies. This macro significantly contributes to code reuse and consistency across different builder implementations. Pay special attention to the chain and amount_msats methods (lines 252-281), ensuring that they correctly enforce the constraints specified by the offer and the invoice request semantics. The build_with_checks method (lines 302-330) is crucial for validating the invoice request before construction. Ensure that all checks are comprehensive and correctly implemented, particularly the checks for offer expiration, supported chain, and amount constraints.

  • 511-542: The unsigned_invoice_request_sign_method macro defines the sign method for UnsignedInvoiceRequest. This method is critical for the security of the invoice request process, as it involves signing the request. Ensure that the signing operation is correctly implemented and that the method properly handles any errors that may arise during the signing process. The use of unwrap in line 528 to write the signature TLV stream should be carefully evaluated to ensure it does not introduce potential panics in error scenarios.
  • 661-685: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [664-718]

The invoice_request_respond_with_explicit_signing_pubkey_methods macro facilitates the creation of an InvoiceBuilder for responding to an invoice request with an explicit signing pubkey. This macro plays a crucial role in the invoice generation process. It's important to ensure that the method correctly handles all required fields and adheres to the Bolt12 specification. Additionally, verify that the error handling for unknown required features (line 712) is appropriate and that the method's implementation does not introduce any security vulnerabilities, especially in the handling of payment paths and hash.

  • 720-746: The invoice_request_verify_method macro introduces a verify method for InvoiceRequest and VerifiedInvoiceRequest. This method is essential for ensuring the authenticity and integrity of the invoice request. Carefully review the implementation to ensure that it correctly verifies the request against the provided key and correctly handles the derived keys. The error handling in this method should be robust, given its critical role in the verification process.
  • 779-803: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [782-827]

The invoice_request_respond_with_derived_signing_pubkey_methods macro enables responding to an invoice request using derived signing keys. This functionality is crucial for offers that involve derived metadata. Ensure that the implementation correctly uses the derived keys for signing the Bolt12Invoice and that the method properly handles all required parameters and error conditions, particularly the handling of unknown required features and invalid metadata.

  • 1083-1097: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [1086-2120]

The test cases provided in the file are comprehensive and cover a wide range of scenarios, including building invoice requests with defaults, handling expiration, derived metadata, chains, amounts, features, quantities, payer notes, and various failure cases. Ensure that these tests adequately cover the new functionality introduced in this PR, particularly focusing on edge cases and error handling paths. It's also important to verify that the cryptographic operations involved in signing and verifying invoice requests are correctly tested to ensure their security and correctness.

Comment on lines 83 to 103
Verification(secp256k1::Error),
}

/// A function for signing a [`TaggedHash`].
pub trait SignFunction<T: AsRef<TaggedHash>, E> {
/// Signs a [`TaggedHash`] computed over the merkle root of `message`'s TLV stream.
fn sign(&self, message: &T) -> Result<Signature, E>;
}

impl<F, T, E> SignFunction<T, E> for F
where
F: Fn(&T) -> Result<Signature, E>,
T: AsRef<TaggedHash>,
{
fn sign(&self, message: &T) -> Result<Signature, E> {
self(message)
}
}

/// Signs a [`TaggedHash`] computed over the merkle root of `message`'s TLV stream, checking if it
/// can be verified with the supplied `pubkey`.
///
Copy link

Choose a reason for hiding this comment

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

📝 NOTE
This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [1-427]

General review of the entire file reveals a well-structured and documented implementation of tagged hashes and related functionality. The use of Rust's type system and traits to enforce correctness and modularity is notable. However, consider reviewing the error handling strategy throughout the file, especially in functions that perform cryptographic operations, to ensure that errors are handled gracefully and informatively for the caller.

Consider adding more detailed error messages or custom error types where generic errors are currently used, to improve debuggability and error handling for library users.

@jkczyz jkczyz force-pushed the 2024-02-bindings-builders branch from 7917a91 to 57ccdbc Compare March 5, 2024 22:09
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Review Status

Actionable comments generated: 0

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 7a35bf8 and 57ccdbc.
Files selected for processing (11)
  • fuzz/src/invoice_request_deser.rs (1 hunks)
  • fuzz/src/offer_deser.rs (1 hunks)
  • fuzz/src/refund_deser.rs (1 hunks)
  • lightning/src/crypto/mod.rs (1 hunks)
  • lightning/src/ln/channelmanager.rs (9 hunks)
  • lightning/src/offers/invoice.rs (19 hunks)
  • lightning/src/offers/invoice_request.rs (18 hunks)
  • lightning/src/offers/merkle.rs (6 hunks)
  • lightning/src/offers/offer.rs (16 hunks)
  • lightning/src/offers/refund.rs (13 hunks)
  • lightning/src/offers/test_utils.rs (1 hunks)
Files skipped from review as they are similar to previous changes (8)
  • fuzz/src/invoice_request_deser.rs
  • fuzz/src/offer_deser.rs
  • fuzz/src/refund_deser.rs
  • lightning/src/crypto/mod.rs
  • lightning/src/ln/channelmanager.rs
  • lightning/src/offers/invoice.rs
  • lightning/src/offers/merkle.rs
  • lightning/src/offers/test_utils.rs
Additional comments: 19
lightning/src/offers/refund.rs (3)
  • 142-145: The introduction of RefundMaybeWithDerivedMetadataBuilder is a positive step towards enhancing C-binding compatibility. Ensure that memory management practices for the Secp256k1 context are thoroughly verified in the C-binding environment.
  • 133-150: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [147-178]

The use of refund_explicit_metadata_builder_methods and refund_builder_methods macros is a good practice to reduce code duplication and ensure consistency across builder types. Consider adding detailed comments within the macro definitions to enhance readability and maintainability.

Also applies to: 180-324

  • 366-371: The implementation of conversions between RefundBuilder and RefundMaybeWithDerivedMetadataBuilder is correctly done. To ensure robustness, consider adding unit tests that verify these conversions under various scenarios.
lightning/src/offers/offer.rs (7)
  • 137-140: The introduction of OfferWithExplicitMetadataBuilder struct is well-implemented, providing a clear structure for building offers with explicit metadata. The use of PhantomData to mark the metadata strategy is a good Rust pattern for type safety without runtime cost. However, ensure that documentation is clear on how to use this builder, especially for developers working on C bindings.
  • 151-154: Similarly, OfferWithDerivedMetadataBuilder is correctly implemented for offers with derived metadata. The consistent use of PhantomData here aligns with Rust's type safety principles. As with the explicit metadata builder, clear documentation on its usage in the context of C bindings is essential.
  • 172-172: The MetadataStrategy trait is correctly defined as a marker trait to categorize metadata strategies. This design supports the type safety and flexibility of the builder patterns introduced.
  • 173-173: Implementations of MetadataStrategy for ExplicitMetadata and DerivedMetadata are straightforward and correctly done. These implementations enable compile-time checks for the metadata strategy used in the builders, enhancing type safety.
  • 204-216: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [176-211]

The offer_explicit_metadata_builder_methods macro is well-designed, encapsulating common builder methods for explicit metadata builders. This approach maintains code modularity and reusability. However, it's important to ensure that these macros are thoroughly tested, especially since macros can sometimes obscure logic and make debugging more challenging.

  • 224-235: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [213-242]

Similarly, the offer_derived_metadata_builder_methods macro effectively encapsulates builder methods for derived metadata strategies. The design choice to parameterize the secp_context enhances flexibility. As with the explicit metadata macro, comprehensive testing is crucial to ensure reliability and correctness.

  • 1033-1033: The test module structure is comprehensive, covering a wide range of scenarios for the new builder patterns and struct implementations. It's important to ensure that tests for the new C-binding compatible builders (OfferWithExplicitMetadataBuilder and OfferWithDerivedMetadataBuilder) are included and cover all relevant use cases to ensure their functionality is correctly implemented.
lightning/src/offers/invoice_request.rs (9)
  • 75-75: The import of BlindedPayInfo is updated to remove other unused imports. This change aligns with the PR's objective to refactor the module for better C-binding compatibility. However, ensure that the removal of other imports (DerivedSigningPubkey, ExplicitSigningPubkey, InvoiceBuilder) does not affect other parts of the codebase that might rely on them.
  • 84-91: The conditional compilation directives (#[cfg(not(c_bindings))] and #[cfg(c_bindings)]) are used to import different sets of structs based on whether C-bindings are being compiled. This is a good practice for maintaining compatibility with both Rust-native and C-binding environments. Ensure that the corresponding builder structs (InvoiceWithDerivedSigningPubkeyBuilder, InvoiceWithExplicitSigningPubkeyBuilder) are implemented correctly and are functionally equivalent to their Rust-native counterparts.
  • 120-127: The introduction of InvoiceRequestWithExplicitPayerIdBuilder struct is a key part of the PR's objective to support explicit payer ID strategies in C-bindings. The struct's design, with fields for offer, invoice request contents, payer ID, and a secp256k1 context, seems appropriate for its purpose. However, ensure that the secp_ctx field is properly utilized in all relevant methods, especially in signing operations, to maintain security and correctness.
  • 134-141: Similarly, the InvoiceRequestWithDerivedPayerIdBuilder struct supports derived payer ID strategies in C-bindings. The struct's design is consistent with the explicit payer ID builder, which is good for maintainability. As with the explicit payer ID builder, ensure the secp_ctx is correctly used in all operations requiring cryptographic functionality.
  • 185-206: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [161-196]

The macro invoice_request_explicit_payer_id_builder_methods introduces shared methods for builders handling explicit payer IDs. This approach of using macros to define shared functionality is efficient and reduces code duplication. However, ensure that the macro is correctly expanded in all contexts where it's used, and that the methods it defines are thoroughly tested, especially the build method which constructs the unsigned invoice request and performs semantic checks.

  • 214-241: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [198-233]

The macro invoice_request_derived_payer_id_builder_methods serves a similar purpose for derived payer ID strategies. It's important to verify that the derivation of payer IDs and metadata is secure and correctly implemented, particularly in the deriving_payer_id method. Additionally, the build_and_sign method must ensure that the invoice request is correctly signed and verified, which is crucial for the integrity of the invoice request process.

  • 273-475: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [236-362]

The macro invoice_request_builder_methods introduces generic builder methods applicable to both explicit and derived payer ID strategies. This macro significantly reduces code duplication and ensures consistency across different builder implementations. Pay special attention to the build_with_checks and build_without_checks methods to ensure that all necessary validations are performed, and that the resulting unsigned invoice request is correctly constructed.

  • 377-405: The macro invoice_request_builder_test_methods introduces methods specifically for testing purposes, allowing for unchecked setting of fields and building of invoice requests. This is a practical approach for writing comprehensive tests. Ensure that these methods are used responsibly in tests to cover various edge cases and potential error conditions, without introducing risks to the production code.
  • 511-544: The macro unsigned_invoice_request_sign_method defines the sign method for UnsignedInvoiceRequest, allowing it to be signed with a given function. This is a critical part of the invoice request lifecycle. It's essential to ensure that the signing process is secure, correctly implements the Schnorr signature algorithm, and that the resulting InvoiceRequest is correctly formed with the signature appended. Additionally, verify that error handling in the signing process is robust, especially in the context of C-bindings where error propagation might differ.

@jkczyz jkczyz force-pushed the 2024-02-bindings-builders branch from 57ccdbc to 5db29b7 Compare March 6, 2024 05:05
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Review Status

Actionable comments generated: 10

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 7a35bf8 and 5db29b7.
Files selected for processing (11)
  • fuzz/src/invoice_request_deser.rs (1 hunks)
  • fuzz/src/offer_deser.rs (1 hunks)
  • fuzz/src/refund_deser.rs (1 hunks)
  • lightning/src/crypto/mod.rs (1 hunks)
  • lightning/src/ln/channelmanager.rs (9 hunks)
  • lightning/src/offers/invoice.rs (22 hunks)
  • lightning/src/offers/invoice_request.rs (19 hunks)
  • lightning/src/offers/merkle.rs (6 hunks)
  • lightning/src/offers/offer.rs (16 hunks)
  • lightning/src/offers/refund.rs (13 hunks)
  • lightning/src/offers/test_utils.rs (1 hunks)
Files skipped from review as they are similar to previous changes (8)
  • fuzz/src/invoice_request_deser.rs
  • fuzz/src/offer_deser.rs
  • fuzz/src/refund_deser.rs
  • lightning/src/crypto/mod.rs
  • lightning/src/ln/channelmanager.rs
  • lightning/src/offers/invoice.rs
  • lightning/src/offers/merkle.rs
  • lightning/src/offers/test_utils.rs
Additional comments: 14
lightning/src/offers/refund.rs (6)
  • 142-145: The introduction of RefundMaybeWithDerivedMetadataBuilder struct is a key part of making the offers module more compatible with C-bindings by avoiding Rust-specific features. This change aligns with the PR objectives and addresses the need for more versatile builder patterns in different programming environments.
  • 133-150: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [147-178]

The macro refund_explicit_metadata_builder_methods introduces a set of methods for building refunds with explicit metadata. This approach simplifies the construction process and makes it more accessible for environments requiring C-bindings. It's a good practice to encapsulate common functionality in macros for reuse and maintainability.

  • 268-376: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [180-324]

The macro refund_builder_methods defines a comprehensive set of builder methods for RefundBuilder and RefundMaybeWithDerivedMetadataBuilder. This macro cleverly uses Rust's macro system to inject these methods into the respective structs, ensuring code reuse and maintainability. However, it's crucial to ensure that the macro's complexity doesn't hinder future modifications or understanding by new contributors.

  • 326-341: The macro refund_builder_test_methods is designed for testing purposes, providing methods to manipulate the refund builder's state in tests. While this approach is practical for isolating test logic from production code, it's important to ensure that these methods are only compiled in test environments to avoid any unintended side effects or security implications in production builds.
  • 367-373: The conversion implementation from RefundBuilder to RefundMaybeWithDerivedMetadataBuilder facilitates seamless transitions between different builder types, enhancing the flexibility of the refund building process. This conversion is crucial for supporting both explicit and derived metadata strategies in a unified manner.
  • 503-516: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [494-548]

The macros respond_with_explicit_signing_pubkey_methods and respond_with_derived_signing_pubkey_methods introduce methods for creating invoices with explicit and derived signing keys, respectively. These additions are essential for supporting a variety of refund scenarios and signing strategies, contributing to the module's versatility. It's important to ensure that these methods are well-documented and tested to guarantee their reliability and ease of use.

lightning/src/offers/offer.rs (1)
  • 100-107: Conditional compilation directives have been added to import different structs based on the c_bindings feature flag. This approach ensures compatibility with C-bindings by using specialized builder structs when the c_bindings feature is enabled. This change aligns with the PR's objective to enhance the offers module's compatibility with C-bindings.
lightning/src/offers/invoice_request.rs (7)
  • 75-75: The import of BlindedPayInfo is updated to remove other unused imports. This change aligns with the PR's objective to refactor the builder patterns for C-bindings compatibility.
  • 84-91: The conditional compilation directives (#[cfg(not(c_bindings))] and #[cfg(c_bindings)]) are used to differentiate between the types imported for Rust and C-bindings environments. This is a good practice for maintaining compatibility across different environments.
  • 120-127: The introduction of InvoiceRequestWithExplicitPayerIdBuilder struct under the #[cfg(c_bindings)] flag is a key part of making the builders suitable for C-bindings. This struct is specifically designed for explicit payer ID scenarios, which is a thoughtful approach to handling different payer ID strategies.
  • 135-141: Similarly, the InvoiceRequestWithDerivedPayerIdBuilder struct is introduced for derived payer ID scenarios under the C-bindings environment. This struct complements the explicit payer ID builder, ensuring both strategies are supported in C-bindings.
  • 416-422: The implementation of explicit and derived payer ID builder methods using the previously defined macros is a good application of DRY (Don't Repeat Yourself) principles. This ensures consistency and reduces the potential for errors.
  • 431-455: The conditional compilation for C-bindings and test configurations is handled well, ensuring that the code is adaptable to different environments and testing needs. This flexibility is crucial for a library that targets both Rust and C environments.
  • 457-481: The conversion implementations from specific payer ID builders to the generic InvoiceRequestBuilder are crucial for integrating the new builder patterns into the existing codebase. This approach facilitates a smooth transition between different payer ID strategies.

Comment on lines 381 to 413
#[cfg(test)]
macro_rules! invoice_request_builder_test_methods { (
$self: ident, $self_type: ty, $return_type: ty, $return_value: expr $(, $self_mut: tt)?
) => {
#[cfg_attr(c_bindings, allow(dead_code))]
fn chain_unchecked($($self_mut)* $self: $self_type, network: Network) -> $return_type {
let chain = ChainHash::using_genesis_block(network);
$self.invoice_request.chain = Some(chain);
$return_value
}
}

impl<'a, 'b, T: secp256k1::Signing> InvoiceRequestBuilder<'a, 'b, DerivedPayerId, T> {
/// Builds a signed [`InvoiceRequest`] after checking for valid semantics.
pub fn build_and_sign(self) -> Result<InvoiceRequest, Bolt12SemanticError> {
let (unsigned_invoice_request, keys, secp_ctx) = self.build_with_checks()?;
debug_assert!(keys.is_some());

let secp_ctx = secp_ctx.unwrap();
let keys = keys.unwrap();
let invoice_request = unsigned_invoice_request
.sign::<_, Infallible>(
|message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
)
.unwrap();
Ok(invoice_request)
#[cfg_attr(c_bindings, allow(dead_code))]
fn amount_msats_unchecked($($self_mut)* $self: $self_type, amount_msats: u64) -> $return_type {
$self.invoice_request.amount_msats = Some(amount_msats);
$return_value
}
}

#[cfg(test)]
impl<'a, 'b, P: PayerIdStrategy, T: secp256k1::Signing> InvoiceRequestBuilder<'a, 'b, P, T> {
fn chain_unchecked(mut self, network: Network) -> Self {
let chain = ChainHash::using_genesis_block(network);
self.invoice_request.chain = Some(chain);
self
#[cfg_attr(c_bindings, allow(dead_code))]
fn features_unchecked($($self_mut)* $self: $self_type, features: InvoiceRequestFeatures) -> $return_type {
$self.invoice_request.features = features;
$return_value
}

fn amount_msats_unchecked(mut self, amount_msats: u64) -> Self {
self.invoice_request.amount_msats = Some(amount_msats);
self
#[cfg_attr(c_bindings, allow(dead_code))]
fn quantity_unchecked($($self_mut)* $self: $self_type, quantity: u64) -> $return_type {
$self.invoice_request.quantity = Some(quantity);
$return_value
}

fn features_unchecked(mut self, features: InvoiceRequestFeatures) -> Self {
self.invoice_request.features = features;
self
#[cfg_attr(c_bindings, allow(dead_code))]
pub(super) fn build_unchecked($self: $self_type) -> UnsignedInvoiceRequest {
$self.build_without_checks().0
}
} }
Copy link

Choose a reason for hiding this comment

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

The macro invoice_request_builder_test_methods is designed for testing purposes, which is a good practice. It's important to ensure that these test methods are comprehensive and cover all relevant cases to maintain the robustness of the builder patterns.

Expand the unit tests using this macro to cover a wide range of scenarios, including edge cases, to ensure the builder patterns are thoroughly validated.

@jkczyz jkczyz force-pushed the 2024-02-bindings-builders branch from 5db29b7 to f15cb6c Compare March 6, 2024 14:49
@jkczyz
Copy link
Contributor Author

jkczyz commented Mar 6, 2024

I managed to get at least one instance of this working by adding another optional parameter to the macro for specifying mut. So might be able to avoid anything too invasive or needing to drop -D warnings.

FYI, I added a bunch of #[cfg_attr(c_bindings, allow(dead_code))] to resolve the rest of the warnings.

Use the macros introduced in the previous commit to define two builders
for each type parameterization of OfferBuilder
- OfferWithExplicitMetadataBuilder
- OfferWithDerivedMetadataBuilder

The difference between these and OfferBuilder is that these have methods
that take `self` by mutable reference instead of by value and don't
return anything instead returning the modified builder. This is required
because bindings don't support move semantics nor impl blocks specific
to a certain type parameterization. Because of this, the builder's
contents must be cloned when building an Offer.

Keeps OfferBuilder defined so that it can be used internally in
ChannelManager::create_offer_builder even when compiled for c_bindings.
RefundBuilder is not exported to bindings because it has methods that
take `self` by value. Define these methods using macros such that
different builders and related methods can be defined for c_bindings.
Use the macros introduced in the previous commit to define a builder
called RefundMaybeWithDerivedMetadataBuilder.

The difference between this and RefundBuilder is that this has methods
that take `self` by mutable reference instead of by value and don't
return anything instead returning the modified builder. This is required
because bindings don't support move semantics. Because of this, the
builder's contents must be cloned when building a Refund.

Keeps RefundBuilder defined so that it can be used internally in
ChannelManager::create_refund_builder even when compiled for c_bindings.
This avoids needing to create additional macros when adding c_bindings
support.
InvoiceRequestBuilder is not exported to bindings because it has methods
that take `self` by value and are only implemented for certain type
parameterizations. Define these methods using macros such that different
builders and related methods can be defined for c_bindings.
Use the macros introduced in the previous commit to define two builders
for each type parameterization of InvoiceRequestBuilder
- InvoiceRequestWithExplicitPayerIdBuilder
- InvoiceRequestWithDerivedPayerIdBuilder

The difference between these and InvoiceRequestBuilder is that these
have methods that take `self` by mutable reference instead of by value
and don't return anything instead returning the modified builder. This
is required because bindings don't support move semantics nor impl
blocks specific to a certain type parameterization. Because of this, the
builder's contents must be cloned when building an InvoiceRequest.

Keeps InvoiceRequestBuilder defined so that it can be used internally in
ChannelManager::pay_for_offer even when compiled for c_bindings.
This avoids needing to create additional macros when adding c_bindings
support.
InvoiceBuilder is not exported to bindings because it has methods that
take `self` by value and are only implemented for certain type
parameterizations. Define these methods using macros such that different
builders and related methods can be defined for c_bindings.
@jkczyz jkczyz force-pushed the 2024-02-bindings-builders branch from f15cb6c to e3e5de3 Compare March 6, 2024 15:25
@jkczyz
Copy link
Contributor Author

jkczyz commented Mar 6, 2024

Sqaushed

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Review Status

Actionable comments generated: 8

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 7a35bf8 and e3e5de3.
Files selected for processing (11)
  • fuzz/src/invoice_request_deser.rs (1 hunks)
  • fuzz/src/offer_deser.rs (1 hunks)
  • fuzz/src/refund_deser.rs (1 hunks)
  • lightning/src/crypto/mod.rs (1 hunks)
  • lightning/src/ln/channelmanager.rs (9 hunks)
  • lightning/src/offers/invoice.rs (22 hunks)
  • lightning/src/offers/invoice_request.rs (19 hunks)
  • lightning/src/offers/merkle.rs (6 hunks)
  • lightning/src/offers/offer.rs (16 hunks)
  • lightning/src/offers/refund.rs (13 hunks)
  • lightning/src/offers/test_utils.rs (1 hunks)
Files skipped from review as they are similar to previous changes (8)
  • fuzz/src/invoice_request_deser.rs
  • fuzz/src/offer_deser.rs
  • fuzz/src/refund_deser.rs
  • lightning/src/crypto/mod.rs
  • lightning/src/ln/channelmanager.rs
  • lightning/src/offers/invoice.rs
  • lightning/src/offers/merkle.rs
  • lightning/src/offers/test_utils.rs
Additional comments: 12
lightning/src/offers/refund.rs (5)
  • 142-145: The introduction of RefundMaybeWithDerivedMetadataBuilder struct is a significant change aimed at enhancing C-bindings compatibility. It's crucial to ensure that the struct's design aligns with the overall architecture and does not introduce redundancy or complexity. Additionally, the use of Secp256k1<secp256k1::All> as the type for secp_ctx is appropriate, considering the context of C-bindings where a broader set of secp256k1 functionalities might be required.
  • 133-150: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [147-178]

The macro refund_explicit_metadata_builder_methods introduces a set of builder methods for constructing a Refund with explicit metadata. This approach is consistent with the Rust builder pattern, promoting code reuse and maintainability. However, it's essential to ensure that the macro is well-documented, especially regarding its usage and the context in which it should be employed. Additionally, the error handling within the new method is clear and concise, properly addressing potential semantic errors.

  • 268-376: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [180-324]

The refund_builder_methods macro significantly extends the builder pattern for Refund construction, introducing a flexible and modular way to set various refund properties. This macro enhances code modularity and reuse across different contexts (e.g., with or without C-bindings). It's important to verify that the macro's design does not introduce unnecessary complexity or obscure the code's readability. The method implementations within the macro, such as absolute_expiry, issuer, path, and build, are logically structured and provide clear functionality. The build method, in particular, demonstrates careful consideration of refund semantics and error handling.

  • 367-373: The conversion implementation from RefundBuilder<'a, secp256k1::All> to RefundMaybeWithDerivedMetadataBuilder<'a> is a crucial aspect of ensuring interoperability between different builder patterns, especially in the context of C-bindings. This conversion should be seamless and error-free, preserving the integrity of the refund's properties during the conversion process. It's essential to ensure that this implementation does not introduce any unintended side effects or data loss.
  • 503-516: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [494-548]

The macros respond_with_explicit_signing_pubkey_methods and respond_with_derived_signing_pubkey_methods introduce specialized methods for creating invoices with explicit or derived signing keys, respectively. These macros contribute to the flexibility and extensibility of the refund functionality, allowing for different use cases and privacy considerations. It's important to ensure that these methods are well-documented and that their error handling is robust, especially in the context of unknown required features and the derivation of signing keys.

lightning/src/offers/offer.rs (4)
  • 100-107: The use of conditional compilation to differentiate between standard Rust usage and C-bindings is appropriate and ensures compatibility with different programming environments.
  • 204-216: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [176-213]

Modifications to OfferBuilder methods to accommodate explicit and derived metadata strategies are necessary and well-implemented. It's important to review these changes for potential regressions or compatibility issues with existing code. The logic introduced should remain clear and maintainable.

  • 126-159: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [156-173]

The trait implementations for MetadataStrategy, ExplicitMetadata, and DerivedMetadata are crucial for the type safety and flexibility of the new metadata strategies. Reviewing these traits for simplicity and effectiveness is important to avoid introducing unnecessary complexity.

  • 204-216: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [176-213]

The error handling implemented in the builder methods and utility functions is crucial for the robustness of the code. Reviewing the error handling for completeness and ensuring that error messages are clear and informative is important for a good developer experience.

lightning/src/offers/invoice_request.rs (3)
  • 75-76: The import of BlindedPayInfo from crate::offers::invoice has been modified to only include BlindedPayInfo, removing DerivedSigningPubkey, ExplicitSigningPubkey, and InvoiceBuilder. This change aligns with the PR's objective to introduce C-bindings compatible builders by removing Rust-specific features from the import list.
  • 84-91: Conditional compilation directives are used here to import different structs based on whether c_bindings is enabled. This approach ensures compatibility with C bindings by importing the appropriate builder structs (InvoiceWithDerivedSigningPubkeyBuilder, InvoiceWithExplicitSigningPubkeyBuilder) when c_bindings is defined. It's a good practice to ensure that the code can adapt to different compilation targets, enhancing the module's versatility.
  • 115-141: The introduction of InvoiceRequestWithExplicitPayerIdBuilder and InvoiceRequestWithDerivedPayerIdBuilder structs is a key part of making the offers module compatible with C-bindings. These structs provide a clear and specific way to build InvoiceRequest instances based on the payer ID strategy, which is either explicit or derived. This change enhances modularity and clarity in the codebase, making it easier to understand and maintain. It's also aligned with the PR's objectives to introduce C-bindings compatible builders.

Comment on lines 217 to 244
secp_ctx: Some(secp_ctx),
}
}
}

impl<'a, 'b, P: PayerIdStrategy, T: secp256k1::Signing> InvoiceRequestBuilder<'a, 'b, P, T> {
/// Builds a signed [`InvoiceRequest`] after checking for valid semantics.
pub fn build_and_sign($self: $self_type) -> Result<InvoiceRequest, Bolt12SemanticError> {
let (unsigned_invoice_request, keys, secp_ctx) = $self.build_with_checks()?;
#[cfg(c_bindings)]
let mut unsigned_invoice_request = unsigned_invoice_request;
debug_assert!(keys.is_some());

let secp_ctx = secp_ctx.unwrap();
let keys = keys.unwrap();
let invoice_request = unsigned_invoice_request
.sign(|message: &UnsignedInvoiceRequest| -> Result<_, Infallible> {
Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
})
.unwrap();
Ok(invoice_request)
}
} }

macro_rules! invoice_request_builder_methods { (
$self: ident, $self_type: ty, $return_type: ty, $return_value: expr, $secp_context: ty $(, $self_mut: tt)?
) => {
#[cfg_attr(c_bindings, allow(dead_code))]
fn create_contents(offer: &Offer, metadata: Metadata) -> InvoiceRequestContentsWithoutPayerId {
let offer = offer.contents.clone();
InvoiceRequestContentsWithoutPayerId {
Copy link

Choose a reason for hiding this comment

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

📝 NOTE
This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [200-237]

Similar to the previous comment, the macro invoice_request_derived_payer_id_builder_methods serves a similar purpose for derived payer ID scenarios. The use of macros for code reuse is commendable. However, the same considerations regarding documentation and testing apply here. Ensure this macro is well-documented and covered by unit tests to maintain code clarity and reliability.

// Before the macro definition, add a comment explaining its purpose and usage.

Comment on lines 277 to 483
self.invoice_request.chain = Some(chain);
self
#[cfg_attr(c_bindings, allow(dead_code))]
fn features_unchecked($($self_mut)* $self: $self_type, features: InvoiceRequestFeatures) -> $return_type {
$self.invoice_request.features = features;
$return_value
}

fn amount_msats_unchecked(mut self, amount_msats: u64) -> Self {
self.invoice_request.amount_msats = Some(amount_msats);
self
#[cfg_attr(c_bindings, allow(dead_code))]
fn quantity_unchecked($($self_mut)* $self: $self_type, quantity: u64) -> $return_type {
$self.invoice_request.quantity = Some(quantity);
$return_value
}

fn features_unchecked(mut self, features: InvoiceRequestFeatures) -> Self {
self.invoice_request.features = features;
self
#[cfg_attr(c_bindings, allow(dead_code))]
pub(super) fn build_unchecked($self: $self_type) -> UnsignedInvoiceRequest {
$self.build_without_checks().0
}
} }

impl<'a, 'b, T: secp256k1::Signing> InvoiceRequestBuilder<'a, 'b, ExplicitPayerId, T> {
invoice_request_explicit_payer_id_builder_methods!(self, Self);
}

impl<'a, 'b, T: secp256k1::Signing> InvoiceRequestBuilder<'a, 'b, DerivedPayerId, T> {
invoice_request_derived_payer_id_builder_methods!(self, Self, T);
}

impl<'a, 'b, P: PayerIdStrategy, T: secp256k1::Signing> InvoiceRequestBuilder<'a, 'b, P, T> {
invoice_request_builder_methods!(self, Self, Self, self, T, mut);

#[cfg(test)]
invoice_request_builder_test_methods!(self, Self, Self, self, mut);
}

fn quantity_unchecked(mut self, quantity: u64) -> Self {
self.invoice_request.quantity = Some(quantity);
self
#[cfg(all(c_bindings, not(test)))]
impl<'a, 'b> InvoiceRequestWithExplicitPayerIdBuilder<'a, 'b> {
invoice_request_explicit_payer_id_builder_methods!(self, &mut Self);
invoice_request_builder_methods!(self, &mut Self, (), (), secp256k1::All);
}

#[cfg(all(c_bindings, test))]
impl<'a, 'b> InvoiceRequestWithExplicitPayerIdBuilder<'a, 'b> {
invoice_request_explicit_payer_id_builder_methods!(self, &mut Self);
invoice_request_builder_methods!(self, &mut Self, &mut Self, self, secp256k1::All);
invoice_request_builder_test_methods!(self, &mut Self, &mut Self, self);
}

#[cfg(all(c_bindings, not(test)))]
impl<'a, 'b> InvoiceRequestWithDerivedPayerIdBuilder<'a, 'b> {
invoice_request_derived_payer_id_builder_methods!(self, &mut Self, secp256k1::All);
invoice_request_builder_methods!(self, &mut Self, (), (), secp256k1::All);
}

#[cfg(all(c_bindings, test))]
impl<'a, 'b> InvoiceRequestWithDerivedPayerIdBuilder<'a, 'b> {
invoice_request_derived_payer_id_builder_methods!(self, &mut Self, secp256k1::All);
invoice_request_builder_methods!(self, &mut Self, &mut Self, self, secp256k1::All);
invoice_request_builder_test_methods!(self, &mut Self, &mut Self, self);
}

#[cfg(c_bindings)]
impl<'a, 'b> From<InvoiceRequestWithExplicitPayerIdBuilder<'a, 'b>>
for InvoiceRequestBuilder<'a, 'b, ExplicitPayerId, secp256k1::All> {
fn from(builder: InvoiceRequestWithExplicitPayerIdBuilder<'a, 'b>) -> Self {
let InvoiceRequestWithExplicitPayerIdBuilder {
offer, invoice_request, payer_id, payer_id_strategy, secp_ctx,
} = builder;

Self {
offer, invoice_request, payer_id, payer_id_strategy, secp_ctx,
}
}
}

#[cfg(c_bindings)]
impl<'a, 'b> From<InvoiceRequestWithDerivedPayerIdBuilder<'a, 'b>>
for InvoiceRequestBuilder<'a, 'b, DerivedPayerId, secp256k1::All> {
fn from(builder: InvoiceRequestWithDerivedPayerIdBuilder<'a, 'b>) -> Self {
let InvoiceRequestWithDerivedPayerIdBuilder {
offer, invoice_request, payer_id, payer_id_strategy, secp_ctx,
} = builder;

pub(super) fn build_unchecked(self) -> UnsignedInvoiceRequest {
self.build_without_checks().0
Self {
offer, invoice_request, payer_id, payer_id_strategy, secp_ctx,
}
}
}

Copy link

Choose a reason for hiding this comment

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

📝 NOTE
This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [239-379]

The macro invoice_request_builder_methods encapsulates common builder methods, demonstrating effective use of macros for code reuse. While this approach is efficient, it's crucial to maintain clear documentation and comprehensive testing. Add detailed documentation for this macro, explaining its purpose, parameters, and usage. Ensure that unit tests cover the scenarios facilitated by this macro to guarantee its correct functionality.

// Before the macro definition, add a comment explaining its purpose and usage.

Comment on lines +137 to +154
pub struct OfferWithExplicitMetadataBuilder<'a> {
offer: OfferContents,
metadata_strategy: core::marker::PhantomData<ExplicitMetadata>,
secp_ctx: Option<&'a Secp256k1<secp256k1::All>>,
}

/// Builds an [`Offer`] for the "offer to be paid" flow.
///
/// See [module-level documentation] for usage.
///
/// This is not exported to bindings users as builder patterns don't map outside of move semantics.
///
/// [module-level documentation]: self
#[cfg(c_bindings)]
pub struct OfferWithDerivedMetadataBuilder<'a> {
offer: OfferContents,
metadata_strategy: core::marker::PhantomData<DerivedMetadata>,
secp_ctx: Option<&'a Secp256k1<secp256k1::All>>,
Copy link

Choose a reason for hiding this comment

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

The introduction of OfferWithExplicitMetadataBuilder and OfferWithDerivedMetadataBuilder structs is a significant enhancement for supporting C-bindings. Using PhantomData to mark the metadata strategy is a Rust idiomatic approach. It's crucial to ensure these structs are well-documented for clarity, especially for developers working with C-bindings.

Would you like assistance in enhancing the documentation for these structs?

/// User-defined error when signing the message.
Signing(E),
/// Error when verifying the produced signature using the given pubkey.
Verification(secp256k1::Error),
}

/// A function for signing a [`TaggedHash`].
pub trait SignFunction<T: AsRef<TaggedHash>> {
Copy link
Collaborator

Choose a reason for hiding this comment

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

A trait in the C bindings cannot be generic (its always concretized into one instance), but generally I'm not sure this is quite the right interface anyway - shouldn't an implementation of this interface have some idea of what its signing aside from just the TaggedHash? (or, if we think it doesn't need context, let's just pass it the TaggedHash directly?)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

A trait in the C bindings cannot be generic (its always concretized into one instance), but generally I'm not sure this is quite the right interface anyway - shouldn't an implementation of this interface have some idea of what its signing aside from just the TaggedHash?

The generic version is primarily so it can be used by the module-visible sign_message function for different types. However, the public interfaces use concrete types (e.g., UnsignedBolt12Invoice::sign takes SignFunction<UnsignedBolt12Invoice>).

Assuming type aliases aren't an option, we could add parameter-specific traits that have blanket implementations for Fn and implementations of SignFunction for their concrete types.

trait SignBolt12InvoiceFn {
       type Error;

       fn sign_invoice(&self, message: &UnsignedBolt12Invoice) -> Result<Signature, Self::Error>;
}

impl<F, E> SignBolt12InvoiceFn for F
where
       F: Fn(&UnsignedBolt12Invoice) -> Result<Signature, E>,
{
       type Error = E;

       fn sign_invoice(&self, message: &UnsignedBolt12Invoice) -> Result<Signature, E> {
               self(message)
       }
}

impl<F: SignBolt12InvoiceFn> SignFunction<UnsignedBolt12Invoice> for F {
       type Error = <Self as SignFunction<UnsignedBolt12Invoice>>::Error;

       fn sign(&self, message: &UnsignedBolt12Invoice) -> Result<Signature, Self::Error> {
               self.sign_invoice(message)
       }
}

(or, if we think it doesn't need context, let's just pass it the TaggedHash directly?)

I thought there may have been a need by VLS but can't recall the specifics. Still would be nice to have the actual message.

Copy link
Collaborator

Choose a reason for hiding this comment

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

The generic version is primarily so it can be used by the module-visible sign_message function for different types. However, the public interfaces use concrete types (e.g., UnsignedBolt12Invoice::sign takes SignFunction).

Mmm, right, still its a bit awkward for users who want to verify, you have to have two separate implementations (on the same struct?) rather than a single one. I guess its not that bad, but its still a bit more awkward than just two separate functions.

I'm not convinced all this work is worth it just to verify the signature. We could simply skip that step, remove sign_message and move on?

Assuming type aliases aren't an option, we could add parameter-specific traits that have blanket implementations for Fn and implementations of SignFunction for their concrete types.

At that point we could just have two separate traits (if we drop sign_message)?

I thought there may have been a need by VLS but can't recall the specifics. Still would be nice to have the actual message.

Right, I was just thinking with the generic version its pretty awkward to implement, but its not as bad as I thought it was at first glance.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The generic version is primarily so it can be used by the module-visible sign_message function for different types. However, the public interfaces use concrete types (e.g., UnsignedBolt12Invoice::sign takes SignFunction).

Mmm, right, still its a bit awkward for users who want to verify, you have to have two separate implementations (on the same struct?) rather than a single one. I guess its not that bad, but its still a bit more awkward than just two separate functions.

Not sure I follow the point about verifying.

I'm not convinced all this work is worth it just to verify the signature. We could simply skip that step, remove sign_message and move on?

Eh, was just about done implementing it. So pushed the version with dedicated traits. Required a small change from what I wrote above, though just simplifying it.

Plus, at least it verifies that they signed the right thing now that TaggedHash is no longer part of any of the signing interfaces. 😛

@@ -1656,7 +1803,7 @@ mod tests {
if let Err(e) = invoice_request.clone()
.verify(&expanded_key, &secp_ctx).unwrap()
.respond_using_derived_keys_no_std(payment_paths(), payment_hash(), now()).unwrap()
.build_and_sign(&secp_ctx)
.build_and_sign::<secp256k1::All>(&secp_ctx)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Builds locally seem to pass without this and the below change.

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, looks like this isn't needed anymore. Dropped in upcoming push.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Review Status

Actionable comments generated: 8

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between aa3dbe8 and 24c5c79.
Files selected for processing (11)
  • fuzz/src/invoice_request_deser.rs (1 hunks)
  • fuzz/src/offer_deser.rs (1 hunks)
  • fuzz/src/refund_deser.rs (1 hunks)
  • lightning/src/crypto/mod.rs (1 hunks)
  • lightning/src/ln/channelmanager.rs (9 hunks)
  • lightning/src/offers/invoice.rs (21 hunks)
  • lightning/src/offers/invoice_request.rs (20 hunks)
  • lightning/src/offers/merkle.rs (6 hunks)
  • lightning/src/offers/offer.rs (16 hunks)
  • lightning/src/offers/refund.rs (13 hunks)
  • lightning/src/offers/test_utils.rs (1 hunks)
Files skipped from review as they are similar to previous changes (7)
  • fuzz/src/invoice_request_deser.rs
  • fuzz/src/offer_deser.rs
  • fuzz/src/refund_deser.rs
  • lightning/src/crypto/mod.rs
  • lightning/src/ln/channelmanager.rs
  • lightning/src/offers/merkle.rs
  • lightning/src/offers/test_utils.rs
Additional comments: 18
lightning/src/offers/refund.rs (6)
  • 142-145: The introduction of RefundMaybeWithDerivedMetadataBuilder struct is a key change to support C-bindings. This struct is designed to work around the limitations of C-bindings with Rust's move semantics and type parameterizations. However, it's important to ensure that the naming convention and struct design align with the project's standards and that the documentation is clear and concise for future maintainers and users.

Ensure the struct's documentation clearly explains its purpose and usage, especially in the context of C-bindings.

  • 133-150: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [147-178]

The macro refund_explicit_metadata_builder_methods introduces a set of builder methods for constructing a Refund with explicit metadata. This approach modularizes the code and makes it reusable for different builder types. However, it's crucial to verify that the macro is correctly applied to the intended builder structs and that the documentation within the macro is accurate and helpful.

Verify that the macro is applied consistently across the relevant builder structs and that the documentation within the macro is clear and informative.

  • 268-376: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [180-324]

The macro refund_builder_methods defines a comprehensive set of builder methods for constructing a Refund. This macro significantly reduces code duplication by allowing these methods to be shared across different builder implementations. It's important to ensure that the macro's parameters are well-documented and that the methods it generates are correctly implemented and tested.

Carefully review the macro's parameters and the generated methods for correctness and completeness. Consider adding unit tests specifically targeting the behavior of these generated methods.

  • 326-341: The macro refund_builder_test_methods is designed for testing purposes, providing methods to manipulate the state of a RefundBuilder during tests. While this approach is practical for testing, it's essential to ensure that these methods are not exposed in the production code and that their usage is clearly documented for developers writing tests.

Confirm that these test methods are appropriately gated behind the #[cfg(test)] attribute and that their intended usage is documented for developers.

  • 368-373: The implementation of the From trait for converting a RefundBuilder into a RefundMaybeWithDerivedMetadataBuilder is a critical part of supporting the new C-bindings compatible builders. This conversion mechanism must be carefully reviewed to ensure it correctly transfers all relevant state from one builder to the other without losing any information or introducing inconsistencies.

Double-check the implementation of this From trait to ensure it accurately transfers the state between builders and consider adding tests to verify this behavior.

  • 503-516: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [494-548]

The macros respond_with_explicit_signing_pubkey_methods and respond_with_derived_signing_pubkey_methods introduce methods for creating invoices with explicit or derived signing keys. These macros enhance the flexibility of the Refund struct in generating invoices. It's important to ensure that these methods are correctly implemented, especially regarding error handling and feature checks.

Review the implementation of these methods for correctness, especially in handling errors and checking for unknown required features. Consider adding more comprehensive tests to cover various scenarios and edge cases.

lightning/src/offers/offer.rs (1)
  • 100-107: Conditional compilation is used to import different structs based on the c_bindings feature flag. This approach ensures that the appropriate builder structs are available depending on the compilation target. It's a clean way to handle differences in requirements between standard Rust usage and C-bindings.
lightning/src/offers/invoice_request.rs (7)
  • 75-75: The removal of multiple imports (DerivedSigningPubkey, ExplicitSigningPubkey, InvoiceBuilder) and the addition of only BlindedPayInfo suggests a significant refactoring towards C-bindings compatibility. Ensure that the removed entities are no longer required in this context, and their functionalities are adequately replaced or covered by the new implementation.
  • 120-127: The introduction of InvoiceRequestWithExplicitPayerIdBuilder struct is a key part of making the invoice_request module compatible with C-bindings. It's crucial to ensure that this struct and its methods are thoroughly documented, especially since builder patterns are being adapted for use outside of Rust's move semantics. Consider adding detailed documentation comments explaining the purpose of this struct, its relationship to the C-bindings, and examples of its usage.
  • 134-141: Similar to the previous comment, the InvoiceRequestWithDerivedPayerIdBuilder struct is introduced to support C-bindings. Documentation is equally important here to explain its usage and the scenarios in which it should be used over the InvoiceRequestWithExplicitPayerIdBuilder. Detailed comments and examples would greatly aid in understanding and maintaining this code.
  • 187-209: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [161-198]

The macro invoice_request_explicit_payer_id_builder_methods encapsulates methods for the explicit payer ID builder. Using macros for code reuse and to encapsulate common patterns is a good practice, especially in a context where multiple builder patterns share similar methods. However, it's crucial to ensure that these macros are well-documented to maintain code readability and ease of maintenance. Consider adding comprehensive documentation explaining the macro's purpose, parameters, and usage examples.

  • 217-245: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [200-237]

Similar to the previous comment, the macro invoice_request_derived_payer_id_builder_methods serves a similar purpose for derived payer ID scenarios. The use of macros for code reuse is commendable. However, the same considerations regarding documentation and testing apply here. Ensure this macro is well-documented and covered by unit tests to maintain code clarity and reliability.

  • 277-484: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [239-379]

The macro invoice_request_builder_methods encapsulates common builder methods, demonstrating effective use of macros for code reuse. While this approach is efficient, it's crucial to maintain clear documentation and comprehensive testing. Add detailed documentation for this macro, explaining its purpose, parameters, and usage. Ensure that unit tests cover the scenarios facilitated by this macro to guarantee its correct functionality.

  • 381-414: The macro invoice_request_builder_test_methods is designed for testing purposes, which is a good practice. It's important to ensure that these test methods are comprehensive and cover all relevant cases to maintain the robustness of the builder patterns. Expand the unit tests using this macro to cover a wide range of scenarios, including edge cases, to ensure the builder patterns are thoroughly validated.
lightning/src/offers/invoice.rs (4)
  • 239-272: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [209-266]

The macro invoice_explicit_signing_pubkey_builder_methods introduces several builder methods for constructing invoices with explicit signing public keys. It's crucial to ensure that these methods are well-documented, especially since macros can sometimes obscure the logic. Additionally, the conditional compilation with #[cfg_attr(c_bindings, allow(dead_code))] is a good practice for C-bindings compatibility but warrants a brief comment explaining its purpose for future maintainers.

Consider adding comments explaining the use of conditional compilation and the purpose of each method within the macro for clarity.

  • 299-341: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [268-334]

Similar to the previous macro, invoice_derived_signing_pubkey_builder_methods introduces methods for constructing invoices with derived signing public keys. The same considerations apply here regarding documentation and the use of conditional compilation. Additionally, ensure consistency in method naming and parameter handling between this macro and invoice_explicit_signing_pubkey_builder_methods to maintain readability and ease of use.

Ensure consistency in naming and parameter handling across macros and consider adding explanatory comments.

  • 379-498: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [336-433]

The invoice_builder_methods macro defines shared builder methods for invoice construction. It's well-structured and uses generics to accommodate different builder types. However, ensure that the documentation for these shared methods is accessible and clear to users of both explicit and derived signing public key builders. The use of generics here is commendable for code reuse but may require additional documentation to explain to new contributors or users.

Enhance documentation for shared builder methods, especially regarding their use with generics and different builder types.

  • 471-495: The conversion implementations from InvoiceWithExplicitSigningPubkeyBuilder and InvoiceWithDerivedSigningPubkeyBuilder to InvoiceBuilder are crucial for enabling flexibility in builder usage. Ensure that these conversions are tested thoroughly, especially in scenarios where builder fields might be manipulated before conversion. This will help prevent subtle bugs in invoice construction when switching between builder types.

Comment on lines +137 to +141
pub struct OfferWithExplicitMetadataBuilder<'a> {
offer: OfferContents,
metadata_strategy: core::marker::PhantomData<ExplicitMetadata>,
secp_ctx: Option<&'a Secp256k1<secp256k1::All>>,
}
Copy link

Choose a reason for hiding this comment

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

The introduction of OfferWithExplicitMetadataBuilder struct is a significant enhancement for supporting C-bindings. Using PhantomData to mark the metadata strategy is a Rust idiomatic approach. It's crucial to ensure this struct is well-documented for clarity, especially for developers working with C-bindings.

Consider enhancing the documentation for this struct to provide clear usage examples and explain the significance of the PhantomData field in the context of C-bindings.

Comment on lines +151 to +154
pub struct OfferWithDerivedMetadataBuilder<'a> {
offer: OfferContents,
metadata_strategy: core::marker::PhantomData<DerivedMetadata>,
secp_ctx: Option<&'a Secp256k1<secp256k1::All>>,
Copy link

Choose a reason for hiding this comment

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

Similarly, the OfferWithDerivedMetadataBuilder struct is introduced to support derived metadata strategies in the context of C-bindings. This struct mirrors the approach taken with OfferWithExplicitMetadataBuilder, using PhantomData for type safety. As with the explicit metadata builder, clear documentation on the usage of this struct in C-bindings scenarios is essential.

Would you like assistance in enhancing the documentation for this struct?

Comment on lines 204 to 216
/// Sets the [`Offer::metadata`] to the given bytes.
///
/// Successive calls to this method will override the previous setting.
pub fn metadata(mut self, metadata: Vec<u8>) -> Result<Self, Bolt12SemanticError> {
self.offer.metadata = Some(Metadata::Bytes(metadata));
Ok(self)
pub fn metadata(mut $self: $self_type, metadata: Vec<u8>) -> Result<$return_type, Bolt12SemanticError> {
$self.offer.metadata = Some(Metadata::Bytes(metadata));
Ok($return_value)
}
}
} }

impl<'a, T: secp256k1::Signing> OfferBuilder<'a, DerivedMetadata, T> {
macro_rules! offer_derived_metadata_builder_methods { ($secp_context: ty) => {
/// Similar to [`OfferBuilder::new`] except, if [`OfferBuilder::path`] is called, the signing
/// pubkey is derived from the given [`ExpandedKey`] and [`EntropySource`]. This provides
/// recipient privacy by using a different signing pubkey for each offer. Otherwise, the
Copy link

Choose a reason for hiding this comment

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

📝 NOTE
This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [176-213]

The use of macros to define builder methods and handle conditional compilation is effective in reducing code duplication and improving maintainability. Ensuring that these macros are well-documented and tested is important for clarity and correctness.

Would you like assistance in enhancing the documentation and testing for these macros?

Comment on lines +227 to +232
secp_ctx: &'a Secp256k1<$secp_context>
) -> Self where ES::Target: EntropySource {
let nonce = Nonce::from_entropy_source(entropy_source);
let derivation_material = MetadataMaterial::new(nonce, expanded_key, IV_BYTES, None);
let metadata = Metadata::DerivedSigningPubkey(derivation_material);
OfferBuilder {
Self {
Copy link

Choose a reason for hiding this comment

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

The addition of new methods for building offers with explicit and derived metadata is crucial for the usability of the new metadata strategies. Ensuring these methods are intuitive and well-documented is important for developer experience. The implementation should be reviewed for efficiency and error resistance.

Would you like assistance in enhancing the documentation for these new methods to improve usability?

Comment on lines +419 to +423
#[cfg(all(c_bindings, not(test)))]
impl<'a> OfferWithExplicitMetadataBuilder<'a> {
offer_explicit_metadata_builder_methods!(self, &mut Self, (), ());
offer_builder_methods!(self, &mut Self, (), ());
}
Copy link

Choose a reason for hiding this comment

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

The implementation of OfferWithExplicitMetadataBuilder for non-test configurations uses a void return type for builder methods. This design choice is unusual for a builder pattern, as it typically returns self for method chaining. Consider revisiting this design to align with common builder pattern practices, especially for consistency with the Rust ecosystem.

- offer_explicit_metadata_builder_methods!(self, &mut Self, (), ());
+ offer_explicit_metadata_builder_methods!(self, &mut Self, &mut Self, self);

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
#[cfg(all(c_bindings, not(test)))]
impl<'a> OfferWithExplicitMetadataBuilder<'a> {
offer_explicit_metadata_builder_methods!(self, &mut Self, (), ());
offer_builder_methods!(self, &mut Self, (), ());
}
#[cfg(all(c_bindings, not(test)))]
impl<'a> OfferWithExplicitMetadataBuilder<'a> {
offer_explicit_metadata_builder_methods!(self, &mut Self, &mut Self, self);
offer_builder_methods!(self, &mut Self, (), ());
}

Comment on lines +445 to +451
#[cfg(c_bindings)]
impl<'a> From<OfferBuilder<'a, DerivedMetadata, secp256k1::All>>
for OfferWithDerivedMetadataBuilder<'a> {
fn from(builder: OfferBuilder<'a, DerivedMetadata, secp256k1::All>) -> Self {
let OfferBuilder { offer, metadata_strategy, secp_ctx } = builder;

Self { offer, metadata_strategy, secp_ctx }
Copy link

Choose a reason for hiding this comment

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

The conversion from OfferBuilder<'a, DerivedMetadata, secp256k1::All> to OfferWithDerivedMetadataBuilder<'a> is implemented correctly, ensuring that all relevant fields are transferred during the conversion. This functionality is crucial for enabling the use of derived metadata strategies in C-bindings. Adding targeted unit tests to verify the correctness of this conversion process would enhance confidence in this functionality.

Would you like assistance in creating unit tests for verifying the correctness of this conversion process?

@@ -914,7 +1033,15 @@

#[cfg(test)]
mod tests {
use super::{Amount, Offer, OfferBuilder, OfferTlvStreamRef, Quantity};
use super::{Amount, Offer, OfferTlvStreamRef, Quantity};
Copy link

Choose a reason for hiding this comment

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

The updates to the tests to accommodate the changes in the offer building process are necessary and well-implemented. It's important to ensure that various scenarios and edge cases are covered to ensure robustness. Consider adding more tests for edge cases if not already covered.

Would you like assistance in adding more tests to cover additional scenarios and edge cases?

/// A function for signing an [`UnsignedBolt12Invoice`].
pub trait SignBolt12InvoiceFn {
/// Error type returned by the function.
type Error;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Unconstrained associated types aren't representable in bindings (what type would we use?). We could patch the error type out in bindings with yet another commit in the bindings-specific tree, but I'm not really sure what value it adds for users as it stands anyway. IMO error types (beyond unit structs) are only useful if they represent some different functionality that the caller will take (otherwise different failure scenarios should simply be logged at the failure site).

In general, signing an offer/invoice_request is something done inside ChannelManager (or LNDK) but even if it isn't, I'm not sure what different action a user might want based on different signing errors? In theory maybe they have something akin to our async signing and want an Async error tag vs a "real" error, but in that case they're probably better off doing a proper async thing and passing the invoice/request to be signed on to an async task right away. I'm just not convinced the error type adds anything but extra logic.

Copy link
Contributor Author

@jkczyz jkczyz Mar 7, 2024

Choose a reason for hiding this comment

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

Ah, ok. Didn't realize that was an issue. We could just constrain it by Debug, but happy to drop it entirely if you prefer.

Edit: Oh, unconstrained not unbound. I see.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Latest push drops the E type parameter from SignError in favor of using the unit type in SignFn.

Copy link
Collaborator

Choose a reason for hiding this comment

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

We could bound it on Debug too, that would work (in a theoretical sense, for std-types we do some manual implementation which we'd have to do here, but...)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm fine with just dropping the custom error, which has been done in the branch now.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Review Status

Actionable comments generated: 7

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 24c5c79 and d071113.
Files selected for processing (8)
  • fuzz/src/invoice_request_deser.rs (2 hunks)
  • fuzz/src/offer_deser.rs (2 hunks)
  • fuzz/src/refund_deser.rs (2 hunks)
  • lightning/src/ln/channelmanager.rs (9 hunks)
  • lightning/src/offers/invoice.rs (22 hunks)
  • lightning/src/offers/invoice_request.rs (22 hunks)
  • lightning/src/offers/merkle.rs (6 hunks)
  • lightning/src/offers/test_utils.rs (3 hunks)
Files skipped from review as they are similar to previous changes (5)
  • fuzz/src/invoice_request_deser.rs
  • fuzz/src/offer_deser.rs
  • fuzz/src/refund_deser.rs
  • lightning/src/ln/channelmanager.rs
  • lightning/src/offers/merkle.rs
Additional comments: 5
lightning/src/offers/test_utils.rs (1)
  • 23-25: The addition of the fail_sign function introduces a utility for testing failure scenarios in signing operations. This is a good practice for comprehensive testing, ensuring that error handling paths are also verified. However, it's important to ensure that this function is used in relevant test cases to validate error scenarios effectively.
  • Ensure that test cases utilizing fail_sign are implemented to cover various failure scenarios in the signing process.
lightning/src/offers/invoice_request.rs (2)
  • 74-90: The changes to the imports and the addition of new builder structs (InvoiceRequestWithExplicitPayerIdBuilder and InvoiceRequestWithDerivedPayerIdBuilder) align with the PR's objective to introduce C-bindings compatible builders. This approach simplifies the API for C-bindings by avoiding Rust-specific features like generics and move semantics, which are not easily representable in C. However, ensure that the removed imports (DerivedSigningPubkey, ExplicitSigningPubkey, InvoiceBuilder) are no longer needed due to the restructuring and that their functionalities are adequately covered by the new builder patterns and macro definitions.
  • 1114-1128: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [1106-2151]

The test cases provided in the tests module cover a wide range of scenarios, including building invoice requests with defaults, handling expiration, deriving metadata, specifying chains, amounts, features, quantities, payer notes, and various failure cases. These tests are crucial for ensuring the correctness and robustness of the new builder patterns and signing functionalities. However, ensure that the tests cover all new code paths introduced by the PR, especially the error handling logic in the new builder methods and macros. Adding tests for edge cases, such as invalid inputs or unexpected states, will further enhance the test suite's comprehensiveness.

lightning/src/offers/invoice.rs (2)
  • 239-272: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [209-266]

The macro invoice_explicit_signing_pubkey_builder_methods defines methods for building invoices with explicit signing pubkeys. It's well-structured and leverages Rust's powerful macro system for code reuse. However, ensure that the #[cfg_attr(c_bindings, allow(dead_code))] attribute is indeed necessary for all methods within this macro. If some methods are used outside of C bindings context, this attribute might suppress useful compiler warnings.

Additionally, consider adding unit tests specifically targeting the behavior of these generated methods to ensure their correctness across different configurations.

  • 299-341: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [268-334]

Similar to the previous macro, invoice_derived_signing_pubkey_builder_methods is well-implemented for handling derived signing pubkeys. The same considerations regarding the #[cfg_attr(c_bindings, allow(dead_code))] attribute and the suggestion for targeted unit tests apply here as well.

It's also worth noting the use of cloning in the build_and_sign method under the c_bindings configuration. Cloning could potentially lead to performance issues for large invoices. If performance becomes a concern, consider exploring more efficient ways to handle this scenario, possibly by optimizing the data structures involved or by reevaluating the need for cloning.

pub(crate) fn payer_keys() -> KeyPair {
let secp_ctx = Secp256k1::new();
KeyPair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap())
}

pub(crate) fn payer_sign<T: AsRef<TaggedHash>>(message: &T) -> Result<Signature, Infallible> {
pub(crate) fn payer_sign<T: AsRef<TaggedHash>>(message: &T) -> Result<Signature, ()> {
Copy link

Choose a reason for hiding this comment

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

The modification of the return type of the payer_sign function from Result<Signature, Infallible> to Result<Signature, ()> aligns with the changes made to the recipient_sign function and the newly added fail_sign function. This change standardizes the error type across these functions, facilitating easier error handling in tests. However, using a unit type () for errors is not very descriptive and might not provide enough context for debugging or handling specific error scenarios.

Consider using a custom error type or an existing error enumeration that provides more context about the failure, enhancing maintainability and debuggability of the test suite.

// Example of a custom error type
enum SignError {
    GenericFailure,
    // Other specific error cases can be added here
}

// Then, modify the function signature to use this error type
pub(crate) fn payer_sign<T: AsRef<TaggedHash>>(message: &T) -> Result<Signature, SignError> {
    // Function implementation...
}

@@ -40,7 +44,7 @@
KeyPair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[43; 32]).unwrap())
}

pub(crate) fn recipient_sign<T: AsRef<TaggedHash>>(message: &T) -> Result<Signature, Infallible> {
pub(crate) fn recipient_sign<T: AsRef<TaggedHash>>(message: &T) -> Result<Signature, ()> {
Copy link

Choose a reason for hiding this comment

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

The modification of the return type of the recipient_sign function mirrors the change made to the payer_sign function, standardizing the error handling approach across these utility functions. As mentioned in the previous comment, consider using a more descriptive error type instead of the unit type () to provide better context for error scenarios.

Refer to the previous comment for the recommendation on using a custom error type or an existing error enumeration for improved error context.

Comment on lines +159 to +189
/// Builds a [`Bolt12Invoice`] from either:
/// - an [`InvoiceRequest`] for the "offer to be paid" flow or
/// - a [`Refund`] for the "offer for money" flow.
///
/// See [module-level documentation] for usage.
///
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
/// [`Refund`]: crate::offers::refund::Refund
/// [module-level documentation]: self
#[cfg(c_bindings)]
pub struct InvoiceWithExplicitSigningPubkeyBuilder<'a> {
invreq_bytes: &'a Vec<u8>,
invoice: InvoiceContents,
signing_pubkey_strategy: ExplicitSigningPubkey,
}

/// Builds a [`Bolt12Invoice`] from either:
/// - an [`InvoiceRequest`] for the "offer to be paid" flow or
/// - a [`Refund`] for the "offer for money" flow.
///
/// See [module-level documentation] for usage.
///
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
/// [`Refund`]: crate::offers::refund::Refund
/// [module-level documentation]: self
#[cfg(c_bindings)]
pub struct InvoiceWithDerivedSigningPubkeyBuilder<'a> {
invreq_bytes: &'a Vec<u8>,
invoice: InvoiceContents,
signing_pubkey_strategy: DerivedSigningPubkey,
}
Copy link

Choose a reason for hiding this comment

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

The introduction of InvoiceWithExplicitSigningPubkeyBuilder and InvoiceWithDerivedSigningPubkeyBuilder is a positive step towards enhancing C-bindings compatibility. However, it's essential to ensure comprehensive documentation for these new structs, given the complexity of builder patterns. Additionally, consider potential safety or usability concerns with the public exposure of these builders in the context of C-bindings.

Would you like assistance in drafting the documentation or evaluating safety concerns?

Comment on lines 379 to 498
#[cfg(feature = "std")] {
if self.invoice.is_offer_or_refund_expired() {
return Err(Bolt12SemanticError::AlreadyExpired);
}
}
invoice_explicit_signing_pubkey_builder_methods!(self, Self);
}

#[cfg(not(feature = "std"))] {
if self.invoice.is_offer_or_refund_expired_no_std(self.invoice.created_at()) {
return Err(Bolt12SemanticError::AlreadyExpired);
}
}
impl<'a> InvoiceBuilder<'a, DerivedSigningPubkey> {
invoice_derived_signing_pubkey_builder_methods!(self, Self);
}

let InvoiceBuilder { invreq_bytes, invoice, .. } = self;
Ok(UnsignedBolt12Invoice::new(invreq_bytes, invoice))
}
impl<'a, S: SigningPubkeyStrategy> InvoiceBuilder<'a, S> {
invoice_builder_methods!(self, Self, Self, self, S, mut);
}

impl<'a> InvoiceBuilder<'a, DerivedSigningPubkey> {
/// Builds a signed [`Bolt12Invoice`] after checking for valid semantics.
pub fn build_and_sign<T: secp256k1::Signing>(
self, secp_ctx: &Secp256k1<T>
) -> Result<Bolt12Invoice, Bolt12SemanticError> {
#[cfg(feature = "std")] {
if self.invoice.is_offer_or_refund_expired() {
return Err(Bolt12SemanticError::AlreadyExpired);
}
}
#[cfg(all(c_bindings, not(test)))]
impl<'a> InvoiceWithExplicitSigningPubkeyBuilder<'a> {
invoice_explicit_signing_pubkey_builder_methods!(self, &mut Self);
invoice_builder_methods!(self, &mut Self, (), (), ExplicitSigningPubkey);
}

#[cfg(not(feature = "std"))] {
if self.invoice.is_offer_or_refund_expired_no_std(self.invoice.created_at()) {
return Err(Bolt12SemanticError::AlreadyExpired);
}
#[cfg(all(c_bindings, test))]
impl<'a> InvoiceWithExplicitSigningPubkeyBuilder<'a> {
invoice_explicit_signing_pubkey_builder_methods!(self, &mut Self);
invoice_builder_methods!(self, &mut Self, &mut Self, self, ExplicitSigningPubkey);
}

#[cfg(all(c_bindings, not(test)))]
impl<'a> InvoiceWithDerivedSigningPubkeyBuilder<'a> {
invoice_derived_signing_pubkey_builder_methods!(self, &mut Self);
invoice_builder_methods!(self, &mut Self, (), (), DerivedSigningPubkey);
}

#[cfg(all(c_bindings, test))]
impl<'a> InvoiceWithDerivedSigningPubkeyBuilder<'a> {
invoice_derived_signing_pubkey_builder_methods!(self, &mut Self);
invoice_builder_methods!(self, &mut Self, &mut Self, self, DerivedSigningPubkey);
}

#[cfg(c_bindings)]
impl<'a> From<InvoiceWithExplicitSigningPubkeyBuilder<'a>>
for InvoiceBuilder<'a, ExplicitSigningPubkey> {
fn from(builder: InvoiceWithExplicitSigningPubkeyBuilder<'a>) -> Self {
let InvoiceWithExplicitSigningPubkeyBuilder {
invreq_bytes, invoice, signing_pubkey_strategy,
} = builder;

Self {
invreq_bytes, invoice, signing_pubkey_strategy,
}
}
}

let InvoiceBuilder {
invreq_bytes, invoice, signing_pubkey_strategy: DerivedSigningPubkey(keys)
} = self;
let unsigned_invoice = UnsignedBolt12Invoice::new(invreq_bytes, invoice);
#[cfg(c_bindings)]
impl<'a> From<InvoiceWithDerivedSigningPubkeyBuilder<'a>>
for InvoiceBuilder<'a, DerivedSigningPubkey> {
fn from(builder: InvoiceWithDerivedSigningPubkeyBuilder<'a>) -> Self {
let InvoiceWithDerivedSigningPubkeyBuilder {
invreq_bytes, invoice, signing_pubkey_strategy,
} = builder;

let invoice = unsigned_invoice
.sign::<_, Infallible>(
|message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
)
.unwrap();
Ok(invoice)
Self {
invreq_bytes, invoice, signing_pubkey_strategy,
}
}
}

Copy link

Choose a reason for hiding this comment

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

📝 NOTE
This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [336-433]

The invoice_builder_methods macro is a key component of the invoice building process, providing common functionality across different builder types. The use of generics and conditional compilation tags (#[cfg_attr(c_bindings, allow(dead_code))]) is appropriate and demonstrates a deep understanding of Rust's capabilities.

One area for improvement is the documentation within the macro. Given the complexity and importance of these methods, adding detailed comments explaining the purpose and usage of each method would greatly enhance maintainability and readability for future contributors.

Moreover, the error handling within these methods seems to rely on the Bolt12SemanticError type. Ensure that this error handling strategy is consistent and comprehensive, covering all potential error cases to prevent runtime panics or unexpected behavior.

Comment on lines +511 to +534
/// A function for signing an [`UnsignedBolt12Invoice`].
pub trait SignBolt12InvoiceFn {
/// Signs a [`TaggedHash`] computed over the merkle root of `message`'s TLV stream.
fn sign_invoice(&self, message: &UnsignedBolt12Invoice) -> Result<Signature, ()>;
}

impl<F> SignBolt12InvoiceFn for F
where
F: Fn(&UnsignedBolt12Invoice) -> Result<Signature, ()>,
{
fn sign_invoice(&self, message: &UnsignedBolt12Invoice) -> Result<Signature, ()> {
self(message)
}
}

impl<F> SignFn<UnsignedBolt12Invoice> for F
where
F: SignBolt12InvoiceFn,
{
fn sign(&self, message: &UnsignedBolt12Invoice) -> Result<Signature, ()> {
self.sign_invoice(message)
}
}

Copy link

Choose a reason for hiding this comment

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

The SignBolt12InvoiceFn trait and its implementation provide a flexible way to sign invoices. This design allows for different signing mechanisms to be easily integrated, which is a strong architectural choice.

However, it's crucial to ensure that the error type returned (Result<Signature, ()>) is sufficiently informative for callers to understand the nature of any errors that occur during signing. Using a unit type (()) for errors limits the ability to convey meaningful error information. Consider defining a more descriptive error type that can provide insights into why signing might fail, enhancing debuggability and error handling.

Comment on lines 186 to 208
secp_ctx: None,
}
}
}

impl<'a, 'b, T: secp256k1::Signing> InvoiceRequestBuilder<'a, 'b, DerivedPayerId, T> {
/// Builds an unsigned [`InvoiceRequest`] after checking for valid semantics. It can be signed
/// by [`UnsignedInvoiceRequest::sign`].
pub fn build($self: $self_type) -> Result<UnsignedInvoiceRequest, Bolt12SemanticError> {
let (unsigned_invoice_request, keys, _) = $self.build_with_checks()?;
debug_assert!(keys.is_none());
Ok(unsigned_invoice_request)
}
} }

macro_rules! invoice_request_derived_payer_id_builder_methods { (
$self: ident, $self_type: ty, $secp_context: ty
) => {
#[cfg_attr(c_bindings, allow(dead_code))]
pub(super) fn deriving_payer_id<ES: Deref>(
offer: &'a Offer, expanded_key: &ExpandedKey, entropy_source: ES,
secp_ctx: &'b Secp256k1<T>, payment_id: PaymentId
secp_ctx: &'b Secp256k1<$secp_context>, payment_id: PaymentId
) -> Self where ES::Target: EntropySource {
let nonce = Nonce::from_entropy_source(entropy_source);
let payment_id = Some(payment_id);
Copy link

Choose a reason for hiding this comment

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

📝 NOTE
This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [160-198]

The introduction of macro definitions (invoice_request_explicit_payer_id_builder_methods, invoice_request_derived_payer_id_builder_methods, and invoice_request_builder_methods) is a strategic move to reduce code duplication across different builder implementations. These macros encapsulate common builder patterns, making the code more maintainable and easier to extend in the future. However, it's crucial to ensure that these macros are well-documented. Adding comprehensive documentation explaining each macro's purpose, parameters, and usage examples will significantly improve code readability and maintainability. Consider adding comments before each macro definition to serve this purpose.

// Before each macro definition, add a comment explaining its purpose, parameters, and usage examples.

@TheBlueMatt
Copy link
Collaborator

This LGTM, please squash.

Use the macros introduced in the previous commit to define two builders
for each type parameterization of InvoiceBuilder
- InvoiceWithExplicitSigningPubkeyBuilder
- InvoiceWithDerivedSigningPubkeyBuilder

The difference between these and InvoiceBuilder is that these have
methods that take `self` by mutable reference instead of by value and
don't return anything instead returning the modified builder. This is
required because bindings don't support move semantics nor impl blocks
specific to a certain type parameterization. Because of this, the
builder's contents must be cloned when building a Bolt12Invoice.

Keeps InvoiceBuilder defined so that it can be used internally in
ChannelManager's OffersMessageHandler even when compiled for c_bindings.
Replace the Fn trait bound on signing methods with a dedicated trait
since Fn is not supported in bindings. Implement the trait for Fn so
that closures can still be used in Rust.
SignError allows implementors of SignFunction to return a custom error
type. Drop this as an unconstrained type causes problems with bindings
and isn't useful unless the caller can take some sort of action based on
different errors.
@jkczyz jkczyz force-pushed the 2024-02-bindings-builders branch from d071113 to 9277166 Compare March 8, 2024 16:39
@jkczyz
Copy link
Contributor Author

jkczyz commented Mar 8, 2024

@coderabbitai pause

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Review Status

Actionable comments generated: 6

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between aa3dbe8 and 9277166.
Files selected for processing (11)
  • fuzz/src/invoice_request_deser.rs (2 hunks)
  • fuzz/src/offer_deser.rs (2 hunks)
  • fuzz/src/refund_deser.rs (2 hunks)
  • lightning/src/crypto/mod.rs (1 hunks)
  • lightning/src/ln/channelmanager.rs (9 hunks)
  • lightning/src/offers/invoice.rs (22 hunks)
  • lightning/src/offers/invoice_request.rs (22 hunks)
  • lightning/src/offers/merkle.rs (6 hunks)
  • lightning/src/offers/offer.rs (16 hunks)
  • lightning/src/offers/refund.rs (13 hunks)
  • lightning/src/offers/test_utils.rs (3 hunks)
Files skipped from review as they are similar to previous changes (7)
  • fuzz/src/invoice_request_deser.rs
  • fuzz/src/offer_deser.rs
  • fuzz/src/refund_deser.rs
  • lightning/src/crypto/mod.rs
  • lightning/src/ln/channelmanager.rs
  • lightning/src/offers/merkle.rs
  • lightning/src/offers/test_utils.rs
Additional comments: 36
lightning/src/offers/refund.rs (2)
  • 142-145: The introduction of RefundMaybeWithDerivedMetadataBuilder struct is a key part of making the offers module compatible with C-bindings. However, it's important to ensure that the documentation is clear about its purpose and usage, especially since builder patterns are not typically used outside of Rust's move semantics. Consider adding more detailed documentation to explain how this struct should be used in the context of C-bindings.
  • 133-150: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [147-178]

The macro refund_explicit_metadata_builder_methods introduces a set of methods for building a refund with explicit metadata. This approach helps to reduce code duplication and maintain consistency across different builder implementations. However, ensure that the macro is well-documented, explaining each method's purpose and the conditions under which they should be used. Additionally, validate that the error handling (e.g., Bolt12SemanticError::InvalidAmount) is consistent with the rest of the module's error handling strategy.

lightning/src/offers/offer.rs (7)
  • 137-141: The introduction of OfferWithExplicitMetadataBuilder struct is a significant enhancement for supporting C-bindings. Using PhantomData to mark the metadata strategy is a Rust idiomatic approach. It's crucial to ensure this struct is well-documented for clarity, especially for developers working with C-bindings.

Consider enhancing the documentation for this struct to provide clear usage examples and explain the significance of the PhantomData field in the context of C-bindings.

  • 151-154: Similarly, the OfferWithDerivedMetadataBuilder struct is introduced to support derived metadata strategies in the context of C-bindings. This struct mirrors the approach taken with OfferWithExplicitMetadataBuilder, using PhantomData for type safety. As with the explicit metadata builder, clear documentation on the usage of this struct in C-bindings scenarios is essential.

Would you like assistance in enhancing the documentation for this struct?

  • 204-216: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [176-211]

The use of macros to define builder methods and handle conditional compilation is effective in reducing code duplication and improving maintainability. Ensuring that these macros are well-documented and tested is important for clarity and correctness.

Would you like assistance in enhancing the documentation and testing for these macros?

  • 224-235: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [213-242]

The addition of new methods for building offers with explicit and derived metadata is crucial for the usability of the new metadata strategies. Ensuring these methods are intuitive and well-documented is important for developer experience. The implementation should be reviewed for efficiency and error resistance.

Would you like assistance in enhancing the documentation for these new methods to improve usability?

  • 260-328: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [244-380]

The implementation of OfferWithExplicitMetadataBuilder and OfferWithDerivedMetadataBuilder for different configurations (with and without the test feature) demonstrates careful consideration of testing needs. However, it's important to ensure that the differences in method signatures between test and non-test configurations do not lead to confusion or errors. Clear documentation and possibly compile-time checks (if feasible) could help mitigate any potential issues arising from these differences.

Consider adding documentation or compile-time assertions to clarify the differences in method signatures between test and non-test configurations, ensuring that developers and contributors are aware of these nuances.

Adding documentation or compile-time assertions could enhance clarity and prevent potential confusion regarding method signatures in different configurations.

  • 445-451: The conversion from OfferBuilder<'a, DerivedMetadata, secp256k1::All> to OfferWithDerivedMetadataBuilder<'a> is implemented correctly, ensuring that all relevant fields are transferred during the conversion. This functionality is crucial for enabling the use of derived metadata strategies in C-bindings. Adding targeted unit tests to verify the correctness of this conversion process would enhance confidence in this functionality.

Would you like assistance in creating unit tests for verifying the correctness of this conversion process?

  • 1036-1036: The updates to the tests to accommodate the changes in the offer building process are necessary and well-implemented. It's important to ensure that various scenarios and edge cases are covered to ensure robustness. Consider adding more tests for edge cases if not already covered.

Would you like assistance in adding more tests to cover additional scenarios and edge cases?

lightning/src/offers/invoice_request.rs (25)
  • 29-29: The import of UnsignedInvoiceRequest is correct and necessary for the example provided in the documentation. This import is used to demonstrate how to create an UnsignedInvoiceRequest from an Offer.
  • 39-39: The import of ExplicitPayerId, InvoiceRequestBuilder is correct and necessary for the example provided in the documentation. These imports are used to demonstrate how to create an InvoiceRequest with an explicit payer ID.
  • 64-64: The import of KeyPair, PublicKey, Secp256k1 from bitcoin::secp256k1 is correct and necessary for the cryptographic operations involved in creating and signing invoice requests.
  • 74-74: The import of BlindedPayInfo is correct and necessary for handling blinded payment information in the context of invoice requests.
  • 83-90: The conditional compilation directives (#[cfg(not(c_bindings))] and #[cfg(c_bindings)]) are used correctly to import different structs based on whether the code is being compiled with C bindings. This ensures compatibility with both Rust-native and C-bindings use cases.
  • 114-126: The definition of InvoiceRequestWithExplicitPayerIdBuilder struct is correct and follows Rust conventions. It is designed to build an InvoiceRequest with an explicit payer ID, which is a necessary feature for supporting C bindings. The use of lifetime annotations and optional fields is appropriate for the builder pattern.
  • 128-140: The definition of InvoiceRequestWithDerivedPayerIdBuilder struct is correct and follows Rust conventions. It is designed to build an InvoiceRequest with a derived payer ID, which is a necessary feature for supporting C bindings. The use of lifetime annotations and optional fields is appropriate for the builder pattern.
  • 186-208: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [160-197]

The macro invoice_request_explicit_payer_id_builder_methods is defined correctly to reduce code duplication by encapsulating common builder methods for explicit payer ID. However, it's crucial to ensure that these macros are well-documented to improve code readability and maintainability.

Consider adding comprehensive documentation explaining the purpose, parameters, and usage examples of this macro.

  • 216-244: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [199-236]

The macro invoice_request_derived_payer_id_builder_methods is defined correctly to encapsulate common builder methods for derived payer ID. Similar to the previous macro, adding comprehensive documentation would significantly improve code readability and maintainability.

Consider adding comments before the macro definition to explain its purpose, parameters, and usage examples.

  • 276-483: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [238-378]

The macro invoice_request_builder_methods is correctly defined to encapsulate common builder methods. This approach helps in reducing code duplication and maintaining consistency across different builder implementations. As with the other macros, adding detailed documentation would enhance the understandability of the code.

Consider adding detailed comments explaining the macro's purpose, parameters, and usage examples.

  • 380-413: The macro invoice_request_builder_test_methods is correctly defined for adding test-specific builder methods. This macro is used to extend builder functionality in test scenarios, which is a good practice for isolating test code from production code. Documentation for this macro, explaining its intended use in tests, would be beneficial.

Consider adding comments to explain the purpose and usage of this macro in the context of testing.

  • 415-428: The implementation of InvoiceRequestBuilder for ExplicitPayerId and DerivedPayerId strategies is correct. The use of macros to define common methods reduces code duplication and enhances maintainability. Ensuring that these implementations are well-documented, especially regarding how they fit into the overall builder pattern, would be beneficial.
  • 430-441: The implementation of InvoiceRequestWithExplicitPayerIdBuilder for C bindings is correct. The use of macros to define common methods and conditional compilation for test scenarios demonstrates a good understanding of Rust's features for cross-compatibility and testing.
  • 443-454: The implementation of InvoiceRequestWithDerivedPayerIdBuilder for C bindings is correct. It follows the same pattern as the explicit payer ID builder, using macros and conditional compilation effectively. This consistency in implementation style is commendable.
  • 457-480: The From trait implementations for converting between InvoiceRequestWithExplicitPayerIdBuilder and InvoiceRequestWithDerivedPayerIdBuilder to their respective InvoiceRequestBuilder variants are correct. This facilitates easy conversion between builder types, enhancing the flexibility of the builder pattern.
  • 543-573: The macro unsigned_invoice_request_sign_method is correctly defined to add a sign method to UnsignedInvoiceRequest. This macro encapsulates the signing logic, including the creation of a signature TLV stream. Adding documentation to explain the macro's purpose and usage would improve code readability.

Consider adding comments before the macro definition to explain its purpose and usage.

  • 692-716: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [695-749]

The macro invoice_request_respond_with_explicit_signing_pubkey_methods is correctly defined to add methods for responding to an invoice request with an explicit signing pubkey. This macro encapsulates common logic for creating an InvoiceBuilder based on the invoice request. Adding documentation to explain the macro's purpose, parameters, and usage examples would enhance code readability.

Consider adding detailed comments explaining the macro's purpose and usage.

  • 751-777: The macro invoice_request_verify_method is correctly defined to add a verify method to InvoiceRequest. This macro encapsulates the verification logic, including the extraction of derived keys if possible. Adding documentation to explain the macro's purpose and usage would improve code readability and maintainability.

Consider adding comments before the macro definition to explain its purpose and usage.

  • 810-834: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [813-858]

The macro invoice_request_respond_with_derived_signing_pubkey_methods is correctly defined to add methods for responding to an invoice request using derived signing keys. This macro encapsulates common logic for creating an InvoiceBuilder based on the invoice request and derived keys. Adding documentation to explain the macro's purpose, parameters, and usage examples would enhance code readability.

Consider adding detailed comments explaining the macro's purpose and usage.

  • 519-519: The implementation of UnsignedInvoiceRequest with its new method is correct. It properly constructs an UnsignedInvoiceRequest by serializing the TLV stream and computing the tagged hash. This is a crucial step in the invoice request creation process.
  • 541-541: The method tagged_hash in UnsignedInvoiceRequest is correctly implemented to return the tagged hash of the invoice request. This is necessary for signing the invoice request.
  • 576-582: The implementation of UnsignedInvoiceRequest for non-C bindings is correct. It uses the unsigned_invoice_request_sign_method macro to add the sign method, which is essential for signing the invoice request.
  • 780-793: The implementation of InvoiceRequest for both non-C bindings and C bindings is correct. It uses macros to add various methods, including those for responding to an invoice request with an explicit signing pubkey and verifying the request. This demonstrates a good understanding of Rust's features for cross-compatibility.
  • 860-870: The implementation of VerifiedInvoiceRequest is correct. It uses macros to add methods for responding to an invoice request using both explicit and derived signing pubkeys. This flexibility is important for handling different scenarios in invoice request processing.
  • 1106-1106: The test cases provided in the file are comprehensive and cover a wide range of scenarios, including building invoice requests with defaults, handling derived metadata, and testing various builder methods. These tests are crucial for ensuring the correctness and reliability of the invoice request functionality.
lightning/src/offers/invoice.rs (2)
  • 159-189: The introduction of InvoiceWithExplicitSigningPubkeyBuilder and InvoiceWithDerivedSigningPubkeyBuilder structs is a significant enhancement towards making the offers module compatible with C-bindings. A few observations and recommendations:
  • The documentation is clear and concise, effectively explaining the purpose and usage of each struct. However, it would be beneficial to include examples or more detailed usage scenarios, especially considering the complexity of builder patterns and their importance in the context of C-bindings.
  • The use of #[cfg(c_bindings)] ensures that these structs are only compiled when C-bindings are enabled, which is a good practice to avoid unnecessary compilation for standard Rust usage. It's important to ensure that all related code paths and functionalities are also conditionally compiled to maintain consistency.
  • Both structs share a similar structure, differing only in the signing_pubkey_strategy field type. This design choice is appropriate given their distinct purposes but consider if there's an opportunity to abstract common functionalities or fields into a base struct or trait to reduce code duplication.

Overall, these additions are well-thought-out and align with the PR's objectives. Just ensure that the documentation is as helpful as possible for developers integrating with C-bindings.

Consider enhancing the documentation with more detailed examples or usage scenarios to aid developers in understanding and utilizing these new builder patterns effectively.

  • 379-498: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [336-433]

The invoice_builder_methods macro defines common builder methods applicable to both explicit and derived signing pubkey strategies. This macro effectively reduces duplication by centralizing shared functionality. Key points:

  • The macro cleverly uses parameters to customize the behavior of the defined methods based on the builder's context, which is a good use of Rust macros to achieve code reuse.
  • The use of #[cfg_attr(c_bindings, allow(dead_code))] is consistent with previous macros, helping maintain clean compilation logs under different build configurations.
  • Methods like relative_expiry, fallback_v0_p2wsh, fallback_v0_p2wpkh, and fallback_v1_p2tr_tweaked provide essential functionalities for setting invoice properties. Their implementation is straightforward and aligns with the builder pattern's expectations.
  • The conditional compilation within methods, such as in relative_expiry, to handle differences between std and no_std environments is necessary and well-implemented. However, ensure that all conditional compilation paths are thoroughly tested to prevent runtime issues in different build configurations.

This macro is a good example of leveraging Rust's macro system to abstract and reuse code effectively. Continue to monitor for opportunities to refine and simplify the macro as the codebase evolves.

Comment on lines 268 to 376
let mut metadata = core::mem::take(&mut $self.refund.payer.0);

if self.refund.paths.is_none() {
if $self.refund.paths.is_none() {
metadata = metadata.without_keys();
}

let mut tlv_stream = self.refund.as_tlv_stream();
let mut tlv_stream = $self.refund.as_tlv_stream();
tlv_stream.0.metadata = None;
if metadata.derives_payer_keys() {
tlv_stream.2.payer_id = None;
}

let (derived_metadata, keys) = metadata.derive_from(tlv_stream, self.secp_ctx);
let (derived_metadata, keys) = metadata.derive_from(tlv_stream, $self.secp_ctx);
metadata = derived_metadata;
if let Some(keys) = keys {
self.refund.payer_id = keys.public_key();
$self.refund.payer_id = keys.public_key();
}

self.refund.payer.0 = metadata;
$self.refund.payer.0 = metadata;
}

let mut bytes = Vec::new();
self.refund.write(&mut bytes).unwrap();
$self.refund.write(&mut bytes).unwrap();

Ok(Refund {
bytes,
#[cfg(not(c_bindings))]
contents: $self.refund,
#[cfg(c_bindings)]
contents: $self.refund.clone(),
})
}
} }

#[cfg(test)]
macro_rules! refund_builder_test_methods { (
$self: ident, $self_type: ty, $return_type: ty, $return_value: expr $(, $self_mut: tt)?
) => {
#[cfg_attr(c_bindings, allow(dead_code))]
pub(crate) fn clear_paths($($self_mut)* $self: $self_type) -> $return_type {
$self.refund.paths = None;
$return_value
}

Ok(Refund { bytes, contents: self.refund })
#[cfg_attr(c_bindings, allow(dead_code))]
fn features_unchecked($($self_mut)* $self: $self_type, features: InvoiceRequestFeatures) -> $return_type {
$self.refund.features = features;
$return_value
}
} }

impl<'a> RefundBuilder<'a, secp256k1::SignOnly> {
refund_explicit_metadata_builder_methods!();
}

#[cfg(test)]
impl<'a, T: secp256k1::Signing> RefundBuilder<'a, T> {
pub(crate) fn clear_paths(mut self) -> Self {
self.refund.paths = None;
self
}
refund_builder_methods!(self, Self, Self, self, T, mut);

#[cfg(test)]
refund_builder_test_methods!(self, Self, Self, self, mut);
}

#[cfg(all(c_bindings, not(test)))]
impl<'a> RefundMaybeWithDerivedMetadataBuilder<'a> {
refund_explicit_metadata_builder_methods!();
refund_builder_methods!(self, &mut Self, (), (), secp256k1::All);
}

#[cfg(all(c_bindings, test))]
impl<'a> RefundMaybeWithDerivedMetadataBuilder<'a> {
refund_explicit_metadata_builder_methods!();
refund_builder_methods!(self, &mut Self, &mut Self, self, secp256k1::All);
refund_builder_test_methods!(self, &mut Self, &mut Self, self);
}

fn features_unchecked(mut self, features: InvoiceRequestFeatures) -> Self {
self.refund.features = features;
self
#[cfg(c_bindings)]
impl<'a> From<RefundBuilder<'a, secp256k1::All>>
for RefundMaybeWithDerivedMetadataBuilder<'a> {
fn from(builder: RefundBuilder<'a, secp256k1::All>) -> Self {
let RefundBuilder { refund, secp_ctx } = builder;

Self { refund, secp_ctx }
}
}

Copy link

Choose a reason for hiding this comment

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

📝 NOTE
This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [180-324]

The refund_builder_methods macro is a critical component for defining common builder methods across different refund builder types. It's essential to ensure that the macro is flexible enough to accommodate various builder patterns, especially considering the conditional compilation directives used within. Pay particular attention to the build method (lines 285-322), ensuring that the logic for handling derived metadata and conditional compilation for c_bindings is correct and does not introduce any potential issues when used in a C-bindings context.

Comment on lines +367 to +373
#[cfg(c_bindings)]
impl<'a> From<RefundBuilder<'a, secp256k1::All>>
for RefundMaybeWithDerivedMetadataBuilder<'a> {
fn from(builder: RefundBuilder<'a, secp256k1::All>) -> Self {
let RefundBuilder { refund, secp_ctx } = builder;

Self { refund, secp_ctx }
Copy link

Choose a reason for hiding this comment

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

The From implementation for converting RefundBuilder to RefundMaybeWithDerivedMetadataBuilder is a crucial part of supporting C-bindings. Ensure that this conversion correctly preserves all necessary information from the RefundBuilder to the RefundMaybeWithDerivedMetadataBuilder. Additionally, consider any potential edge cases where information might be lost or incorrectly transformed during this conversion.

Comment on lines 503 to 516
/// [`Duration`]: core::time::Duration
#[cfg(feature = "std")]
pub fn respond_with(
&self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash,
&$self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash,
signing_pubkey: PublicKey,
) -> Result<InvoiceBuilder<ExplicitSigningPubkey>, Bolt12SemanticError> {
) -> Result<$builder, Bolt12SemanticError> {
let created_at = std::time::SystemTime::now()
.duration_since(std::time::SystemTime::UNIX_EPOCH)
.expect("SystemTime::now() should come after SystemTime::UNIX_EPOCH");

self.respond_with_no_std(payment_paths, payment_hash, signing_pubkey, created_at)
$self.respond_with_no_std(payment_paths, payment_hash, signing_pubkey, created_at)
}

/// Creates an [`InvoiceBuilder`] for the refund with the given required fields.
Copy link

Choose a reason for hiding this comment

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

📝 NOTE
This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [494-548]

The macros respond_with_explicit_signing_pubkey_methods and respond_with_derived_signing_pubkey_methods introduce methods for creating invoices with explicit and derived signing pubkeys, respectively. It's important to ensure that these methods are correctly implemented and that they align with the overall design goals of supporting C-bindings. Additionally, verify that the error handling (e.g., Bolt12SemanticError::UnknownRequiredFeatures) is consistent and that the documentation clearly explains how and when to use these methods.

Comment on lines +496 to +518
/// A function for signing an [`UnsignedInvoiceRequest`].
pub trait SignInvoiceRequestFn {
/// Signs a [`TaggedHash`] computed over the merkle root of `message`'s TLV stream.
fn sign_invoice_request(&self, message: &UnsignedInvoiceRequest) -> Result<Signature, ()>;
}

impl<F> SignInvoiceRequestFn for F
where
F: Fn(&UnsignedInvoiceRequest) -> Result<Signature, ()>,
{
fn sign_invoice_request(&self, message: &UnsignedInvoiceRequest) -> Result<Signature, ()> {
self(message)
}
}

impl<F> SignFn<UnsignedInvoiceRequest> for F
where
F: SignInvoiceRequestFn,
{
fn sign(&self, message: &UnsignedInvoiceRequest) -> Result<Signature, ()> {
self.sign_invoice_request(message)
}
}
Copy link

Choose a reason for hiding this comment

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

The trait SignInvoiceRequestFn and its implementation for function pointers are correctly defined to abstract the signing functionality for UnsignedInvoiceRequest. However, using a unit type (()) for errors might limit the ability to convey meaningful error information to the caller. Consider introducing a more descriptive error type if there are multiple reasons why signing could fail.

Consider defining a more descriptive error type for signing failures.

pub trait SignInvoiceRequestFn {
    fn sign_invoice_request(&self, message: &UnsignedInvoiceRequest) -> Result<Signature, SignError>;
}

Comment on lines 239 to 272

Self::new(&refund.bytes, contents, ExplicitSigningPubkey {})
}
}

impl<'a> InvoiceBuilder<'a, DerivedSigningPubkey> {
/// Builds an unsigned [`Bolt12Invoice`] after checking for valid semantics. It can be signed by
/// [`UnsignedBolt12Invoice::sign`].
pub fn build($self: $self_type) -> Result<UnsignedBolt12Invoice, Bolt12SemanticError> {
#[cfg(feature = "std")] {
if $self.invoice.is_offer_or_refund_expired() {
return Err(Bolt12SemanticError::AlreadyExpired);
}
}

#[cfg(not(feature = "std"))] {
if $self.invoice.is_offer_or_refund_expired_no_std($self.invoice.created_at()) {
return Err(Bolt12SemanticError::AlreadyExpired);
}
}

let Self { invreq_bytes, invoice, .. } = $self;
#[cfg(not(c_bindings))] {
Ok(UnsignedBolt12Invoice::new(invreq_bytes, invoice))
}
#[cfg(c_bindings)] {
Ok(UnsignedBolt12Invoice::new(invreq_bytes, invoice.clone()))
}
}
} }

macro_rules! invoice_derived_signing_pubkey_builder_methods { ($self: ident, $self_type: ty) => {
#[cfg_attr(c_bindings, allow(dead_code))]
pub(super) fn for_offer_using_keys(
invoice_request: &'a InvoiceRequest, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>,
created_at: Duration, payment_hash: PaymentHash, keys: KeyPair
Copy link

Choose a reason for hiding this comment

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

📝 NOTE
This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [209-266]

The macro invoice_explicit_signing_pubkey_builder_methods effectively encapsulates methods specific to building Bolt12Invoice objects with an explicit signing public key. A few points to consider:

  • The macro uses #[cfg_attr(c_bindings, allow(dead_code))] to suppress unused code warnings when compiling with C-bindings. This approach is practical but ensure it's consistently applied across all relevant methods to maintain clean compilation logs.
  • The separation of methods into for_offer and for_refund provides clear pathways for building invoices based on different flows, which is a good design choice. However, there's a significant amount of code duplication between these methods and their counterparts in invoice_derived_signing_pubkey_builder_methods. Consider if there's a way to abstract common logic into shared functions or macros to reduce duplication and simplify maintenance.
  • The build method's conditional compilation for handling c_bindings and non-c_bindings scenarios is well-handled. It ensures compatibility while addressing the need to clone the invoice contents when necessary. Just double-check that this cloning does not introduce performance issues or unintended side effects in the C-bindings context.

Overall, the macro is well-designed and serves its purpose effectively. Pay attention to potential code duplication and the implications of conditional compilation on code maintainability and performance.

Explore ways to reduce code duplication between the for_offer and for_refund methods and their counterparts in invoice_derived_signing_pubkey_builder_methods. Abstracting common logic into shared functions or macros could simplify maintenance and enhance readability.

Comment on lines 299 to 341

Self::new(&refund.bytes, contents, DerivedSigningPubkey(keys))
}
}

impl<'a, S: SigningPubkeyStrategy> InvoiceBuilder<'a, S> {
/// Builds a signed [`Bolt12Invoice`] after checking for valid semantics.
pub fn build_and_sign<T: secp256k1::Signing>(
$self: $self_type, secp_ctx: &Secp256k1<T>
) -> Result<Bolt12Invoice, Bolt12SemanticError> {
#[cfg(feature = "std")] {
if $self.invoice.is_offer_or_refund_expired() {
return Err(Bolt12SemanticError::AlreadyExpired);
}
}

#[cfg(not(feature = "std"))] {
if $self.invoice.is_offer_or_refund_expired_no_std($self.invoice.created_at()) {
return Err(Bolt12SemanticError::AlreadyExpired);
}
}

let Self {
invreq_bytes, invoice, signing_pubkey_strategy: DerivedSigningPubkey(keys)
} = $self;
#[cfg(not(c_bindings))]
let unsigned_invoice = UnsignedBolt12Invoice::new(invreq_bytes, invoice);
#[cfg(c_bindings)]
let mut unsigned_invoice = UnsignedBolt12Invoice::new(invreq_bytes, invoice.clone());

let invoice = unsigned_invoice
.sign(|message: &UnsignedBolt12Invoice|
Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
)
.unwrap();
Ok(invoice)
}
} }

macro_rules! invoice_builder_methods { (
$self: ident, $self_type: ty, $return_type: ty, $return_value: expr, $type_param: ty $(, $self_mut: tt)?
) => {
pub(crate) fn amount_msats(
invoice_request: &InvoiceRequest
) -> Result<u64, Bolt12SemanticError> {
Copy link

Choose a reason for hiding this comment

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

📝 NOTE
This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [268-334]

The invoice_derived_signing_pubkey_builder_methods macro complements the explicit signing pubkey builder by providing methods for building Bolt12Invoice objects using derived signing public keys. Similar to the previous macro, here are some considerations:

  • The structure and purpose of this macro mirror those of invoice_explicit_signing_pubkey_builder_methods, focusing instead on derived signing keys. The consistency in design between these macros is good for maintainability and understanding the codebase.
  • Again, there's noticeable code duplication, especially in the structure of the for_offer_using_keys and for_refund_using_keys methods compared to their explicit counterparts. This reinforces the suggestion to find ways to abstract common logic and reduce duplication.
  • The build_and_sign method introduces a unique aspect of directly building and signing the invoice within the same step, which is specific to the derived signing pubkey scenario. This method's design is logical, but ensure that the signing process's error handling is robust and clear to the caller.

Consider the previous advice on reducing code duplication and ensuring comprehensive documentation, especially regarding the unique aspects of using derived signing keys.

Address the code duplication between this macro and invoice_explicit_signing_pubkey_builder_methods by abstracting common logic. Ensure robust error handling in the build_and_sign method, particularly for the signing process.

@valentinewallace valentinewallace merged commit 670b41a into lightningdevkit:main Mar 8, 2024
15 of 16 checks passed
@jkczyz jkczyz mentioned this pull request Mar 22, 2024
60 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.

4 participants