Skip to content

Commit

Permalink
Mapping of JsonRPCErrorCodes
Browse files Browse the repository at this point in the history
Allow the mapping of error_codes defined in JsonRpc
  • Loading branch information
ErikDeSmedt committed Sep 15, 2023
1 parent de84996 commit ce2d9fa
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 34 deletions.
71 changes: 56 additions & 15 deletions libs/gl-client/src/lsps/json_rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -15,7 +17,9 @@ pub fn generate_random_rpc_id() -> String {
}

#[derive(Debug, Serialize, Deserialize)]
pub struct JsonRpcMethod<I, O, E> {
pub struct JsonRpcMethod<I, O, E>
where E : MapErrorCode
{
pub method: &'static str,
#[serde(skip_serializing)]
request: std::marker::PhantomData<I>,
Expand All @@ -25,7 +29,9 @@ pub struct JsonRpcMethod<I, O, E> {
error_type: std::marker::PhantomData<E>,
}

impl<I, O, E> JsonRpcMethod<I, O, E> {
impl<I, O, E> JsonRpcMethod<I, O, E>
where E : MapErrorCode
{
pub const fn new(method: &'static str) -> Self {
return Self {
method: method,
Expand All @@ -49,13 +55,17 @@ impl<I, O, E> JsonRpcMethod<I, O, E> {
}
}

impl<O, E> JsonRpcMethod<NoParams, O, E> {
impl<O, E> JsonRpcMethod<NoParams, O, E>
where E : MapErrorCode
{
pub fn create_request_no_params(&self, json_rpc_id: String) -> JsonRpcRequest<NoParams> {
self.create_request(NoParams::default(), json_rpc_id)
}
}

impl<'a, I, O, E> std::convert::From<&JsonRpcMethod<I, O, E>> for String {
impl<'a, I, O, E> std::convert::From<&JsonRpcMethod<I, O, E>> for String
where E :MapErrorCode
{
fn from(value: &JsonRpcMethod<I, O, E>) -> Self {
return value.method.clone().into();
}
Expand All @@ -64,7 +74,7 @@ impl<'a, I, O, E> std::convert::From<&JsonRpcMethod<I, O, E>> for String {
impl<'de, I, O, E> JsonRpcMethod<I, O, E>
where
O: Deserialize<'de>,
E: Deserialize<'de>,
E: Deserialize<'de> + MapErrorCode,
{
pub fn parse_json_response_str(
&self,
Expand All @@ -77,7 +87,7 @@ where
impl<I, O, E> JsonRpcMethod<I, O, E>
where
O: DeserializeOwned,
E: DeserializeOwned,
E: DeserializeOwned + MapErrorCode,
{
pub fn parse_json_response_value(
&self,
Expand Down Expand Up @@ -119,7 +129,9 @@ impl Serialize for NoParams {
}

impl<I> JsonRpcRequest<I> {
pub fn new<O, E>(method: JsonRpcMethod<I, O, E>, params: I) -> Self {
pub fn new<O, E>(method: JsonRpcMethod<I, O, E>, params: I) -> Self
where E : MapErrorCode
{
return Self {
jsonrpc: String::from("2.0"),
id: generate_random_rpc_id(),
Expand All @@ -130,7 +142,9 @@ impl<I> JsonRpcRequest<I> {
}

impl JsonRpcRequest<NoParams> {
pub fn new_no_params<O, E>(method: JsonRpcMethod<NoParams, O, E>) -> Self {
pub fn new_no_params<O, E>(method: JsonRpcMethod<NoParams, O, E>) -> Self
where E : MapErrorCode
{
return Self {
jsonrpc: String::from("2.0"),
id: generate_random_rpc_id(),
Expand Down Expand Up @@ -162,12 +176,38 @@ pub enum JsonRpcResponse<O, E> {
}

#[derive(Debug, Serialize, Deserialize)]
pub struct ErrorData<E> {
pub struct ErrorData<E>
{
pub code: i64,
pub message: String,
pub data: Option<E>,
}

impl<E> ErrorData<E>
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 {

Expand Down Expand Up @@ -249,7 +289,7 @@ mod test {

#[test]
fn create_rpc_request_from_call() {
let rpc_method = JsonRpcMethod::<NoParams, (), ()>::new("test.method");
let rpc_method = JsonRpcMethod::<NoParams, (), DefaultError>::new("test.method");
let json_rpc_id = generate_random_rpc_id();
let rpc_request = rpc_method.create_request_no_params(json_rpc_id);

Expand All @@ -260,7 +300,7 @@ mod test {

#[test]
fn parse_rpc_response_success_from_call() {
let rpc_method = JsonRpcMethod::<NoParams, String, ()>::new("test.return_string");
let rpc_method = JsonRpcMethod::<NoParams, String, DefaultError>::new("test.return_string");

let json_value = serde_json::json!({
"jsonrpc" : "2.0",
Expand All @@ -284,8 +324,8 @@ mod test {

#[test]
fn parse_rpc_response_failure_from_call() {
let rpc_method: JsonRpcMethod<NoParams, String, ()> =
JsonRpcMethod::<NoParams, String, ()>::new("test.return_string");
let rpc_method =
JsonRpcMethod::<NoParams, String, DefaultError>::new("test.return_string");

let json_value = serde_json::json!({
"jsonrpc" : "2.0",
Expand All @@ -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")
Expand Down
18 changes: 9 additions & 9 deletions libs/gl-client/src/lsps/json_rpc_erased.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

use crate::lsps::json_rpc::{
ErrorData, JsonRpcMethod, JsonRpcRequest, JsonRpcResponse, JsonRpcResponseFailure,
JsonRpcResponseSuccess,
JsonRpcResponseSuccess, MapErrorCode
};
use serde::Serialize;

Expand Down Expand Up @@ -54,7 +54,7 @@ impl<I, O, E> JsonRpcMethodErased for JsonRpcMethod<I, O, E>
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
Expand Down Expand Up @@ -95,7 +95,7 @@ impl<I, O, E> JsonRpcMethod<I, O, E>
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<dyn JsonRpcMethodErased> {
return Box::new(self);
Expand Down Expand Up @@ -143,7 +143,7 @@ pub trait JsonRpcMethodUnerased<'a, I, O, E> {
impl<'a, I, O, E> JsonRpcMethodUnerased<'a, I, O, E> for JsonRpcMethod<I, O, E>
where
O: serde::de::DeserializeOwned,
E: serde::de::DeserializeOwned,
E: serde::de::DeserializeOwned + MapErrorCode,
{
fn name(&self) -> &str {
JsonRpcMethod::name(self)
Expand Down Expand Up @@ -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 {
Expand All @@ -306,7 +306,7 @@ mod test {

#[test]
fn create_rpc_request_from_method_erased() {
let rpc_method = JsonRpcMethod::<TestRequestStruct, (), ()>::new("test.method");
let rpc_method = JsonRpcMethod::<TestRequestStruct, (), DefaultError>::new("test.method");
let rpc_method_erased = rpc_method.erase_box();

// This rpc-request should work becasue the parameters match the schema
Expand All @@ -322,7 +322,7 @@ mod test {

#[test]
fn create_rpc_request_from_method_erased_checks_types() {
let rpc_method = JsonRpcMethod::<TestRequestStruct, (), ()>::new("test.method");
let rpc_method = JsonRpcMethod::<TestRequestStruct, (), DefaultError>::new("test.method");
let rpc_method_erased = rpc_method.erase_box();

// This rpc-request should fail because the parameters do not match the schema
Expand All @@ -336,7 +336,7 @@ mod test {
#[test]
fn parse_rpc_request_from_method_erased() {
let rpc_method =
JsonRpcMethod::<TestRequestStruct, TestResponseStruct, ()>::new("test.method");
JsonRpcMethod::<TestRequestStruct, TestResponseStruct, DefaultError>::new("test.method");
let rpc_method_erased = rpc_method.erase_box();

let json_value = serde_json::json!({
Expand All @@ -353,7 +353,7 @@ mod test {
#[test]
fn parse_rpc_request_from_method_erased_fails() {
let rpc_method =
JsonRpcMethod::<TestRequestStruct, TestResponseStruct, ()>::new("test.method");
JsonRpcMethod::<TestRequestStruct, TestResponseStruct, DefaultError>::new("test.method");
let rpc_method_erased = rpc_method.erase_box();

let json_value = serde_json::json!({
Expand Down
52 changes: 42 additions & 10 deletions libs/gl-client/src/lsps/message.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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
Expand All @@ -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<NoParams, ProtocolList, ()>;
pub type Lsps1Info = JsonRpcMethod<NoParams, Lsps1InfoResponse, ()>;
pub type Lsps1Order = JsonRpcMethod<Lsps1GetOrderRequest, Lsps1GetOrderResponse, ()>;
pub type Lsps2GetVersions = JsonRpcMethod<NoParams, Lsps2GetVersionsResponse, ()>;
pub type Lsps2GetInfo = JsonRpcMethod<Lsps2GetInfoRequest, Lsp2GetInfoResponse, ()>;
pub type Lsps2Buy = JsonRpcMethod<Lsps2BuyRequest, Lsps2BuyResponse, ()>;
pub type Lsps0ListProtocols = JsonRpcMethod<NoParams, ProtocolList, DefaultError>;
pub type Lsps1Info = JsonRpcMethod<NoParams, Lsps1InfoResponse, DefaultError>;
pub type Lsps1Order = JsonRpcMethod<Lsps1GetOrderRequest, Lsps1GetOrderResponse, DefaultError>;
pub type Lsps2GetVersions = JsonRpcMethod<NoParams, Lsps2GetVersionsResponse, DefaultError>;
pub type Lsps2GetInfo = JsonRpcMethod<Lsps2GetInfoRequest, Lsp2GetInfoResponse, Lsp2GetInfoError>;
pub type Lsps2Buy = JsonRpcMethod<Lsps2BuyRequest, Lsps2BuyResponse, Lsps2BuyError>;


pub const LSPS0_LIST_PROTOCOLS: Lsps0ListProtocols =
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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 {
Expand All @@ -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 {

Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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\"";

Expand Down
1 change: 1 addition & 0 deletions libs/gl-client/src/lsps/mod.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down

0 comments on commit ce2d9fa

Please sign in to comment.