Skip to content

Commit

Permalink
Merge pull request fedimint#6284 from joschisan/lnv2_cache
Browse files Browse the repository at this point in the history
chore: test gateway cache and selection
  • Loading branch information
joschisan authored Nov 5, 2024
2 parents 9cd3ac3 + da0f859 commit cb0da5a
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 41 deletions.
6 changes: 3 additions & 3 deletions modules/fedimint-lnv2-client/src/api.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::collections::BTreeMap;
use std::collections::{BTreeMap, BTreeSet};
use std::time::Duration;

use bitcoin30::secp256k1;
Expand All @@ -19,7 +19,6 @@ use fedimint_lnv2_common::endpoint_constants::{
SEND_PAYMENT_ENDPOINT,
};
use fedimint_lnv2_common::ContractId;
use itertools::Itertools;
use lightning_invoice::Bolt11Invoice;
use rand::seq::SliceRandom;
use secp256k1::schnorr::Signature;
Expand Down Expand Up @@ -126,8 +125,9 @@ where
let mut union = gateways
.values()
.flatten()
.dedup()
.cloned()
.collect::<BTreeSet<SafeUrl>>()
.into_iter()
.collect::<Vec<SafeUrl>>();

// Shuffling the gateways ensures that payments are distributed over the
Expand Down
5 changes: 5 additions & 0 deletions modules/fedimint-lnv2-client/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ enum Opts {

#[derive(Clone, Subcommand, Serialize)]
enum GatewayOpts {
/// Update cache of gateway information to optimise gateway selection for a
/// given invoice.
Cache,
/// Select an online vetted gateway; this command is intended for testing.
Select {
#[arg(long)]
Expand Down Expand Up @@ -79,6 +82,8 @@ pub(crate) async fn handle_cli_command(
),
Opts::AwaitReceive { operation_id } => json(lightning.await_receive(operation_id).await?),
Opts::Gateway(gateway_opts) => match gateway_opts {
#[allow(clippy::unit_arg)]
GatewayOpts::Cache => json(lightning.update_gateway_cache().await),
GatewayOpts::Select { invoice } => json(lightning.select_gateway(invoice).await?.0),
GatewayOpts::List { peer } => match peer {
Some(peer) => json(lightning.module_api.gateways_from_peer(peer).await?),
Expand Down
36 changes: 15 additions & 21 deletions modules/fedimint-lnv2-client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ mod send_sm;

use std::collections::BTreeMap;
use std::sync::Arc;
use std::time::Duration;

use api::{GatewayConnection, RealGatewayConnection};
use async_stream::stream;
Expand Down Expand Up @@ -41,7 +40,6 @@ use fedimint_core::encoding::{Decodable, Encodable};
use fedimint_core::module::{
ApiAuth, ApiVersion, CommonModuleInit, ModuleCommon, ModuleInit, MultiApiVersion,
};
use fedimint_core::task::sleep;
use fedimint_core::time::duration_since_epoch;
use fedimint_core::util::SafeUrl;
use fedimint_core::{apply, async_trait_maybe_send, Amount, OutPoint, PeerId, TransactionId};
Expand Down Expand Up @@ -447,29 +445,25 @@ fn generate_ephemeral_tweak(static_pk: PublicKey) -> ([u8; 32], PublicKey) {
}

impl LightningClientModule {
/// This method updates the mapping from lightning node public keys to
/// gateway api endpoints maintained in the module database once a day. When
/// paying an invoice this enables the client to select the gateway that has
/// created the invoice, if possible, such that the payment does not go
/// over lightning, reducing fees and latency.
/// Updates the mapping from lightning node public keys to gateway api
/// endpoints maintained in the module database. When paying an invoice this
/// enables the client to select the gateway that has created the invoice,
/// if possible, such that the payment does not go over lightning, reducing
/// fees and latency.
///
/// Client integrators are expected to call this function in a spawned task.
pub async fn update_gateway_map(&self) -> ! {
loop {
if let Ok(gateways) = self.module_api.gateways().await {
let mut dbtx = self.client_ctx.module_db().begin_transaction().await;

for gateway in gateways {
if let Ok(Some(routing_info)) = self.routing_info(&gateway).await {
dbtx.insert_entry(&GatewayKey(routing_info.lightning_public_key), &gateway)
.await;
}
}
/// Client integrators are expected to call this function once a day.
pub async fn update_gateway_cache(&self) {
if let Ok(gateways) = self.module_api.gateways().await {
let mut dbtx = self.client_ctx.module_db().begin_transaction().await;

dbtx.commit_tx().await;
for gateway in gateways {
if let Ok(Some(routing_info)) = self.routing_info(&gateway).await {
dbtx.insert_entry(&GatewayKey(routing_info.lightning_public_key), &gateway)
.await;
}
}

sleep(Duration::from_secs(24 * 60 * 60)).await;
dbtx.commit_tx().await;
}
}

Expand Down
69 changes: 52 additions & 17 deletions modules/fedimint-lnv2-tests/src/bin/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ use devimint::federation::Client;
use devimint::version_constants::VERSION_0_5_0_ALPHA;
use devimint::{cmd, util};
use fedimint_core::core::OperationId;
use fedimint_core::util::SafeUrl;
use fedimint_lnv2_client::{FinalReceiveState, FinalSendState};
use lightning_invoice::Bolt11Invoice;
use substring::Substring;
Expand Down Expand Up @@ -56,24 +55,27 @@ async fn main() -> anyhow::Result<()> {
}

async fn test_gateway_registration(dev_fed: &DevJitFed) -> anyhow::Result<()> {
info!("Testing gateway registration...");

let client = dev_fed
.fed()
.await?
.new_joined_client("lnv2-test-gateway-registration-client")
.await?;

let gw_lnd = dev_fed.gw_lnd().await?;
let gw_ldk = dev_fed
.gw_ldk_connected()
.await?
.as_ref()
.expect("Gateways of version 0.5.0 or higher support LDK");

let gateway = SafeUrl::parse(&gw_ldk.addr).expect("LDK gateway address is invalid url");
let gateways = [gw_lnd.addr.clone(), gw_ldk.addr.clone()];

for peer in 0..dev_fed.fed().await?.members.len() {
assert!(add_gateway(&client, peer, &gateway.to_string()).await?);
info!("Testing registration of gateways...");

for gateway in &gateways {
for peer in 0..dev_fed.fed().await?.members.len() {
assert!(add_gateway(&client, peer, gateway).await?);
}
}

assert_eq!(
Expand All @@ -83,7 +85,7 @@ async fn test_gateway_registration(dev_fed: &DevJitFed) -> anyhow::Result<()> {
.as_array()
.expect("JSON Value is not an array")
.len(),
1
2
);

assert_eq!(
Expand All @@ -93,20 +95,53 @@ async fn test_gateway_registration(dev_fed: &DevJitFed) -> anyhow::Result<()> {
.as_array()
.expect("JSON Value is not an array")
.len(),
1
2
);

assert_eq!(
cmd!(client, "module", "lnv2", "gateway", "select")
info!("Testing selection of gateways...");

assert!(gateways.contains(
&cmd!(client, "module", "lnv2", "gateway", "select")
.out_json()
.await?
.as_str()
.expect("JSON Value is not a string"),
gateway.to_string().as_str()
);
.expect("JSON Value is not a string")
.to_string()
));

cmd!(client, "module", "lnv2", "gateway", "cache")
.out_json()
.await?;

for _ in 0..10 {
for gateway in &gateways {
let invoice = receive(&client, gateway, 1_000_000).await?.0;

assert_eq!(
cmd!(
client,
"module",
"lnv2",
"gateway",
"select",
"--invoice",
invoice.to_string()
)
.out_json()
.await?
.as_str()
.expect("JSON Value is not a string"),
gateway
)
}
}

info!("Testing deregistration of gateways...");

for peer in 0..dev_fed.fed().await?.members.len() {
assert!(remove_gateway(&client, peer, &gateway).await?);
for gateway in &gateways {
for peer in 0..dev_fed.fed().await?.members.len() {
assert!(remove_gateway(&client, peer, gateway).await?);
}
}

assert!(cmd!(client, "module", "lnv2", "gateway", "list")
Expand Down Expand Up @@ -255,7 +290,7 @@ async fn add_gateway(client: &Client, peer: usize, gateway: &String) -> anyhow::
.ok_or(anyhow::anyhow!("JSON Value is not a boolean"))
}

async fn remove_gateway(client: &Client, peer: usize, gateway: &SafeUrl) -> anyhow::Result<bool> {
async fn remove_gateway(client: &Client, peer: usize, gateway: &String) -> anyhow::Result<bool> {
cmd!(
client,
"--our-id",
Expand All @@ -266,7 +301,7 @@ async fn remove_gateway(client: &Client, peer: usize, gateway: &SafeUrl) -> anyh
"lnv2",
"gateway",
"remove",
gateway.to_string()
gateway
)
.out_json()
.await?
Expand Down

0 comments on commit cb0da5a

Please sign in to comment.