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

Add LND Integration Tests in CI #509

Merged
merged 3 commits into from
Mar 31, 2025
Merged

Conversation

moisesPompilio
Copy link
Contributor

This PR introduces integration tests for LND, mirroring the existing CLN tests. Here's what was done:

  • Dockerized LND Node: Set up a Docker container to run an LND node in CI, configured to work with LDK-Node.

  • Rust Library Choice: Used lnd-grpc-rust instead of tonic_lnd, as it’s more up-to-date and compatible with recent LND versions.

  • Test Implementation:

    • Opened a channel with LND.
    • Generated an invoice on LND and paid it with LDK.
    • Generated an invoice on LDK and paid it with LND.
    • Closed the channel from both sides.
  • CI Workflow: Added a workflow to the CI pipeline to run the LND tests.

close #505

@tnull tnull self-requested a review March 26, 2025 08:12
Copy link
Collaborator

@tnull tnull left a comment

Choose a reason for hiding this comment

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

Cool, thank you for looking into this! Already looks pretty good!

run: docker compose -f docker-compose-lnd.yml up -d

- name: Set permissions for data_lnd folder
run: sudo chmod -R 755 ./data_lnd/
Copy link
Collaborator

Choose a reason for hiding this comment

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

Curious to learn why we need this? Doesn't LND set the correct permissions already?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In PR 4622, LND added automatic file creation. However, it sets the file permissions to 0700, which prevents the test code from reading the files. As a result, it's necessary to explicitly modify the permissions for the test code to access them.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I see, makes sense. Mind adding that context as a comment 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.

Yes, I added a comment to provide that context.

Copy link
Contributor

@elnosh elnosh left a comment

Choose a reason for hiding this comment

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

I ran the test locally and passed. Although I had to change the permissions for data_lnd directory. Maybe those could be set in the docker-compose-lnd.yml instead of only CI?

Sets up a local environment for LND integration testing in CI

Closes 505
@moisesPompilio
Copy link
Contributor Author

I ran the test locally and passed. Although I had to change the permissions for data_lnd directory. Maybe those could be set in the docker-compose-lnd.yml instead of only CI?

I think it's better for the user or CI to set the permissions, as modifying file permissions automatically via Docker can be invasive and might require sudo. Now, the user or CI specifies the directory for LND files, allowing them to decide where the files will be stored and handle permission modifications as needed.

Copy link
Collaborator

@tnull tnull left a comment

Choose a reason for hiding this comment

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

Already looks pretty good, just some minor comments.

let cert = cert_bytes.as_hex().to_string();
let macaroon = mac_bytes.as_hex().to_string();

let client = connect(cert, macaroon, socket).await?;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Given this is test code, can we just expect here and drop the Result from the return type?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, I removed the Result and added the expect with indications of where they failed.

use ldk_node::{Builder, Event};

use lnd_grpc_rust::lnrpc::{
invoice::InvoiceState::Settled, GetInfoRequest, GetInfoResponse, Invoice, ListInvoiceRequest,
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 prefix all these imports with Lnd to make it easier to parse which type comes from 'which side'? I.e., make all of these Invoice as LndInvoice, etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, I updated the imports and prefixed them with Lnd

run: docker compose -f docker-compose-lnd.yml up -d

- name: Set permissions for data_lnd folder
run: sudo chmod -R 755 ./data_lnd/
Copy link
Collaborator

Choose a reason for hiding this comment

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

I see, makes sense. Mind adding that context as a comment here?

tokio::time::sleep(Duration::from_secs(1)).await;

// Send a payment to LDK
let mut rng = thread_rng();
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 don't need this to be random at all, no? Let's just make up a fixed string 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.

Ok, I removed the RNG and replaced it with a fixed string.

assert_eq!(lnd_listed_invoices.first().unwrap().state, Settled as i32);

// Sleep to process payment in LND
tokio::time::sleep(Duration::from_secs(1)).await;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Such sleeps tend to be or get flaky in CI. Rather than just sleeping a fixed amount here, any chance we can poll repeatedly in smaller intervals until we see the payment processed, or reach a max number of tries?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, I replaced the sleep with a retry mechanism (max 25 attempts) since my tests showed variance up to 20. This avoids CI flakiness. I also improved error checking to verify if the invoice was actually paid.

@moisesPompilio moisesPompilio force-pushed the issue_505 branch 2 times, most recently from dd4bb77 to 6f1017c Compare March 27, 2025 19:26
@tnull tnull removed the request for review from joostjager March 28, 2025 09:09
Copy link
Collaborator

@tnull tnull left a comment

Choose a reason for hiding this comment

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

Thanks for addressing the feedback, one comment.

// This retries the payment multiple times to account for that delay.
let max_retries = 25;

for attempt in 1..=max_retries {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Thanks for adding this retry logic, but it's a bit weird to have it be part of pay_invoice, given that we're waiting on the previous operation to complete essentially. Rather than just always retrying here, can we instead retry checking the incoming payment settled at the callsite, and then move on to pay_invoice when we're positive it should succeed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, after reviewing the process, I realized the payment is registered as paid but not immediately available for routing. To address this, I added a check to ensure the payment can be routed through the channel. Since it takes around 0.6 to 1.1 seconds for this to happen, I added 7 retries with 200ms intervals, totaling a maximum wait time of 1.4 seconds to ensure the balance is available for routing.

env:
LND_DATA_DIR: ${{ env.LND_DATA_DIR }}

# In PR 4622 (https://github.com/lightningnetwork/lnd/pull/4622),
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: Thanks for adding comment, but let's move it down into the item this is meant for.

Adds tests to:
- Open a payment channel with LND.
- Request and pay an invoice, verifying receipt.
- Generate an invoice for LND to pay, verifying receipt.
Uses lnd_grpc_rust for gRPC communication.

Closes lightningdevkit#505
Configures CI to run LND tests using the Docker Compose environment.

Closes lightningdevkit#505
Copy link
Collaborator

@tnull tnull left a comment

Choose a reason for hiding this comment

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

LGTM, thank you!

@tnull tnull merged commit a6cb7a3 into lightningdevkit:main Mar 31, 2025
28 of 62 checks passed
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.

Setup initial integration tests with LND
3 participants