diff --git a/Cargo.toml b/Cargo.toml index c1cb3e58f..fe07cdffd 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,17 +42,17 @@ default = [] # lightning-macros = { version = "0.2.0" } # Branch: https://github.com/moneydevkit/rust-lightning/commits/lsp-0.2.0/ -lightning = { git = "https://github.com/moneydevkit/rust-lightning", rev = "aad7c226d7e41f319bfb9d79be4219e2abc9f823", features = ["std"] } -lightning-types = { git = "https://github.com/moneydevkit/rust-lightning", rev = "aad7c226d7e41f319bfb9d79be4219e2abc9f823" } -lightning-invoice = { git = "https://github.com/moneydevkit/rust-lightning", rev = "aad7c226d7e41f319bfb9d79be4219e2abc9f823", features = ["std"] } -lightning-net-tokio = { git = "https://github.com/moneydevkit/rust-lightning", rev = "aad7c226d7e41f319bfb9d79be4219e2abc9f823" } -lightning-persister = { git = "https://github.com/moneydevkit/rust-lightning", rev = "aad7c226d7e41f319bfb9d79be4219e2abc9f823", features = ["tokio"] } -lightning-background-processor = { git = "https://github.com/moneydevkit/rust-lightning", rev = "aad7c226d7e41f319bfb9d79be4219e2abc9f823" } -lightning-rapid-gossip-sync = { git = "https://github.com/moneydevkit/rust-lightning", rev = "aad7c226d7e41f319bfb9d79be4219e2abc9f823" } -lightning-block-sync = { git = "https://github.com/moneydevkit/rust-lightning", rev = "aad7c226d7e41f319bfb9d79be4219e2abc9f823", features = ["rest-client", "rpc-client", "tokio"] } -lightning-transaction-sync = { git = "https://github.com/moneydevkit/rust-lightning", rev = "aad7c226d7e41f319bfb9d79be4219e2abc9f823", features = ["esplora-async-https", "time", "electrum-rustls-ring"] } -lightning-liquidity = { git = "https://github.com/moneydevkit/rust-lightning", rev = "aad7c226d7e41f319bfb9d79be4219e2abc9f823", features = ["std"] } -lightning-macros = { git = "https://github.com/moneydevkit/rust-lightning", rev = "aad7c226d7e41f319bfb9d79be4219e2abc9f823" } +lightning = { git = "https://github.com/moneydevkit/rust-lightning", rev = "bdcdf57de38baeabce4bf8486769bc60d0aa0f8d", features = ["std"] } +lightning-types = { git = "https://github.com/moneydevkit/rust-lightning", rev = "bdcdf57de38baeabce4bf8486769bc60d0aa0f8d" } +lightning-invoice = { git = "https://github.com/moneydevkit/rust-lightning", rev = "bdcdf57de38baeabce4bf8486769bc60d0aa0f8d", features = ["std"] } +lightning-net-tokio = { git = "https://github.com/moneydevkit/rust-lightning", rev = "bdcdf57de38baeabce4bf8486769bc60d0aa0f8d" } +lightning-persister = { git = "https://github.com/moneydevkit/rust-lightning", rev = "bdcdf57de38baeabce4bf8486769bc60d0aa0f8d", features = ["tokio"] } +lightning-background-processor = { git = "https://github.com/moneydevkit/rust-lightning", rev = "bdcdf57de38baeabce4bf8486769bc60d0aa0f8d" } +lightning-rapid-gossip-sync = { git = "https://github.com/moneydevkit/rust-lightning", rev = "bdcdf57de38baeabce4bf8486769bc60d0aa0f8d" } +lightning-block-sync = { git = "https://github.com/moneydevkit/rust-lightning", rev = "bdcdf57de38baeabce4bf8486769bc60d0aa0f8d", features = ["rest-client", "rpc-client", "tokio"] } +lightning-transaction-sync = { git = "https://github.com/moneydevkit/rust-lightning", rev = "bdcdf57de38baeabce4bf8486769bc60d0aa0f8d", features = ["esplora-async-https", "time", "electrum-rustls-ring"] } +lightning-liquidity = { git = "https://github.com/moneydevkit/rust-lightning", rev = "bdcdf57de38baeabce4bf8486769bc60d0aa0f8d", features = ["std"] } +lightning-macros = { git = "https://github.com/moneydevkit/rust-lightning", rev = "bdcdf57de38baeabce4bf8486769bc60d0aa0f8d" } #lightning = { path = "../rust-lightning/lightning", features = ["std"] } #lightning-types = { path = "../rust-lightning/lightning-types" } @@ -101,7 +101,7 @@ winapi = { version = "0.3", features = ["winbase"] } [dev-dependencies] # lightning = { version = "0.2.0", features = ["std", "_test_utils"] } # Branch: https://github.com/moneydevkit/rust-lightning/commits/lsp-0.2.0/ -lightning = { git = "https://github.com/moneydevkit/rust-lightning", rev = "aad7c226d7e41f319bfb9d79be4219e2abc9f823", features = ["std", "_test_utils"] } +lightning = { git = "https://github.com/moneydevkit/rust-lightning", rev = "bdcdf57de38baeabce4bf8486769bc60d0aa0f8d", features = ["std", "_test_utils"] } #lightning = { path = "../rust-lightning/lightning", features = ["std", "_test_utils"] } proptest = "1.0.0" regex = "1.5.6" diff --git a/src/builder.rs b/src/builder.rs index 5328b368c..8c358767c 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -489,14 +489,14 @@ impl NodeBuilder { /// /// Will mark the LSP as trusted for 0-confirmation channels, see [`Config::trusted_peers_0conf`]. pub fn set_liquidity_source_lsps4( - &mut self, node_id: PublicKey, address: SocketAddress, + &mut self, node_id: PublicKey, address: SocketAddress, fee_claim: Option, ) -> &mut Self { // Mark the LSP as trusted for 0conf self.config.trusted_peers_0conf.push(node_id.clone()); let liquidity_source_config = self.liquidity_source_config.get_or_insert(LiquiditySourceConfig::default()); - let lsps4_client_config = LSPS4ClientConfig { node_id, address }; + let lsps4_client_config = LSPS4ClientConfig { node_id, address, fee_claim }; liquidity_source_config.lsps4_client = Some(lsps4_client_config); self } @@ -1774,7 +1774,11 @@ fn build_with_store_internal( }); lsc.lsps4_client.as_ref().map(|config| { - liquidity_source_builder.lsps4_client(config.node_id, config.address.clone()) + liquidity_source_builder.lsps4_client( + config.node_id, + config.address.clone(), + config.fee_claim.clone(), + ) }); let promise_secret = { @@ -2053,7 +2057,34 @@ pub(crate) fn sanitize_alias(alias_str: &str) -> Result { #[cfg(test)] mod tests { - use super::{sanitize_alias, BuildError, NodeAlias}; + use super::{sanitize_alias, BuildError, NodeAlias, NodeBuilder}; + use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey}; + use lightning::ln::msgs::SocketAddress; + + fn dummy_lsp() -> (PublicKey, SocketAddress) { + let node_id = + PublicKey::from_secret_key(&Secp256k1::new(), &SecretKey::from_slice(&[1u8; 32]).unwrap()); + let address = SocketAddress::TcpIpV4 { addr: [127, 0, 0, 1], port: 9735 }; + (node_id, address) + } + + #[test] + fn lsps4_source_stores_the_fee_claim() { + let (node_id, address) = dummy_lsp(); + let mut builder = NodeBuilder::new(); + builder.set_liquidity_source_lsps4(node_id, address, Some("deadbeef".to_string())); + let config = builder.liquidity_source_config.unwrap().lsps4_client.unwrap(); + assert_eq!(config.fee_claim, Some("deadbeef".to_string())); + } + + #[test] + fn lsps4_source_without_a_claim_stores_none() { + let (node_id, address) = dummy_lsp(); + let mut builder = NodeBuilder::new(); + builder.set_liquidity_source_lsps4(node_id, address, None); + let config = builder.liquidity_source_config.unwrap().lsps4_client.unwrap(); + assert_eq!(config.fee_claim, None); + } #[test] fn sanitize_empty_node_alias() { diff --git a/src/liquidity.rs b/src/liquidity.rs index 843ffb342..495e2b5e9 100644 --- a/src/liquidity.rs +++ b/src/liquidity.rs @@ -13,7 +13,7 @@ use std::sync::{Arc, Mutex, RwLock}; use std::time::{Duration, SystemTime, UNIX_EPOCH}; use bitcoin::hashes::{sha256, Hash}; -use bitcoin::secp256k1::{PublicKey, Secp256k1}; +use bitcoin::secp256k1::{PublicKey, Secp256k1, XOnlyPublicKey}; use bitcoin::Transaction; use chrono::Utc; use bitcoin::Amount; @@ -164,6 +164,7 @@ struct LSPS4Client { lsp_node_id: PublicKey, lsp_address: SocketAddress, ldk_client_config: LdkLSPS4ClientConfig, + fee_claim: Option, pending_register_node_requests: Mutex>>, } @@ -172,6 +173,9 @@ struct LSPS4Client { pub(crate) struct LSPS4ClientConfig { pub node_id: PublicKey, pub address: SocketAddress, + /// Opaque lowercase-hex signed grant presented on `register_node`; the LSP verifies it against + /// its configured issuer keys. `None` leaves the node on the standard fee policy. + pub fee_claim: Option, } struct LSPS4Service { service_config: LSPS4ServiceConfig, @@ -198,6 +202,10 @@ pub struct LSPS4ServiceConfig { /// channels with the peer. The first entry applies when there are no channels, the second /// when there is already one, and so on. If empty, no tiering is applied. pub channel_size_tiers: Vec, + /// X-only public keys whose signatures grant a non-standard fee policy. A `register_node` + /// fee claim is honoured only if it verifies against one of these keys. Empty means no claim + /// is ever honoured and every peer stays on the standard policy. + pub issuer_pubkeys: Vec, } pub(crate) struct LiquiditySourceBuilder @@ -304,7 +312,7 @@ where } pub(crate) fn lsps4_client( - &mut self, lsp_node_id: PublicKey, lsp_address: SocketAddress, + &mut self, lsp_node_id: PublicKey, lsp_address: SocketAddress, fee_claim: Option, ) -> &mut Self { let ldk_client_config = LdkLSPS4ClientConfig {}; let pending_register_node_requests = Mutex::new(HashMap::new()); @@ -312,6 +320,7 @@ where lsp_node_id, lsp_address, ldk_client_config, + fee_claim, pending_register_node_requests, }); self @@ -326,12 +335,12 @@ where } pub(crate) fn lsps4_service(&mut self, service_config: LSPS4ServiceConfig) -> &mut Self { - let ldk_service_config = - LdkLSPS4ServiceConfig { - cltv_expiry_delta: LSPS2_CHANNEL_CLTV_EXPIRY_DELTA, - forwarding_fee_proportional_millionths: service_config + let ldk_service_config = LdkLSPS4ServiceConfig { + cltv_expiry_delta: LSPS2_CHANNEL_CLTV_EXPIRY_DELTA, + forwarding_fee_proportional_millionths: service_config .forwarding_fee_proportional_millionths, - }; + issuer_pubkeys: service_config.issuer_pubkeys.clone(), + }; self.lsps4_service = Some(LSPS4Service { service_config, ldk_service_config }); self } @@ -1826,7 +1835,9 @@ where { let mut pending_register_node_requests_lock = lsps4_client.pending_register_node_requests.lock().unwrap(); - let request_id = client_handler.register_node(lsps4_client.lsp_node_id).unwrap(); + let request_id = client_handler + .register_node(lsps4_client.lsp_node_id, lsps4_client.fee_claim.clone()) + .unwrap(); pending_register_node_requests_lock.insert(request_id, register_node_sender); }