Skip to content

Eclair node support #225

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

Merged
merged 1 commit into from
Mar 18, 2025
Merged

Conversation

alexzaitsev
Copy link
Contributor

Added Eclair node support. Tracking issue: #26

Tested on local Polar network with different use cases.

@carlaKC carlaKC requested review from carlaKC and f3r10 March 3, 2025 13:18
@carlaKC
Copy link
Contributor

carlaKC commented Mar 3, 2025

Thanks for the PR, awesome stuff! On my list to review this week 🫡

Small CI nitpick - make check failing, just needs a cargo fmt!

@alexzaitsev
Copy link
Contributor Author

@carlaKC should be good now. Should I squash every time I push some fix?

@carlaKC
Copy link
Contributor

carlaKC commented Mar 3, 2025

@carlaKC should be good now. Should I squash every time I push some fix?

My personal preference is to use fixup commits while review is ongoing and then squash once the PR is ready to merge.
In this case, feel free to squash since it's just a formatting change and nobody has reviewed.

@alexzaitsev
Copy link
Contributor Author

I squshed. I'm not aware about fixup commits, will study this. Thank you

@alexzaitsev alexzaitsev requested a review from f3r10 March 4, 2025 02:56
@f3r10
Copy link
Collaborator

f3r10 commented Mar 5, 2025

hi @alexzaitsev thanks for the PR. I am trying to run it with polar but I am getting this error:
Config validation failed: Destination node does not support keysend
Do you know how to enable keysend on Eclair with polar 🤔 ?

Copy link
Collaborator

@f3r10 f3r10 left a comment

Choose a reason for hiding this comment

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

Tested ACK: I tested it using Polar and it works great.

@alexzaitsev alexzaitsev requested a review from f3r10 March 6, 2025 16:40
Copy link
Contributor

@carlaKC carlaKC 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 an awesome first PR! I've wanted this feature for a while, so it's great to finally have it on the way :)

Just some low hanging fruit from me about how we're structuring the EclairNode, but looks great overall.

README.md Outdated
Comment on lines 58 to 59
enabled in LND using `--accept-keysend` (for CLN node it is enabled by default).
Use `--features.keysend=optional` to enable keysend in Eclair.
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: Now that we've got another node let's make this a list to improve readability, something like:

which must be enabed as follows:
* LND: `--accept-keysend`
* Eclair: ...

Copy link
Contributor Author

@alexzaitsev alexzaitsev Mar 6, 2025

Choose a reason for hiding this comment

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

Done: 185d020
I also moved this info to prerequisites section where it should be IMO. Let me know if you have any objections.

}

