diff --git a/broker/Cargo.lock b/broker/Cargo.lock index f556bb5..ea6d710 100644 --- a/broker/Cargo.lock +++ b/broker/Cargo.lock @@ -1687,6 +1687,15 @@ dependencies = [ "hashbrown 0.14.0", ] +[[package]] +name = "lru" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2994eeba8ed550fd9b47a0b38f0242bc3344e496483c6180b69139cc2fa5d1d7" +dependencies = [ + "hashbrown 0.14.0", +] + [[package]] name = "lss-connector" version = "0.1.0" @@ -3316,6 +3325,7 @@ dependencies = [ "fern", "hex", "log", + "lru 0.12.1", "lss-connector", "once_cell", "pretty_env_logger", @@ -4149,7 +4159,7 @@ dependencies = [ "hyper", "lightning-storage-server", "log", - "lru", + "lru 0.11.0", "nix", "prost", "serde", diff --git a/broker/Cargo.toml b/broker/Cargo.toml index c498987..697a746 100644 --- a/broker/Cargo.toml +++ b/broker/Cargo.toml @@ -17,6 +17,7 @@ confy = "0.4.0" fern = { version = "0.6", features = ["colored"] } hex = "0.4.3" log = "0.4" +lru = "0.12.1" once_cell = "1.12.0" pretty_env_logger = "0.4.0" rocket = { version = "0.5.0-rc.2", features = ["json"] } diff --git a/broker/src/looper.rs b/broker/src/looper.rs index 7bf66c9..3e31542 100644 --- a/broker/src/looper.rs +++ b/broker/src/looper.rs @@ -4,11 +4,24 @@ use crate::conn::{ChannelRequest, LssReq}; use crate::handle::handle_message; use crate::secp256k1::PublicKey; use log::*; +use lru::LruCache; use rocket::tokio::sync::mpsc; +use sphinx_signer::lightning_signer::bitcoin::hashes::{sha256::Hash as Sha256Hash, Hash}; +use std::num::NonZeroUsize; use std::thread; +use std::time::Duration; +use std::time::SystemTime; use vls_protocol::{msgs, msgs::Message, Error, Result}; use vls_proxy::client::Client; +const PREAPPROVE_CACHE_TTL: Duration = Duration::from_secs(60); +const PREAPPROVE_CACHE_SIZE: usize = 6; + +struct PreapprovalCacheEntry { + tstamp: SystemTime, + reply_bytes: Vec, +} + #[derive(Clone, Debug)] pub struct ClientId { pub peer_id: PublicKey, @@ -22,6 +35,7 @@ pub struct SignerLoop { vls_tx: mpsc::Sender, lss_tx: mpsc::Sender, client_id: Option, + preapproval_cache: LruCache, } impl SignerLoop { @@ -32,12 +46,14 @@ impl SignerLoop { lss_tx: mpsc::Sender, ) -> Self { let log_prefix = format!("{}/{}", std::process::id(), client.id()); + let preapproval_cache = LruCache::new(NonZeroUsize::new(PREAPPROVE_CACHE_SIZE).unwrap()); Self { client, log_prefix, vls_tx, lss_tx, client_id: None, + preapproval_cache, } } @@ -49,12 +65,14 @@ impl SignerLoop { client_id: ClientId, ) -> Self { let log_prefix = format!("{}/{}", std::process::id(), client.id()); + let preapproval_cache = LruCache::new(NonZeroUsize::new(PREAPPROVE_CACHE_SIZE).unwrap()); Self { client, log_prefix, vls_tx, lss_tx, client_id: Some(client_id), + preapproval_cache, } } @@ -98,7 +116,7 @@ impl SignerLoop { self.client.write(reply)?; } msg => { - if let Message::HsmdInit(m) = msg { + if let Message::HsmdInit(ref m) = msg { if let Some(net) = network { if ChainHash::using_genesis_block(net).as_bytes() != m.chain_params.as_ref() @@ -109,10 +127,65 @@ impl SignerLoop { log::error!("No Network provided"); } } - let reply = - handle_message(&self.client_id, raw_msg, &self.vls_tx, &self.lss_tx); - // Write the reply to CLN - self.client.write_vec(reply)?; + // check if we got the same preapprove message less than PREAPPROVE_CACHE_TTL seconds ago + if let Message::PreapproveInvoice(_) | Message::PreapproveKeysend(_) = msg { + let now = SystemTime::now(); + let req_hash = Sha256Hash::hash(&raw_msg); + if let Some(entry) = self.preapproval_cache.get(&req_hash) { + let age = now.duration_since(entry.tstamp).expect("age"); + if age < PREAPPROVE_CACHE_TTL { + debug!("{} found in preapproval cache", self.log_prefix); + let reply = entry.reply_bytes.clone(); + self.client.write_vec(reply)?; + continue; + } + } + } + + let reply_bytes = handle_message( + &self.client_id, + raw_msg.clone(), + &self.vls_tx, + &self.lss_tx, + ); + + // post signer response processing + let reply = msgs::from_vec(reply_bytes.clone()).expect("parse reply failed"); + match reply { + // did we just preapprove a keysend ? if so add it to the cache + Message::PreapproveKeysendReply(pkr) => { + if pkr.result { + debug!("{} adding keysend to preapproval cache", self.log_prefix); + let now = SystemTime::now(); + let req_hash = Sha256Hash::hash(&raw_msg); + self.preapproval_cache.put( + req_hash, + PreapprovalCacheEntry { + tstamp: now, + reply_bytes: reply_bytes.clone(), + }, + ); + } + } + // did we just preapprove an invoice ? if so add it to the cache + Message::PreapproveInvoiceReply(pir) => { + if pir.result { + debug!("{} adding invoice to preapproval cache", self.log_prefix); + let now = SystemTime::now(); + let req_hash = Sha256Hash::hash(&raw_msg); + self.preapproval_cache.put( + req_hash, + PreapprovalCacheEntry { + tstamp: now, + reply_bytes: reply_bytes.clone(), + }, + ); + } + } + _ => {} // for future messages needing post signer response processing + } + // write the reply to CLN + self.client.write_vec(reply_bytes)?; } } }