use std::collections::HashSet;
use std::fmt::{self, Display, Formatter};
use std::io::{Read, Write};
use amplify::Wrapper;
use bitcoin::hashes::Hash;
use internet2::tlv;
use lightning_encoding::{LightningDecode, LightningEncode};
use lnpbp::chain::AssetId;
use super::{ChannelId, InitFeatures};
#[derive(Wrapper, Clone, Eq, PartialEq, Default, Debug, From)]
#[cfg_attr(feature = "strict_encoding", derive(NetworkEncode, NetworkDecode))]
pub struct AssetList(HashSet<AssetId>);
impl LightningEncode for AssetList {
fn lightning_encode<E: Write>(
&self,
mut e: E,
) -> Result<usize, lightning_encoding::Error> {
self.0.iter().try_fold(0usize, |len, asset| {
Ok(len + asset.lightning_encode(&mut e)?)
})
}
}
impl LightningDecode for AssetList {
fn lightning_decode<D: Read>(
mut d: D,
) -> Result<Self, lightning_encoding::Error> {
let mut vec = Vec::with_capacity(32);
let len = d.read_to_end(&mut vec)?;
if len % 32 != 0 {
return Err(lightning_encoding::Error::DataIntegrityError(
format!(
"Init/networks length {} is not proportional to 32 bytes",
len
),
));
}
let assets = vec
.chunks(32)
.into_iter()
.map(AssetId::from_slice)
.collect::<Result<HashSet<AssetId>, _>>()
.expect("AssetId must be always constructable from 32-byte slice");
Ok(AssetList(assets))
}
}
#[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("init({global_features}, {local_features}, {assets:#?})")]
pub struct Init {
pub global_features: InitFeatures,
pub local_features: InitFeatures,
#[lightning_encoding(tlv = 1)]
#[cfg_attr(feature = "strict_encoding", network_encoding(tlv = 1))]
pub assets: AssetList,
#[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("ping({pong_size})")]
pub struct Ping {
pub pong_size: u16,
pub ignored: Vec<u8>,
}
#[derive(Clone, PartialEq, Eq, Debug, Error, LightningEncode, LightningDecode)]
#[cfg_attr(feature = "strict_encoding", derive(NetworkEncode, NetworkDecode))]
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(())
}
}
#[cfg(test)]
mod test {
use internet2::TypedEnum;
use lightning_encoding::LightningDecode;
use super::*;
use crate::bolt::Messages;
#[test]
fn default_init() {
let init_msg = Messages::Init(Init {
global_features: none!(),
local_features: default!(),
assets: none!(),
unknown_tlvs: none!(),
});
assert_eq!(&init_msg.serialize(), &[
0x00, 0x10, 0, 0, 0, 0
]);
}
#[test]
fn real_clightning_testvec() {
let init_recv = [
0u8, 16, 0, 2, 34, 0, 0, 3, 2, 170, 162, 1, 32, 111, 226, 140, 10, 182, 241, 179, 114, 193, 166, 162, 70, 174, 99,
247, 79, 147, 30, 131, 101, 225, 90, 8, 156, 104, 214, 25, 0, 0, 0,
0, 0,
];
Messages::lightning_deserialize(init_recv).unwrap();
}
#[test]
fn real_lnd_testvec() {
let init_recv = [
0, 16, 0, 2, 18, 0, 0, 253, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 136, 130, 82,
161,
];
Messages::lightning_deserialize(&init_recv).unwrap();
}
}