use serde::de::Error as DeError;
use serde::{Deserialize, Serialize};
use crate::lsps::error::map_json_rpc_error_code_to_str;
use crate::lsps::json_rpc::MapErrorCode;
use crate::lsps::lsps0::common_schemas::{IsoDatetime, MsatAmount, ShortChannelId};
const MAX_PROMISE_LEN_BYTES: usize = 512;
#[derive(Debug)]
struct Promise {
promise: String,
}
impl Promise {
fn new(promise: String) -> Result<Self, String> {
if promise.len() <= MAX_PROMISE_LEN_BYTES {
Ok(Promise { promise })
} else {
Err(String::from("Promise exceeds maximum length"))
}
}
}
impl Serialize for Promise {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&self.promise)
}
}
impl<'de> Deserialize<'de> for Promise {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let str_repr = String::deserialize(deserializer)?;
let promise = Promise::new(str_repr.clone());
format!("{:?}", &promise);
Promise::new(str_repr.clone())
.map_err(|_| D::Error::custom("promise exceeds max length"))
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Lsps2GetVersionsResponse {
versions: Vec<u64>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Lsps2GetInfoRequest {
version: i64,
token: Option<String>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Lsps2GetInfoResponse {
opening_fee_params_menu: Vec<OpeningFeeParamsMenuItem>,
min_payment_size_msat: String,
max_payment_size_msat: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Lsps2GetInfoError {}
impl MapErrorCode for Lsps2GetInfoError {
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 {
min_fee_msat: MsatAmount,
proportional: u64,
valid_until: IsoDatetime,
min_lifetime: u64,
max_client_to_self_delay: u64,
promise: Promise,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Lsps2BuyRequest {
version: String,
opening_fee_params: OpeningFeeParamsMenuItem,
payment_size_msat: MsatAmount,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Lsps2BuyResponse {
jit_channel_scid: ShortChannelId,
lsp_cltv_expiry_delta: u64,
#[serde(default)]
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 {
use super::*;
#[test]
fn parsing_error_when_opening_fee_menu_has_extra_fields() {
let fee_menu_item = serde_json::json!(
{
"min_fee_msat": "546000",
"proportional": 1200,
"valid_until": "2023-02-23T08:47:30.511Z",
"min_lifetime": 1008,
"max_client_to_self_delay": 2016,
"promise": "abcdefghijklmnopqrstuvwxyz",
"extra_field" : "This shouldn't be their"
}
);
let parsed_opening_fee_menu_item: Result<OpeningFeeParamsMenuItem, _> =
serde_json::from_value(fee_menu_item);
assert!(
parsed_opening_fee_menu_item.is_err_and(|x| format!("{}", x).contains("extra_field"))
)
}
#[test]
fn parse_valid_promise() {
let promise_json = "\"abcdefghijklmnopqrstuvwxyz\"";
let promise = serde_json::from_str::<Promise>(promise_json).expect("Can parse promise");
assert_eq!(promise.promise, "abcdefghijklmnopqrstuvwxyz");
}
#[test]
fn parse_too_long_promise_fails() {
let a_513_chars = "\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"";
let a_512_chars = "\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"";
serde_json::from_str::<Promise>(a_512_chars)
.expect("Should fail because 512 bytes is the max length");
serde_json::from_str::<Promise>(a_513_chars).expect_err("Should fail to parse promise ");
}
#[test]
fn client_trust_lsp_defaults_to_false() {
let data = serde_json::json!({
"jit_channel_scid" : "0x12x12",
"lsp_cltv_expiry_delta" : 144
});
let buy_response =
serde_json::from_value::<Lsps2BuyResponse>(data).expect("The response can be parsed");
assert!(
!buy_response.client_trusts_lsp,
"If the field is absent it assumed the client should not trust the LSP"
)
}
}