plugins: lsps: refactor lsps2 and lsps0 error enums

Split them up how they are actually defined.

This commit is part of a series that refactor the lsp plugin crate into
a modularized crate with the goal to separate the actual plugin runtime
as much as possible from library code to make it accessible for 3rd
party plugin implementations.

Signed-off-by: Peter Neuroth <pet.v.ne@gmail.com>
This commit is contained in:
Peter Neuroth
2025-11-26 14:08:14 +01:00
committed by madelinevibes
parent b88dc5972f
commit b7be5ef3a2
8 changed files with 92 additions and 106 deletions

7
Cargo.lock generated
View File

@@ -553,6 +553,7 @@ dependencies = [
"cln-rpc",
"hex",
"log",
"paste",
"rand 0.9.2",
"serde",
"serde_json",
@@ -1763,6 +1764,12 @@ dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "paste"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
name = "pem"
version = "3.0.5"

View File

@@ -20,6 +20,7 @@ cln-plugin = { workspace = true }
cln-rpc = { workspace = true }
hex = "0.4"
log = "0.4"
paste = "1.0.15"
rand = "0.9"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

View File

@@ -1,4 +1,4 @@
use crate::proto::jsonrpc::{Result, RpcError};
use crate::proto::jsonrpc::{Result, RpcError, RpcErrorExt as _};
use async_trait::async_trait;
use log::{debug, trace};
use std::{collections::HashMap, sync::Arc};

View File

@@ -5,7 +5,7 @@ use crate::{
DS_MAIN_KEY, DS_SUB_KEY,
},
proto::{
jsonrpc::{JsonRpcResponse as _, RequestObject, RpcError},
jsonrpc::{JsonRpcResponse, RequestObject, RpcError, RpcErrorExt as _},
lsps0::{Msat, ShortChannelId},
lsps2::{
compute_opening_fee,

View File

@@ -205,6 +205,38 @@ where
}
}
/// Macro to generate RpcError helper methods for protocol-specific error codes
///
/// This generates two methods for each error code:
/// - `method_name(message)` - Creates error without data
/// - `method_name_with_data(message, data)` - Creates error with data
macro_rules! rpc_error_methods {
($($method:ident => $code:expr),* $(,)?) => {
$(
paste::paste! {
fn $method<T: std::fmt::Display>(message: T) -> $crate::proto::jsonrpc::RpcError {
$crate::proto::jsonrpc::RpcError {
code: $code,
message: message.to_string(),
data: None,
}
}
fn [<$method _with_data>]<T: std::fmt::Display>(
message: T,
data: serde_json::Value,
) -> $crate::proto::jsonrpc::RpcError {
$crate::proto::jsonrpc::RpcError {
code: $code,
message: message.to_string(),
data: Some(data),
}
}
}
)*
};
}
/// # RpcError
///
/// Represents an error object in a JSON-RPC 2.0 Response object (section 5.1).
@@ -255,75 +287,20 @@ impl RpcError {
data: Some(data),
}
}
}
/// Invalid JSON was received by the server.
/// An error occurred on the server while parsing the JSON text.
pub fn parse_error<T: core::fmt::Display>(message: T) -> Self {
Self::custom_error(PARSE_ERROR, message)
}
/// Invalid JSON was received by the server.
/// An error occurred on the server while parsing the JSON text.
pub fn parse_error_with_data<T: core::fmt::Display>(
message: T,
data: serde_json::Value,
) -> Self {
Self::custom_error_with_data(PARSE_ERROR, message, data)
}
/// The JSON sent is not a valid Request object.
pub fn invalid_request<T: core::fmt::Display>(message: T) -> Self {
Self::custom_error(INVALID_REQUEST, message)
}
/// The JSON sent is not a valid Request object.
pub fn invalid_request_with_data<T: core::fmt::Display>(
message: T,
data: serde_json::Value,
) -> Self {
Self::custom_error_with_data(INVALID_REQUEST, message, data)
}
/// The method does not exist / is not available.
pub fn method_not_found<T: core::fmt::Display>(message: T) -> Self {
Self::custom_error(METHOD_NOT_FOUND, message)
}
/// The method does not exist / is not available.
pub fn method_not_found_with_data<T: core::fmt::Display>(
message: T,
data: serde_json::Value,
) -> Self {
Self::custom_error_with_data(METHOD_NOT_FOUND, message, data)
}
/// Invalid method parameter(s).
pub fn invalid_params<T: core::fmt::Display>(message: T) -> Self {
Self::custom_error(INVALID_PARAMS, message)
}
/// Invalid method parameter(s).
pub fn invalid_params_with_data<T: core::fmt::Display>(
message: T,
data: serde_json::Value,
) -> Self {
Self::custom_error_with_data(INVALID_PARAMS, message, data)
}
/// Internal JSON-RPC error.
pub fn internal_error<T: core::fmt::Display>(message: T) -> Self {
Self::custom_error(INTERNAL_ERROR, message)
}
/// Internal JSON-RPC error.
pub fn internal_error_with_data<T: core::fmt::Display>(
message: T,
data: serde_json::Value,
) -> Self {
Self::custom_error_with_data(INTERNAL_ERROR, message, data)
pub trait RpcErrorExt {
rpc_error_methods! {
parse_error => PARSE_ERROR,
internal_error => INTERNAL_ERROR,
invalid_params => INVALID_PARAMS,
method_not_found => METHOD_NOT_FOUND,
invalid_request => INVALID_REQUEST,
}
}
impl RpcErrorExt for RpcError {}
impl fmt::Display for RpcError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(

View File

@@ -1,4 +1,4 @@
use crate::proto::jsonrpc::JsonRpcRequest;
use crate::proto::jsonrpc::{JsonRpcRequest, RpcError};
use core::fmt;
use serde::{
de::{self},
@@ -13,19 +13,20 @@ pub const LSP_FEATURE_BIT: usize = 729;
// Required message type for BOLT8 transport.
pub const LSPS0_MESSAGE_TYPE: u16 = 37913;
// Lsps0 error definitions. Are in the range 00000 to 00099.
pub const CLIENT_REJECTED: i64 = 1;
pub enum Error {
ClientRejected(String),
// Lsps0 specific error codes defined in BLIP-50.
// Are in the range 00000 to 00099.
pub mod error_codes {
pub const CLIENT_REJECTED: i64 = 001;
}
impl Error {
pub fn client_rejected(msg: String) -> Error {
Self::ClientRejected(msg)
pub trait LSPS0RpcErrorExt {
rpc_error_methods! {
client_rejected => error_codes::CLIENT_REJECTED,
}
}
impl LSPS0RpcErrorExt for RpcError {}
/// Represents a monetary amount as defined in LSPS0.msat. Is converted to a
/// `String` in json messages with a suffix `_msat` or `_sat` and internally
/// represented as Millisatoshi `u64`.

View File

@@ -1,6 +1,6 @@
use crate::{
proto::jsonrpc::{JsonRpcRequest, RpcError},
proto::lsps0::{DateTime, Msat, Ppm, ShortChannelId},
use crate::proto::{
jsonrpc::{JsonRpcRequest, RpcError},
lsps0::{DateTime, Msat, Ppm, ShortChannelId},
};
use bitcoin::hashes::{sha256, Hash, HashEngine, Hmac, HmacEngine};
use chrono::Utc;
@@ -12,55 +12,54 @@ pub mod failure_codes {
pub const UNKNOWN_NEXT_PEER: &'static str = "4010";
}
// Lsps2 specific error codes defined in BLIP-52.
// Are in the range 00200 to 00299.
pub mod error_codes {
pub const INVALID_OPENING_FEE_PARAMS: i64 = 201;
pub const PAYMENT_SIZE_TOO_SMALL: i64 = 202;
pub const PAYMENT_SIZE_TOO_LARGE: i64 = 203;
}
#[derive(Clone, Debug, PartialEq)]
pub enum Error {
InvalidOpeningFeeParams,
PaymentSizeTooSmall,
PaymentSizeTooLarge,
ClientRejected,
}
impl core::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let err_str = match self {
Error::InvalidOpeningFeeParams => "invalid opening fee params",
Error::PaymentSizeTooSmall => "payment size too small",
Error::PaymentSizeTooLarge => "payment size too large",
Error::ClientRejected => "client rejected",
Error::InvalidOpeningFeeParams => "Invalid opening fee params",
Error::PaymentSizeTooSmall => "Payment size too small",
Error::PaymentSizeTooLarge => "Payment size too large",
};
write!(f, "{}", &err_str)
}
}
impl From<Error> for RpcError {
fn from(value: Error) -> Self {
match value {
Error::InvalidOpeningFeeParams => RpcError {
code: 201,
message: "invalid opening fee params".to_string(),
data: None,
},
Error::PaymentSizeTooSmall => RpcError {
code: 202,
message: "payment size too small".to_string(),
data: None,
},
Error::PaymentSizeTooLarge => RpcError {
code: 203,
message: "payment size too large".to_string(),
data: None,
},
Error::ClientRejected => RpcError {
code: 001,
message: "client rejected".to_string(),
data: None,
},
fn from(error: Error) -> Self {
match error {
Error::InvalidOpeningFeeParams => RpcError::invalid_opening_fee_params(error),
Error::PaymentSizeTooSmall => RpcError::payment_size_too_small(error),
Error::PaymentSizeTooLarge => RpcError::payment_size_too_large(error),
}
}
}
impl core::error::Error for Error {}
pub trait LSPS2RpcErrorExt {
rpc_error_methods! {
invalid_opening_fee_params => error_codes::INVALID_OPENING_FEE_PARAMS,
payment_size_too_small => error_codes::PAYMENT_SIZE_TOO_SMALL,
payment_size_too_large => error_codes::PAYMENT_SIZE_TOO_LARGE
}
}
impl LSPS2RpcErrorExt for RpcError {}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Lsps2GetInfoRequest {
#[serde(skip_serializing_if = "Option::is_none")]

View File

@@ -1,3 +1,4 @@
#[macro_use]
pub mod jsonrpc;
pub mod lsps0;
pub mod lsps2;