diff --git a/plugins/lsps-plugin/src/client.rs b/plugins/lsps-plugin/src/client.rs index 7e142a9b0..df648ee52 100644 --- a/plugins/lsps-plugin/src/client.rs +++ b/plugins/lsps-plugin/src/client.rs @@ -12,6 +12,7 @@ use cln_lsps::{ }, core::{ client::LspsClient, + features::is_feature_bit_set_reversed, tlv::{encode_tu64, TLV_FORWARD_AMT, TLV_PAYMENT_SECRET}, transport::{MultiplexedTransport, PendingRequests}, }, @@ -19,7 +20,6 @@ use cln_lsps::{ lsps0::{Msat, LSP_FEATURE_BIT}, lsps2::{compute_opening_fee, Lsps2BuyResponse, Lsps2GetInfoResponse, OpeningFeeParams}, }, - util, }; use cln_plugin::options; use cln_rpc::{ @@ -772,7 +772,7 @@ async fn check_peer_lsp_status( let has_lsp_feature = if let Some(f_str) = &peer.features { let feature_bits = hex::decode(f_str) .map_err(|e| anyhow!("Invalid feature bits hex for peer {peer_id}, {f_str}: {e}"))?; - util::is_feature_bit_set_reversed(&feature_bits, LSP_FEATURE_BIT) + is_feature_bit_set_reversed(&feature_bits, LSP_FEATURE_BIT) } else { false }; diff --git a/plugins/lsps-plugin/src/util.rs b/plugins/lsps-plugin/src/core/features.rs similarity index 62% rename from plugins/lsps-plugin/src/util.rs rename to plugins/lsps-plugin/src/core/features.rs index 06784911d..1d7d4f1d3 100644 --- a/plugins/lsps-plugin/src/util.rs +++ b/plugins/lsps-plugin/src/core/features.rs @@ -1,9 +1,4 @@ -use anyhow::anyhow; -use anyhow::Result; -use cln_rpc::primitives::PublicKey; use core::fmt; -use serde_json::Value; -use std::str::FromStr; /// Checks whether a feature bit is set in a bitmap interpreted as /// **big-endian across bytes**, while keeping **LSB-first within each byte**. @@ -97,103 +92,10 @@ impl std::error::Error for UnwrapError { } } -/// Wraps a payload with a peer ID for internal LSPS message transmission. -pub fn try_wrap_payload_with_peer_id(payload: &[u8], peer_id: PublicKey) -> Result> { - // We expect the payload to be valid json, so no empty payload allowed, also - // checks that we have curly braces at start and end. - if payload.is_empty() || payload[0] != b'{' || payload[payload.len() - 1] != b'}' { - return Err(anyhow!("payload no valid json")); - } - - let pubkey_hex = peer_id.to_string(); - let mut result = Vec::with_capacity(pubkey_hex.len() + payload.len() + 13); - - result.extend_from_slice(&payload[..payload.len() - 1]); - result.extend_from_slice(b",\"peer_id\":\""); - result.extend_from_slice(pubkey_hex.as_bytes()); - result.extend_from_slice(b"\"}"); - Ok(result) -} - -/// Safely unwraps payload data and a peer ID -pub fn try_unwrap_payload_with_peer_id(data: &[u8]) -> Result<(Vec, PublicKey)> { - let mut json: Value = - serde_json::from_slice(data).map_err(|e| UnwrapError::SerdeFailure(e.to_string()))?; - - if let Value::Object(ref mut map) = json { - if let Some(Value::String(peer_id)) = map.remove("peer_id") { - let modified_json = serde_json::to_string(&json) - .map_err(|e| UnwrapError::SerdeFailure(e.to_string()))?; - return Ok(( - modified_json.into_bytes(), - PublicKey::from_str(&peer_id) - .map_err(|e| UnwrapError::InvalidPublicKey(e.to_string()))?, - )); - } - } - Err(UnwrapError::InvalidPublicKey(String::from( - "public key missing", - )))? -} - -/// Unwraps payload data and peer ID, panicking on error -/// -/// This is a convenience function for cases where one knows the data is valid. -pub fn unwrap_payload_with_peer_id(data: &[u8]) -> (Vec, PublicKey) { - try_unwrap_payload_with_peer_id(data).expect("Failed to unwrap payload with peer_id") -} - -/// Wraps payload data and peer ID, panicking on error -/// -/// This is a convenience function for cases where one knows that the payload is -/// valid. -pub fn wrap_payload_with_peer_id(payload: &[u8], peer_id: PublicKey) -> Vec { - try_wrap_payload_with_peer_id(payload, peer_id).expect("Failed to wrap payload with peer_id") -} - #[cfg(test)] mod tests { - use serde_json::json; - use super::*; - // Valid test public key - const PUBKEY: [u8; 33] = [ - 0x02, 0x79, 0xbe, 0x66, 0x7e, 0xf9, 0xdc, 0xbb, 0xac, 0x55, 0xa0, 0x62, 0x95, 0xce, 0x87, - 0x0b, 0x07, 0x02, 0x9b, 0xfc, 0xdb, 0x2d, 0xce, 0x28, 0xd9, 0x59, 0xf2, 0x81, 0x5b, 0x16, - 0xf8, 0x17, 0x98, - ]; - - #[test] - fn test_wrap_and_unwrap_roundtrip() { - let peer_id = PublicKey::from_slice(&PUBKEY).unwrap(); - let payload = - json!({"jsonrpc": "2.0","method": "some-method","params": {},"id": "some-id"}); - let wrapped = wrap_payload_with_peer_id(payload.to_string().as_bytes(), peer_id); - - let (unwrapped_payload, unwrapped_peer_id) = unwrap_payload_with_peer_id(&wrapped); - let value: serde_json::Value = serde_json::from_slice(&unwrapped_payload).unwrap(); - - assert_eq!(value, payload); - assert_eq!(unwrapped_peer_id, peer_id); - } - - #[test] - fn test_invalid_pubkey() { - let mut invalid_data = vec![0u8; 40]; - // Set an invalid public key (all zeros) - invalid_data[0] = 0x02; // Valid prefix - // But rest remains zeros which is invalid - let payload = json!({"jsonrpc": "2.0","method": "some-method","params": {},"id": "some-id","peer_id": hex::encode(&invalid_data)}); - - let result = try_unwrap_payload_with_peer_id(payload.to_string().as_bytes()); - assert!(result.is_err()); - assert!(matches!( - result.unwrap_err().downcast_ref::(), - Some(UnwrapError::InvalidPublicKey(_)) - )); - } - #[test] fn test_basic_bit_checks() { // Example bitmap: diff --git a/plugins/lsps-plugin/src/core/mod.rs b/plugins/lsps-plugin/src/core/mod.rs index 87a7705e8..19c7e1671 100644 --- a/plugins/lsps-plugin/src/core/mod.rs +++ b/plugins/lsps-plugin/src/core/mod.rs @@ -1,4 +1,5 @@ pub mod client; +pub mod features; pub mod lsps2; pub mod router; pub mod server; diff --git a/plugins/lsps-plugin/src/lib.rs b/plugins/lsps-plugin/src/lib.rs index 9d719d541..e1f5e07f4 100644 --- a/plugins/lsps-plugin/src/lib.rs +++ b/plugins/lsps-plugin/src/lib.rs @@ -1,4 +1,3 @@ pub mod cln_adapters; pub mod core; pub mod proto; -pub mod util;