use amplify::DumbDefault;
use std::collections::HashSet;
use std::fmt::{self, Display, Formatter};
use std::io;
use bitcoin::hashes::{sha256, Hmac};
use bitcoin::secp256k1::{PublicKey, Signature};
use bitcoin::{Script, Txid};
use internet2::{tlv, CreateUnmarshaller, Payload, Unmarshall, Unmarshaller};
use lightning_encoding::{self, LightningDecode, LightningEncode};
use lnpbp::chain::AssetId;
use wallet::SECP256K1_PUBKEY_DUMB;
use wallet::{HashLock, HashPreimage};
use super::payment::{
AddressList, Alias, ChannelId, NodeColor, ShortChannelId, TempChannelId,
};
use crate::InitFeatures;
#[cfg(feature = "rgb")]
use rgb::Consignment;
lazy_static! {
pub static ref LNPWP_UNMARSHALLER: Unmarshaller<Messages> =
Messages::create_unmarshaller();
}
#[derive(Clone, Debug, Display, Api, StrictEncode, StrictDecode)]
#[api(encoding = "lightning")]
#[strict_encoding_crate(lnpbp::strict_encoding)]
#[non_exhaustive]
pub enum Messages {
#[api(type = 16)]
#[display(inner)]
Init(Init),
#[api(type = 17)]
#[display(inner)]
Error(Error),
#[api(type = 18)]
#[display(inner)]
Ping(Ping),
#[api(type = 19)]
#[display("pong(...)")]
Pong(Vec<u8>),
#[api(type = 32)]
#[display(inner)]
OpenChannel(OpenChannel),
#[api(type = 33)]
#[display(inner)]
AcceptChannel(AcceptChannel),
#[api(type = 34)]
#[display(inner)]
FundingCreated(FundingCreated),
#[api(type = 35)]
#[display(inner)]
FundingSigned(FundingSigned),
#[api(type = 36)]
#[display(inner)]
FundingLocked(FundingLocked),
#[api(type = 38)]
#[display(inner)]
Shutdown(Shutdown),
#[api(type = 39)]
#[display(inner)]
ClosingSigned(ClosingSigned),
#[api(type = 128)]
#[display(inner)]
UpdateAddHtlc(UpdateAddHtlc),
#[api(type = 130)]
#[display(inner)]
UpdateFulfillHtlc(UpdateFulfillHtlc),
#[api(type = 131)]
#[display(inner)]
UpdateFailHtlc(UpdateFailHtlc),
#[api(type = 135)]
#[display(inner)]
UpdateFailMalformedHtlc(UpdateFailMalformedHtlc),
#[api(type = 132)]
#[display(inner)]
CommitmentSigned(CommitmentSigned),
#[api(type = 133)]
#[display(inner)]
RevokeAndAck(RevokeAndAck),
#[api(type = 134)]
#[display(inner)]
UpdateFee(UpdateFee),
#[api(type = 136)]
#[display(inner)]
ChannelReestablish(ChannelReestablish),
#[api(type = 259)]
#[display(inner)]
AnnouncementSignatures(AnnouncementSignatures),
#[api(type = 256)]
#[display(inner)]
ChannelAnnouncements(ChannelAnnouncements),
#[api(type = 257)]
#[display(inner)]
NodeAnnouncements(NodeAnnouncements),
#[api(type = 258)]
#[display(inner)]
ChannelUpdate(ChannelUpdate),
#[api(type = 261)]
#[display(inner)]
QueryShortChannelIds(QueryShortChannelIds),
#[api(type = 262)]
#[display(inner)]
ReplyShortChannelIdsEnd(ReplyShortChannelIdsEnd),
#[api(type = 263)]
#[display(inner)]
QueryChannelRange(QueryChannelRange),
#[api(type = 264)]
#[display(inner)]
ReplyChannelRange(ReplyChannelRange),
#[api(type = 265)]
#[display(inner)]
GossipTimestampFilter(GossipTimestampFilter),
#[cfg(feature = "rgb")]
#[api(type = 57156)]
#[display(inner)]
AssignFunds(AssignFunds),
}
#[derive(
Clone,
PartialEq,
Eq,
Debug,
Display,
LightningEncode,
LightningDecode,
StrictEncode,
StrictDecode,
)]
#[strict_encoding_crate(lnpbp::strict_encoding)]
#[display("init({global_features}, {local_features}, {assets:#?})")]
pub struct Init {
pub global_features: InitFeatures,
pub local_features: InitFeatures,
#[tlv(type = 1)]
pub assets: HashSet<AssetId>,
#[tlv(unknown)]
pub unknown_tlvs: tlv::Map,
}
#[derive(
Clone,
PartialEq,
Eq,
Debug,
Display,
LightningEncode,
LightningDecode,
StrictEncode,
StrictDecode,
)]
#[strict_encoding_crate(lnpbp::strict_encoding)]
#[display(Debug)]
pub struct Ping {
pub ignored: Vec<u8>,
pub pong_size: u16,
}
#[derive(
Clone,
PartialEq,
Debug,
Error,
LightningEncode,
LightningDecode,
StrictEncode,
StrictDecode,
)]
#[strict_encoding_crate(lnpbp::strict_encoding)]
pub struct Error {
pub channel_id: ChannelId,
pub data: Vec<u8>,
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str("Error")?;
if self.channel_id.is_wildcard() {
f.write_str(" on all channels")?;
} else {
write!(f, " on channel {}", self.channel_id)?;
}
if let Ok(msg) = String::from_utf8(self.data.clone()) {
write!(f, ": {}", msg)?;
}
Ok(())
}
}
#[derive(
Clone,
PartialEq,
Eq,
Debug,
Display,
LightningEncode,
LightningDecode,
StrictEncode,
StrictDecode,
)]
#[strict_encoding_crate(lnpbp::strict_encoding)]
#[display("open_channel({chain_hash}, {temporary_channel_id}, {funding_satoshis}, {channel_flags}, ...)")]
pub struct OpenChannel {
pub chain_hash: AssetId,
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,
#[tlv(type = 0)]
pub shutdown_scriptpubkey: Option<Script>,
#[tlv(unknown)]
pub unknown_tlvs: tlv::Map,
}
#[derive(
Clone,
PartialEq,
Eq,
Debug,
Display,
LightningEncode,
LightningDecode,
StrictEncode,
StrictDecode,
)]
#[strict_encoding_crate(lnpbp::strict_encoding)]
#[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,
}
#[derive(
Clone,
PartialEq,
Eq,
Debug,
Display,
LightningEncode,
LightningDecode,
StrictEncode,
StrictDecode,
)]
#[strict_encoding_crate(lnpbp::strict_encoding)]
#[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,
LightningEncode,
LightningDecode,
StrictEncode,
StrictDecode,
)]
#[strict_encoding_crate(lnpbp::strict_encoding)]
#[display("funding_signed({channel_id}, ...signature)")]
pub struct FundingSigned {
pub channel_id: ChannelId,
pub signature: Signature,
}
#[derive(
Clone,
PartialEq,
Eq,
Debug,
Display,
LightningEncode,
LightningDecode,
StrictEncode,
StrictDecode,
)]
#[strict_encoding_crate(lnpbp::strict_encoding)]
#[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,
LightningEncode,
LightningDecode,
StrictEncode,
StrictDecode,
)]
#[strict_encoding_crate(lnpbp::strict_encoding)]
#[display("shutdown({channel_id}, {scriptpubkey})")]
pub struct Shutdown {
pub channel_id: ChannelId,
pub scriptpubkey: Script,
}
#[derive(
Clone,
PartialEq,
Eq,
Debug,
Display,
LightningEncode,
LightningDecode,
StrictEncode,
StrictDecode,
)]
#[strict_encoding_crate(lnpbp::strict_encoding)]
#[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,
LightningEncode,
LightningDecode,
StrictEncode,
StrictDecode,
)]
#[strict_encoding_crate(lnpbp::strict_encoding)]
#[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: OnionPacket,
#[tlv(type = 1)]
pub asset_id: Option<AssetId>,
}
#[derive(
Clone,
PartialEq,
Eq,
Debug,
Display,
LightningEncode,
LightningDecode,
StrictEncode,
StrictDecode,
)]
#[strict_encoding_crate(lnpbp::strict_encoding)]
#[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,
LightningEncode,
LightningDecode,
StrictEncode,
StrictDecode,
)]
#[strict_encoding_crate(lnpbp::strict_encoding)]
#[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,
LightningEncode,
LightningDecode,
StrictEncode,
StrictDecode,
)]
#[strict_encoding_crate(lnpbp::strict_encoding)]
#[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,
LightningEncode,
LightningDecode,
StrictEncode,
StrictDecode,
)]
#[strict_encoding_crate(lnpbp::strict_encoding)]
#[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,
LightningEncode,
LightningDecode,
StrictEncode,
StrictDecode,
)]
#[strict_encoding_crate(lnpbp::strict_encoding)]
#[display("revoke_and_ack({channel_id}, {next_per_commitment_point}, ...per_commitment_secret)")]
pub struct RevokeAndAck {
pub channel_id: ChannelId,
pub per_commitment_secret: [u8; 32],
pub next_per_commitment_point: PublicKey,
}
#[derive(
Clone,
PartialEq,
Eq,
Debug,
Display,
LightningEncode,
LightningDecode,
StrictEncode,
StrictDecode,
)]
#[strict_encoding_crate(lnpbp::strict_encoding)]
#[display("update_fee({channel_id}, {feerate_per_kw})")]
pub struct UpdateFee {
pub channel_id: ChannelId,
pub feerate_per_kw: u32,
}
#[derive(
Clone,
PartialEq,
Eq,
Debug,
Display,
LightningEncode,
LightningDecode,
StrictEncode,
StrictDecode,
)]
#[strict_encoding_crate(lnpbp::strict_encoding)]
#[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: [u8; 32],
pub my_current_per_commitment_point: PublicKey,
}
#[derive(
Clone,
PartialEq,
Eq,
Debug,
Display,
LightningEncode,
LightningDecode,
StrictEncode,
StrictDecode,
)]
#[strict_encoding_crate(lnpbp::strict_encoding)]
#[display(
"announcement_signature({channel_id}, {short_channel_id}, ...signatures)"
)]
pub struct AnnouncementSignatures {
pub channel_id: ChannelId,
pub short_channel_id: ShortChannelId,
pub node_signature: Signature,
pub bitcoin_signature: Signature,
}
#[derive(
Clone,
PartialEq,
Eq,
Debug,
Display,
LightningEncode,
LightningDecode,
StrictEncode,
StrictDecode,
)]
#[strict_encoding_crate(lnpbp::strict_encoding)]
#[display("channel_announcement({chain_hash}, {short_channel_id}, ...)")]
pub struct ChannelAnnouncements {
pub node_signature_1: Signature,
pub node_signature_2: Signature,
pub bitcoin_signature_1: Signature,
pub bitcoin_signature_2: Signature,
pub features: InitFeatures,
pub chain_hash: AssetId,
pub short_channel_id: ShortChannelId,
pub node_id_1: PublicKey,
pub node_id_2: PublicKey,
pub bitcoin_key_1: PublicKey,
pub bitcoin_key_2: PublicKey,
}
#[derive(
Clone,
PartialEq,
Eq,
Debug,
Display,
LightningEncode,
LightningDecode,
StrictEncode,
StrictDecode,
)]
#[strict_encoding_crate(lnpbp::strict_encoding)]
#[display("node_announcement({node_id}, {alias}, {addresses}, ...)")]
pub struct NodeAnnouncements {
pub signature: Signature,
pub features: InitFeatures,
pub timestamp: u32,
pub node_id: PublicKey,
pub rgb_color: NodeColor,
pub alias: Alias,
pub addresses: AddressList,
}
#[derive(
Clone,
PartialEq,
Eq,
Debug,
Display,
LightningEncode,
LightningDecode,
StrictEncode,
StrictDecode,
)]
#[strict_encoding_crate(lnpbp::strict_encoding)]
#[display("channel_id({chain_hash}, {short_channel_id}, {timestamp}, ...)")]
pub struct ChannelUpdate {
pub signature: Signature,
pub chain_hash: AssetId,
pub short_channel_id: ShortChannelId,
pub timestamp: u32,
pub message_flags: u8,
pub channle_flags: u8,
pub cltv_expiry_delta: u16,
pub htlc_minimum_msal: u64,
pub fee_base_msat: u32,
pub fee_proportional_millionths: u32,
pub htlc_maximum_msat: u64,
}
#[derive(
Clone,
PartialEq,
Eq,
Debug,
Display,
LightningEncode,
LightningDecode,
StrictEncode,
StrictDecode,
)]
#[strict_encoding_crate(lnpbp::strict_encoding)]
#[display("query_short_channel_ids({chain_hash}, {short_ids:#?}, ...tlvs)")]
pub struct QueryShortChannelIds {
pub chain_hash: AssetId,
pub short_ids: Vec<ShortChannelId>,
}
#[derive(
Clone,
PartialEq,
Eq,
Debug,
Display,
LightningEncode,
LightningDecode,
StrictEncode,
StrictDecode,
)]
#[strict_encoding_crate(lnpbp::strict_encoding)]
#[display("reply_short_channel_ids_end({chain_hash}, {full_information})")]
pub struct ReplyShortChannelIdsEnd {
pub chain_hash: AssetId,
pub full_information: u8,
}
#[derive(
Clone,
PartialEq,
Eq,
Debug,
Display,
LightningEncode,
LightningDecode,
StrictEncode,
StrictDecode,
)]
#[strict_encoding_crate(lnpbp::strict_encoding)]
#[display(
"querry_channel_range({chain_hash}, {first_blocknum}, {number_of_blocks}, ...tlvs)"
)]
pub struct QueryChannelRange {
pub chain_hash: AssetId,
pub first_blocknum: u32,
pub number_of_blocks: u32,
}
#[derive(
Clone,
PartialEq,
Eq,
Debug,
Display,
LightningEncode,
LightningDecode,
StrictEncode,
StrictDecode,
)]
#[strict_encoding_crate(lnpbp::strict_encoding)]
#[display(
"reply_channel_range({chain_hash}, {first_blocknum}, {number_of_blocks}, ...)"
)]
pub struct ReplyChannelRange {
pub chain_hash: AssetId,
pub first_blocknum: u32,
pub number_of_blocks: u32,
pub full_information: u8,
pub encoded_short_ids: Vec<ShortChannelId>,
}
#[derive(
Clone,
PartialEq,
Eq,
Debug,
Display,
LightningEncode,
LightningDecode,
StrictEncode,
StrictDecode,
)]
#[strict_encoding_crate(lnpbp::strict_encoding)]
#[display("gossip_time_stamp_filter({chain_hash}, {first_timestamp}, {timestamp_range})")]
pub struct GossipTimestampFilter {
pub chain_hash: AssetId,
pub first_timestamp: u32,
pub timestamp_range: u32,
}
#[cfg(feature = "rgb")]
#[derive(
Clone,
PartialEq,
Eq,
Debug,
Display,
LightningEncode,
LightningDecode,
StrictEncode,
StrictDecode,
)]
#[strict_encoding_crate(lnpbp::strict_encoding)]
#[display("assign_funds({channel_id}, {outpoint}, ...)")]
pub struct AssignFunds {
pub channel_id: ChannelId,
pub consignment: Consignment,
pub outpoint: bitcoin::OutPoint,
pub blinding: u64,
}
impl LightningEncode for Messages {
fn lightning_encode<E: io::Write>(&self, e: E) -> Result<usize, io::Error> {
Payload::from(self.clone()).lightning_encode(e)
}
}
impl LightningDecode for Messages {
fn lightning_decode<D: io::Read>(
d: D,
) -> Result<Self, lightning_encoding::Error> {
Ok((&*LNPWP_UNMARSHALLER
.unmarshall(&Vec::<u8>::lightning_decode(d)?)
.map_err(|_| {
lightning_encoding::Error::DataIntegrityError(s!(
"can't unmarshall LMP message"
))
})?)
.clone())
}
}
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: *SECP256K1_PUBKEY_DUMB,
revocation_basepoint: *SECP256K1_PUBKEY_DUMB,
payment_point: *SECP256K1_PUBKEY_DUMB,
delayed_payment_basepoint: *SECP256K1_PUBKEY_DUMB,
htlc_basepoint: *SECP256K1_PUBKEY_DUMB,
first_per_commitment_point: *SECP256K1_PUBKEY_DUMB,
channel_flags: 0,
shutdown_scriptpubkey: None,
unknown_tlvs: none!(),
}
}
}
#[derive(
Clone,
PartialEq,
Eq,
Debug,
Display,
LightningEncode,
LightningDecode,
StrictEncode,
StrictDecode,
)]
#[strict_encoding_crate(lnpbp::strict_encoding)]
#[display(Debug)]
pub struct OnionPacket {
pub version: u8,
pub public_key: bitcoin::secp256k1::PublicKey,
pub hop_data: Vec<u8>,
pub hmac: Hmac<sha256::Hash>,
}
impl DumbDefault for OnionPacket {
fn dumb_default() -> Self {
OnionPacket {
version: 0,
public_key: *SECP256K1_PUBKEY_DUMB,
hop_data: empty!(),
hmac: zero!(),
}
}
}
#[cfg(test)]
mod test {
use super::*;
use bitcoin::hashes::hex::FromHex;
use internet2::TypedEnum;
#[test]
fn bolt1_testvec() {
let init_msg = Messages::Init(Init {
global_features: none!(),
local_features: none!(),
assets: none!(),
unknown_tlvs: none!(),
});
assert_eq!(
init_msg.serialize(),
Vec::<u8>::from_hex("001000000000").unwrap()
);
}
}