From 60093c6355539f5cda34442d2b8f206678c7bee4 Mon Sep 17 00:00:00 2001 From: joemphilips Date: Fri, 3 Feb 2023 21:51:47 +0900 Subject: [PATCH] wip: support submarine swap in bifrost * messages resembles those of [peerswap protocol](https://github.com/ElementsProject/peerswap/blob/master/docs/peer-protocol.md#the-swap_in_request-message) * It does not support compatibility with peerswap daemon * LNPBP specification is not ready yet. --- lnp2p/src/bifrost/mod.rs | 45 +++++ lnp2p/src/bifrost/peerswap.rs | 300 ++++++++++++++++++++++++++++++++++ lnp2p/src/bifrost/types.rs | 6 + lnp2p/src/bolt/types.rs | 5 + 4 files changed, 356 insertions(+) create mode 100644 lnp2p/src/bifrost/peerswap.rs diff --git a/lnp2p/src/bifrost/mod.rs b/lnp2p/src/bifrost/mod.rs index 98cb917..d9c1d40 100644 --- a/lnp2p/src/bifrost/mod.rs +++ b/lnp2p/src/bifrost/mod.rs @@ -172,6 +172,7 @@ mod app; mod channel; mod ctrl; mod msg; +mod peerswap; mod proposals; mod types; @@ -185,6 +186,7 @@ use internet2::{CreateUnmarshaller, Payload, Unmarshall, Unmarshaller}; use lnpbp::bech32::Blob; pub use msg::Msg; use once_cell::sync::Lazy; +pub use peerswap::*; pub use proposals::*; use strict_encoding::{self, StrictDecode, StrictEncode}; pub use types::{ @@ -257,6 +259,49 @@ pub enum Messages { #[api(type = 0x0028)] CloseChannel(CloseChannel), + + #[api(type = 0x0040)] + SwapInRequest(SwapInRequestMsg), + + #[api(type = 0x0041)] + SwapInAgreement(SwapInAgreementMsg), + + #[api(type = 0x0042)] + SwapOutRequest(SwapOutRequestMsg), + + #[api(type = 0x0043)] + SwapOutAgreement(SwapOutAgreementMsg), + + #[api(type = 0x0044)] + OpeningTxBroadcasted(OpeningTxBroadcastedMsg), + + #[api(type = 0x0045)] + Cancel(CancelMsg), + + #[api(type = 0x0046)] + CoopClose(CoopCloseMsg), +} + +impl Messages { + pub fn swap_id(&self) -> Option<&SwapId> { + match self { + Self::SwapInRequest(SwapInRequestMsg { swap_id, .. }) + | Self::SwapOutRequest(SwapOutRequestMsg { swap_id, .. }) + | Self::SwapInAgreement(SwapInAgreementMsg { swap_id, .. }) + | Self::SwapOutAgreement(SwapOutAgreementMsg { swap_id, .. }) + | Self::OpeningTxBroadcasted(OpeningTxBroadcastedMsg { + swap_id, + .. + }) + | Self::Cancel(CancelMsg { swap_id, .. }) + | Self::CoopClose(CoopCloseMsg { swap_id, .. }) => Some(swap_id), + _ => None, + } + } + + pub fn is_swap_msg(&self) -> bool { + self.swap_id().is_some() + } } impl StrictEncode for Messages { diff --git a/lnp2p/src/bifrost/peerswap.rs b/lnp2p/src/bifrost/peerswap.rs new file mode 100644 index 0000000..0806a98 --- /dev/null +++ b/lnp2p/src/bifrost/peerswap.rs @@ -0,0 +1,300 @@ +// LNP P2P library, plmeneting both bolt (BOLT) and Bifrost P2P messaging +// system for Lightning network protocol (LNP) +// +// Written in 2020-2021 by +// Dr. Maxim Orlovsky +// +// To the extent possible under law, the author(s) have dedicated all +// copyright and related and neighboring rights to this software to +// the public domain worldwide. This software is distributed without +// any warranty. +// +// You should have received a copy of the MIT License +// along with this software. +// If not, see . + +use amplify::{Display, Slice32}; +use bitcoin::hashes::{sha256, sha256t}; +use bitcoin::Txid; +use lnpbp::chain::AssetId; +use secp256k1::{PublicKey, SecretKey}; +#[cfg(feature = "serde")] +use serde_with::{As, DisplayFromStr}; + +use crate::bolt; + +pub const PROTOCOL_VERSION: u16 = 1; +pub const BIFROST_APP_PEERSWAP: u16 = 0x8008; + +// SHA256("bifrost:swap") +const SWAP_ID_MIDSTATE: [u8; 32] = [ + 0x4e, 0x2e, 0x6e, 0xb2, 0xa3, 0xda, 0x16, 0xbc, 0x03, 0xe3, 0x38, 0x30, + 0xb3, 0xfa, 0xae, 0x6f, 0xe2, 0x76, 0x00, 0x1b, 0x2e, 0x79, 0xf1, 0x8f, + 0xd3, 0x8c, 0x43, 0xdc, 0x79, 0xfb, 0x99, 0xdd, +]; + +/// Tag used for [`SwapId`] hash type +pub struct SwapIdTag; + +impl sha256t::Tag for SwapIdTag { + #[inline] + fn engine() -> sha256::HashEngine { + let midstate = sha256::Midstate::from_inner(SWAP_ID_MIDSTATE); + sha256::HashEngine::from_midstate(midstate, 64) + } +} + +#[derive( + Clone, + Copy, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + Debug, + Display, + Default, + Wrapper, + From, + NetworkEncode, + NetworkDecode +)] +#[display(LowerHex)] +#[wrapper(FromStr, LowerHex, UpperHex, BorrowSlice)] +#[cfg_attr( + feature = "serde", + serde_as, + derive(Serialize, Deserialize), + serde(crate = "serde_crate", transparent) +)] +pub struct SwapId( + #[cfg_attr(feature = "serde", serde(with = "As::"))] + Slice32, +); + +impl SwapId { + #[inline] + pub fn random() -> Self { + SwapId::from(Slice32::random()) + } +} + +#[derive( + Clone, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + Debug, + Display, + NetworkEncode, + NetworkDecode +)] +#[cfg_attr( + feature = "serde", + serde_as, + derive(Serialize, Deserialize), + serde(crate = "serde_crate") +)] +#[display(Debug)] +#[network_encoding(use_tlv)] +pub struct SwapInRequestMsg { + pub protocol_version: u64, + pub swap_id: SwapId, + pub asset: Option, + pub network: String, + pub scid: bolt::ChannelId, + pub amount: u64, + pub pubkey: PublicKey, +} + +#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display, Error)] +#[display(doc_comments)] +pub enum ValidationError { + /// Network and asset has different value. + NetworkMismatch, + + /// Unknown Network {0} + UnknownNetwork(String), +} + +impl SwapInRequestMsg { + pub fn validate(&self) -> Result<(), ValidationError> { + let _network_ok = { + match self.network.as_str() { + "mainnet" => Ok(()), + "testnet" => Ok(()), + "testnet3" => Ok(()), + "signet" => Ok(()), + "regtest" => Ok(()), + x => Err(ValidationError::UnknownNetwork(x.to_string())), + } + }?; + + Ok(()) + } +} + +#[derive( + Clone, + PartialEq, + Eq, + PartialOrd, + Ord, + Debug, + Display, + NetworkEncode, + NetworkDecode +)] +#[cfg_attr( + feature = "serde", + serde_as, + derive(Serialize, Deserialize), + serde(crate = "serde_crate") +)] +#[display(Debug)] +#[network_encoding(use_tlv)] +pub struct SwapInAgreementMsg { + pub protocol_version: u64, + pub swap_id: SwapId, + pub pubkey: PublicKey, + pub premium: u64, +} + +#[derive( + Clone, + PartialEq, + Eq, + Hash, + Ord, + PartialOrd, + Debug, + Display, + NetworkEncode, + NetworkDecode +)] +#[cfg_attr( + feature = "serde", + serde_as, + derive(Serialize, Deserialize), + serde(crate = "serde_crate") +)] +#[display(Debug)] +#[network_encoding(use_tlv)] +pub struct SwapOutRequestMsg { + pub protocol_version: u64, + pub swap_id: SwapId, + pub asset: Option, + pub network: String, + pub scid: bolt::ChannelId, + pub amount: u64, + pub pubkey: PublicKey, +} + +#[derive( + Clone, + PartialEq, + Eq, + Hash, + Ord, + PartialOrd, + Debug, + Display, + NetworkEncode, + NetworkDecode +)] +#[cfg_attr( + feature = "serde", + serde_as, + derive(Serialize, Deserialize), + serde(crate = "serde_crate") +)] +#[display(Debug)] +#[network_encoding(use_tlv)] +pub struct SwapOutAgreementMsg { + pub protocol_version: u64, + pub swap_id: SwapId, + pub pubkey: PublicKey, + pub payreq: String, +} + +#[derive( + Clone, + PartialEq, + Eq, + Hash, + Ord, + PartialOrd, + Debug, + Display, + NetworkEncode, + NetworkDecode +)] +#[cfg_attr( + feature = "serde", + serde_as, + derive(Serialize, Deserialize), + serde(crate = "serde_crate") +)] +#[display(Debug)] +#[network_encoding(use_tlv)] +pub struct OpeningTxBroadcastedMsg { + pub swap_id: SwapId, + pub payreq: String, + pub tx_id: Txid, + pub script_out: u64, + pub blinding_key: SecretKey, +} + +#[derive( + Clone, + PartialEq, + Eq, + Hash, + Ord, + PartialOrd, + Debug, + Display, + NetworkEncode, + NetworkDecode +)] +#[cfg_attr( + feature = "serde", + serde_as, + derive(Serialize, Deserialize), + serde(crate = "serde_crate") +)] +#[display(Debug)] +#[network_encoding(use_tlv)] +pub struct CancelMsg { + pub swap_id: SwapId, + pub message: String, +} + +#[derive( + Clone, + PartialEq, + Eq, + Hash, + Ord, + PartialOrd, + Debug, + Display, + NetworkEncode, + NetworkDecode +)] +#[cfg_attr( + feature = "serde", + serde_as, + derive(Serialize, Deserialize), + serde(crate = "serde_crate") +)] +#[display(Debug)] +#[network_encoding(use_tlv)] +pub struct CoopCloseMsg { + pub swap_id: SwapId, + pub message: String, + pub privkey: SecretKey, +} diff --git a/lnp2p/src/bifrost/types.rs b/lnp2p/src/bifrost/types.rs index 6e60bb8..0db1c80 100644 --- a/lnp2p/src/bifrost/types.rs +++ b/lnp2p/src/bifrost/types.rs @@ -59,6 +59,12 @@ impl sha256t::Tag for ChannelIdTag { )] #[wrapper(Debug, LowerHex, BorrowSlice)] #[wrapper(Index, IndexRange, IndexFrom, IndexTo, IndexFull)] +#[cfg_attr( + feature = "serde", + serde_as, + derive(Serialize, Deserialize), + serde(crate = "serde_crate", transparent) +)] pub struct ChannelId(sha256t::Hash); impl ChannelId { diff --git a/lnp2p/src/bolt/types.rs b/lnp2p/src/bolt/types.rs index a8caa3a..157f995 100644 --- a/lnp2p/src/bolt/types.rs +++ b/lnp2p/src/bolt/types.rs @@ -326,6 +326,11 @@ pub struct Alias( )] #[derive(LightningEncode, LightningDecode)] #[cfg_attr(feature = "strict_encoding", derive(NetworkEncode, NetworkDecode))] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(crate = "serde_crate") +)] #[display("{block_height}x{tx_index}x{output_index}")] pub struct ShortChannelId { pub block_height: u24,