From d810594a3af568ec81c2b96bcc2e144c0d61c59f Mon Sep 17 00:00:00 2001 From: Srg213 Date: Wed, 25 Oct 2023 08:01:38 +0530 Subject: [PATCH 1/4] add LSPS1 messages --- src/channel_request/mod.rs | 1 + src/channel_request/msgs.rs | 192 +++++++++++++++++++++++++++++++ src/transport/message_handler.rs | 11 +- src/transport/msgs.rs | 105 ++++++++++++++++- 4 files changed, 307 insertions(+), 2 deletions(-) create mode 100644 src/channel_request/msgs.rs diff --git a/src/channel_request/mod.rs b/src/channel_request/mod.rs index b3b102c..fea74fa 100644 --- a/src/channel_request/mod.rs +++ b/src/channel_request/mod.rs @@ -8,3 +8,4 @@ // licenses. //! Types and primitives that implement the LSPS1: Channel Request specification. +pub mod msgs; \ No newline at end of file diff --git a/src/channel_request/msgs.rs b/src/channel_request/msgs.rs new file mode 100644 index 0000000..1f0b15b --- /dev/null +++ b/src/channel_request/msgs.rs @@ -0,0 +1,192 @@ +use std::convert::TryFrom; + +use serde::{Deserialize, Serialize}; + +//use bitcoin::hashes::hmac::{Hmac, HmacEngine}; +//use bitcoin::hashes::sha256::Hash as Sha256; +//use bitcoin::hashes::{Hash, HashEngine}; +use crate::transport::msgs::{LSPSMessage, RequestId, ResponseError}; + +pub(crate) const LSPS1_GET_INFO_METHOD_NAME: &str = "lsps1.get_info"; +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 REFUND_ONCHAIN_ADDRESS: bool = false; + +// Create a const to show preferred way for user payment +// Should this be set everytime before payment? +// Ask user for lighting or onchain and then set the const to +// lightning or onchain + +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, Hash)] +pub struct OrderId(pub String); + +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, Default)] +#[serde(default)] +pub struct GetInfoRequest {} + +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +pub struct OptionsSupported { + pub minimum_channel_confirmations: u8, + pub minimum_onchain_payment_confirmations: u8, + pub supports_zero_channel_reserve: bool, + pub min_onchain_payment_size_sat: Option, + pub max_channel_expiry_blocks: u32, + pub min_initial_client_balance_sat: u64, + pub max_initial_client_balance_sat: u64, + pub min_initial_lsp_balance_sat: u64, + pub max_initial_lsp_balance_sat: u64, + pub min_channel_balance_sat: u64, + pub max_channel_balance_sat: u64, +} + +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +pub struct GetInfoResponse { + pub supported_versions: Vec, + pub website: String, + // This field should not be null + pub options: OptionsSupported, +} + +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +pub struct CreateOrderRequest { + pub order: Order, +} + +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +pub struct Order { + // order_id should be separate field + pub order_id: Option, + pub api_version: u16, + pub lsp_balance_sat: u64, + pub client_balance_sat: u64, + pub confirms_within_blocks: u32, + pub channel_expiry_blocks: u32, + pub token: String, + pub announce_channel: bool, + pub refund_onchain_address: Option, +} + +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +pub struct CreateOrderResponse { + pub order: Order, + pub order_id: OrderId, + pub order_state: OrderState, + pub created_at: String, + pub expires_at: String, + pub payment: Payment, + pub channel: Option, +} + +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +pub enum OrderState { + Requested, + Created, + Completed, + Failed, +} + +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +pub struct Payment { + pub state: PaymentState, + pub fee_total_sat: u64, + pub order_total_sat: u64, + pub onchain_address: String, + pub bolt11_invoice: String, + pub onchain_block_confirmations_required: u8, + pub minimum_fee_for_0conf: u8, + pub onchain_payment: OnchainPayment, +} + +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +pub enum PaymentState { + ExpectPayment, + Hold, + Paid, + Refunded, +} + +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +pub struct OnchainPayment { + pub outpoint: String, + pub sat: u64, + pub confirmed: bool, +} + +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +pub struct ChannelInfo { + pub state: ChannelStatus, + pub funded_at: String, + pub funding_outpoint: String, + pub scid: Option, + pub expires_at: String, + pub closing_transaction: Option, + pub closed_at: Option, +} + +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +pub enum ChannelStatus { + Opening, + Opened, + Closed, +} + +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +pub struct GetOrderRequest { + pub order_id: OrderId, +} + +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +pub struct GetOrderResponse { + pub response: Order, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum LSPS1Request { + GetInfo(GetInfoRequest), + CreateOrder(CreateOrderRequest), + GetOrder(GetOrderRequest), +} + +impl LSPS1Request { + pub fn method(&self) -> &str { + match self { + LSPS1Request::GetInfo(_) => LSPS1_GET_INFO_METHOD_NAME, + LSPS1Request::CreateOrder(_) => LSPS1_CREATE_ORDER_METHOD_NAME, + LSPS1Request::GetOrder(_) => LSPS1_GET_ORDER_METHOD_NAME, + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum LSPS1Response { + GetInfo(GetInfoResponse), + CreateOrder(CreateOrderResponse), + OrderError(ResponseError), + GetOrder(GetOrderResponse), + GetOrderError(ResponseError), +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum LSPS1Message { + Request(RequestId, LSPS1Request), + Response(RequestId, LSPS1Response), +} + +impl TryFrom for LSPS1Message { + type Error = (); + + fn try_from(message: LSPSMessage) -> Result { + if let LSPSMessage::LSPS1(message) = message { + return Ok(message); + } + + Err(()) + } +} + +impl From for LSPSMessage { + fn from(message: LSPS1Message) -> Self { + LSPSMessage::LSPS1(message) + } +} diff --git a/src/transport/message_handler.rs b/src/transport/message_handler.rs index 300ee65..e4d09e4 100644 --- a/src/transport/message_handler.rs +++ b/src/transport/message_handler.rs @@ -2,7 +2,7 @@ use crate::events::{Event, EventQueue}; use crate::jit_channel::channel_manager::JITChannelManager; use crate::jit_channel::msgs::{OpeningFeeParams, RawOpeningFeeParams}; use crate::transport::msgs::RequestId; -use crate::transport::msgs::{LSPSMessage, RawLSPSMessage, LSPS_MESSAGE_TYPE_ID}; +use crate::transport::msgs::{LSPSMessage, RawLSPSMessage, LSPS_MESSAGE_TYPE_ID_ID}; use crate::transport::protocol::LSPS0MessageHandler; use lightning::chain::chaininterface::{BroadcasterInterface, FeeEstimator}; @@ -404,6 +404,15 @@ where { return Err(LightningError { err: format!("Received LSPS2 message without LSPS2 message handler configured. From node = {:?}", sender_node_id), action: ErrorAction::IgnoreAndLog(Level::Info)}); } }, + _ => { + return Err(LightningError { + err: format!( + "Received message without message handler configured. From node = {:?}", + sender_node_id + ), + action: ErrorAction::IgnoreAndLog(Level::Info), + }); + } } Ok(()) } diff --git a/src/transport/msgs.rs b/src/transport/msgs.rs index 99b2578..2fc81ac 100644 --- a/src/transport/msgs.rs +++ b/src/transport/msgs.rs @@ -2,7 +2,10 @@ use crate::jit_channel::msgs::{ LSPS2Message, LSPS2Request, LSPS2Response, LSPS2_BUY_METHOD_NAME, LSPS2_GET_INFO_METHOD_NAME, LSPS2_GET_VERSIONS_METHOD_NAME, }; - +use crate::channel_request::msgs::{ + LSPS1Message, LSPS1Request, LSPS1Response, LSPS1_CREATE_ORDER_METHOD_NAME, LSPS1_GET_INFO_METHOD_NAME, + LSPS1_GET_ORDER_METHOD_NAME, +}; use lightning::impl_writeable_msg; use lightning::ln::wire; use serde::de; @@ -95,6 +98,7 @@ impl TryFrom for LSPS0Message { LSPSMessage::Invalid => Err(()), LSPSMessage::LSPS0(message) => Ok(message), LSPSMessage::LSPS2(_) => Err(()), + LSPSMessage::LSPS1(_) => Err(()), } } } @@ -110,6 +114,7 @@ pub enum LSPSMessage { Invalid, LSPS0(LSPS0Message), LSPS2(LSPS2Message), + LSPS1(LSPS1Message), } impl LSPSMessage { @@ -129,6 +134,9 @@ impl LSPSMessage { LSPSMessage::LSPS2(LSPS2Message::Request(request_id, request)) => { Some((request_id.0.clone(), request.method().to_string())) } + LSPSMessage::LSPS1(LSPS1Message::Request(request_id, request)) => { + Some((request_id.0.clone(), request.method().to_string())) + } _ => None, } } @@ -167,6 +175,43 @@ impl Serialize for LSPSMessage { } } } + LSPSMessage::LSPS1(LSPS1Message::Request(request_id, request)) => { + jsonrpc_object.serialize_field(JSONRPC_ID_FIELD_KEY, &request_id.0)?; + jsonrpc_object.serialize_field(JSONRPC_METHOD_FIELD_KEY, request.method())?; + + match request { + LSPS1Request::GetInfo(params) => { + jsonrpc_object.serialize_field(JSONRPC_PARAMS_FIELD_KEY, params)? + } + LSPS1Request::CreateOrder(params) => { + jsonrpc_object.serialize_field(JSONRPC_PARAMS_FIELD_KEY, params)? + } + LSPS1Request::GetOrder(params) => { + jsonrpc_object.serialize_field(JSONRPC_PARAMS_FIELD_KEY, params)? + } + } + } + LSPSMessage::LSPS1(LSPS1Message::Response(request_id, response)) => { + jsonrpc_object.serialize_field(JSONRPC_ID_FIELD_KEY, &request_id.0)?; + + match response { + LSPS1Response::GetInfo(result) => { + jsonrpc_object.serialize_field(JSONRPC_RESULT_FIELD_KEY, result)? + } + LSPS1Response::CreateOrder(result) => { + jsonrpc_object.serialize_field(JSONRPC_ERROR_FIELD_KEY, result)? + } + LSPS1Response::OrderError(error) => { + jsonrpc_object.serialize_field(JSONRPC_RESULT_FIELD_KEY, error)? + } + LSPS1Response::GetOrder(result) => { + jsonrpc_object.serialize_field(JSONRPC_ERROR_FIELD_KEY, result)? + } + LSPS1Response::GetOrderError(error) => { + jsonrpc_object.serialize_field(JSONRPC_ERROR_FIELD_KEY, &error)? + } + } + } LSPSMessage::LSPS2(LSPS2Message::Request(request_id, request)) => { jsonrpc_object.serialize_field(JSONRPC_ID_FIELD_KEY, &request_id.0)?; jsonrpc_object.serialize_field(JSONRPC_METHOD_FIELD_KEY, request.method())?; @@ -274,6 +319,30 @@ impl<'de, 'a> Visitor<'de> for LSPSMessageVisitor<'a> { LSPS0Request::ListProtocols(ListProtocolsRequest {}), ))) } + LSPS1_GET_INFO_METHOD_NAME => { + let request = serde_json::from_value(params.unwrap_or(json!({}))) + .map_err(de::Error::custom)?; + Ok(LSPSMessage::LSPS1(LSPS1Message::Request( + RequestId(id), + LSPS1Request::GetInfo(request), + ))) + } + LSPS1_CREATE_ORDER_METHOD_NAME => { + let request = serde_json::from_value(params.unwrap_or(json!({}))) + .map_err(de::Error::custom)?; + Ok(LSPSMessage::LSPS1(LSPS1Message::Request( + RequestId(id), + LSPS1Request::CreateOrder(request), + ))) + } + LSPS1_GET_ORDER_METHOD_NAME => { + let request = serde_json::from_value(params.unwrap_or(json!({}))) + .map_err(de::Error::custom)?; + Ok(LSPSMessage::LSPS1(LSPS1Message::Request( + RequestId(id), + LSPS1Request::GetOrder(request), + ))) + } LSPS2_GET_VERSIONS_METHOD_NAME => { let request = serde_json::from_value(params.unwrap_or(json!({}))) .map_err(de::Error::custom)?; @@ -334,6 +403,40 @@ impl<'de, 'a> Visitor<'de> for LSPSMessageVisitor<'a> { Err(de::Error::custom("Received invalid lsps2.get_versions response.")) } } + LSPS1_CREATE_ORDER_METHOD_NAME => { + if let Some(error) = error { + Ok(LSPSMessage::LSPS1(LSPS1Message::Response( + RequestId(id), + LSPS1Response::OrderError(error), + ))) + } else if let Some(result) = result { + let response = + serde_json::from_value(result).map_err(de::Error::custom)?; + Ok(LSPSMessage::LSPS1(LSPS1Message::Response( + RequestId(id), + LSPS1Response::CreateOrder(response), + ))) + } else { + Err(de::Error::custom("Received invalid JSON-RPC object: one of method, result, or error required")) + } + } + LSPS1_GET_ORDER_METHOD_NAME => { + if let Some(error) = error { + Ok(LSPSMessage::LSPS1(LSPS1Message::Response( + RequestId(id), + LSPS1Response::GetOrderError(error), + ))) + } else if let Some(result) = result { + let response = + serde_json::from_value(result).map_err(de::Error::custom)?; + Ok(LSPSMessage::LSPS1(LSPS1Message::Response( + RequestId(id), + LSPS1Response::GetOrder(response), + ))) + } else { + Err(de::Error::custom("Received invalid JSON-RPC object: one of method, result, or error required")) + } + } LSPS2_GET_INFO_METHOD_NAME => { if let Some(error) = error { Ok(LSPSMessage::LSPS2(LSPS2Message::Response( From 62ab73466e42d9c99fcd87b5ef65c8ff055966cf Mon Sep 17 00:00:00 2001 From: Srg213 Date: Wed, 25 Oct 2023 08:11:58 +0530 Subject: [PATCH 2/4] Added LSPS1 events --- src/channel_request/event.rs | 66 ++++++++++++++++++++++++++++++++++++ src/channel_request/mod.rs | 3 +- src/events.rs | 4 +++ src/lib.rs | 1 + src/transport/msgs.rs | 8 ++--- 5 files changed, 77 insertions(+), 5 deletions(-) create mode 100644 src/channel_request/event.rs diff --git a/src/channel_request/event.rs b/src/channel_request/event.rs new file mode 100644 index 0000000..e5bf5a8 --- /dev/null +++ b/src/channel_request/event.rs @@ -0,0 +1,66 @@ +use bitcoin::secp256k1::PublicKey; + +use super::msgs::{ChannelInfo, OptionsSupported, Order, OrderId, Payment}; +use crate::transport::msgs::RequestId; + +/// An Event which you should probably take some action in response to. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum Event { + GetInfoResponse { + request_id: RequestId, + + counterparty_node_id: PublicKey, + + version: Vec, + + website: String, + + options_supported: OptionsSupported, + }, + + CreateInvoice { + request_id: RequestId, + + counterparty_node_id: PublicKey, + + order: Order, + + order_id: OrderId, + }, + + DisplayOrder { + request_id: RequestId, + + counterparty_node_id: PublicKey, + + order: Order, + + payment: Payment, + + channel: Option, + }, + + PayforChannel { + request_id: RequestId, + + counterparty_node_id: PublicKey, + + order: Order, + + payment: Payment, + + channel: Option, + }, + + CheckPaymentConfirmation { + request_id: RequestId, + + counterparty_node_id: PublicKey, + + order_id: OrderId, + }, + + OpenChannel {}, + + Refund {}, +} diff --git a/src/channel_request/mod.rs b/src/channel_request/mod.rs index fea74fa..01353a6 100644 --- a/src/channel_request/mod.rs +++ b/src/channel_request/mod.rs @@ -8,4 +8,5 @@ // licenses. //! Types and primitives that implement the LSPS1: Channel Request specification. -pub mod msgs; \ No newline at end of file +pub mod event; +pub mod msgs; diff --git a/src/events.rs b/src/events.rs index f54d9c6..bb1e9b6 100644 --- a/src/events.rs +++ b/src/events.rs @@ -14,6 +14,8 @@ //! [`LiquidityManager::get_and_clear_pending_events`] to receive events. //! //! [`LiquidityManager::get_and_clear_pending_events`]: crate::LiquidityManager::get_and_clear_pending_events +/// [`crate::LiquidityManager::get_and_clear_pending_events()`] to receive events. +use crate::channel_request; use crate::jit_channel; use std::collections::VecDeque; use std::sync::{Condvar, Mutex}; @@ -60,4 +62,6 @@ impl EventQueue { pub enum Event { /// An LSPS2 (JIT Channel) protocol event. LSPS2(jit_channel::LSPS2Event), + /// An LSPS1 protocol event. + LSPS1(channel_request::event::Event), } diff --git a/src/lib.rs b/src/lib.rs index 6c928fd..00cae42 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,6 +24,7 @@ pub mod jit_channel; mod transport; mod utils; +pub use channel_request::event::Event as LSPS1Event; pub use transport::message_handler::{ JITChannelsConfig, LiquidityManager, LiquidityProviderConfig, }; diff --git a/src/transport/msgs.rs b/src/transport/msgs.rs index 2fc81ac..a4a70fb 100644 --- a/src/transport/msgs.rs +++ b/src/transport/msgs.rs @@ -1,11 +1,11 @@ +use crate::channel_request::msgs::{ + LSPS1Message, LSPS1Request, LSPS1Response, LSPS1_CREATE_ORDER_METHOD_NAME, + LSPS1_GET_INFO_METHOD_NAME, LSPS1_GET_ORDER_METHOD_NAME, +}; use crate::jit_channel::msgs::{ LSPS2Message, LSPS2Request, LSPS2Response, LSPS2_BUY_METHOD_NAME, LSPS2_GET_INFO_METHOD_NAME, LSPS2_GET_VERSIONS_METHOD_NAME, }; -use crate::channel_request::msgs::{ - LSPS1Message, LSPS1Request, LSPS1Response, LSPS1_CREATE_ORDER_METHOD_NAME, LSPS1_GET_INFO_METHOD_NAME, - LSPS1_GET_ORDER_METHOD_NAME, -}; use lightning::impl_writeable_msg; use lightning::ln::wire; use serde::de; From 376c93b9b955de151fa688288397f99d24021ee5 Mon Sep 17 00:00:00 2001 From: Srg213 Date: Thu, 9 Nov 2023 17:30:48 +0530 Subject: [PATCH 3/4] Added Channel Manager for LSPS1 --- src/channel_request/channel_manager.rs | 1000 ++++++++++++++++++++++++ src/channel_request/event.rs | 26 +- src/channel_request/mod.rs | 5 +- src/channel_request/msgs.rs | 18 +- src/channel_request/utils.rs | 24 + src/transport/message_handler.rs | 63 +- src/transport/msgs.rs | 7 +- 7 files changed, 1106 insertions(+), 37 deletions(-) create mode 100644 src/channel_request/channel_manager.rs create mode 100644 src/channel_request/utils.rs diff --git a/src/channel_request/channel_manager.rs b/src/channel_request/channel_manager.rs new file mode 100644 index 0000000..84efb2a --- /dev/null +++ b/src/channel_request/channel_manager.rs @@ -0,0 +1,1000 @@ +// This file is Copyright its original authors, visible in version contror +// history. +// +// This file is licensed under the Apache License, Version 2.0 or the MIT license +// , at your option. +// You may not use this file except in accordance with one or both of these +// licenses. + +use std::collections::HashMap; +use std::marker::Sync; +use std::ops::Deref; +use std::option; +use std::sync::{Arc, Mutex, RwLock}; +use chrono::Utc; + +use bitcoin::secp256k1::PublicKey; +use lightning::chain; +use lightning::chain::chaininterface::{BroadcasterInterface, FeeEstimator}; +use lightning::ln::channelmanager::{ChannelManager, InterceptId}; +use lightning::ln::msgs::{ + ChannelMessageHandler, ErrorAction, LightningError, OnionMessageHandler, RoutingMessageHandler, +}; +use lightning::ln::peer_handler::{CustomMessageHandler, PeerManager, SocketDescriptor}; +use lightning::ln::ChannelId; +use lightning::routing::router::Router; +use lightning::sign::{EntropySource, NodeSigner, SignerProvider}; +use lightning::util::errors::APIError; +use lightning::util::logger::{Level, Logger}; + +use crate::events::EventQueue; +use crate::transport::message_handler::{ProtocolMessageHandler, CRChannelConfig}; +use crate::transport::msgs::{LSPSMessage, RequestId}; +use crate::utils; +use crate::{events::Event, transport::msgs::ResponseError}; + +use super::msgs::{ + ChannelInfo, ChannelStatus, CreateOrderRequest, CreateOrderResponse, GetInfoRequest, + GetInfoResponse, GetOrderRequest, GetOrderResponse, LSPS1Message, LSPS1Request, LSPS1Response, + OptionsSupported, Order, OrderId, OrderState, Payment, LSPS1_CREATE_ORDER_REQUEST_INVALID_VERSION_ERROR_CODE, LSPS1_CREATE_ORDER_REQUEST_ORDER_MISMATCH_ERROR_CODE, +}; +use super::utils::{is_valid}; + +const SUPPORTED_SPEC_VERSIONS: [u16; 1] = [1]; + +struct ChannelStateError(String); + +impl From for LightningError { + fn from(value: ChannelStateError) -> Self { + LightningError { err: value.0, action: ErrorAction::IgnoreAndLog(Level::Info) } + } +} + +#[derive(PartialEq, Debug)] +enum InboundRequestState { + InfoRequested, + OptionsSupport { + version: u16, + options_supported: OptionsSupported + }, + OrderRequested { + version: u16, + order: Order, + }, + PendingPayment { order_id: OrderId }, + AwaitingConfirmation { id: u128, order_id: OrderId }, +} + +impl InboundRequestState { + fn info_received(&self, versions: Vec, options: OptionsSupported) -> Result { + let max_shared_version = versions + .iter() + .filter(|version| SUPPORTED_SPEC_VERSIONS.contains(version)) + .max() + .cloned() + .ok_or(ChannelStateError(format!( + "LSP does not support any of our specification versions. ours = {:?}. theirs = {:?}", + SUPPORTED_SPEC_VERSIONS, versions + )))?; + + match self { + InboundRequestState::InfoRequested => { + Ok(InboundRequestState::OptionsSupport { version: max_shared_version, options_supported: options }) + } + state => Err(ChannelStateError(format!( + "Received unexpected get_versions response. Channel was in state: {:?}", + state + ))), + } + } + + pub fn order_requested(&self, order: Order) -> Result { + match self { + InboundRequestState::OptionsSupport { version , options_supported } => { + if is_valid(&order, options_supported) { + Ok(InboundRequestState::OrderRequested { + version: *version, + order, + }) + } + else { + return Err(ChannelStateError(format!( + "The order created does not match options supported by LSP. Options Supported by LSP are {:?}. The order created was {:?}", + options_supported, order + ))); + } + } + state => Err(ChannelStateError(format!( + "Received create order request for wrong channel. Channel was in state: {:?}", + state + ))), + } + } + + pub fn order_received(&self, response_order: &Order, order_id: OrderId) -> Result { + match self { + InboundRequestState::OrderRequested { version, order } => { + if response_order == order { + Ok(InboundRequestState::PendingPayment { order_id }) + } + else { + Err(ChannelStateError(format!( + "Received order is different from created order. The order created was : {:?}. Order Received from LSP is : {:?}", + order, response_order + ))) + } + } + state => Err(ChannelStateError(format!( + "Received unexpected create order response. Channel was in state: {:?}", + state + ))), + } + } + + pub fn pay_for_channel(&self, channel_id: u128) -> Result { + match self { + InboundRequestState::PendingPayment { order_id } => { + Ok(InboundRequestState::AwaitingConfirmation { + id: channel_id, + order_id: order_id.clone(), + }) + } + state => Err(ChannelStateError(format!( + "Received unexpected response. Channel was in state: {:?}", + state + ))), + } + } +} + +struct InboundCRChannel { + id: u128, + state: InboundRequestState, +} + +impl InboundCRChannel { + pub fn new(id: u128) -> Self { + Self { id, state: InboundRequestState::InfoRequested } + } + + pub fn info_received(&mut self, versions: Vec, options: OptionsSupported) -> Result { + self.state = self.state.info_received(versions, options)?; + + match self.state { + InboundRequestState::OptionsSupport { version, .. } => Ok(version), + _ => Err(LightningError { + action: ErrorAction::IgnoreAndLog(Level::Error), + err: "impossible state transition".to_string(), + }), + } + } + + pub fn order_requested(&mut self, order: Order) -> Result { + self.state = self.state.order_requested(order)?; + + match self.state { + InboundRequestState::OrderRequested { version, .. } => { + Ok(version) + } + _ => { + return Err(LightningError { + action: ErrorAction::IgnoreAndLog(Level::Error), + err: "impossible state transition".to_string(), + }); + } + } + } + + pub fn order_received(&mut self, order: &Order, order_id: OrderId) -> Result<(), LightningError> { + self.state = self.state.order_received(order, order_id)?; + Ok(()) + } + + pub fn pay_for_channel(&mut self, channel_id: u128) -> Result<(), LightningError> { + self.state = self.state.pay_for_channel(channel_id)?; + Ok(()) + } +} + +#[derive(PartialEq, Debug)] +enum OutboundRequestState { + OrderCreated { order_id: OrderId }, + WaitingPayment { order_id: OrderId }, + Refund, + Ready, +} + +impl OutboundRequestState { + pub fn create_payment_invoice(&self) -> Result { + match self { + OutboundRequestState::OrderCreated { order_id } => { + Ok(OutboundRequestState::WaitingPayment { order_id: order_id.clone() }) + } + state => Err(ChannelStateError(format!( + "Received unexpected get_versions response. JIT Channel was in state: {:?}", + state + ))), + } + } +} + +struct OutboundCRChannelConfig { + options_supported: OptionsSupported, + /* supported_versions: Vec, */ + zero_conf_channels: bool, + zero_conf_payments: bool, + onchain_payment: bool, +} + +struct OutboundCRChannel { + state: OutboundRequestState, + config: OutboundCRChannelConfig, +} + +impl OutboundCRChannel { + pub fn new(options_supported: OptionsSupported, order_id: OrderId) -> Self { + let zero_conf_channels = options_supported.minimum_channel_confirmations == 0; + let zero_conf_payments = options_supported.minimum_onchain_payment_confirmations == 0; + let onchain_payment = options_supported.min_onchain_payment_size_sat == None; + let supports_zero_channel_reserve = options_supported.supports_zero_channel_reserve; + + Self { + state: OutboundRequestState::OrderCreated { + order_id + }, + config: OutboundCRChannelConfig { + options_supported, + /* supported_versions: options_supported.supports_zero_channel_reserve,*/ + zero_conf_payments, + onchain_payment, + zero_conf_channels, + } + } + } + pub fn create_payment_invoice(&mut self) -> Result<(), LightningError> { + self.state = self.state.create_payment_invoice()?; + Ok(()) + } + + pub fn check_order_validity(&mut self, order: &Order) -> bool { + let options = &self.config.options_supported; + + is_valid(order, options) + } +} + +#[derive(Default)] +struct PeerState { + inbound_channels_by_id: HashMap, + outbound_channels_by_order_id: HashMap, + request_to_cid: HashMap, + orders_to_id: HashMap, + pending_requests: HashMap, +} + +impl PeerState { + pub fn insert_inbound_channel(&mut self, id: u128, channel: InboundCRChannel) { + self.inbound_channels_by_id.insert(id, channel); + } + + pub fn insert_outbound_channel(&mut self, order_id: OrderId, channel: OutboundCRChannel) { + self.outbound_channels_by_order_id.insert(order_id, channel); + } + + pub fn insert_request(&mut self, request_id: RequestId, channel_id: u128) { + self.request_to_cid.insert(request_id, channel_id); + } + + pub fn remove_inbound_channel(&mut self, id: u128) { + self.inbound_channels_by_id.remove(&id); + } + + pub fn remove_outbound_channel(&mut self, order_id: OrderId) { + self.outbound_channels_by_order_id.remove(&order_id); + } +} + +pub struct CRManager< + ES: Deref, + M: Deref, + T: Deref, + F: Deref, + R: Deref, + SP: Deref, + Descriptor: SocketDescriptor, + L: Deref, + RM: Deref, + CM: Deref, + OM: Deref, + CMH: Deref, + NS: Deref, +> where + ES::Target: EntropySource, + M::Target: chain::Watch<::Signer>, + T::Target: BroadcasterInterface, + F::Target: FeeEstimator, + R::Target: Router, + SP::Target: SignerProvider, + L::Target: Logger, + RM::Target: RoutingMessageHandler, + CM::Target: ChannelMessageHandler, + OM::Target: OnionMessageHandler, + CMH::Target: CustomMessageHandler, + NS::Target: NodeSigner, +{ + entropy_source: ES, + peer_manager: Mutex>>>, + channel_manager: Arc>, + pending_messages: Arc>>, + pending_events: Arc, + per_peer_state: RwLock>>, + options_config: Option, + website: Option, + max_fees: Option, +} + +impl< + ES: Deref, + M: Deref, + T: Deref, + F: Deref, + R: Deref, + SP: Deref, + Descriptor: SocketDescriptor, + L: Deref, + RM: Deref, + CM: Deref, + OM: Deref, + CMH: Deref, + NS: Deref, + > CRManager +where + ES::Target: EntropySource, + M::Target: chain::Watch<::Signer>, + T::Target: BroadcasterInterface, + F::Target: FeeEstimator, + R::Target: Router, + SP::Target: SignerProvider, + L::Target: Logger, + RM::Target: RoutingMessageHandler, + CM::Target: ChannelMessageHandler, + OM::Target: OnionMessageHandler, + CMH::Target: CustomMessageHandler, + NS::Target: NodeSigner, +{ + pub(crate) fn new( + entropy_source: ES, config: &CRChannelConfig, + pending_messages: Arc>>, + pending_events: Arc, + channel_manager: Arc>, + ) -> Self { + Self { + entropy_source, + pending_messages, + pending_events, + per_peer_state: RwLock::new(HashMap::new()), + peer_manager: Mutex::new(None), + channel_manager, + options_config: config.options_supported.clone(), + website: config.website.clone(), + max_fees: config.max_fees, + } + } + + pub fn set_peer_manager( + &self, peer_manager: Arc>, + ) { + *self.peer_manager.lock().unwrap() = Some(peer_manager); + } + + pub fn request_for_info(&self, counterparty_node_id: PublicKey, channel_id: u128) { + let channel = InboundCRChannel::new(channel_id); + + let mut outer_state_lock = self.per_peer_state.write().unwrap(); + let inner_state_lock = outer_state_lock + .entry(counterparty_node_id) + .or_insert(Mutex::new(PeerState::default())); + let peer_state = inner_state_lock.get_mut().unwrap(); + peer_state.insert_inbound_channel(channel_id, channel); + + let request_id = self.generate_request_id(); + peer_state.insert_request(request_id.clone(), channel_id); + + { + let mut pending_messages = self.pending_messages.lock().unwrap(); + pending_messages.push(( + counterparty_node_id, + LSPS1Message::Request(request_id, LSPS1Request::GetInfo(GetInfoRequest {})).into(), + )); + } + + if let Some(peer_manager) = self.peer_manager.lock().unwrap().as_ref() { + peer_manager.process_events(); + } + } + + fn handle_get_info_request( + &self, request_id: RequestId, counterparty_node_id: &PublicKey, + ) -> Result<(), LightningError> { + let response = GetInfoResponse { + supported_versions: SUPPORTED_SPEC_VERSIONS.to_vec(), + website: self.website.clone().unwrap().to_string(), + options: self.options_config.clone().ok_or(LightningError{ + err: format!("Configuration for LSP server not set."), + action: ErrorAction::IgnoreAndLog(Level::Info), + }).unwrap(), + }; + + self.enqueue_response(*counterparty_node_id, request_id, LSPS1Response::GetInfo(response)); + Ok(()) + } + + fn handle_get_info_response( + &self, request_id: RequestId, counterparty_node_id: &PublicKey, result: GetInfoResponse, + ) -> Result<(), LightningError> { + let outer_state_lock = self.per_peer_state.write().unwrap(); + + match outer_state_lock.get(counterparty_node_id) { + Some(inner_state_lock) => { + let mut peer_state = inner_state_lock.lock().unwrap(); + + let channel_id = + peer_state.request_to_cid.remove(&request_id).ok_or(LightningError { + err: format!( + "Received get_versions response for an unknown request: {:?}", + request_id + ), + action: ErrorAction::IgnoreAndLog(Level::Info), + })?; + + let inbound_channel = peer_state + .inbound_channels_by_id + .get_mut(&channel_id) + .ok_or(LightningError { + err: format!( + "Received get_versions response for an unknown channel: {:?}", + channel_id + ), + action: ErrorAction::IgnoreAndLog(Level::Info), + })?; + + let version = match inbound_channel.info_received(result.supported_versions, result.options.clone()) { + Ok(version) => version, + Err(e) => { + peer_state.remove_inbound_channel(channel_id); + return Err(e); + } + }; + + + self.enqueue_event(Event::LSPS1(super::event::Event::GetInfoResponse { + id: channel_id, + request_id, + counterparty_node_id: *counterparty_node_id, + version, + website: result.website, + options_supported: result.options, + })) + } + None => { + return Err(LightningError { + err: format!( + "Received get_info response from unknown peer: {:?}", + counterparty_node_id + ), + action: ErrorAction::IgnoreAndLog(Level::Info), + }) + } + } + Ok(()) + } + + pub fn place_order( + &self, channel_id: u128, counterparty_node_id: &PublicKey, order: Order, + ) -> Result<(), APIError> { + let outer_state_lock = self.per_peer_state.write().unwrap(); + + match outer_state_lock.get(counterparty_node_id) { + Some(inner_state_lock) => { + let mut peer_state = inner_state_lock.lock().unwrap(); + + let inbound_channel = peer_state + .inbound_channels_by_id + .get_mut(&channel_id) + .ok_or(APIError::APIMisuseError { + err: format!("Channel with id {} not found", channel_id), + })?; + + let version = match inbound_channel.order_requested(order.clone()) { + Ok(version) => version, + Err(e) => { + peer_state.remove_inbound_channel(channel_id); + return Err(APIError::APIMisuseError { err: e.err }); + } + }; + + let request_id = self.generate_request_id(); + peer_state.insert_request(request_id.clone(), channel_id); + + { + let mut pending_messages = self.pending_messages.lock().unwrap(); + pending_messages.push(( + *counterparty_node_id, + LSPS1Message::Request( + request_id, + LSPS1Request::CreateOrder(CreateOrderRequest { + order, + version: version + }), + ) + .into(), + )); + } + if let Some(peer_manager) = self.peer_manager.lock().unwrap().as_ref() { + peer_manager.process_events(); + } + } + None => { + return Err(APIError::APIMisuseError { + err: format!("No existing state with counterparty {}", counterparty_node_id), + }) + } + } + Ok(()) + } + + fn handle_create_order_request( + &self, request_id: RequestId, counterparty_node_id: &PublicKey, params: CreateOrderRequest, + ) -> Result<(), LightningError> { + + if !SUPPORTED_SPEC_VERSIONS.contains(¶ms.version) { + self.enqueue_response( + *counterparty_node_id, + request_id, + LSPS1Response::CreateOrderError(ResponseError { + code: LSPS1_CREATE_ORDER_REQUEST_INVALID_VERSION_ERROR_CODE, + message: format!("version {} is not supported", params.version), + data: Some(format!("Supported versions are {:?}", SUPPORTED_SPEC_VERSIONS)), + }), + ); + return Err(LightningError { + err: format!("client requested unsupported version {}", params.version), + action: ErrorAction::IgnoreAndLog(Level::Info), + }); + } + + if !is_valid(¶ms.order, &self.options_config.as_ref().unwrap()) { + self.enqueue_response( + *counterparty_node_id, + request_id, + LSPS1Response::CreateOrderError(ResponseError { + code: LSPS1_CREATE_ORDER_REQUEST_ORDER_MISMATCH_ERROR_CODE, + message: format!("Order does not match options supported by LSP server"), + data: Some(format!("Supported options are {:?}", &self.options_config.as_ref().unwrap())), + }), + ); + return Err(LightningError { + err: format!("client requested unsupported version {}", params.version), + action: ErrorAction::IgnoreAndLog(Level::Info), + }); + } + + let mut outer_state_lock = self.per_peer_state.write().unwrap(); + + let mut inner_state_lock = outer_state_lock + .entry(*counterparty_node_id) + .or_insert(Mutex::new(PeerState::default())); + let peer_state = inner_state_lock.get_mut().unwrap(); + + peer_state + .pending_requests + .insert(request_id.clone(), LSPS1Request::CreateOrder(params.clone())); + + self.enqueue_event(Event::LSPS1(super::event::Event::CreateInvoice { + request_id, + counterparty_node_id: *counterparty_node_id, + order: params.order, + })); + + Ok(()) + } + + pub fn send_invoice_for_order( + &self, request_id: RequestId, counterparty_node_id: &PublicKey, payment: Payment, created_at: chrono::DateTime, expires_at: chrono::DateTime, + ) -> Result<(), APIError> { + let outer_state_lock = self.per_peer_state.read().unwrap(); + + match outer_state_lock.get(counterparty_node_id) { + Some(inner_state_lock) => { + let mut peer_state = inner_state_lock.lock().unwrap(); + + match peer_state.pending_requests.remove(&request_id) { + Some(LSPS1Request::CreateOrder(params)) => { + let order_id = self.generate_order_id(); + let channel = OutboundCRChannel::new(self.options_config.clone().unwrap(), order_id.clone()); + + peer_state.insert_outbound_channel(order_id.clone(), channel); + + peer_state.orders_to_id.insert(order_id.clone(), params.order.clone()); + + self.enqueue_response( + *counterparty_node_id, + request_id, + LSPS1Response::CreateOrder(CreateOrderResponse { + order: params.order, + order_id, + order_state: OrderState::Created, + created_at, + expires_at, + payment, + channel: None, + }), + ); + } + + _ => return Err(APIError::APIMisuseError { + err: format!("No pending buy request for request_id: {:?}", request_id), + }), + } + } + None => return Err(APIError::APIMisuseError { + err: format!("No state for the counterparty exists: {:?}", counterparty_node_id), + }), + } + + Ok(()) + } + + fn handle_create_order_response( + &self, request_id: RequestId, counterparty_node_id: &PublicKey, + response: CreateOrderResponse, + ) -> Result<(), LightningError> { + let outer_state_lock = self.per_peer_state.read().unwrap(); + match outer_state_lock.get(&counterparty_node_id) { + Some(inner_state_lock) => { + let mut peer_state = inner_state_lock.lock().unwrap(); + + let channel_id = + peer_state.request_to_cid.remove(&request_id).ok_or(LightningError { + err: format!( + "Received get_versions response for an unknown request: {:?}", + request_id + ), + action: ErrorAction::IgnoreAndLog(Level::Info), + })?; + + let inbound_channel = peer_state + .inbound_channels_by_id + .get_mut(&channel_id) + .ok_or(LightningError { + err: format!( + "Received get_versions response for an unknown channel: {:?}", + channel_id + ), + action: ErrorAction::IgnoreAndLog(Level::Info), + } + )?; + + if let Err(e) = inbound_channel.order_received(&response.order, response.order_id.clone()) { + peer_state.remove_inbound_channel(channel_id); + return Err(e); + } + + let total_fees = response.payment.fee_total_sat + response.order.client_balance_sat; + let max_fees = self.max_fees.unwrap_or(u64::MAX); + + if total_fees == response.payment.order_total_sat && total_fees < max_fees { + + self.enqueue_event(Event::LSPS1(super::event::Event::DisplayOrder { + id: channel_id, + counterparty_node_id: *counterparty_node_id, + order: response.order, + payment: response.payment, + channel: response.channel, + })); + } else { + peer_state.remove_inbound_channel(channel_id); + return Err(LightningError { + err: format!("Fees are too high : {:?}", total_fees), + action: ErrorAction::IgnoreAndLog(Level::Info), + }); + } + } + None => { + return Err(LightningError { + err: format!("No existing state with counterparty {}", counterparty_node_id), + action: ErrorAction::IgnoreAndLog(Level::Info), + }) + } + } + + Ok(()) + } + + fn handle_create_order_error( + &self, request_id: RequestId, counterparty_node_id: &PublicKey, error: ResponseError, + ) -> Result<(), LightningError> { + let outer_state_lock = self.per_peer_state.read().unwrap(); + match outer_state_lock.get(&counterparty_node_id) { + Some(inner_state_lock) => { + let mut peer_state = inner_state_lock.lock().unwrap(); + + let channel_id = + peer_state.request_to_cid.remove(&request_id).ok_or(LightningError { + err: format!( + "Received get_versions response for an unknown request: {:?}", + request_id + ), + action: ErrorAction::IgnoreAndLog(Level::Info), + })?; + + let inbound_channel = peer_state + .inbound_channels_by_id + .get_mut(&channel_id) + .ok_or(LightningError { + err: format!( + "Received get_versions response for an unknown channel: {:?}", + channel_id + ), + action: ErrorAction::IgnoreAndLog(Level::Info), + } + )?; + + match &inbound_channel.state { + InboundRequestState::OrderRequested { version, order } => { + return Err(LightningError { err: format!( "Received error response from create order request ({:?}) with counterparty {:?}. Error code = {}, message = {}", request_id, counterparty_node_id, error.code, error.message), action: ErrorAction::IgnoreAndLog(Level::Info)}); + } + _ => { + return Err(LightningError { err: format!("Received an unexpected error response for a create order request from counterparty ({:?})", counterparty_node_id), action: ErrorAction::IgnoreAndLog(Level::Info)}); + } + } + } + None => { + return Err(LightningError { err: format!("Received error response for a create order request from an unknown counterparty ({:?})",counterparty_node_id), action: ErrorAction::IgnoreAndLog(Level::Info)}); + } + } + Ok(()) + } + + // Send a request to get the order status + pub fn check_order_status( + &self, counterparty_node_id: &PublicKey, order_id: OrderId, channel_id: u128, + ) -> Result<(), APIError> { + let outer_state_lock = self.per_peer_state.write().unwrap(); + match outer_state_lock.get(&counterparty_node_id) { + Some(inner_state_lock) => { + let mut peer_state = inner_state_lock.lock().unwrap(); + + if let Some(inbound_channel) = + peer_state.inbound_channels_by_id.get_mut(&channel_id) + { + if let Err(e) = inbound_channel.pay_for_channel(channel_id) { + peer_state.remove_inbound_channel(channel_id); + return Err(APIError::APIMisuseError { + err: e.err + }); + } + + let request_id = self.generate_request_id(); + peer_state.insert_request(request_id.clone(), channel_id); + + { + let mut pending_messages = self.pending_messages.lock().unwrap(); + pending_messages.push(( + *counterparty_node_id, + LSPS1Message::Request( + request_id, + LSPS1Request::GetOrder(GetOrderRequest { order_id: order_id.clone() }), + ) + .into(), + )); + } + if let Some(peer_manager) = self.peer_manager.lock().unwrap().as_ref() { + peer_manager.process_events(); + } + } else { + return Err(APIError::APIMisuseError { + err: format!("Channel with id {} not found", channel_id), + }); + } + } + None => { + return Err(APIError::APIMisuseError { + err: format!("No existing state with counterparty {}", counterparty_node_id), + }) + } + } + + Ok(()) + } + + fn handle_get_order_request( + &self, request_id: RequestId, counterparty_node_id: &PublicKey, params: GetOrderRequest, + ) -> Result<(), LightningError> { + let outer_state_lock = self.per_peer_state.read().unwrap(); + match outer_state_lock.get(&counterparty_node_id) { + Some(inner_state_lock) => { + let mut peer_state = inner_state_lock.lock().unwrap(); + + let outbound_channel = peer_state + .outbound_channels_by_order_id + .get_mut(¶ms.order_id) + .ok_or(LightningError { + err: format!( + "Received get order request for unknown order id {:?}", + params.order_id + ), + action: ErrorAction::IgnoreAndLog(Level::Info), + })?; + + + if let Err(e) = outbound_channel.create_payment_invoice() { + peer_state.outbound_channels_by_order_id.remove(¶ms.order_id); + // Check payment confirmation then Refund. + self.enqueue_event(Event::LSPS1(super::event::Event::Refund { request_id, counterparty_node_id: todo!(), order_id: todo!() })); + return Err(e); + } + + peer_state.pending_requests.insert(request_id.clone(),LSPS1Request::GetOrder(params.clone())); + + + self.enqueue_event(Event::LSPS1(super::event::Event::CheckPaymentConfirmation { + request_id, + counterparty_node_id: *counterparty_node_id, + order_id: params.order_id, + })); + } + None => { + return Err(LightningError { + err: format!("Received error response for a create order request from an unknown counterparty ({:?})",counterparty_node_id), + action: ErrorAction::IgnoreAndLog(Level::Info) + }); + } + } + + Ok(()) + } + + pub fn update_order_status( + &self, request_id: RequestId, counterparty_node_id: PublicKey, order_id: OrderId, + request: GetOrderRequest, + ) -> Result<(), APIError> { + let outer_state_lock = self.per_peer_state.read().unwrap(); + + match outer_state_lock.get(&counterparty_node_id) { + Some(inner_state_lock) => { + let peer_state = inner_state_lock.lock().unwrap(); + + let order = peer_state.orders_to_id.get(&order_id).unwrap(); + + self.enqueue_response( + counterparty_node_id, + request_id, + LSPS1Response::GetOrder(GetOrderResponse { response: order.clone() }), + ) + } + None => { + return Err(APIError::APIMisuseError { + err: format!("No existing state with counterparty {}", counterparty_node_id), + }) + } + } + Ok(()) + } + + fn handle_get_order_response(&self, request_id: RequestId, counterparty_node_id: &PublicKey, params: GetOrderResponse) + -> Result<(), LightningError>{ + // TODO + Ok(()) + } + + fn handle_get_order_error(&self,request_id: RequestId, counterparty_node_id: &PublicKey, params: ResponseError) + -> Result<(), LightningError>{ + // TODO: Channel Open/Refund Case. + Ok(()) + } + + fn enqueue_response( + &self, counterparty_node_id: PublicKey, request_id: RequestId, response: LSPS1Response, + ) { + { + let mut pending_messages = self.pending_messages.lock().unwrap(); + pending_messages + .push((counterparty_node_id,LSPS1Message::Response(request_id, response).into())); + } + + if let Some(peer_manager) = self.peer_manager.lock().unwrap().as_ref() { + peer_manager.process_events(); + } + } + + fn enqueue_event(&self, event: Event) { + self.pending_events.enqueue(event); + } + + fn generate_channel_id(&self) -> u128 { + let bytes = self.entropy_source.get_secure_random_bytes(); + let mut id_bytes: [u8; 16] = [0; 16]; + id_bytes.copy_from_slice(&bytes[0..16]); + u128::from_be_bytes(id_bytes) + } + + fn generate_request_id(&self) -> RequestId { + let bytes = self.entropy_source.get_secure_random_bytes(); + RequestId(utils::hex_str(&bytes[0..16])) + } + + fn generate_order_id(&self) -> OrderId { + let bytes = self.entropy_source.get_secure_random_bytes(); + OrderId(utils::hex_str(&bytes[0..16])) + } +} + +impl< + ES: Deref, + M: Deref, + T: Deref, + F: Deref, + R: Deref, + SP: Deref, + Descriptor: SocketDescriptor, + L: Deref, + RM: Deref, + CM: Deref, + OM: Deref, + CMH: Deref, + NS: Deref, + > ProtocolMessageHandler for CRManager +where + ES::Target: EntropySource, + M::Target: chain::Watch<::Signer>, + T::Target: BroadcasterInterface, + F::Target: FeeEstimator, + R::Target: Router, + SP::Target: SignerProvider, + L::Target: Logger, + RM::Target: RoutingMessageHandler, + CM::Target: ChannelMessageHandler, + OM::Target: OnionMessageHandler, + CMH::Target: CustomMessageHandler, + NS::Target: NodeSigner, +{ + type ProtocolMessage = LSPS1Message; + const PROTOCOL_NUMBER: Option = Some(2); + + fn handle_message( + &self, message: Self::ProtocolMessage, counterparty_node_id: &PublicKey, + ) -> Result<(), LightningError> { + match message { + LSPS1Message::Request(request_id, request) => match request { + super::msgs::LSPS1Request::GetInfo(_) => { + self.handle_get_info_request(request_id, counterparty_node_id) + } + super::msgs::LSPS1Request::CreateOrder(params) => { + self.handle_create_order_request(request_id, counterparty_node_id, params) + } + super::msgs::LSPS1Request::GetOrder(params) => { + self.handle_get_order_request(request_id, counterparty_node_id, params) + } + }, + LSPS1Message::Response(request_id, response) => match response { + super::msgs::LSPS1Response::GetInfo(params) => { + self.handle_get_info_response(request_id, counterparty_node_id, params) + } + super::msgs::LSPS1Response::CreateOrder(params) => { + self.handle_create_order_response(request_id, counterparty_node_id, params) + } + super::msgs::LSPS1Response::CreateOrderError(params) => { + self.handle_create_order_error(request_id, counterparty_node_id, params) + } + super::msgs::LSPS1Response::GetOrder(params) => { + self.handle_get_order_response(request_id, counterparty_node_id, params) + } + super::msgs::LSPS1Response::GetOrderError(error) => { + self.handle_get_order_error(request_id, counterparty_node_id, error) + } + }, + } + } +} diff --git a/src/channel_request/event.rs b/src/channel_request/event.rs index e5bf5a8..0396c5b 100644 --- a/src/channel_request/event.rs +++ b/src/channel_request/event.rs @@ -1,3 +1,5 @@ +#![allow(missing_docs)] + use bitcoin::secp256k1::PublicKey; use super::msgs::{ChannelInfo, OptionsSupported, Order, OrderId, Payment}; @@ -7,11 +9,13 @@ use crate::transport::msgs::RequestId; #[derive(Clone, Debug, PartialEq, Eq)] pub enum Event { GetInfoResponse { + id: u128, + request_id: RequestId, counterparty_node_id: PublicKey, - version: Vec, + version: u16, website: String, @@ -24,12 +28,10 @@ pub enum Event { counterparty_node_id: PublicKey, order: Order, - - order_id: OrderId, }, DisplayOrder { - request_id: RequestId, + id: u128, counterparty_node_id: PublicKey, @@ -60,7 +62,19 @@ pub enum Event { order_id: OrderId, }, - OpenChannel {}, + OpenChannel { + request_id: RequestId, + + counterparty_node_id: PublicKey, - Refund {}, + order_id: OrderId, + }, + + Refund { + request_id: RequestId, + + counterparty_node_id: PublicKey, + + order_id: OrderId, + }, } diff --git a/src/channel_request/mod.rs b/src/channel_request/mod.rs index 01353a6..07f75f1 100644 --- a/src/channel_request/mod.rs +++ b/src/channel_request/mod.rs @@ -8,5 +8,8 @@ // licenses. //! Types and primitives that implement the LSPS1: Channel Request specification. -pub mod event; +pub(crate) mod channel_manager; +pub(crate) mod event; +/// Message, request, and other primitive types used to implement LSPS2. pub mod msgs; +pub(crate) mod utils; diff --git a/src/channel_request/msgs.rs b/src/channel_request/msgs.rs index 1f0b15b..4e9c755 100644 --- a/src/channel_request/msgs.rs +++ b/src/channel_request/msgs.rs @@ -1,4 +1,5 @@ use std::convert::TryFrom; +use chrono::Utc; use serde::{Deserialize, Serialize}; @@ -11,7 +12,11 @@ pub(crate) const LSPS1_GET_INFO_METHOD_NAME: &str = "lsps1.get_info"; 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 REFUND_ONCHAIN_ADDRESS: bool = false; +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_VERSION_ERROR_CODE: i32 = 1; +pub(crate) const LSPS1_CREATE_ORDER_REQUEST_INVALID_TOKEN_ERROR_CODE: i32 = 2; // Create a const to show preferred way for user payment // Should this be set everytime before payment? @@ -50,13 +55,12 @@ pub struct GetInfoResponse { #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub struct CreateOrderRequest { + pub version: u16, pub order: Order, } #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub struct Order { - // order_id should be separate field - pub order_id: Option, pub api_version: u16, pub lsp_balance_sat: u64, pub client_balance_sat: u64, @@ -69,11 +73,11 @@ pub struct Order { #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub struct CreateOrderResponse { - pub order: Order, pub order_id: OrderId, + pub order: Order, pub order_state: OrderState, - pub created_at: String, - pub expires_at: String, + pub created_at: chrono::DateTime, + pub expires_at: chrono::DateTime, pub payment: Payment, pub channel: Option, } @@ -162,7 +166,7 @@ impl LSPS1Request { pub enum LSPS1Response { GetInfo(GetInfoResponse), CreateOrder(CreateOrderResponse), - OrderError(ResponseError), + CreateOrderError(ResponseError), GetOrder(GetOrderResponse), GetOrderError(ResponseError), } diff --git a/src/channel_request/utils.rs b/src/channel_request/utils.rs new file mode 100644 index 0000000..e302a40 --- /dev/null +++ b/src/channel_request/utils.rs @@ -0,0 +1,24 @@ +use std::option; + +use super::msgs::{OptionsSupported, Order}; + +pub fn check_range(min: u64, max: u64, value: u64) -> bool { + (value >= min) && (value <= max) +} + +pub fn is_valid(order: &Order, options: &OptionsSupported) -> bool { + let bool = check_range( + options.min_initial_client_balance_sat, + options.max_initial_client_balance_sat, + order.client_balance_sat, + ) && check_range( + options.min_initial_lsp_balance_sat, + options.max_initial_lsp_balance_sat, + order.lsp_balance_sat, + ) && check_range( + 1, + options.max_channel_expiry_blocks.into(), + order.channel_expiry_blocks.into()); + + bool +} diff --git a/src/transport/message_handler.rs b/src/transport/message_handler.rs index e4d09e4..20b5ca7 100644 --- a/src/transport/message_handler.rs +++ b/src/transport/message_handler.rs @@ -1,3 +1,5 @@ +use crate::channel_request::channel_manager::CRManager; +use crate::channel_request::msgs::OptionsSupported; use crate::events::{Event, EventQueue}; use crate::jit_channel::channel_manager::JITChannelManager; use crate::jit_channel::msgs::{OpeningFeeParams, RawOpeningFeeParams}; @@ -11,8 +13,8 @@ use lightning::ln::channelmanager::{ChainParameters, ChannelManager, InterceptId use lightning::ln::features::{InitFeatures, NodeFeatures}; use lightning::ln::msgs::{ ChannelMessageHandler, ErrorAction, LightningError, OnionMessageHandler, RoutingMessageHandler, -}; -use lightning::ln::peer_handler::{CustomMessageHandler, PeerManager, SocketDescriptor}; +, RoutingMessageHandler, ChannelMessageHandler, OnionMessageHandler}; +use lightning::ln::peer_handler::{{CustomMessageHandler, PeerManager, SocketDescriptor}, SocketDescriptor}; use lightning::ln::wire::CustomMessageReader; use lightning::ln::ChannelId; use lightning::routing::router::Router; @@ -50,6 +52,8 @@ pub(crate) trait ProtocolMessageHandler { /// Allows end-user to configure options when using the [`LiquidityManager`] /// to provide liquidity services to clients. pub struct LiquidityProviderConfig { + /// LSPS1 Configuration + pub lsps1_config: Option, /// Optional configuration for JIT channels /// should you want to support them. pub jit_channels: Option, @@ -67,6 +71,16 @@ pub struct JITChannelsConfig { pub max_payment_size_msat: u64, } +pub struct CRChannelConfig { + pub token: Option, + + pub max_fees: Option, + + pub options_supported: Option, + + pub website: Option, +} + /// The main interface into LSP functionality. /// /// Should be used as a [`CustomMessageHandler`] for your @@ -120,6 +134,8 @@ pub struct LiquidityManager< pending_events: Arc, request_id_to_method_map: Mutex>, lsps0_message_handler: LSPS0MessageHandler, + lsps1_message_handler: + Option>, lsps2_message_handler: Option>, provider_config: Option, @@ -130,21 +146,16 @@ pub struct LiquidityManager< } impl< - ES: Deref + Clone, + ES: Deref, M: Deref, T: Deref, F: Deref, R: Deref, SP: Deref, L: Deref, - Descriptor: SocketDescriptor, - RM: Deref, - CM: Deref, - OM: Deref, - CMH: Deref, NS: Deref, C: Deref, - > LiquidityManager + > LiquidityManager where ES::Target: EntropySource, M::Target: chain::Watch<::Signer>, @@ -173,7 +184,7 @@ where { let pending_events = Arc::new(EventQueue::default()); let lsps0_message_handler = - LSPS0MessageHandler::new(entropy_source.clone(), vec![], Arc::clone(&pending_messages)); + LSPS0MessageHandler::new(entropy_source.clone().clone(), vec![], Arc::clone(&pending_messages)); let lsps2_message_handler = provider_config.as_ref().and_then(|config| { config.jit_channels.as_ref().map(|jit_channels_config| { @@ -187,11 +198,24 @@ where { }) }); + let lsps1_message_handler = provider_config.as_ref().and_then(|config| { + config.lsps1_config.as_ref().map(|lsps1_config| { + CRManager::new( + entropy_source.clone(), + lsps1_config, + Arc::clone(&pending_messages), + Arc::clone(&pending_events), + Arc::clone(&channel_manager), + ) + }) + }); + Self { pending_messages, pending_events, request_id_to_method_map: Mutex::new(HashMap::new()), lsps0_message_handler, + lsps1_message_handler, lsps2_message_handler, provider_config, channel_manager, @@ -404,15 +428,14 @@ where { return Err(LightningError { err: format!("Received LSPS2 message without LSPS2 message handler configured. From node = {:?}", sender_node_id), action: ErrorAction::IgnoreAndLog(Level::Info)}); } }, - _ => { - return Err(LightningError { - err: format!( - "Received message without message handler configured. From node = {:?}", - sender_node_id - ), - action: ErrorAction::IgnoreAndLog(Level::Info), - }); - } + LSPSMessage::LSPS1(msg) => match &self.lsps1_message_handler { + Some(lsps1_message_handler) => { + lsps1_message_handler.handle_message(msg, sender_node_id)?; + } + None => { + return Err(LightningError { err: format!("Received LSPS1 message without LSPS1 message handler configured. From node = {:?}", sender_node_id), action: ErrorAction::IgnoreAndLog(Level::Info)}); + } + }, } Ok(()) } @@ -424,7 +447,7 @@ where { } impl< - ES: Deref + Clone, + ES: Deref + Clone + Clone, M: Deref, T: Deref, F: Deref, diff --git a/src/transport/msgs.rs b/src/transport/msgs.rs index a4a70fb..f9ab605 100644 --- a/src/transport/msgs.rs +++ b/src/transport/msgs.rs @@ -134,9 +134,10 @@ impl LSPSMessage { LSPSMessage::LSPS2(LSPS2Message::Request(request_id, request)) => { Some((request_id.0.clone(), request.method().to_string())) } - LSPSMessage::LSPS1(LSPS1Message::Request(request_id, request)) => { - Some((request_id.0.clone(), request.method().to_string())) - } + LSPSMessage::LSPS1(LSPS1Message::Request( + request_id, + request, + )) => Some((request_id.0.clone(), request.method().to_string())), _ => None, } } From f1e259ece5325d9ce870eee2e6468cf0153091a4 Mon Sep 17 00:00:00 2001 From: Srg213 Date: Mon, 13 Nov 2023 19:09:27 +0530 Subject: [PATCH 4/4] Add LSPS1 message handler --- src/channel_request/channel_manager.rs | 387 +++++++++++++++---------- src/channel_request/event.rs | 23 +- src/channel_request/msgs.rs | 13 +- src/channel_request/utils.rs | 9 +- src/transport/message_handler.rs | 109 +++++-- src/transport/msgs.rs | 11 +- 6 files changed, 333 insertions(+), 219 deletions(-) diff --git a/src/channel_request/channel_manager.rs b/src/channel_request/channel_manager.rs index 84efb2a..4ec4535 100644 --- a/src/channel_request/channel_manager.rs +++ b/src/channel_request/channel_manager.rs @@ -7,39 +7,37 @@ // You may not use this file except in accordance with one or both of these // licenses. +use chrono::Utc; use std::collections::HashMap; -use std::marker::Sync; use std::ops::Deref; -use std::option; use std::sync::{Arc, Mutex, RwLock}; -use chrono::Utc; use bitcoin::secp256k1::PublicKey; use lightning::chain; use lightning::chain::chaininterface::{BroadcasterInterface, FeeEstimator}; -use lightning::ln::channelmanager::{ChannelManager, InterceptId}; +use lightning::ln::channelmanager::ChannelManager; use lightning::ln::msgs::{ ChannelMessageHandler, ErrorAction, LightningError, OnionMessageHandler, RoutingMessageHandler, }; use lightning::ln::peer_handler::{CustomMessageHandler, PeerManager, SocketDescriptor}; -use lightning::ln::ChannelId; use lightning::routing::router::Router; use lightning::sign::{EntropySource, NodeSigner, SignerProvider}; use lightning::util::errors::APIError; use lightning::util::logger::{Level, Logger}; use crate::events::EventQueue; -use crate::transport::message_handler::{ProtocolMessageHandler, CRChannelConfig}; +use crate::transport::message_handler::{CRChannelConfig, ProtocolMessageHandler}; use crate::transport::msgs::{LSPSMessage, RequestId}; use crate::utils; use crate::{events::Event, transport::msgs::ResponseError}; use super::msgs::{ - ChannelInfo, ChannelStatus, CreateOrderRequest, CreateOrderResponse, GetInfoRequest, - GetInfoResponse, GetOrderRequest, GetOrderResponse, LSPS1Message, LSPS1Request, LSPS1Response, - OptionsSupported, Order, OrderId, OrderState, Payment, LSPS1_CREATE_ORDER_REQUEST_INVALID_VERSION_ERROR_CODE, LSPS1_CREATE_ORDER_REQUEST_ORDER_MISMATCH_ERROR_CODE, + ChannelInfo, CreateOrderRequest, CreateOrderResponse, GetInfoRequest, GetInfoResponse, + GetOrderRequest, GetOrderResponse, LSPS1Message, LSPS1Request, LSPS1Response, OptionsSupported, + Order, OrderId, OrderState, Payment, LSPS1_CREATE_ORDER_REQUEST_INVALID_VERSION_ERROR_CODE, + LSPS1_CREATE_ORDER_REQUEST_ORDER_MISMATCH_ERROR_CODE, }; -use super::utils::{is_valid}; +use super::utils::is_valid; const SUPPORTED_SPEC_VERSIONS: [u16; 1] = [1]; @@ -54,20 +52,16 @@ impl From for LightningError { #[derive(PartialEq, Debug)] enum InboundRequestState { InfoRequested, - OptionsSupport { - version: u16, - options_supported: OptionsSupported - }, - OrderRequested { - version: u16, - order: Order, - }, + OptionsSupport { version: u16, options_supported: OptionsSupported }, + OrderRequested { version: u16, order: Order }, PendingPayment { order_id: OrderId }, AwaitingConfirmation { id: u128, order_id: OrderId }, } impl InboundRequestState { - fn info_received(&self, versions: Vec, options: OptionsSupported) -> Result { + fn info_received( + &self, versions: Vec, options: OptionsSupported, + ) -> Result { let max_shared_version = versions .iter() .filter(|version| SUPPORTED_SPEC_VERSIONS.contains(version)) @@ -79,9 +73,10 @@ impl InboundRequestState { )))?; match self { - InboundRequestState::InfoRequested => { - Ok(InboundRequestState::OptionsSupport { version: max_shared_version, options_supported: options }) - } + InboundRequestState::InfoRequested => Ok(InboundRequestState::OptionsSupport { + version: max_shared_version, + options_supported: options, + }), state => Err(ChannelStateError(format!( "Received unexpected get_versions response. Channel was in state: {:?}", state @@ -91,14 +86,10 @@ impl InboundRequestState { pub fn order_requested(&self, order: Order) -> Result { match self { - InboundRequestState::OptionsSupport { version , options_supported } => { + InboundRequestState::OptionsSupport { version, options_supported } => { if is_valid(&order, options_supported) { - Ok(InboundRequestState::OrderRequested { - version: *version, - order, - }) - } - else { + Ok(InboundRequestState::OrderRequested { version: *version, order }) + } else { return Err(ChannelStateError(format!( "The order created does not match options supported by LSP. Options Supported by LSP are {:?}. The order created was {:?}", options_supported, order @@ -112,13 +103,14 @@ impl InboundRequestState { } } - pub fn order_received(&self, response_order: &Order, order_id: OrderId) -> Result { + pub fn order_received( + &self, response_order: &Order, order_id: OrderId, + ) -> Result { match self { InboundRequestState::OrderRequested { version, order } => { if response_order == order { Ok(InboundRequestState::PendingPayment { order_id }) - } - else { + } else { Err(ChannelStateError(format!( "Received order is different from created order. The order created was : {:?}. Order Received from LSP is : {:?}", order, response_order @@ -145,7 +137,7 @@ impl InboundRequestState { state ))), } - } + } } struct InboundCRChannel { @@ -158,7 +150,9 @@ impl InboundCRChannel { Self { id, state: InboundRequestState::InfoRequested } } - pub fn info_received(&mut self, versions: Vec, options: OptionsSupported) -> Result { + pub fn info_received( + &mut self, versions: Vec, options: OptionsSupported, + ) -> Result { self.state = self.state.info_received(versions, options)?; match self.state { @@ -174,9 +168,7 @@ impl InboundCRChannel { self.state = self.state.order_requested(order)?; match self.state { - InboundRequestState::OrderRequested { version, .. } => { - Ok(version) - } + InboundRequestState::OrderRequested { version, .. } => Ok(version), _ => { return Err(LightningError { action: ErrorAction::IgnoreAndLog(Level::Error), @@ -186,7 +178,9 @@ impl InboundCRChannel { } } - pub fn order_received(&mut self, order: &Order, order_id: OrderId) -> Result<(), LightningError> { + pub fn order_received( + &mut self, order: &Order, order_id: OrderId, + ) -> Result<(), LightningError> { self.state = self.state.order_received(order, order_id)?; Ok(()) } @@ -194,14 +188,13 @@ impl InboundCRChannel { pub fn pay_for_channel(&mut self, channel_id: u128) -> Result<(), LightningError> { self.state = self.state.pay_for_channel(channel_id)?; Ok(()) - } + } } #[derive(PartialEq, Debug)] enum OutboundRequestState { OrderCreated { order_id: OrderId }, WaitingPayment { order_id: OrderId }, - Refund, Ready, } @@ -220,11 +213,10 @@ impl OutboundRequestState { } struct OutboundCRChannelConfig { - options_supported: OptionsSupported, - /* supported_versions: Vec, */ - zero_conf_channels: bool, - zero_conf_payments: bool, - onchain_payment: bool, + order: Order, + created_at: chrono::DateTime, + expires_at: chrono::DateTime, + payment: Payment, } struct OutboundCRChannel { @@ -233,23 +225,13 @@ struct OutboundCRChannel { } impl OutboundCRChannel { - pub fn new(options_supported: OptionsSupported, order_id: OrderId) -> Self { - let zero_conf_channels = options_supported.minimum_channel_confirmations == 0; - let zero_conf_payments = options_supported.minimum_onchain_payment_confirmations == 0; - let onchain_payment = options_supported.min_onchain_payment_size_sat == None; - let supports_zero_channel_reserve = options_supported.supports_zero_channel_reserve; - + pub fn new( + order: Order, created_at: chrono::DateTime, expires_at: chrono::DateTime, + order_id: OrderId, payment: Payment, + ) -> Self { Self { - state: OutboundRequestState::OrderCreated { - order_id - }, - config: OutboundCRChannelConfig { - options_supported, - /* supported_versions: options_supported.supports_zero_channel_reserve,*/ - zero_conf_payments, - onchain_payment, - zero_conf_channels, - } + state: OutboundRequestState::OrderCreated { order_id }, + config: OutboundCRChannelConfig { order, created_at, expires_at, payment }, } } pub fn create_payment_invoice(&mut self) -> Result<(), LightningError> { @@ -257,10 +239,10 @@ impl OutboundCRChannel { Ok(()) } - pub fn check_order_validity(&mut self, order: &Order) -> bool { - let options = &self.config.options_supported; + pub fn check_order_validity(&self, options_supported: &OptionsSupported) -> bool { + let order = &self.config.order; - is_valid(order, options) + is_valid(order, options_supported) } } @@ -269,7 +251,6 @@ struct PeerState { inbound_channels_by_id: HashMap, outbound_channels_by_order_id: HashMap, request_to_cid: HashMap, - orders_to_id: HashMap, pending_requests: HashMap, } @@ -420,10 +401,14 @@ where let response = GetInfoResponse { supported_versions: SUPPORTED_SPEC_VERSIONS.to_vec(), website: self.website.clone().unwrap().to_string(), - options: self.options_config.clone().ok_or(LightningError{ - err: format!("Configuration for LSP server not set."), - action: ErrorAction::IgnoreAndLog(Level::Info), - }).unwrap(), + options: self + .options_config + .clone() + .ok_or(LightningError { + err: format!("Configuration for LSP server not set."), + action: ErrorAction::IgnoreAndLog(Level::Info), + }) + .unwrap(), }; self.enqueue_response(*counterparty_node_id, request_id, LSPS1Response::GetInfo(response)); @@ -442,7 +427,7 @@ where let channel_id = peer_state.request_to_cid.remove(&request_id).ok_or(LightningError { err: format!( - "Received get_versions response for an unknown request: {:?}", + "Received get_info response for an unknown request: {:?}", request_id ), action: ErrorAction::IgnoreAndLog(Level::Info), @@ -453,20 +438,21 @@ where .get_mut(&channel_id) .ok_or(LightningError { err: format!( - "Received get_versions response for an unknown channel: {:?}", + "Received get_info response for an unknown channel: {:?}", channel_id ), action: ErrorAction::IgnoreAndLog(Level::Info), })?; - let version = match inbound_channel.info_received(result.supported_versions, result.options.clone()) { + let version = match inbound_channel + .info_received(result.supported_versions, result.options.clone()) + { Ok(version) => version, Err(e) => { peer_state.remove_inbound_channel(channel_id); return Err(e); } }; - self.enqueue_event(Event::LSPS1(super::event::Event::GetInfoResponse { id: channel_id, @@ -506,7 +492,7 @@ where err: format!("Channel with id {} not found", channel_id), })?; - let version = match inbound_channel.order_requested(order.clone()) { + let version = match inbound_channel.order_requested(order.clone()) { Ok(version) => version, Err(e) => { peer_state.remove_inbound_channel(channel_id); @@ -523,10 +509,7 @@ where *counterparty_node_id, LSPS1Message::Request( request_id, - LSPS1Request::CreateOrder(CreateOrderRequest { - order, - version: version - }), + LSPS1Request::CreateOrder(CreateOrderRequest { order, version }), ) .into(), )); @@ -547,7 +530,6 @@ where fn handle_create_order_request( &self, request_id: RequestId, counterparty_node_id: &PublicKey, params: CreateOrderRequest, ) -> Result<(), LightningError> { - if !SUPPORTED_SPEC_VERSIONS.contains(¶ms.version) { self.enqueue_response( *counterparty_node_id, @@ -571,7 +553,10 @@ where LSPS1Response::CreateOrderError(ResponseError { code: LSPS1_CREATE_ORDER_REQUEST_ORDER_MISMATCH_ERROR_CODE, message: format!("Order does not match options supported by LSP server"), - data: Some(format!("Supported options are {:?}", &self.options_config.as_ref().unwrap())), + data: Some(format!( + "Supported options are {:?}", + &self.options_config.as_ref().unwrap() + )), }), ); return Err(LightningError { @@ -582,7 +567,7 @@ where let mut outer_state_lock = self.per_peer_state.write().unwrap(); - let mut inner_state_lock = outer_state_lock + let inner_state_lock = outer_state_lock .entry(*counterparty_node_id) .or_insert(Mutex::new(PeerState::default())); let peer_state = inner_state_lock.get_mut().unwrap(); @@ -601,7 +586,8 @@ where } pub fn send_invoice_for_order( - &self, request_id: RequestId, counterparty_node_id: &PublicKey, payment: Payment, created_at: chrono::DateTime, expires_at: chrono::DateTime, + &self, request_id: RequestId, counterparty_node_id: &PublicKey, payment: Payment, + created_at: chrono::DateTime, expires_at: chrono::DateTime, ) -> Result<(), APIError> { let outer_state_lock = self.per_peer_state.read().unwrap(); @@ -612,12 +598,16 @@ where match peer_state.pending_requests.remove(&request_id) { Some(LSPS1Request::CreateOrder(params)) => { let order_id = self.generate_order_id(); - let channel = OutboundCRChannel::new(self.options_config.clone().unwrap(), order_id.clone()); + let channel = OutboundCRChannel::new( + params.order.clone(), + created_at.clone(), + expires_at.clone(), + order_id.clone(), + payment.clone(), + ); peer_state.insert_outbound_channel(order_id.clone(), channel); - peer_state.orders_to_id.insert(order_id.clone(), params.order.clone()); - self.enqueue_response( *counterparty_node_id, request_id, @@ -632,15 +622,22 @@ where }), ); } - - _ => return Err(APIError::APIMisuseError { - err: format!("No pending buy request for request_id: {:?}", request_id), - }), + + _ => { + return Err(APIError::APIMisuseError { + err: format!("No pending buy request for request_id: {:?}", request_id), + }) + } } } - None => return Err(APIError::APIMisuseError { - err: format!("No state for the counterparty exists: {:?}", counterparty_node_id), - }), + None => { + return Err(APIError::APIMisuseError { + err: format!( + "No state for the counterparty exists: {:?}", + counterparty_node_id + ), + }) + } } Ok(()) @@ -656,27 +653,28 @@ where let mut peer_state = inner_state_lock.lock().unwrap(); let channel_id = - peer_state.request_to_cid.remove(&request_id).ok_or(LightningError { - err: format!( - "Received get_versions response for an unknown request: {:?}", - request_id - ), - action: ErrorAction::IgnoreAndLog(Level::Info), - })?; + peer_state.request_to_cid.remove(&request_id).ok_or(LightningError { + err: format!( + "Received create_order response for an unknown request: {:?}", + request_id + ), + action: ErrorAction::IgnoreAndLog(Level::Info), + })?; let inbound_channel = peer_state .inbound_channels_by_id .get_mut(&channel_id) .ok_or(LightningError { err: format!( - "Received get_versions response for an unknown channel: {:?}", + "Received create_order response for an unknown channel: {:?}", channel_id ), action: ErrorAction::IgnoreAndLog(Level::Info), - } - )?; + })?; - if let Err(e) = inbound_channel.order_received(&response.order, response.order_id.clone()) { + if let Err(e) = + inbound_channel.order_received(&response.order, response.order_id.clone()) + { peer_state.remove_inbound_channel(channel_id); return Err(e); } @@ -685,7 +683,6 @@ where let max_fees = self.max_fees.unwrap_or(u64::MAX); if total_fees == response.payment.order_total_sat && total_fees < max_fees { - self.enqueue_event(Event::LSPS1(super::event::Event::DisplayOrder { id: channel_id, counterparty_node_id: *counterparty_node_id, @@ -696,14 +693,17 @@ where } else { peer_state.remove_inbound_channel(channel_id); return Err(LightningError { - err: format!("Fees are too high : {:?}", total_fees), - action: ErrorAction::IgnoreAndLog(Level::Info), + err: format!("Fees are too high : {:?}", total_fees), + action: ErrorAction::IgnoreAndLog(Level::Info), }); } } None => { return Err(LightningError { - err: format!("No existing state with counterparty {}", counterparty_node_id), + err: format!( + "Received create_order response from unknown peer: {}", + counterparty_node_id + ), action: ErrorAction::IgnoreAndLog(Level::Info), }) } @@ -721,43 +721,32 @@ where let mut peer_state = inner_state_lock.lock().unwrap(); let channel_id = - peer_state.request_to_cid.remove(&request_id).ok_or(LightningError { - err: format!( - "Received get_versions response for an unknown request: {:?}", - request_id - ), - action: ErrorAction::IgnoreAndLog(Level::Info), - })?; + peer_state.request_to_cid.remove(&request_id).ok_or(LightningError { + err: format!( + "Received create order error for an unknown request: {:?}", + request_id + ), + action: ErrorAction::IgnoreAndLog(Level::Info), + })?; let inbound_channel = peer_state .inbound_channels_by_id .get_mut(&channel_id) .ok_or(LightningError { err: format!( - "Received get_versions response for an unknown channel: {:?}", + "Received create order error for an unknown channel: {:?}", channel_id ), action: ErrorAction::IgnoreAndLog(Level::Info), - } - )?; - - match &inbound_channel.state { - InboundRequestState::OrderRequested { version, order } => { - return Err(LightningError { err: format!( "Received error response from create order request ({:?}) with counterparty {:?}. Error code = {}, message = {}", request_id, counterparty_node_id, error.code, error.message), action: ErrorAction::IgnoreAndLog(Level::Info)}); - } - _ => { - return Err(LightningError { err: format!("Received an unexpected error response for a create order request from counterparty ({:?})", counterparty_node_id), action: ErrorAction::IgnoreAndLog(Level::Info)}); - } - } + })?; + Ok(()) } None => { return Err(LightningError { err: format!("Received error response for a create order request from an unknown counterparty ({:?})",counterparty_node_id), action: ErrorAction::IgnoreAndLog(Level::Info)}); } } - Ok(()) } - // Send a request to get the order status pub fn check_order_status( &self, counterparty_node_id: &PublicKey, order_id: OrderId, channel_id: u128, ) -> Result<(), APIError> { @@ -771,9 +760,7 @@ where { if let Err(e) = inbound_channel.pay_for_channel(channel_id) { peer_state.remove_inbound_channel(channel_id); - return Err(APIError::APIMisuseError { - err: e.err - }); + return Err(APIError::APIMisuseError { err: e.err }); } let request_id = self.generate_request_id(); @@ -785,7 +772,9 @@ where *counterparty_node_id, LSPS1Message::Request( request_id, - LSPS1Request::GetOrder(GetOrderRequest { order_id: order_id.clone() }), + LSPS1Request::GetOrder(GetOrderRequest { + order_id: order_id.clone(), + }), ) .into(), )); @@ -828,16 +817,19 @@ where action: ErrorAction::IgnoreAndLog(Level::Info), })?; - if let Err(e) = outbound_channel.create_payment_invoice() { peer_state.outbound_channels_by_order_id.remove(¶ms.order_id); - // Check payment confirmation then Refund. - self.enqueue_event(Event::LSPS1(super::event::Event::Refund { request_id, counterparty_node_id: todo!(), order_id: todo!() })); + self.enqueue_event(Event::LSPS1(super::event::Event::Refund { + request_id, + counterparty_node_id: *counterparty_node_id, + order_id: params.order_id, + })); return Err(e); } - peer_state.pending_requests.insert(request_id.clone(),LSPS1Request::GetOrder(params.clone())); - + peer_state + .pending_requests + .insert(request_id.clone(), LSPS1Request::GetOrder(params.clone())); self.enqueue_event(Event::LSPS1(super::event::Event::CheckPaymentConfirmation { request_id, @@ -846,9 +838,9 @@ where })); } None => { - return Err(LightningError { - err: format!("Received error response for a create order request from an unknown counterparty ({:?})",counterparty_node_id), - action: ErrorAction::IgnoreAndLog(Level::Info) + return Err(LightningError { + err: format!("Received error response for a create order request from an unknown counterparty ({:?})",counterparty_node_id), + action: ErrorAction::IgnoreAndLog(Level::Info), }); } } @@ -858,21 +850,39 @@ where pub fn update_order_status( &self, request_id: RequestId, counterparty_node_id: PublicKey, order_id: OrderId, - request: GetOrderRequest, + order_state: OrderState, channel: Option, ) -> Result<(), APIError> { let outer_state_lock = self.per_peer_state.read().unwrap(); match outer_state_lock.get(&counterparty_node_id) { Some(inner_state_lock) => { - let peer_state = inner_state_lock.lock().unwrap(); + let mut peer_state = inner_state_lock.lock().unwrap(); - let order = peer_state.orders_to_id.get(&order_id).unwrap(); + if let Some(outbound_channel) = + peer_state.outbound_channels_by_order_id.get_mut(&order_id) + { + let config = &outbound_channel.config; - self.enqueue_response( - counterparty_node_id, - request_id, - LSPS1Response::GetOrder(GetOrderResponse { response: order.clone() }), - ) + self.enqueue_response( + counterparty_node_id, + request_id, + LSPS1Response::GetOrder(GetOrderResponse { + response: CreateOrderResponse { + order_id, + order: config.order.clone(), + order_state, + created_at: config.created_at, + expires_at: config.expires_at, + payment: config.payment.clone(), + channel, + }, + }), + ) + } else { + return Err(APIError::APIMisuseError { + err: format!("Channel with order_id {} not found", order_id.0), + }); + } } None => { return Err(APIError::APIMisuseError { @@ -883,16 +893,81 @@ where Ok(()) } - fn handle_get_order_response(&self, request_id: RequestId, counterparty_node_id: &PublicKey, params: GetOrderResponse) - -> Result<(), LightningError>{ - // TODO + fn handle_get_order_response( + &self, request_id: RequestId, counterparty_node_id: &PublicKey, params: GetOrderResponse, + ) -> Result<(), LightningError> { + let outer_state_lock = self.per_peer_state.read().unwrap(); + match outer_state_lock.get(&counterparty_node_id) { + Some(inner_state_lock) => { + let mut peer_state = inner_state_lock.lock().unwrap(); + + let channel_id = + peer_state.request_to_cid.remove(&request_id).ok_or(LightningError { + err: format!( + "Received get_versions response for an unknown request: {:?}", + request_id + ), + action: ErrorAction::IgnoreAndLog(Level::Info), + })?; + + let inbound_channel = peer_state + .inbound_channels_by_id + .get_mut(&channel_id) + .ok_or(LightningError { + err: format!( + "Received get_versions response for an unknown channel: {:?}", + channel_id + ), + action: ErrorAction::IgnoreAndLog(Level::Info), + })?; + } + None => { + return Err(LightningError { + err: format!( + "Received get_order response from unknown peer: {}", + counterparty_node_id + ), + action: ErrorAction::IgnoreAndLog(Level::Info), + }) + } + } + Ok(()) } - fn handle_get_order_error(&self,request_id: RequestId, counterparty_node_id: &PublicKey, params: ResponseError) - -> Result<(), LightningError>{ - // TODO: Channel Open/Refund Case. - Ok(()) + fn handle_get_order_error( + &self, request_id: RequestId, counterparty_node_id: &PublicKey, params: ResponseError, + ) -> Result<(), LightningError> { + let outer_state_lock = self.per_peer_state.read().unwrap(); + match outer_state_lock.get(&counterparty_node_id) { + Some(inner_state_lock) => { + let mut peer_state = inner_state_lock.lock().unwrap(); + + let channel_id = + peer_state.request_to_cid.remove(&request_id).ok_or(LightningError { + err: format!( + "Received get_order error for an unknown request: {:?}", + request_id + ), + action: ErrorAction::IgnoreAndLog(Level::Info), + })?; + + let _inbound_channel = peer_state + .inbound_channels_by_id + .get_mut(&channel_id) + .ok_or(LightningError { + err: format!( + "Received get_order error for an unknown channel: {:?}", + channel_id + ), + action: ErrorAction::IgnoreAndLog(Level::Info), + })?; + Ok(()) + } + None => { + return Err(LightningError { err: format!("Received get_order response for a create order request from an unknown counterparty ({:?})",counterparty_node_id), action: ErrorAction::IgnoreAndLog(Level::Info)}); + } + } } fn enqueue_response( @@ -901,7 +976,7 @@ where { let mut pending_messages = self.pending_messages.lock().unwrap(); pending_messages - .push((counterparty_node_id,LSPS1Message::Response(request_id, response).into())); + .push((counterparty_node_id, LSPS1Message::Response(request_id, response).into())); } if let Some(peer_manager) = self.peer_manager.lock().unwrap().as_ref() { diff --git a/src/channel_request/event.rs b/src/channel_request/event.rs index 0396c5b..9942e82 100644 --- a/src/channel_request/event.rs +++ b/src/channel_request/event.rs @@ -5,76 +5,55 @@ use bitcoin::secp256k1::PublicKey; use super::msgs::{ChannelInfo, OptionsSupported, Order, OrderId, Payment}; use crate::transport::msgs::RequestId; -/// An Event which you should probably take some action in response to. +/// An "Event" which you should probably take some action in response to. #[derive(Clone, Debug, PartialEq, Eq)] pub enum Event { GetInfoResponse { id: u128, - request_id: RequestId, - counterparty_node_id: PublicKey, - version: u16, - website: String, - options_supported: OptionsSupported, }, CreateInvoice { request_id: RequestId, - counterparty_node_id: PublicKey, - order: Order, }, DisplayOrder { id: u128, - counterparty_node_id: PublicKey, - order: Order, - payment: Payment, - channel: Option, }, PayforChannel { request_id: RequestId, - counterparty_node_id: PublicKey, - order: Order, - payment: Payment, - channel: Option, }, CheckPaymentConfirmation { request_id: RequestId, - counterparty_node_id: PublicKey, - order_id: OrderId, }, OpenChannel { request_id: RequestId, - counterparty_node_id: PublicKey, - order_id: OrderId, }, Refund { request_id: RequestId, - counterparty_node_id: PublicKey, - order_id: OrderId, }, } diff --git a/src/channel_request/msgs.rs b/src/channel_request/msgs.rs index 4e9c755..2d8dd0e 100644 --- a/src/channel_request/msgs.rs +++ b/src/channel_request/msgs.rs @@ -1,11 +1,8 @@ -use std::convert::TryFrom; use chrono::Utc; +use std::convert::TryFrom; use serde::{Deserialize, Serialize}; -//use bitcoin::hashes::hmac::{Hmac, HmacEngine}; -//use bitcoin::hashes::sha256::Hash as Sha256; -//use bitcoin::hashes::{Hash, HashEngine}; use crate::transport::msgs::{LSPSMessage, RequestId, ResponseError}; pub(crate) const LSPS1_GET_INFO_METHOD_NAME: &str = "lsps1.get_info"; @@ -18,11 +15,6 @@ pub(crate) const LSPS1_CREATE_ORDER_REQUEST_CLIENT_REJECTED_ERROR_CODE: i32 = 10 pub(crate) const LSPS1_CREATE_ORDER_REQUEST_INVALID_VERSION_ERROR_CODE: i32 = 1; pub(crate) const LSPS1_CREATE_ORDER_REQUEST_INVALID_TOKEN_ERROR_CODE: i32 = 2; -// Create a const to show preferred way for user payment -// Should this be set everytime before payment? -// Ask user for lighting or onchain and then set the const to -// lightning or onchain - #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, Hash)] pub struct OrderId(pub String); @@ -49,7 +41,6 @@ pub struct OptionsSupported { pub struct GetInfoResponse { pub supported_versions: Vec, pub website: String, - // This field should not be null pub options: OptionsSupported, } @@ -142,7 +133,7 @@ pub struct GetOrderRequest { #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub struct GetOrderResponse { - pub response: Order, + pub response: CreateOrderResponse, } #[derive(Clone, Debug, PartialEq, Eq)] diff --git a/src/channel_request/utils.rs b/src/channel_request/utils.rs index e302a40..298b6d4 100644 --- a/src/channel_request/utils.rs +++ b/src/channel_request/utils.rs @@ -1,5 +1,3 @@ -use std::option; - use super::msgs::{OptionsSupported, Order}; pub fn check_range(min: u64, max: u64, value: u64) -> bool { @@ -16,9 +14,10 @@ pub fn is_valid(order: &Order, options: &OptionsSupported) -> bool { options.max_initial_lsp_balance_sat, order.lsp_balance_sat, ) && check_range( - 1, - options.max_channel_expiry_blocks.into(), - order.channel_expiry_blocks.into()); + 1, + options.max_channel_expiry_blocks.into(), + order.channel_expiry_blocks.into(), + ); bool } diff --git a/src/transport/message_handler.rs b/src/transport/message_handler.rs index 20b5ca7..bd5fa57 100644 --- a/src/transport/message_handler.rs +++ b/src/transport/message_handler.rs @@ -1,20 +1,24 @@ +#![allow(missing_docs)] use crate::channel_request::channel_manager::CRManager; -use crate::channel_request::msgs::OptionsSupported; +use crate::channel_request::msgs::{ + ChannelInfo, OptionsSupported, Order, OrderId, OrderState, Payment, +}; use crate::events::{Event, EventQueue}; use crate::jit_channel::channel_manager::JITChannelManager; use crate::jit_channel::msgs::{OpeningFeeParams, RawOpeningFeeParams}; use crate::transport::msgs::RequestId; -use crate::transport::msgs::{LSPSMessage, RawLSPSMessage, LSPS_MESSAGE_TYPE_ID_ID}; +use crate::transport::msgs::{LSPSMessage, RawLSPSMessage, LSPS_MESSAGE_TYPE_ID}; use crate::transport::protocol::LSPS0MessageHandler; +use chrono::Utc; use lightning::chain::chaininterface::{BroadcasterInterface, FeeEstimator}; use lightning::chain::{self, BestBlock, Confirm, Filter, Listen}; use lightning::ln::channelmanager::{ChainParameters, ChannelManager, InterceptId}; use lightning::ln::features::{InitFeatures, NodeFeatures}; use lightning::ln::msgs::{ ChannelMessageHandler, ErrorAction, LightningError, OnionMessageHandler, RoutingMessageHandler, -, RoutingMessageHandler, ChannelMessageHandler, OnionMessageHandler}; -use lightning::ln::peer_handler::{{CustomMessageHandler, PeerManager, SocketDescriptor}, SocketDescriptor}; +}; +use lightning::ln::peer_handler::{CustomMessageHandler, PeerManager, SocketDescriptor}; use lightning::ln::wire::CustomMessageReader; use lightning::ln::ChannelId; use lightning::routing::router::Router; @@ -73,11 +77,8 @@ pub struct JITChannelsConfig { pub struct CRChannelConfig { pub token: Option, - pub max_fees: Option, - pub options_supported: Option, - pub website: Option, } @@ -146,16 +147,21 @@ pub struct LiquidityManager< } impl< - ES: Deref, + ES: Deref + Clone, M: Deref, T: Deref, F: Deref, R: Deref, SP: Deref, L: Deref, + Descriptor: SocketDescriptor, + RM: Deref, + CM: Deref, + OM: Deref, + CMH: Deref, NS: Deref, C: Deref, - > LiquidityManager + > LiquidityManager where ES::Target: EntropySource, M::Target: chain::Watch<::Signer>, @@ -183,8 +189,11 @@ where { let pending_messages = Arc::new(Mutex::new(vec![])); let pending_events = Arc::new(EventQueue::default()); - let lsps0_message_handler = - LSPS0MessageHandler::new(entropy_source.clone().clone(), vec![], Arc::clone(&pending_messages)); + let lsps0_message_handler = LSPS0MessageHandler::new( + entropy_source.clone().clone(), + vec![], + Arc::clone(&pending_messages), + ); let lsps2_message_handler = provider_config.as_ref().and_then(|config| { config.jit_channels.as_ref().map(|jit_channels_config| { @@ -251,11 +260,73 @@ where { pub fn set_peer_manager( &self, peer_manager: Arc>, ) { + if let Some(lsps1_message_handler) = &self.lsps1_message_handler { + lsps1_message_handler.set_peer_manager(peer_manager.clone()); + } if let Some(lsps2_message_handler) = &self.lsps2_message_handler { lsps2_message_handler.set_peer_manager(peer_manager); } } + pub fn request_for_info( + &self, counterparty_node_id: PublicKey, channel_id: u128, + ) -> Result<(), APIError> { + if let Some(lsps1_message_handler) = &self.lsps1_message_handler { + lsps1_message_handler.request_for_info(counterparty_node_id, channel_id); + } + Ok(()) + } + + pub fn place_order( + &self, channel_id: u128, counterparty_node_id: &PublicKey, order: Order, + ) -> Result<(), APIError> { + if let Some(lsps1_message_handler) = &self.lsps1_message_handler { + lsps1_message_handler.place_order(channel_id, counterparty_node_id, order)?; + } + Ok(()) + } + + pub fn send_invoice_for_order( + &self, request_id: RequestId, counterparty_node_id: &PublicKey, payment: Payment, + created_at: chrono::DateTime, expires_at: chrono::DateTime, + ) -> Result<(), APIError> { + if let Some(lsps1_message_handler) = &self.lsps1_message_handler { + lsps1_message_handler.send_invoice_for_order( + request_id, + counterparty_node_id, + payment, + created_at, + expires_at, + )?; + } + Ok(()) + } + + pub fn check_order_status( + self, channel_id: u128, counterparty_node_id: &PublicKey, order_id: OrderId, + ) -> Result<(), APIError> { + if let Some(lsps1_message_handler) = &self.lsps1_message_handler { + lsps1_message_handler.check_order_status(counterparty_node_id, order_id, channel_id)?; + } + Ok(()) + } + + pub fn update_order_status( + &self, request_id: RequestId, counterparty_node_id: PublicKey, order_id: OrderId, + order_state: OrderState, channel: Option, + ) -> Result<(), APIError> { + if let Some(lsps1_message_handler) = &self.lsps1_message_handler { + lsps1_message_handler.update_order_status( + request_id, + counterparty_node_id, + order_id, + order_state, + channel, + )?; + } + Ok(()) + } + /// Initiate the creation of an invoice that when paid will open a channel /// with enough inbound liquidity to be able to receive the payment. /// @@ -420,14 +491,6 @@ where { LSPSMessage::LSPS0(msg) => { self.lsps0_message_handler.handle_message(msg, sender_node_id)?; } - LSPSMessage::LSPS2(msg) => match &self.lsps2_message_handler { - Some(lsps2_message_handler) => { - lsps2_message_handler.handle_message(msg, sender_node_id)?; - } - None => { - return Err(LightningError { err: format!("Received LSPS2 message without LSPS2 message handler configured. From node = {:?}", sender_node_id), action: ErrorAction::IgnoreAndLog(Level::Info)}); - } - }, LSPSMessage::LSPS1(msg) => match &self.lsps1_message_handler { Some(lsps1_message_handler) => { lsps1_message_handler.handle_message(msg, sender_node_id)?; @@ -436,6 +499,14 @@ where { return Err(LightningError { err: format!("Received LSPS1 message without LSPS1 message handler configured. From node = {:?}", sender_node_id), action: ErrorAction::IgnoreAndLog(Level::Info)}); } }, + LSPSMessage::LSPS2(msg) => match &self.lsps2_message_handler { + Some(lsps2_message_handler) => { + lsps2_message_handler.handle_message(msg, sender_node_id)?; + } + None => { + return Err(LightningError { err: format!("Received LSPS2 message without LSPS2 message handler configured. From node = {:?}", sender_node_id), action: ErrorAction::IgnoreAndLog(Level::Info)}); + } + }, } Ok(()) } diff --git a/src/transport/msgs.rs b/src/transport/msgs.rs index f9ab605..bf89249 100644 --- a/src/transport/msgs.rs +++ b/src/transport/msgs.rs @@ -134,10 +134,9 @@ impl LSPSMessage { LSPSMessage::LSPS2(LSPS2Message::Request(request_id, request)) => { Some((request_id.0.clone(), request.method().to_string())) } - LSPSMessage::LSPS1(LSPS1Message::Request( - request_id, - request, - )) => Some((request_id.0.clone(), request.method().to_string())), + LSPSMessage::LSPS1(LSPS1Message::Request(request_id, request)) => { + Some((request_id.0.clone(), request.method().to_string())) + } _ => None, } } @@ -202,7 +201,7 @@ impl Serialize for LSPSMessage { LSPS1Response::CreateOrder(result) => { jsonrpc_object.serialize_field(JSONRPC_ERROR_FIELD_KEY, result)? } - LSPS1Response::OrderError(error) => { + LSPS1Response::CreateOrderError(error) => { jsonrpc_object.serialize_field(JSONRPC_RESULT_FIELD_KEY, error)? } LSPS1Response::GetOrder(result) => { @@ -408,7 +407,7 @@ impl<'de, 'a> Visitor<'de> for LSPSMessageVisitor<'a> { if let Some(error) = error { Ok(LSPSMessage::LSPS1(LSPS1Message::Response( RequestId(id), - LSPS1Response::OrderError(error), + LSPS1Response::CreateOrderError(error), ))) } else if let Some(result) = result { let response =