diff --git a/src/lsps0/ser.rs b/src/lsps0/ser.rs index f0a2bfa..16b61f7 100644 --- a/src/lsps0/ser.rs +++ b/src/lsps0/ser.rs @@ -15,11 +15,11 @@ use crate::lsps1::msgs::{ use crate::lsps2::msgs::{ LSPS2Message, LSPS2Request, LSPS2Response, LSPS2_BUY_METHOD_NAME, LSPS2_GET_INFO_METHOD_NAME, }; -use crate::prelude::{HashMap, String, ToString, Vec}; +use crate::prelude::{HashMap, String, ToString}; -use lightning::impl_writeable_msg; use lightning::ln::msgs::LightningError; use lightning::ln::wire; +use lightning::util::ser::WithoutLength; use bitcoin::secp256k1::PublicKey; @@ -42,6 +42,8 @@ pub(crate) const JSONRPC_ERROR_FIELD_KEY: &str = "error"; pub(crate) const JSONRPC_INVALID_MESSAGE_ERROR_CODE: i32 = -32700; pub(crate) const JSONRPC_INVALID_MESSAGE_ERROR_MESSAGE: &str = "parse error"; +pub(crate) const _LSPS0_CLIENT_REJECTED_ERROR_CODE: i32 = 001; + #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub(crate) enum LSPSMethod { LSPS0ListProtocols, @@ -160,7 +162,23 @@ pub struct RawLSPSMessage { pub payload: String, } -impl_writeable_msg!(RawLSPSMessage, { payload }, {}); +// We encode `RawLSPSMessage`'s payload without a length prefix as LSPS0 expects it to be the +// remainder of the object. +impl lightning::util::ser::Writeable for RawLSPSMessage { + fn write( + &self, w: &mut W, + ) -> Result<(), lightning::io::Error> { + WithoutLength(&self.payload).write(w)?; + Ok(()) + } +} + +impl lightning::util::ser::Readable for RawLSPSMessage { + fn read(r: &mut R) -> Result { + let payload_without_length = WithoutLength::read(r)?; + Ok(Self { payload: payload_without_length.0 }) + } +} impl wire::Type for RawLSPSMessage { fn type_id(&self) -> u16 { diff --git a/src/lsps1/msgs.rs b/src/lsps1/msgs.rs index 58c48a3..d518c3c 100644 --- a/src/lsps1/msgs.rs +++ b/src/lsps1/msgs.rs @@ -21,9 +21,7 @@ pub(crate) const LSPS1_CREATE_ORDER_METHOD_NAME: &str = "lsps1.create_order"; pub(crate) const LSPS1_GET_ORDER_METHOD_NAME: &str = "lsps1.get_order"; pub(crate) const LSPS1_CREATE_ORDER_REQUEST_INVALID_PARAMS_ERROR_CODE: i32 = -32602; -pub(crate) const LSPS1_CREATE_ORDER_REQUEST_ORDER_MISMATCH_ERROR_CODE: i32 = 1000; -pub(crate) const LSPS1_CREATE_ORDER_REQUEST_CLIENT_REJECTED_ERROR_CODE: i32 = 1001; -pub(crate) const LSPS1_CREATE_ORDER_REQUEST_INVALID_TOKEN_ERROR_CODE: i32 = 2; +pub(crate) const LSPS1_CREATE_ORDER_REQUEST_ORDER_MISMATCH_ERROR_CODE: i32 = 100; /// The identifier of an order. #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, Hash)] diff --git a/src/lsps2/client.rs b/src/lsps2/client.rs index 05431dd..e00ad44 100644 --- a/src/lsps2/client.rs +++ b/src/lsps2/client.rs @@ -269,7 +269,7 @@ where action: ErrorAction::IgnoreAndLog(Level::Info), })?; - if let Ok(intercept_scid) = result.intercept_scid.to_scid() { + if let Ok(intercept_scid) = result.jit_channel_scid.to_scid() { self.pending_events.enqueue(Event::LSPS2Client( LSPS2ClientEvent::InvoiceParametersReady { request_id, @@ -283,7 +283,7 @@ where return Err(LightningError { err: format!( "Received buy response with an invalid intercept scid {:?}", - result.intercept_scid + result.jit_channel_scid ), action: ErrorAction::IgnoreAndLog(Level::Info), }); diff --git a/src/lsps2/msgs.rs b/src/lsps2/msgs.rs index c83e41f..3ef34aa 100644 --- a/src/lsps2/msgs.rs +++ b/src/lsps2/msgs.rs @@ -17,11 +17,11 @@ use crate::utils; pub(crate) const LSPS2_GET_INFO_METHOD_NAME: &str = "lsps2.get_info"; pub(crate) const LSPS2_BUY_METHOD_NAME: &str = "lsps2.buy"; -pub(crate) const LSPS2_GET_INFO_REQUEST_UNRECOGNIZED_OR_STALE_TOKEN_ERROR_CODE: i32 = 2; +pub(crate) const LSPS2_GET_INFO_REQUEST_UNRECOGNIZED_OR_STALE_TOKEN_ERROR_CODE: i32 = 200; -pub(crate) const LSPS2_BUY_REQUEST_INVALID_OPENING_FEE_PARAMS_ERROR_CODE: i32 = 2; -pub(crate) const LSPS2_BUY_REQUEST_PAYMENT_SIZE_TOO_SMALL_ERROR_CODE: i32 = 3; -pub(crate) const LSPS2_BUY_REQUEST_PAYMENT_SIZE_TOO_LARGE_ERROR_CODE: i32 = 4; +pub(crate) const LSPS2_BUY_REQUEST_INVALID_OPENING_FEE_PARAMS_ERROR_CODE: i32 = 201; +pub(crate) const LSPS2_BUY_REQUEST_PAYMENT_SIZE_TOO_SMALL_ERROR_CODE: i32 = 202; +pub(crate) const LSPS2_BUY_REQUEST_PAYMENT_SIZE_TOO_LARGE_ERROR_CODE: i32 = 203; #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] /// A request made to an LSP to learn their current channel fees and parameters. @@ -150,7 +150,7 @@ impl InterceptScid { #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub struct BuyResponse { /// The intercept short channel id used by LSP to identify need to open channel. - pub intercept_scid: InterceptScid, + pub jit_channel_scid: InterceptScid, /// The locktime expiry delta the lsp requires. pub lsp_cltv_expiry_delta: u32, /// A flag that indicates who is trusting who. @@ -375,4 +375,56 @@ mod tests { let json_str = r#"{"opening_fee_params":{"max_client_to_self_delay":128,"max_payment_size_msat":"100000000","min_fee_msat":"100","min_lifetime":144,"min_payment_size_msat":"1","promise":"1134a5c51e3ba2e8f4259610d5e12c1bf4c50ddcd3f8af563e0a00d1fff41dea","proportional":21,"valid_until":"2023-05-20T08:30:45Z"},"payment_size_msat":null}"#; assert_eq!(buy_request_variable, serde_json::from_str(json_str).unwrap()); } + + #[test] + fn parse_spec_test_vectors() { + // Here, we simply assert that we're able to parse all examples given in LSPS2. + let json_str = r#"{ + "opening_fee_params_menu": [ + { + "min_fee_msat": "546000", + "proportional": 1200, + "valid_until": "2023-02-23T08:47:30.511Z", + "min_lifetime": 1008, + "max_client_to_self_delay": 2016, + "min_payment_size_msat": "1000", + "max_payment_size_msat": "1000000", + "promise": "abcdefghijklmnopqrstuvwxyz" + }, + { + "min_fee_msat": "1092000", + "proportional": 2400, + "valid_until": "2023-02-27T21:23:57.984Z", + "min_lifetime": 1008, + "max_client_to_self_delay": 2016, + "min_payment_size_msat": "1000", + "max_payment_size_msat": "1000000", + "promise": "abcdefghijklmnopqrstuvwxyz" + } + ] + }"#; + let _get_info_response: GetInfoResponse = serde_json::from_str(json_str).unwrap(); + + let json_str = r#"{ + "opening_fee_params": { + "min_fee_msat": "546000", + "proportional": 1200, + "valid_until": "2023-02-23T08:47:30.511Z", + "min_lifetime": 1008, + "max_client_to_self_delay": 2016, + "min_payment_size_msat": "1000", + "max_payment_size_msat": "1000000", + "promise": "abcdefghijklmnopqrstuvwxyz" + }, + "payment_size_msat": "42000" + }"#; + let _buy_request: BuyRequest = serde_json::from_str(json_str).unwrap(); + + let json_str = r#"{ + "jit_channel_scid": "29451x4815x1", + "lsp_cltv_expiry_delta" : 144, + "client_trusts_lsp": false + }"#; + let _buy_response: BuyResponse = serde_json::from_str(json_str).unwrap(); + } } diff --git a/src/lsps2/service.rs b/src/lsps2/service.rs index 4f1eb59..34025c8 100644 --- a/src/lsps2/service.rs +++ b/src/lsps2/service.rs @@ -639,7 +639,7 @@ where .insert_outbound_channel(intercept_scid, outbound_jit_channel); let response = LSPS2Response::Buy(BuyResponse { - intercept_scid: intercept_scid.into(), + jit_channel_scid: intercept_scid.into(), lsp_cltv_expiry_delta: cltv_expiry_delta, client_trusts_lsp, });