impl EclairConnection {
fn construct_url(&self, endpoint: &str) -> anyhow::Result<Url> {
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's use a regular Result in this module? We're only using anyhow in main at the moment and I'd actually like to get rid of it in future.

Copy link
Contributor Author

@alexzaitsev alexzaitsev Mar 7, 2025

Choose a reason for hiding this comment

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

Done: f0e1c2f
I used a trait object so I don't need to import url just to return its error from construct_url

}

pub struct EclairNode {
connection: EclairConnection,
Copy link
Contributor

Choose a reason for hiding this comment

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

Thoughts on adding a constructor that accepts the fields from EclairConnection and storing the construct_url and basic_auth values in EclairNode instead of saving the connection details to DRY things up a bit?

That way we don't have to keep a duplicate NodeID (connection+ NodeInfo) and re-construct base_url/

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Addressed: 5f2457c

Comment on lines 177 to 193
// Task: https://github.com/bitcoin-dev-project/sim-ln/issues/26#issuecomment-1691780018
PaymentStatus::Failed => PaymentOutcome::UnexpectedError,
Copy link
Contributor

Choose a reason for hiding this comment

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

Not following the comment linked to here with this line of code?

Does this status from eclair mean that the API call failed?

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 followed other implementations in this codebase. In this particular case here is respective CLN code. Basically, as whole range of errors is not well documented (comparing to LND GRPC, for example), I followed CLN node implementation approach and returning UnexpectedError here. For consistency, I copied this comment as well.

Copy link
Contributor

Choose a reason for hiding this comment

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

Cool got it, could you please expand the comment to just briefly say that PaymentStatus::Failed means that the API call failed and then link to the issue for additional context?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure 9c028fb

Ok(response)
}

async fn request<T: for<'de> Deserialize<'de>>(
Copy link
Contributor

Choose a reason for hiding this comment

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

The division in duties of this wrapping function and request_static weren't immediately obvious to be, could we either add a comment to explain the need for this or move the connection details into its own client struct and implement request on it?

Could work well with the request above, EclairNode then just holds a client / info / network - this is how we have it for LndNode, for example.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

request_static is used in the constructor, this is the only reason why it exists. Otherwise, it will be only request. After all the refactoring it looks a bit cleaner so I won't go deeper for now and leave it as it is. If after second review you still find this can be improved, please let me know and I'll handle it.

Copy link
Contributor

Choose a reason for hiding this comment

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

I was thinking something like this patch to pull the request details out into their own struct, so that the EclairNode's single responsibility is implementing LightningNode trait.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

makes sense! Done 0157beb

Copy link
Contributor

Choose a reason for hiding this comment

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

We can simplify this now that we've got a EclairClient type:

impl EclairClient {
    async fn request<T: for<'de> Deserialize<'de>>(
		&self,
        endpoint: &str,
        params: Option<HashMap<String, String>>,
    ) -> Result<T, Box<dyn Error>> {
        let url = self.base_url.join(endpoint)?;
        let mut request = self.http_client
            .request(Method::POST, url)
            .basic_auth(self.api_username.clone(), Some(self.api_password.clone()));

        if let Some(params) = params {
            let mut form = Form::new();
            for (key, value) in params {
                form = form.text(key, value);
            }
            request = request.multipart(form);
        }

        let response = request
            .send()
            .await?
            .error_for_status()?
            .json::<T>()
            .await?;

        Ok(response)
    }
}

@alexzaitsev alexzaitsev requested a review from carlaKC March 7, 2025 05:43
Copy link
Contributor

@carlaKC carlaKC left a comment

Choose a reason for hiding this comment

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

close close close 🔥

README.md Outdated
```
{
"id": <node_id>,
"base_url": <scheme://ip:port or scheme://domain:port>,
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: remove scheme:// here and http: below so that the readme has the simplest example possible

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Addressed: be85f5b
I also did some light cleanup and moved list of implementations to be above the prerequisites (it's more important IMO). Let me know if any of these changes doesn't work for you.

Ok(response)
}

async fn request<T: for<'de> Deserialize<'de>>(
Copy link
Contributor

Choose a reason for hiding this comment

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

I was thinking something like this patch to pull the request details out into their own struct, so that the EclairNode's single responsibility is implementing LightningNode trait.

Comment on lines 177 to 193
// Task: https://github.com/bitcoin-dev-project/sim-ln/issues/26#issuecomment-1691780018
PaymentStatus::Failed => PaymentOutcome::UnexpectedError,
Copy link
Contributor

Choose a reason for hiding this comment

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

Cool got it, could you please expand the comment to just briefly say that PaymentStatus::Failed means that the API call failed and then link to the issue for additional context?

Copy link
Collaborator

@f3r10 f3r10 left a comment

Choose a reason for hiding this comment

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

I did more tests.

  1. A defined activity between two nodes where only one has all the liquidity. ✅
DEBUG [simln_lib] Send payment: bob(02c560...045557) -> alice(020717...b16e7c): (94c34420ce56e43289c4e761c30e76b5136b02da432d4f25efaf9725dd221bfb).
DEBUG [simln_lib] Tracking payment outcome for: 94c34420ce56e43289c4e761c30e76b5136b02da432d4f25efaf9725dd221bfb.
DEBUG [simln_lib] Track payment 94c34420ce56e43289c4e761c30e76b5136b02da432d4f25efaf9725dd221bfb result: UnexpectedError.
DEBUG [simln_lib] Generated payment: alice(020717...b16e7c) -> bob(02c560...045557): 1000 msat.
DEBUG [simln_lib] Next payment for alice(020717...b16e7c) in 3s.

The failure case: when the node without liquidity sends a payment is handled correctly.

  1. Using the same two nodes I tried to run a random activity ❌
    There is a problem here because it looks like async fn list_channels does not return the defined channels between the nodes.
Node: 020717539a3ad606adb68107c7f9c5abf0fc91e37f13d461ca1949b71546b16e7c not eligible for activity generation: InsufficientCapacity: node needs at least 7600000 capacity (has: 0) to process expected payment amount: 3800000

@alexzaitsev could you check this case from your side, please?

@alexzaitsev
Copy link
Contributor Author

@f3r10 here is dave - heidi channel where all the liquidity is on the dave's side:
Screenshot 2025-03-09 at 21 48 45

I'm getting next output:

2025-03-10T03:48:04.857Z INFO  [sim_cli] Connected to dave - Node ID: 03e8f31de94e6aac1b8de359957e2d07bf89a180d91fb06c3af738bfc836715c56.
2025-03-10T03:48:04.887Z INFO  [sim_cli] Connected to heidi - Node ID: 0233853f612562ae7fd66b496c1ca9eb2e5949c6f3d262cf3a9ed6a22b1077b568.
2025-03-10T03:48:04.887Z INFO  [simln_lib] Running the simulation forever.
2025-03-10T03:48:04.887Z INFO  [simln_lib] Simulation is running on regtest.
2025-03-10T03:48:04.887Z INFO  [simln_lib] Simulating 0 activity on 2 nodes.
2025-03-10T03:48:04.887Z DEBUG [simln_lib] Setting up simulator data collection.
2025-03-10T03:48:04.888Z DEBUG [simln_lib] Simulator data collection set up.
2025-03-10T03:48:04.888Z DEBUG [simln_lib] Starting simulation results consumer.
2025-03-10T03:48:04.888Z DEBUG [simln_lib] Starting results logger.
2025-03-10T03:48:04.888Z DEBUG [simln_lib] Starting simulation results producer.
2025-03-10T03:48:04.888Z INFO  [simln_lib] Summary of results will be reported every 60s.
2025-03-10T03:48:04.903Z WARN  [simln_lib] Node: 03e8f31de94e6aac1b8de359957e2d07bf89a180d91fb06c3af738bfc836715c56 not eligible for activity generation: InsufficientCapacity: node needs at least 7600000 capacity (has: 0) to process expected payment amount: 3800000.
2025-03-10T03:48:04.935Z WARN  [simln_lib] Node: 0233853f612562ae7fd66b496c1ca9eb2e5949c6f3d262cf3a9ed6a22b1077b568 not eligible for activity generation: InsufficientCapacity: node needs at least 7600000 capacity (has: 0) to process expected payment amount: 3800000.
2025-03-10T03:48:04.935Z DEBUG [simln_lib] Simulation results producer exiting.
2025-03-10T03:48:04.935Z DEBUG [simln_lib] Produce simulation results received shutdown signal.
2025-03-10T03:48:04.935Z DEBUG [simln_lib] Exiting results logger.
2025-03-10T03:48:04.935Z DEBUG [simln_lib] Consume simulation result received shutdown signal.
Error: Value error: at least two nodes required for activity generation

Stack backtrace:
   0: std::backtrace_rs::backtrace::libunwind::trace
             at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/../../backtrace/src/backtrace/libunwind.rs:116:5
   1: std::backtrace_rs::backtrace::trace_unsynchronized
             at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/../../backtrace/src/backtrace/mod.rs:66:5
   2: std::backtrace::Backtrace::create
             at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/backtrace.rs:331:13
   3: anyhow::error::<impl core::convert::From<E> for anyhow::Error>::from
             at /Users/p3ngu1n/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/anyhow-1.0.95/src/backtrace.rs:27:14
   4: <core::result::Result<T,F> as core::ops::try_trait::FromResidual<core::result::Result<core::convert::Infallible,E>>>::from_residual
             at /Users/p3ngu1n/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/result.rs:2014:27
   5: sim_cli::main::{{closure}}
             at ./sim-cli/src/main.rs:231:5
   6: <core::pin::Pin<P> as core::future::future::Future>::poll
             at /Users/p3ngu1n/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/future/future.rs:124:9
   7: tokio::runtime::park::CachedParkThread::block_on::{{closure}}
             at /Users/p3ngu1n/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.43.0/src/runtime/park.rs:284:63
   8: tokio::runtime::coop::with_budget
             at /Users/p3ngu1n/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.43.0/src/runtime/coop.rs:107:5
   9: tokio::runtime::coop::budget
             at /Users/p3ngu1n/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.43.0/src/runtime/coop.rs:73:5
  10: tokio::runtime::park::CachedParkThread::block_on
             at /Users/p3ngu1n/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.43.0/src/runtime/park.rs:284:31
  11: tokio::runtime::context::blocking::BlockingRegionGuard::block_on
             at /Users/p3ngu1n/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.43.0/src/runtime/context/blocking.rs:66:9
  12: tokio::runtime::scheduler::multi_thread::MultiThread::block_on::{{closure}}
             at /Users/p3ngu1n/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.43.0/src/runtime/scheduler/multi_thread/mod.rs:87:13
  13: tokio::runtime::context::runtime::enter_runtime
             at /Users/p3ngu1n/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.43.0/src/runtime/context/runtime.rs:65:16
  14: tokio::runtime::scheduler::multi_thread::MultiThread::block_on
             at /Users/p3ngu1n/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.43.0/src/runtime/scheduler/multi_thread/mod.rs:86:9
  15: tokio::runtime::runtime::Runtime::block_on_inner
             at /Users/p3ngu1n/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.43.0/src/runtime/runtime.rs:370:45
  16: tokio::runtime::runtime::Runtime::block_on
             at /Users/p3ngu1n/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.43.0/src/runtime/runtime.rs:340:13
  17: sim_cli::main
             at ./sim-cli/src/main.rs:233:5
  18: core::ops::function::FnOnce::call_once
             at /Users/p3ngu1n/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/ops/function.rs:250:5
  19: std::sys::backtrace::__rust_begin_short_backtrace
             at /Users/p3ngu1n/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/src/rust/library/std/src/sys/backtrace.rs:152:18
  20: std::rt::lang_start::{{closure}}
             at /Users/p3ngu1n/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/src/rust/library/std/src/rt.rs:195:18
  21: core::ops::function::impls::<impl core::ops::function::FnOnce<A> for &F>::call_once
             at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/core/src/ops/function.rs:284:13
  22: std::panicking::try::do_call
             at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/panicking.rs:584:40
  23: std::panicking::try
             at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/panicking.rs:547:19
  24: std::panic::catch_unwind
             at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/panic.rs:358:14
  25: std::rt::lang_start_internal::{{closure}}
             at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/rt.rs:174:48
  26: std::panicking::try::do_call
             at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/panicking.rs:584:40
  27: std::panicking::try
             at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/panicking.rs:547:19
  28: std::panic::catch_unwind
             at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/panic.rs:358:14
  29: std::rt::lang_start_internal
             at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/rt.rs:174:20
  30: std::rt::lang_start
             at /Users/p3ngu1n/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/src/rust/library/std/src/rt.rs:194:17
  31: _main

Process finished with exit code 1

sim.json:

{
    "nodes": [
        {
            "id": "dave",
            "base_url": "http://127.0.0.1:8284",
            "api_username": "",
            "api_password": "eclairpw"
        },
        {
            "id": "heidi",
            "base_url": "http://127.0.0.1:8288",
            "api_username": "",
            "api_password": "eclairpw"
        }
    ]
}

Seems like I have another output? I'm confused.

@alexzaitsev
Copy link
Contributor Author

Ok, I see the issue, I can confirm list_channels returns [] because channels list is empty. I'm working on the fix.

@alexzaitsev
Copy link
Contributor Author

alexzaitsev commented Mar 11, 2025

@f3r10 I applied a few fixes and it should be good now, could you retest?

2025-03-11T04:25:55.315Z INFO  [sim_cli] Connected to niaj - Node ID: 02be1f48019da2b41bb631fb8777208e9f9c2234a9729ba9b16d31b8020de5e0e5.
2025-03-11T04:25:55.332Z INFO  [sim_cli] Connected to mike - Node ID: 026e3bcb2a3daa1b93122ac97496484e01fb8d96715ef8564addc45fc34294a436.
2025-03-11T04:25:55.332Z INFO  [simln_lib] Running the simulation forever.
2025-03-11T04:25:55.332Z INFO  [simln_lib] Simulation is running on regtest.
2025-03-11T04:25:55.332Z INFO  [simln_lib] Simulating 0 activity on 2 nodes.
2025-03-11T04:25:55.332Z DEBUG [simln_lib] Setting up simulator data collection.
2025-03-11T04:25:55.333Z DEBUG [simln_lib] Starting results logger.
2025-03-11T04:25:55.333Z INFO  [simln_lib] Summary of results will be reported every 60s.
2025-03-11T04:25:55.333Z DEBUG [simln_lib] Simulator data collection set up.
2025-03-11T04:25:55.333Z DEBUG [simln_lib] Starting simulation results producer.
2025-03-11T04:25:55.333Z DEBUG [simln_lib] Starting simulation results consumer.
2025-03-11T04:25:55.363Z WARN  [simln_lib] Node: 026e3bcb2a3daa1b93122ac97496484e01fb8d96715ef8564addc45fc34294a436 not eligible for activity generation: InsufficientCapacity: node needs at least 7600000 capacity (has: 1427000) to process expected payment amount: 3800000.
2025-03-11T04:25:55.363Z DEBUG [simln_lib] Exiting results logger.
2025-03-11T04:25:55.363Z DEBUG [simln_lib] Simulation results producer exiting.
2025-03-11T04:25:55.363Z DEBUG [simln_lib] Produce simulation results received shutdown signal.
2025-03-11T04:25:55.363Z DEBUG [simln_lib] Consume simulation result received shutdown signal.
Error: Value error: at least two nodes required for activity generation

I found out that Eclair channels are unstable, especially if you restart those Docker nodes (or whole network). Maybe it's related to Polar, I don't know. It can result in some unexpected behaviour, especially considering the channels look OK in Polar UI.

For example, here is investigation of 2025-03-11T03:46:02.912Z DEBUG [simln_lib] Track payment 514403ff3dea79163540bdb2ed6ff5516da3527957ebc06a8b185ef12b558884 result: UnexpectedError log message:

% curl -s -u :eclairpw -X POST -F paymentHash=514403ff3dea79163540bdb2ed6ff5516da3527957ebc06a8b185ef12b558884 "http://127.0.0.1:8283/getsentinfo"

getsentinfo.json

As you see, it shows "failureMessage": "channel is unavailable (offline or closing)". If I check this node's channels:

% curl -s -u :eclairpw -X POST "http://127.0.0.1:8283/channels"

Indeed shows channels are offline:
channels.json

This is just something to consider as this can be an obstacle in the testing process. For the best experience, I recommend spawning fresh nodes with the fresh channels each time.


Another thing, not related to the current discussion. I noticed pretty often sim-ln tries to send zero amounts:

2025-03-11T03:42:43.464Z DEBUG [simln_lib] Next payment for dave(03e8f3...715c56) in 0ns.
2025-03-11T03:42:43.465Z DEBUG [simln_lib] Skipping zero amount payment for dave(03e8f3...715c56) -> bob(0349e1...7a53e8).
2025-03-11T03:42:43.466Z DEBUG [simln_lib] Next payment for dave(03e8f3...715c56) in 0ns.
2025-03-11T03:42:43.467Z DEBUG [simln_lib] Skipping zero amount payment for dave(03e8f3...715c56) -> bob(0349e1...7a53e8).

Is it expected?

@carlaKC
Copy link
Contributor

carlaKC commented Mar 11, 2025

I noticed pretty often sim-ln tries to send zero amounts:

This is okay! TL;DR on how we generate random activity:

  • We expect each node in the network to send some multiplier of its capacity (-c) in a month; so if your channel is 20k and multiplier is 2, we'll expect your node to send 40k of payments in a month
  • We set a payment amount (-e) which defines the mean payment amount, and then randomly sample around that amount

You can run into this zero-amount behavior when you have channels that are relatively close to the expected payment amount, because to meet the expected amount you don't need to send that many payments. Eg, if the expected payment amount is 10k, your channel will only need to send 4 payments to hit the total it needs to send.

The defaults are set based on mainnet values, so they can be a bit funky in test envs!

@alexzaitsev alexzaitsev requested a review from f3r10 March 11, 2025 23:28
Ok(response)
}

async fn request<T: for<'de> Deserialize<'de>>(
Copy link
Contributor

Choose a reason for hiding this comment

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

We can simplify this now that we've got a EclairClient type:

impl EclairClient {
    async fn request<T: for<'de> Deserialize<'de>>(
		&self,
        endpoint: &str,
        params: Option<HashMap<String, String>>,
    ) -> Result<T, Box<dyn Error>> {
        let url = self.base_url.join(endpoint)?;
        let mut request = self.http_client
            .request(Method::POST, url)
            .basic_auth(self.api_username.clone(), Some(self.api_password.clone()));

        if let Some(params) = params {
            let mut form = Form::new();
            for (key, value) in params {
                form = form.text(key, value);
            }
            request = request.multipart(form);
        }

        let response = request
            .send()
            .await?
            .error_for_status()?
            .json::<T>()
            .await?;

        Ok(response)
    }
}

Copy link
Collaborator

@f3r10 f3r10 left a comment

Choose a reason for hiding this comment

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

I tested the random activity case again and it is working great
image

@alexzaitsev alexzaitsev requested review from f3r10 and carlaKC March 15, 2025 17:26
Copy link
Contributor

@carlaKC carlaKC left a comment

Choose a reason for hiding this comment

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

Very nice! Thanks for going a few rounds on this :)

You can go ahead and squash, then we're ready to hit the button.

@carlaKC
Copy link
Contributor

carlaKC commented Mar 17, 2025

@f3r10 any additional testing you'd like to do here or are we good to go?

Copy link
Collaborator

@f3r10 f3r10 left a comment

Choose a reason for hiding this comment

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

LGTM!!

@carlaKC
Copy link
Contributor

carlaKC commented Mar 17, 2025

Ah sorry @alexzaitsev, gave you some minor conflicts merging #229 - could you rebase and then I'll merge?

@carlaKC carlaKC merged commit 5044fb7 into bitcoin-dev-project:main Mar 18, 2025
2 checks passed
@alexzaitsev alexzaitsev deleted the eclair-support branch March 18, 2025 18:04
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.

3 participants