plugins: lsps: move id generation into jsonrpcrequest
There is no need to generate the id in some client when we can actually generate it on a lower layer as we know by LSPS0 how we have to generate it. Signed-off-by: Peter Neuroth <pet.v.ne@gmail.com>
This commit is contained in:
committed by
madelinevibes
parent
5ed743e10d
commit
2546642c32
@@ -2,9 +2,6 @@ use crate::proto::jsonrpc::{JsonRpcRequest, JsonRpcResponse, RequestObject};
|
||||
use async_trait::async_trait;
|
||||
use bitcoin::secp256k1::PublicKey;
|
||||
use core::fmt::Debug;
|
||||
use log::{debug, error};
|
||||
use rand::rngs::OsRng;
|
||||
use rand::TryRngCore;
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
use serde_json::Value;
|
||||
use std::sync::Arc;
|
||||
@@ -23,6 +20,8 @@ pub enum Error {
|
||||
#[source]
|
||||
source: serde_json::Error,
|
||||
},
|
||||
#[error("request is missing id")]
|
||||
MissingId,
|
||||
}
|
||||
|
||||
impl From<serde_json::Error> for Error {
|
||||
@@ -42,7 +41,6 @@ pub trait Transport: Send + Sync {
|
||||
async fn send(&self, peer: &PublicKey, request: &str) -> Result<String>;
|
||||
async fn notify(&self, peer: &PublicKey, request: &str) -> Result<()>;
|
||||
}
|
||||
}
|
||||
|
||||
/// A typed JSON-RPC client that works with any transport implementation.
|
||||
///
|
||||
@@ -67,19 +65,16 @@ impl<T: Transport> JsonRpcClient<T> {
|
||||
peer_id: &PublicKey,
|
||||
method: &str,
|
||||
params: Option<Value>,
|
||||
id: Option<String>,
|
||||
) -> Result<JsonRpcResponse<Value>> {
|
||||
let id = generate_random_id();
|
||||
|
||||
debug!("Preparing request: method={}, id={}", method, id);
|
||||
let request = RequestObject {
|
||||
jsonrpc: "2.0".into(),
|
||||
method: method.into(),
|
||||
params,
|
||||
id: Some(id.clone().into()),
|
||||
id,
|
||||
};
|
||||
|
||||
let response: JsonRpcResponse<Value> =
|
||||
self.send_request(peer_id, method, &request, id).await?;
|
||||
let response: JsonRpcResponse<Value> = self.send_request(peer_id, &request).await?;
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
@@ -97,13 +92,8 @@ impl<T: Transport> JsonRpcClient<T> {
|
||||
RQ: JsonRpcRequest + Serialize + Send + Sync,
|
||||
RS: DeserializeOwned + Serialize + Debug + Send + Sync,
|
||||
{
|
||||
let method = RQ::METHOD;
|
||||
let id = generate_random_id();
|
||||
|
||||
debug!("Preparing request: method={}, id={}", method, id);
|
||||
let request = request.into_request(Some(id.clone().into()));
|
||||
let response: JsonRpcResponse<RS> =
|
||||
self.send_request(peer_id, method, &request, id).await?;
|
||||
let request = request.into_request();
|
||||
let response: JsonRpcResponse<RS> = self.send_request(peer_id, &request).await?;
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
@@ -114,14 +104,13 @@ impl<T: Transport> JsonRpcClient<T> {
|
||||
method: &str,
|
||||
params: Option<Value>,
|
||||
) -> Result<()> {
|
||||
debug!("Preparing notification: method={}", method);
|
||||
let request = RequestObject {
|
||||
jsonrpc: "2.0".into(),
|
||||
method: method.into(),
|
||||
params,
|
||||
id: None,
|
||||
};
|
||||
Ok(self.send_notification(peer_id, method, &request).await?)
|
||||
Ok(self.send_notification(peer_id, &request).await?)
|
||||
}
|
||||
|
||||
/// Sends a typed notification (no response expected).
|
||||
@@ -129,88 +118,34 @@ impl<T: Transport> JsonRpcClient<T> {
|
||||
where
|
||||
RQ: JsonRpcRequest + Serialize + Send + Sync,
|
||||
{
|
||||
let method = RQ::METHOD;
|
||||
|
||||
debug!("Preparing notification: method={}", method);
|
||||
let request = request.into_request(None);
|
||||
Ok(self.send_notification(peer_id, method, &request).await?)
|
||||
let request = request.into_request();
|
||||
Ok(self.send_notification(peer_id, &request).await?)
|
||||
}
|
||||
|
||||
async fn send_request<RS, RP>(
|
||||
&self,
|
||||
peer: &PublicKey,
|
||||
method: &str,
|
||||
payload: &RP,
|
||||
id: String,
|
||||
) -> Result<JsonRpcResponse<RS>>
|
||||
where
|
||||
RP: Serialize + Send + Sync,
|
||||
RS: DeserializeOwned + Serialize + Debug + Send + Sync,
|
||||
{
|
||||
let request_json = serde_json::to_string(&payload)?;
|
||||
debug!(
|
||||
"Sending request: method={}, id={}, request={:?}",
|
||||
method, id, &request_json
|
||||
);
|
||||
let start = tokio::time::Instant::now();
|
||||
let res_str = self.transport.send(peer, &request_json).await?;
|
||||
let elapsed = start.elapsed();
|
||||
debug!(
|
||||
"Received response: method={}, id={}, response={}, elapsed={}ms",
|
||||
method,
|
||||
id,
|
||||
&res_str,
|
||||
elapsed.as_millis()
|
||||
);
|
||||
Ok(serde_json::from_str(&res_str)?)
|
||||
}
|
||||
|
||||
async fn send_notification<RP>(
|
||||
&self,
|
||||
peer_id: &PublicKey,
|
||||
method: &str,
|
||||
payload: &RP,
|
||||
) -> Result<()>
|
||||
async fn send_notification<RP>(&self, peer_id: &PublicKey, payload: &RP) -> Result<()>
|
||||
where
|
||||
RP: Serialize + Send + Sync,
|
||||
{
|
||||
let request_json = serde_json::to_string(&payload)?;
|
||||
debug!("Sending notification: method={}", method);
|
||||
let start = tokio::time::Instant::now();
|
||||
self.transport.notify(peer_id, &request_json).await?;
|
||||
let elapsed = start.elapsed();
|
||||
debug!(
|
||||
"Sent notification: method={}, elapsed={}ms",
|
||||
method,
|
||||
elapsed.as_millis()
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates a random ID for JSON-RPC requests.
|
||||
///
|
||||
/// Uses a secure random number generator to create a hex-encoded ID. Falls back
|
||||
/// to a timestamp-based ID if random generation fails.
|
||||
fn generate_random_id() -> String {
|
||||
let mut bytes = [0u8; 10];
|
||||
match OsRng.try_fill_bytes(&mut bytes) {
|
||||
Ok(_) => hex::encode(bytes),
|
||||
Err(e) => {
|
||||
// Fallback to a timestamp-based ID if random generation fails
|
||||
error!(
|
||||
"Failed to generate random ID: {}, falling back to timestamp",
|
||||
e
|
||||
);
|
||||
let timestamp = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap_or_default()
|
||||
.as_nanos();
|
||||
format!("fallback-{}", timestamp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
mod test_json_rpc {
|
||||
|
||||
@@ -60,7 +60,7 @@ mod test {
|
||||
lsps2_enabled: false,
|
||||
};
|
||||
|
||||
let request = Lsps0listProtocolsRequest {}.into_request(Some("test-id".to_string()));
|
||||
let request = Lsps0listProtocolsRequest {}.into_request();
|
||||
let payload = create_wrapped_request(&request);
|
||||
|
||||
let result = handler.handle(&payload).await.unwrap();
|
||||
@@ -77,7 +77,7 @@ mod test {
|
||||
lsps2_enabled: true,
|
||||
};
|
||||
|
||||
let request = Lsps0listProtocolsRequest {}.into_request(Some("test-id".to_string()));
|
||||
let request = Lsps0listProtocolsRequest {}.into_request();
|
||||
let payload = create_wrapped_request(&request);
|
||||
|
||||
let result = handler.handle(&payload).await.unwrap();
|
||||
|
||||
@@ -1022,7 +1022,7 @@ mod tests {
|
||||
*fake.lsps2_getpolicy_response.lock().unwrap() = Some(params);
|
||||
let handler = Lsps2GetInfoHandler::new(fake, promise_secret);
|
||||
|
||||
let request = Lsps2GetInfoRequest { token: None }.into_request(Some("test-id".to_string()));
|
||||
let request = Lsps2GetInfoRequest { token: None }.into_request();
|
||||
let payload = create_wrapped_request(&request);
|
||||
|
||||
let result = handler.handle(&payload).await.unwrap();
|
||||
@@ -1053,7 +1053,7 @@ mod tests {
|
||||
data: None,
|
||||
});
|
||||
let handler = Lsps2GetInfoHandler::new(fake, [0; 32]);
|
||||
let request = Lsps2GetInfoRequest { token: None }.into_request(Some("test-id".to_string()));
|
||||
let request = Lsps2GetInfoRequest { token: None }.into_request();
|
||||
let payload = create_wrapped_request(&request);
|
||||
|
||||
let result = handler.handle(&payload).await;
|
||||
@@ -1084,7 +1084,7 @@ mod tests {
|
||||
opening_fee_params: buy,
|
||||
payment_size_msat: Some(Msat(2_000_000)),
|
||||
}
|
||||
.into_request(Some("ok-fixed".into()));
|
||||
.into_request();
|
||||
let payload = create_wrapped_request(&req);
|
||||
|
||||
let out = handler.handle(&payload).await.unwrap();
|
||||
@@ -1116,7 +1116,7 @@ mod tests {
|
||||
opening_fee_params: buy,
|
||||
payment_size_msat: None,
|
||||
}
|
||||
.into_request(Some("ok-var".into()));
|
||||
.into_request();
|
||||
let payload = create_wrapped_request(&req);
|
||||
|
||||
let out = handler.handle(&payload).await.unwrap();
|
||||
@@ -1136,7 +1136,7 @@ mod tests {
|
||||
opening_fee_params: buy_wrong,
|
||||
payment_size_msat: Some(Msat(2_000_000)),
|
||||
}
|
||||
.into_request(Some("bad-promise".into()));
|
||||
.into_request();
|
||||
let err1 = handler
|
||||
.handle(&create_wrapped_request(&req_wrong))
|
||||
.await
|
||||
@@ -1150,7 +1150,7 @@ mod tests {
|
||||
opening_fee_params: buy_past,
|
||||
payment_size_msat: Some(Msat(2_000_000)),
|
||||
}
|
||||
.into_request(Some("past-valid".into()));
|
||||
.into_request();
|
||||
let err2 = handler
|
||||
.handle(&create_wrapped_request(&req_past))
|
||||
.await
|
||||
@@ -1190,7 +1190,7 @@ mod tests {
|
||||
opening_fee_params: buy,
|
||||
payment_size_msat: Some(Msat(9_999)), // strictly less than min_fee => opening_fee >= payment_size
|
||||
}
|
||||
.into_request(Some("too-small".into()));
|
||||
.into_request();
|
||||
|
||||
let err = handler
|
||||
.handle(&create_wrapped_request(&req))
|
||||
@@ -1232,7 +1232,7 @@ mod tests {
|
||||
opening_fee_params: buy,
|
||||
payment_size_msat: Some(Msat(u64::MAX / 2)),
|
||||
}
|
||||
.into_request(Some("overflow".into()));
|
||||
.into_request();
|
||||
|
||||
let err = handler
|
||||
.handle(&create_wrapped_request(&req))
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use rand::{rngs::OsRng, TryRngCore as _};
|
||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
use serde_json::{self, Value};
|
||||
use std::fmt;
|
||||
@@ -16,7 +17,14 @@ pub const INTERNAL_ERROR: i64 = -32603;
|
||||
/// request format.
|
||||
pub trait JsonRpcRequest: Serialize {
|
||||
const METHOD: &'static str;
|
||||
fn into_request(self, id: Option<String>) -> RequestObject<Self>
|
||||
fn into_request(self) -> RequestObject<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Self::into_request_with_id(self, Some(generate_random_id()))
|
||||
}
|
||||
|
||||
fn into_request_with_id(self, id: Option<String>) -> RequestObject<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
@@ -29,6 +37,25 @@ pub trait JsonRpcRequest: Serialize {
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates a random ID for JSON-RPC requests.
|
||||
///
|
||||
/// Uses a secure random number generator to create a hex-encoded ID. Falls back
|
||||
/// to a timestamp-based ID if random generation fails.
|
||||
fn generate_random_id() -> String {
|
||||
let mut bytes = [0u8; 10];
|
||||
match OsRng.try_fill_bytes(&mut bytes) {
|
||||
Ok(_) => hex::encode(bytes),
|
||||
Err(_) => {
|
||||
// Fallback to a timestamp-based ID if random generation fails
|
||||
let timestamp = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap_or_default()
|
||||
.as_nanos();
|
||||
format!("fallback-{}", timestamp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// # RequestObject
|
||||
///
|
||||
/// Represents a JSON-RPC 2.0 Request object, as defined in section 4 of the
|
||||
@@ -41,10 +68,7 @@ pub trait JsonRpcRequest: Serialize {
|
||||
/// to allow it to be encoded as JSON. Typically this will be a struct
|
||||
/// implementing the `JsonRpcRequest` trait.
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct RequestObject<T>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
pub struct RequestObject<T> {
|
||||
/// **REQUIRED**. MUST be `"2.0"`.
|
||||
pub jsonrpc: String,
|
||||
/// **REQUIRED**. The method to be invoked.
|
||||
@@ -395,7 +419,7 @@ mod test_message_serialization {
|
||||
impl JsonRpcRequest for SayHelloRequest {
|
||||
const METHOD: &'static str = "say_hello";
|
||||
}
|
||||
let rpc_request = SayHelloRequest.into_request(Some("unique-id-123".into()));
|
||||
let rpc_request = SayHelloRequest.into_request();
|
||||
assert!(!serde_json::to_string(&rpc_request)
|
||||
.expect("could not convert to json")
|
||||
.contains("\"params\""));
|
||||
@@ -416,7 +440,7 @@ mod test_message_serialization {
|
||||
name: "Satoshi".to_string(),
|
||||
age: 99,
|
||||
}
|
||||
.into_request(Some("unique-id-123".into()));
|
||||
.into_request_with_id(Some("unique-id-123".into()));
|
||||
|
||||
let json_value: serde_json::Value = serde_json::to_value(&rpc_request).unwrap();
|
||||
let expected_value: serde_json::Value = serde_json::json!({
|
||||
|
||||
Reference in New Issue
Block a user