From ce2d9fa97fdddb4e1e1b7e7ed002835590e4beae Mon Sep 17 00:00:00 2001 From: Erik De Smedt Date: Fri, 15 Sep 2023 11:18:13 +0200 Subject: [PATCH] Mapping of JsonRPCErrorCodes Allow the mapping of error_codes defined in JsonRpc --- libs/gl-client/src/lsps/json_rpc.rs | 71 +++++++++++++++++----- libs/gl-client/src/lsps/json_rpc_erased.rs | 18 +++--- libs/gl-client/src/lsps/message.rs | 52 +++++++++++++--- libs/gl-client/src/lsps/mod.rs | 1 + 4 files changed, 108 insertions(+), 34 deletions(-) diff --git a/libs/gl-client/src/lsps/json_rpc.rs b/libs/gl-client/src/lsps/json_rpc.rs index 06f34231e..69d04c451 100644 --- a/libs/gl-client/src/lsps/json_rpc.rs +++ b/libs/gl-client/src/lsps/json_rpc.rs @@ -3,6 +3,8 @@ use serde::de::DeserializeOwned; use serde::ser::SerializeMap; use serde::{Deserialize, Serialize}; +use super::error::map_json_rpc_error_code_to_str; + /// Generate a random json_rpc_id string that follows the requirements of LSPS0 /// /// - Should be a String @@ -15,7 +17,9 @@ pub fn generate_random_rpc_id() -> String { } #[derive(Debug, Serialize, Deserialize)] -pub struct JsonRpcMethod { +pub struct JsonRpcMethod +where E : MapErrorCode +{ pub method: &'static str, #[serde(skip_serializing)] request: std::marker::PhantomData, @@ -25,7 +29,9 @@ pub struct JsonRpcMethod { error_type: std::marker::PhantomData, } -impl JsonRpcMethod { +impl JsonRpcMethod +where E : MapErrorCode +{ pub const fn new(method: &'static str) -> Self { return Self { method: method, @@ -49,13 +55,17 @@ impl JsonRpcMethod { } } -impl JsonRpcMethod { +impl JsonRpcMethod +where E : MapErrorCode +{ pub fn create_request_no_params(&self, json_rpc_id: String) -> JsonRpcRequest { self.create_request(NoParams::default(), json_rpc_id) } } -impl<'a, I, O, E> std::convert::From<&JsonRpcMethod> for String { +impl<'a, I, O, E> std::convert::From<&JsonRpcMethod> for String +where E :MapErrorCode +{ fn from(value: &JsonRpcMethod) -> Self { return value.method.clone().into(); } @@ -64,7 +74,7 @@ impl<'a, I, O, E> std::convert::From<&JsonRpcMethod> for String { impl<'de, I, O, E> JsonRpcMethod where O: Deserialize<'de>, - E: Deserialize<'de>, + E: Deserialize<'de> + MapErrorCode, { pub fn parse_json_response_str( &self, @@ -77,7 +87,7 @@ where impl JsonRpcMethod where O: DeserializeOwned, - E: DeserializeOwned, + E: DeserializeOwned + MapErrorCode, { pub fn parse_json_response_value( &self, @@ -119,7 +129,9 @@ impl Serialize for NoParams { } impl JsonRpcRequest { - pub fn new(method: JsonRpcMethod, params: I) -> Self { + pub fn new(method: JsonRpcMethod, params: I) -> Self + where E : MapErrorCode + { return Self { jsonrpc: String::from("2.0"), id: generate_random_rpc_id(), @@ -130,7 +142,9 @@ impl JsonRpcRequest { } impl JsonRpcRequest { - pub fn new_no_params(method: JsonRpcMethod) -> Self { + pub fn new_no_params(method: JsonRpcMethod) -> Self + where E : MapErrorCode + { return Self { jsonrpc: String::from("2.0"), id: generate_random_rpc_id(), @@ -162,12 +176,38 @@ pub enum JsonRpcResponse { } #[derive(Debug, Serialize, Deserialize)] -pub struct ErrorData { +pub struct ErrorData +{ pub code: i64, pub message: String, pub data: Option, } +impl ErrorData +where E : MapErrorCode { + + pub fn code_str(&self) -> &str { + return E::get_code_str(self.code) + } +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct DefaultError; + +pub trait MapErrorCode { + + fn get_code_str(code : i64) -> &'static str; +} + +impl MapErrorCode for DefaultError { + + fn get_code_str(code : i64) -> &'static str { + map_json_rpc_error_code_to_str(code) + } +} + + + #[cfg(test)] mod test { @@ -249,7 +289,7 @@ mod test { #[test] fn create_rpc_request_from_call() { - let rpc_method = JsonRpcMethod::::new("test.method"); + let rpc_method = JsonRpcMethod::::new("test.method"); let json_rpc_id = generate_random_rpc_id(); let rpc_request = rpc_method.create_request_no_params(json_rpc_id); @@ -260,7 +300,7 @@ mod test { #[test] fn parse_rpc_response_success_from_call() { - let rpc_method = JsonRpcMethod::::new("test.return_string"); + let rpc_method = JsonRpcMethod::::new("test.return_string"); let json_value = serde_json::json!({ "jsonrpc" : "2.0", @@ -284,8 +324,8 @@ mod test { #[test] fn parse_rpc_response_failure_from_call() { - let rpc_method: JsonRpcMethod = - JsonRpcMethod::::new("test.return_string"); + let rpc_method = + JsonRpcMethod::::new("test.return_string"); let json_value = serde_json::json!({ "jsonrpc" : "2.0", @@ -302,9 +342,10 @@ mod test { assert_eq!(err.jsonrpc, "2.0"); assert_eq!(err.error.code, -32700); - assert_eq!(err.error.message, "Failed to parse response"); + assert_eq!(err.error.code_str(), "parsing_error"); - assert_eq!(err.id, "request_id") + assert_eq!(err.error.message, "Failed to parse response"); + assert_eq!(err.id, "request_id"); } JsonRpcResponse::Ok(_ok) => { panic!("Failure deserialized as Ok") diff --git a/libs/gl-client/src/lsps/json_rpc_erased.rs b/libs/gl-client/src/lsps/json_rpc_erased.rs index 1f3dd6432..e6f23bc82 100644 --- a/libs/gl-client/src/lsps/json_rpc_erased.rs +++ b/libs/gl-client/src/lsps/json_rpc_erased.rs @@ -20,7 +20,7 @@ use crate::lsps::json_rpc::{ ErrorData, JsonRpcMethod, JsonRpcRequest, JsonRpcResponse, JsonRpcResponseFailure, - JsonRpcResponseSuccess, + JsonRpcResponseSuccess, MapErrorCode }; use serde::Serialize; @@ -54,7 +54,7 @@ impl JsonRpcMethodErased for JsonRpcMethod where I: serde::de::DeserializeOwned + Serialize, O: serde::de::DeserializeOwned + Serialize, - E: serde::de::DeserializeOwned + Serialize, + E: serde::de::DeserializeOwned + Serialize + MapErrorCode, { fn name(&self) -> &str { &self.method @@ -95,7 +95,7 @@ impl JsonRpcMethod where I: serde::de::DeserializeOwned + Serialize + 'static, O: serde::de::DeserializeOwned + Serialize + 'static, - E: serde::de::DeserializeOwned + Serialize + 'static, + E: serde::de::DeserializeOwned + Serialize + 'static + MapErrorCode, { pub fn erase_box(self) -> Box { return Box::new(self); @@ -143,7 +143,7 @@ pub trait JsonRpcMethodUnerased<'a, I, O, E> { impl<'a, I, O, E> JsonRpcMethodUnerased<'a, I, O, E> for JsonRpcMethod where O: serde::de::DeserializeOwned, - E: serde::de::DeserializeOwned, + E: serde::de::DeserializeOwned + MapErrorCode, { fn name(&self) -> &str { JsonRpcMethod::name(self) @@ -292,7 +292,7 @@ where #[cfg(test)] mod test { use super::*; - use crate::lsps::json_rpc::{generate_random_rpc_id, JsonRpcMethod}; + use crate::lsps::json_rpc::{generate_random_rpc_id, JsonRpcMethod, DefaultError}; #[derive(Serialize, serde::Deserialize)] struct TestRequestStruct { @@ -306,7 +306,7 @@ mod test { #[test] fn create_rpc_request_from_method_erased() { - let rpc_method = JsonRpcMethod::::new("test.method"); + let rpc_method = JsonRpcMethod::::new("test.method"); let rpc_method_erased = rpc_method.erase_box(); // This rpc-request should work becasue the parameters match the schema @@ -322,7 +322,7 @@ mod test { #[test] fn create_rpc_request_from_method_erased_checks_types() { - let rpc_method = JsonRpcMethod::::new("test.method"); + let rpc_method = JsonRpcMethod::::new("test.method"); let rpc_method_erased = rpc_method.erase_box(); // This rpc-request should fail because the parameters do not match the schema @@ -336,7 +336,7 @@ mod test { #[test] fn parse_rpc_request_from_method_erased() { let rpc_method = - JsonRpcMethod::::new("test.method"); + JsonRpcMethod::::new("test.method"); let rpc_method_erased = rpc_method.erase_box(); let json_value = serde_json::json!({ @@ -353,7 +353,7 @@ mod test { #[test] fn parse_rpc_request_from_method_erased_fails() { let rpc_method = - JsonRpcMethod::::new("test.method"); + JsonRpcMethod::::new("test.method"); let rpc_method_erased = rpc_method.erase_box(); let json_value = serde_json::json!({ diff --git a/libs/gl-client/src/lsps/message.rs b/libs/gl-client/src/lsps/message.rs index 49708aca0..20fdecbef 100644 --- a/libs/gl-client/src/lsps/message.rs +++ b/libs/gl-client/src/lsps/message.rs @@ -1,6 +1,6 @@ // TODO: Implement parsing of error types for lsps2.getinfo use crate::lsps::error::LspsError; -pub use crate::lsps::json_rpc::{JsonRpcMethod, NoParams}; +pub use crate::lsps::json_rpc::{JsonRpcMethod, NoParams, DefaultError, MapErrorCode}; use crate::lsps::json_rpc_erased::{JsonRpcMethodErased, JsonRpcMethodUnerased}; use serde::de::Error as DeError; use serde::ser::Error as SeError; @@ -11,6 +11,8 @@ use time::macros::format_description; use time::{OffsetDateTime, PrimitiveDateTime}; use uuid::Uuid; +use super::error::map_json_rpc_error_code_to_str; + type OnchainFeeRate = u64; // All rpc-methods defined in the LSPS standard @@ -23,12 +25,12 @@ type OnchainFeeRate = u64; // 1. Add it to the JsonRpcMethodEnum // 2. Add it to the from_method_name function // 3. Add it to the ref_erase function -pub type Lsps0ListProtocols = JsonRpcMethod; -pub type Lsps1Info = JsonRpcMethod; -pub type Lsps1Order = JsonRpcMethod; -pub type Lsps2GetVersions = JsonRpcMethod; -pub type Lsps2GetInfo = JsonRpcMethod; -pub type Lsps2Buy = JsonRpcMethod; +pub type Lsps0ListProtocols = JsonRpcMethod; +pub type Lsps1Info = JsonRpcMethod; +pub type Lsps1Order = JsonRpcMethod; +pub type Lsps2GetVersions = JsonRpcMethod; +pub type Lsps2GetInfo = JsonRpcMethod; +pub type Lsps2Buy = JsonRpcMethod; pub const LSPS0_LIST_PROTOCOLS: Lsps0ListProtocols = @@ -223,7 +225,6 @@ impl<'de> Deserialize<'de> for MsatAmount { // achieved if the LSPS2 sends a fully compliant timestamp. // // I have decided to fail early if another timestamp is received - #[derive(Debug)] pub struct Datetime { datetime: PrimitiveDateTime, @@ -394,6 +395,21 @@ pub struct Lsp2GetInfoResponse { max_payment_size_msat: String, } +#[derive(Debug, Serialize, Deserialize)] +pub struct Lsp2GetInfoError {} + +impl MapErrorCode for Lsp2GetInfoError { + fn get_code_str(code : i64) -> &'static str { + match code { + 1 => "unsupported_version", + 2 => "unrecognized_or_stale_token", + _ => map_json_rpc_error_code_to_str(code) + } + } +} + + + #[derive(Debug, Serialize, Deserialize)] #[serde(deny_unknown_fields)] pub struct OpeningFeeParamsMenuItem { @@ -420,6 +436,21 @@ pub struct Lsps2BuyResponse { client_trusts_lsp : bool } +#[derive(Debug, Serialize, Deserialize)] +pub struct Lsps2BuyError {} + +impl MapErrorCode for Lsps2BuyError { + fn get_code_str(code : i64) -> &'static str { + match code { + 1 => "unsupported_version", + 2 => "invalid_opening_fee_params", + 3 => "payment_size_too_small", + 4 => "payment_size_too_large", + _ => map_json_rpc_error_code_to_str(code) + } + } +} + #[cfg(test)] mod test { @@ -461,8 +492,6 @@ mod test { fn parsing_error_when_opening_fee_menu_has_extra_fields() { // LSPS2 mentions // Clients MUST fail and abort the flow if a opening_fee_params object has unrecognized fields. - // - // If a new field is added the version number should be incremented let fee_menu_item = serde_json::json!( { "min_fee_msat": "546000", @@ -545,6 +574,9 @@ mod test { #[test] fn parse_too_long_promise_fails() { // Each a char correspond to 1 byte + // We refuse to parse the promise if it is too long + // LSPS2 requires us to ignore Promise that are too long + // so the client cannot be burdened with unneeded storage requirements let a_513_chars = "\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\""; let a_512_chars = "\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\""; diff --git a/libs/gl-client/src/lsps/mod.rs b/libs/gl-client/src/lsps/mod.rs index 8e2b78f76..7ba871b1b 100644 --- a/libs/gl-client/src/lsps/mod.rs +++ b/libs/gl-client/src/lsps/mod.rs @@ -1,3 +1,4 @@ +// NOTE: Both the LSP-spec and this implementation are still moving heavily pub mod error; pub mod json_rpc; pub mod json_rpc_erased;