use std::io;
use std::str::FromStr;
use amplify::flags::FlagVec;
use amplify::{DumbDefault, Slice32};
use bitcoin::hashes::sha256;
use bitcoin::Txid;
use bitcoin_scripts::hlc::{HashLock, HashPreimage};
use bitcoin_scripts::PubkeyScript;
use internet2::presentation::sphinx::Onion;
use internet2::tlv;
use lightning_encoding::Error;
use secp256k1::ecdsa::Signature;
use secp256k1::{PublicKey, SecretKey};
use super::{ChannelId, TempChannelId};
use crate::bolt::PaymentOnion;
pub const PAYMENT_SPHINX_LEN: usize = 1300;
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)]
#[cfg_attr(feature = "strict_encoding", derive(NetworkEncode, NetworkDecode))]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate")
)]
pub enum ChannelType {
#[display("basic")]
Basic,
#[display("static_remotekey")]
StaticRemotekey,
#[display("anchored")]
AnchorOutputsStaticRemotekey,
#[display("anchored_zero_fee")]
AnchorsZeroFeeHtlcTxStaticRemotekey,
}
impl ChannelType {
#[inline]
pub fn has_static_remotekey(self) -> bool {
self != ChannelType::Basic
}
#[inline]
pub fn has_anchor_outputs(self) -> bool {
self == ChannelType::AnchorOutputsStaticRemotekey
}
#[inline]
pub fn has_anchors_zero_fee_htlc_tx(self) -> bool {
self == ChannelType::AnchorsZeroFeeHtlcTxStaticRemotekey
}
#[inline]
pub fn into_option(self) -> Option<ChannelType> {
match self {
ChannelType::Basic => None,
_ => Some(self),
}
}
}
impl TryFrom<FlagVec> for ChannelType {
type Error = lightning_encoding::Error;
fn try_from(mut flags: FlagVec) -> Result<Self, Self::Error> {
if flags.shrink() {
return Err(lightning_encoding::Error::DataIntegrityError(s!(
"non-minimal channel type encoding"
)));
} else if flags.as_inner() == &[] as &[u8] {
return Ok(ChannelType::Basic);
}
let mut iter = flags.iter();
match (iter.next(), iter.next(), iter.next()) {
(Some(12), None, None) => Ok(ChannelType::StaticRemotekey),
(Some(12), Some(20), None) => {
Ok(ChannelType::AnchorOutputsStaticRemotekey)
}
(Some(12), Some(22), None) => {
Ok(ChannelType::AnchorsZeroFeeHtlcTxStaticRemotekey)
}
_ => Err(lightning_encoding::Error::DataIntegrityError(s!(
"invalid combination of channel type flags"
))),
}
}
}
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display, Error)]
#[display("unknown channel type name `{0}`")]
pub struct ChannelTypeParseError(String);
impl FromStr for ChannelType {
type Err = ChannelTypeParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s.to_lowercase().as_str() {
"basic" => ChannelType::Basic,
"static_remotekey" => ChannelType::StaticRemotekey,
"anchored" => ChannelType::AnchorOutputsStaticRemotekey,
"anchored_zero_fee" => {
ChannelType::AnchorsZeroFeeHtlcTxStaticRemotekey
}
_ => return Err(ChannelTypeParseError(s.to_owned())),
})
}
}
impl Default for ChannelType {
#[inline]
fn default() -> Self {
ChannelType::Basic
}
}
impl lightning_encoding::LightningEncode for ChannelType {
fn lightning_encode<E: io::Write>(
&self,
mut e: E,
) -> Result<usize, lightning_encoding::Error> {
let mut flags = FlagVec::new();
match self {
ChannelType::Basic => {
}
ChannelType::StaticRemotekey => {
flags.set(12);
}
ChannelType::AnchorOutputsStaticRemotekey => {
flags.set(12);
flags.set(20);
}
ChannelType::AnchorsZeroFeeHtlcTxStaticRemotekey => {
flags.set(12);
flags.set(22);
}
}
let mut vec = flags.as_inner().to_vec();
vec.reverse();
e.write_all(&vec)?;
Ok(vec.len())
}
}
impl lightning_encoding::LightningDecode for ChannelType {
fn lightning_decode<D: io::Read>(
mut d: D,
) -> Result<Self, lightning_encoding::Error> {
let mut vec = vec![];
d.read_to_end(&mut vec)?;
vec.reverse();
let flags = FlagVec::from_inner(vec);
ChannelType::try_from(flags)
}
fn lightning_deserialize(data: impl AsRef<[u8]>) -> Result<Self, Error> {
let mut vec = data.as_ref().to_vec();
vec.reverse();
let flags = FlagVec::from_inner(vec);
ChannelType::try_from(flags)
}
}
#[derive(Clone, PartialEq, Eq, Debug, Display)]
#[derive(LightningEncode, LightningDecode)]
#[cfg_attr(
feature = "strict_encoding",
derive(NetworkEncode, NetworkDecode),
network_encoding(use_tlv)
)]
#[lightning_encoding(use_tlv)]
#[display(
"open_channel({chain_hash}, {temporary_channel_id}, {funding_satoshis}, \
{channel_flags}, ...)"
)]
pub struct OpenChannel {
pub chain_hash: Slice32,
pub temporary_channel_id: TempChannelId,
pub funding_satoshis: u64,
pub push_msat: u64,
pub dust_limit_satoshis: u64,
pub max_htlc_value_in_flight_msat: u64,
pub channel_reserve_satoshis: u64,
pub htlc_minimum_msat: u64,
pub feerate_per_kw: u32,
pub to_self_delay: u16,
pub max_accepted_htlcs: u16,
pub funding_pubkey: PublicKey,
pub revocation_basepoint: PublicKey,
pub payment_point: PublicKey,
pub delayed_payment_basepoint: PublicKey,
pub htlc_basepoint: PublicKey,
pub first_per_commitment_point: PublicKey,
pub channel_flags: u8,
#[lightning_encoding(tlv = 0)]
#[cfg_attr(feature = "strict_encoding", network_encoding(tlv = 0))]
pub shutdown_scriptpubkey: Option<PubkeyScript>,
#[lightning_encoding(tlv = 1)]
#[cfg_attr(feature = "strict_encoding", network_encoding(tlv = 1))]
pub channel_type: Option<ChannelType>,
#[lightning_encoding(unknown_tlvs)]
#[cfg_attr(feature = "strict_encoding", network_encoding(unknown_tlvs))]
pub unknown_tlvs: tlv::Stream,
}
impl OpenChannel {
#[inline]
pub fn has_static_remotekey(&self) -> bool {
self.channel_type.unwrap_or_default().has_static_remotekey()
}
#[inline]
pub fn has_anchor_outputs(&self) -> bool {
self.channel_type.unwrap_or_default().has_anchor_outputs()
}
#[inline]
pub fn has_anchors_zero_fee_htlc_tx(&self) -> bool {
self.channel_type
.unwrap_or_default()
.has_anchors_zero_fee_htlc_tx()
}
#[inline]
pub fn should_announce_channel(&self) -> bool {
self.channel_flags & 0x01 == 0x01
}
}
#[derive(Clone, PartialEq, Eq, Debug, Display)]
#[derive(LightningEncode, LightningDecode)]
#[cfg_attr(
feature = "strict_encoding",
derive(NetworkEncode, NetworkDecode),
network_encoding(use_tlv)
)]
#[lightning_encoding(use_tlv)]
#[display("accept_channel({temporary_channel_id}, ...)")]
pub struct AcceptChannel {
pub temporary_channel_id: TempChannelId,
pub dust_limit_satoshis: u64,
pub max_htlc_value_in_flight_msat: u64,
pub channel_reserve_satoshis: u64,
pub htlc_minimum_msat: u64,
pub minimum_depth: u32,
pub to_self_delay: u16,
pub max_accepted_htlcs: u16,
pub funding_pubkey: PublicKey,
pub revocation_basepoint: PublicKey,
pub payment_point: PublicKey,
pub delayed_payment_basepoint: PublicKey,
pub htlc_basepoint: PublicKey,
pub first_per_commitment_point: PublicKey,
#[lightning_encoding(tlv = 0)]
#[cfg_attr(feature = "strict_encoding", network_encoding(tlv = 0))]
pub shutdown_scriptpubkey: Option<PubkeyScript>,
#[lightning_encoding(tlv = 1)]
#[cfg_attr(feature = "strict_encoding", network_encoding(tlv = 1))]
pub channel_type: Option<ChannelType>,
#[lightning_encoding(unknown_tlvs)]
#[cfg_attr(feature = "strict_encoding", network_encoding(unknown_tlvs))]
pub unknown_tlvs: tlv::Stream,
}
impl AcceptChannel {
#[inline]
pub fn has_static_remotekey(&self) -> bool {
self.channel_type.unwrap_or_default().has_static_remotekey()
}
#[inline]
pub fn has_anchor_outputs(&self) -> bool {
self.channel_type.unwrap_or_default().has_anchor_outputs()
}
#[inline]
pub fn has_anchors_zero_fee_htlc_tx(&self) -> bool {
self.channel_type
.unwrap_or_default()
.has_anchors_zero_fee_htlc_tx()
}
}
#[derive(Clone, PartialEq, Eq, Debug, Display)]
#[derive(LightningEncode, LightningDecode)]
#[cfg_attr(feature = "strict_encoding", derive(NetworkEncode, NetworkDecode))]
#[display(
"funding_created({temporary_channel_id}, \
{funding_txid}:{funding_output_index}, ...signature)"
)]
pub struct FundingCreated {
pub temporary_channel_id: TempChannelId,
pub funding_txid: Txid,
pub funding_output_index: u16,
pub signature: Signature,
}
#[derive(Clone, PartialEq, Eq, Debug, Display)]
#[derive(LightningEncode, LightningDecode)]
#[cfg_attr(feature = "strict_encoding", derive(NetworkEncode, NetworkDecode))]
#[display("funding_signed({channel_id}, ...signature)")]
pub struct FundingSigned {
pub channel_id: ChannelId,
pub signature: Signature,
}
#[derive(Clone, PartialEq, Eq, Debug, Display)]
#[derive(LightningEncode, LightningDecode)]
#[cfg_attr(feature = "strict_encoding", derive(NetworkEncode, NetworkDecode))]
#[display("funding_locked({channel_id}, {next_per_commitment_point})")]
pub struct FundingLocked {
pub channel_id: ChannelId,
pub next_per_commitment_point: PublicKey,
}
#[derive(Clone, PartialEq, Eq, Debug, Display)]
#[derive(LightningEncode, LightningDecode)]
#[cfg_attr(feature = "strict_encoding", derive(NetworkEncode, NetworkDecode))]
#[display("shutdown({channel_id}, {scriptpubkey})")]
pub struct Shutdown {
pub channel_id: ChannelId,
pub scriptpubkey: PubkeyScript,
}
#[derive(Clone, PartialEq, Eq, Debug, Display)]
#[derive(LightningEncode, LightningDecode)]
#[cfg_attr(feature = "strict_encoding", derive(NetworkEncode, NetworkDecode))]
#[display("closing_signed({channel_id}, ...)")]
pub struct ClosingSigned {
pub channel_id: ChannelId,
pub fee_satoshis: u64,
pub signature: Signature,
}
#[derive(Clone, PartialEq, Eq, Debug, Display)]
#[derive(LightningEncode, LightningDecode)]
#[cfg_attr(
feature = "strict_encoding",
derive(NetworkEncode, NetworkDecode),
network_encoding(use_tlv)
)]
#[lightning_encoding(use_tlv)]
#[display(
"update_add_htlc({channel_id}, {htlc_id}, {amount_msat}, {payment_hash}, \
...)"
)]
pub struct UpdateAddHtlc {
pub channel_id: ChannelId,
pub htlc_id: u64,
pub amount_msat: u64,
pub payment_hash: HashLock,
pub cltv_expiry: u32,
pub onion_routing_packet: Onion<PaymentOnion, PAYMENT_SPHINX_LEN>,
#[lightning_encoding(unknown_tlvs)]
#[cfg_attr(feature = "strict_encoding", network_encoding(unknown_tlvs))]
pub unknown_tlvs: tlv::Stream,
}
#[derive(Clone, PartialEq, Eq, Debug, Display)]
#[derive(LightningEncode, LightningDecode)]
#[cfg_attr(feature = "strict_encoding", derive(NetworkEncode, NetworkDecode))]
#[display("update_fullfill_htlc({channel_id}, {htlc_id}, ...preimages)")]
pub struct UpdateFulfillHtlc {
pub channel_id: ChannelId,
pub htlc_id: u64,
pub payment_preimage: HashPreimage,
}
#[derive(Clone, PartialEq, Eq, Debug, Display)]
#[derive(LightningEncode, LightningDecode)]
#[cfg_attr(feature = "strict_encoding", derive(NetworkEncode, NetworkDecode))]
#[display("update_fail_htlc({channel_id}, {htlc_id}, ...reason)")]
pub struct UpdateFailHtlc {
pub channel_id: ChannelId,
pub htlc_id: u64,
pub reason: Vec<u8>,
}
#[derive(Clone, PartialEq, Eq, Debug, Display)]
#[derive(LightningEncode, LightningDecode)]
#[cfg_attr(feature = "strict_encoding", derive(NetworkEncode, NetworkDecode))]
#[display("update_fail_malformed_htlc({channel_id}, {htlc_id}, ...onion)")]
pub struct UpdateFailMalformedHtlc {
pub channel_id: ChannelId,
pub htlc_id: u64,
pub sha256_of_onion: sha256::Hash,
pub failure_code: u16,
}
#[derive(Clone, PartialEq, Eq, Debug, Display)]
#[derive(LightningEncode, LightningDecode)]
#[cfg_attr(feature = "strict_encoding", derive(NetworkEncode, NetworkDecode))]
#[display("commitment_signed({channel_id}, ...signatures)")]
pub struct CommitmentSigned {
pub channel_id: ChannelId,
pub signature: Signature,
pub htlc_signatures: Vec<Signature>,
}
#[derive(Clone, PartialEq, Eq, Debug, Display)]
#[derive(LightningEncode, LightningDecode)]
#[cfg_attr(feature = "strict_encoding", derive(NetworkEncode, NetworkDecode))]
#[display(
"revoke_and_ack({channel_id}, {next_per_commitment_point}, \
...per_commitment_secret)"
)]
pub struct RevokeAndAck {
pub channel_id: ChannelId,
pub per_commitment_secret: SecretKey,
pub next_per_commitment_point: PublicKey,
}
#[derive(Clone, PartialEq, Eq, Debug, Display)]
#[derive(LightningEncode, LightningDecode)]
#[cfg_attr(feature = "strict_encoding", derive(NetworkEncode, NetworkDecode))]
#[display("update_fee({channel_id}, {feerate_per_kw})")]
pub struct UpdateFee {
pub channel_id: ChannelId,
pub feerate_per_kw: u32,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Display)]
#[derive(LightningEncode, LightningDecode)]
#[cfg_attr(feature = "strict_encoding", derive(NetworkEncode, NetworkDecode))]
#[display("channel_reestablish({channel_id}, {next_commitment_number}, ...)")]
pub struct ChannelReestablish {
pub channel_id: ChannelId,
pub next_commitment_number: u64,
pub next_revocation_number: u64,
pub your_last_per_commitment_secret: Slice32,
pub my_current_per_commitment_point: PublicKey,
}
impl DumbDefault for OpenChannel {
fn dumb_default() -> Self {
OpenChannel {
chain_hash: none!(),
temporary_channel_id: TempChannelId::dumb_default(),
funding_satoshis: 0,
push_msat: 0,
dust_limit_satoshis: 0,
max_htlc_value_in_flight_msat: 0,
channel_reserve_satoshis: 0,
htlc_minimum_msat: 0,
feerate_per_kw: 0,
to_self_delay: 0,
max_accepted_htlcs: 0,
funding_pubkey: dumb_pubkey!(),
revocation_basepoint: dumb_pubkey!(),
payment_point: dumb_pubkey!(),
delayed_payment_basepoint: dumb_pubkey!(),
htlc_basepoint: dumb_pubkey!(),
first_per_commitment_point: dumb_pubkey!(),
channel_flags: 0,
shutdown_scriptpubkey: None,
channel_type: None,
unknown_tlvs: none!(),
}
}
}
impl DumbDefault for AcceptChannel {
fn dumb_default() -> Self {
AcceptChannel {
temporary_channel_id: TempChannelId::dumb_default(),
dust_limit_satoshis: 0,
max_htlc_value_in_flight_msat: 0,
channel_reserve_satoshis: 0,
htlc_minimum_msat: 0,
minimum_depth: 0,
to_self_delay: 0,
max_accepted_htlcs: 0,
funding_pubkey: dumb_pubkey!(),
revocation_basepoint: dumb_pubkey!(),
payment_point: dumb_pubkey!(),
delayed_payment_basepoint: dumb_pubkey!(),
htlc_basepoint: dumb_pubkey!(),
first_per_commitment_point: dumb_pubkey!(),
shutdown_scriptpubkey: None,
channel_type: None,
unknown_tlvs: none!(),
}
}
}
#[cfg(test)]
mod test {
use lightning_encoding::LightningDecode;
use super::*;
use crate::bolt::Messages;
#[test]
fn real_clightning_open_channel() {
let msg_recv = [
0u8, 32, 6, 34, 110, 70, 17, 26, 11, 89, 202, 175, 18, 96, 67, 235,
91, 191, 40, 195, 79, 58, 94, 51, 42, 31, 199, 178, 183, 60, 241,
136, 145, 15, 55, 163, 222, 247, 199, 217, 62, 176, 50, 239, 35, 1,
82, 129, 198, 46, 117, 47, 78, 64, 130, 130, 167, 89, 107, 148,
190, 121, 88, 127, 175, 82, 0, 0, 0, 0, 0, 1, 134, 160, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 34, 255, 255, 255, 255, 255, 255,
255, 255, 0, 0, 0, 0, 0, 0, 3, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 253, 0, 6, 1, 227, 3, 33, 98, 70, 252, 48, 195, 103, 238, 233,
231, 193, 79, 109, 137, 240, 0, 34, 234, 4, 191, 125, 249, 102, 44,
137, 141, 152, 246, 118, 166, 205, 60, 3, 96, 241, 203, 115, 211,
19, 224, 138, 23, 92, 68, 226, 196, 234, 61, 226, 143, 211, 90, 92,
44, 147, 5, 89, 185, 117, 71, 57, 241, 139, 196, 28, 3, 252, 250,
227, 188, 85, 7, 237, 113, 4, 18, 45, 7, 192, 165, 147, 18, 113,
191, 216, 125, 175, 201, 118, 225, 63, 243, 29, 155, 194, 235, 167,
20, 3, 12, 61, 69, 17, 92, 121, 215, 107, 192, 35, 192, 160, 214,
235, 86, 202, 92, 206, 239, 201, 48, 28, 215, 9, 43, 255, 250, 80,
32, 129, 98, 29, 3, 57, 9, 153, 179, 206, 248, 130, 112, 219, 32,
69, 209, 220, 105, 18, 211, 2, 165, 247, 245, 245, 1, 170, 100,
208, 34, 98, 123, 207, 130, 10, 66, 2, 21, 90, 74, 135, 143, 98,
75, 173, 210, 81, 201, 99, 45, 76, 125, 176, 84, 187, 222, 90, 218,
87, 5, 11, 119, 191, 75, 185, 108, 124, 8, 32, 1, 0, 0, 1, 2, 16,
0,
];
Messages::lightning_deserialize(&msg_recv).unwrap();
}
#[test]
fn real_clightning_accept_message() {
let msg_recv = [
0u8, 33, 117, 72, 156, 134, 70, 5, 93, 232, 6, 166, 206, 185, 243,
33, 125, 57, 230, 233, 235, 59, 255, 0, 23, 127, 91, 135, 129, 43,
74, 208, 254, 247, 0, 0, 0, 0, 0, 0, 2, 34, 255, 255, 255, 255,
255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 3, 232, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 0, 6, 1, 227, 3, 147, 217, 39, 113, 17, 182, 164,
198, 126, 180, 51, 123, 215, 81, 65, 205, 222, 78, 101, 98, 199, 9,
5, 82, 67, 253, 162, 180, 223, 72, 98, 66, 2, 128, 65, 61, 107,
193, 243, 6, 121, 64, 101, 217, 132, 255, 102, 24, 104, 82, 231,
85, 38, 41, 202, 139, 32, 111, 38, 234, 127, 68, 163, 60, 140, 2,
39, 52, 86, 138, 94, 124, 142, 9, 235, 164, 16, 181, 217, 161, 26,
12, 8, 130, 181, 137, 220, 99, 201, 127, 201, 112, 190, 163, 193,
106, 156, 37, 2, 190, 147, 103, 247, 7, 229, 100, 68, 242, 62, 188,
34, 207, 164, 62, 66, 28, 7, 175, 210, 8, 124, 194, 36, 83, 236,
44, 127, 223, 168, 157, 68, 3, 14, 128, 103, 81, 154, 149, 202,
159, 71, 124, 151, 73, 105, 239, 176, 47, 156, 129, 14, 188, 71,
184, 153, 30, 177, 53, 89, 69, 99, 111, 56, 131, 3, 199, 31, 18,
222, 84, 187, 107, 58, 128, 108, 91, 102, 62, 231, 232, 67, 121,
29, 89, 1, 3, 82, 96, 15, 23, 248, 232, 249, 141, 149, 229, 70, 1,
0,
];
Messages::lightning_deserialize(&msg_recv).unwrap();
}
#[test]
fn real_clightning_close_message() {
let msg_recv = [
0u8, 38, 240, 6, 9, 251, 176, 118, 10, 79, 144, 36, 249, 193, 225,
103, 87, 223, 185, 26, 36, 177, 75, 202, 215, 227, 75, 79, 49, 101,
79, 167, 93, 206, 0, 22, 0, 20, 42, 238, 172, 27, 222, 161, 61,
181, 251, 208, 97, 79, 71, 255, 98, 8, 213, 205, 114, 94,
];
Messages::lightning_deserialize(&msg_recv).unwrap();
}
#[test]
fn decode_static_remotekey_channel_type() {
let bytes = [0x10, 0u8];
ChannelType::lightning_deserialize(&bytes).unwrap();
}
}