diff --git a/gossip_map/Cargo.toml b/gossip_map/Cargo.toml index da9c053..561811a 100644 --- a/gossip_map/Cargo.toml +++ b/gossip_map/Cargo.toml @@ -4,3 +4,4 @@ version = "0.1.0" edition = "2021" [dependencies] +byteorder = "1.4.3" diff --git a/gossip_map/src/flags.rs b/gossip_map/src/flags.rs new file mode 100644 index 0000000..2ee246f --- /dev/null +++ b/gossip_map/src/flags.rs @@ -0,0 +1,21 @@ +//! Flag implementation for the gossip map types + +pub static GOSSIP_STORE_MAJOR_VERSION: u16 = 0 << 5; +pub static GOSSIP_STORE_MAJOR_VERSION_MASK: u16 = 0xE0; + +/// Deleted fields should be ignored: on restart, they will be removed as the gossip_store is rewritten. +pub static GOSSIP_STORE_LEN_DELETED_BIT: u32 = 0x80000000; +/// The push flag indicates gossip which is generated locally: this is important for gossip timestamp filtering, +/// where peers request gossip and we always send our own gossip messages even if the timestamp wasn't within their +/// request.pub static GOSSIP_STORE_LEN_PUSH_BIT: u32 = 0x40000000; +pub static GOSSIP_STORE_LEN_RATELIMIT_BIT: u32 = 0x20000000; +/// The ratelimit flag indicates that this gossip message came too fast. +/// The message are corded in the gossip map, but don't relay it to peers. +pub static GOSSIP_STORE_LEN_MASK: u16 = 0x0000FFFF; + +// These duplicate constants in lightning/gossipd/gossip_store_wiregen.h +pub const WIRE_GOSSIP_STORE_PRIVATE_CHANNEL: u16 = 4104; +pub const WIRE_GOSSIP_STORE_PRIVATE_UPDATE: u16 = 4102; +pub const WIRE_GOSSIP_STORE_DELETE_CHAN: u16 = 4103; +pub const WIRE_GOSSIP_STORE_ENDED: u16 = 4105; +pub const WIRE_GOSSIP_STORE_CHANNEL_AMOUNT: u16 = 4101; diff --git a/gossip_map/src/lib.rs b/gossip_map/src/lib.rs index 3997ac5..a05d55a 100644 --- a/gossip_map/src/lib.rs +++ b/gossip_map/src/lib.rs @@ -1,20 +1,78 @@ -struct GossipMap; +use byteorder::{BigEndian, ReadBytesExt}; +use flags::{ + GOSSIP_STORE_MAJOR_VERSION, GOSSIP_STORE_MAJOR_VERSION_MASK, WIRE_GOSSIP_STORE_CHANNEL_AMOUNT, + WIRE_GOSSIP_STORE_DELETE_CHAN, WIRE_GOSSIP_STORE_ENDED, WIRE_GOSSIP_STORE_PRIVATE_CHANNEL, + WIRE_GOSSIP_STORE_PRIVATE_UPDATE, +}; +use std::{ + fs::File, + io::{BufReader, Error, ErrorKind}, +}; +use types::{GossipChannel, GossipNode}; + +mod flags; +mod types; + +/// Gossip map implementation, that allow you to manage the gossip_store +/// written by core lightning. +struct GossipMap { + version: u8, + stream: Option>, +} impl GossipMap { // Create a new instance of the gossip map. - pub fn new() -> Self { - GossipMap {} + pub fn new(version: u8) -> Self { + GossipMap { + version, + stream: None, + } } - pub fn get_channel(short_chananel_id: &str) -> Result<(), ()> { - Ok(()) + pub fn from_file(file_name: &str) -> Result { + let gossip_store = File::open(file_name)?; + let stream = BufReader::new(gossip_store); + let mut gossip_map = GossipMap { + version: 0, + stream: Some(stream), + }; + gossip_map.refresh()?; + Ok(gossip_map) } - pub fn get_node(node_id: &str) -> Result<(), ()> { - Ok(()) + pub fn get_channel(short_chananel_id: &str) -> Result<&'static GossipChannel, ()> { + todo!() } - pub fn refresh() -> Result<(), ()> { + pub fn get_node(node_id: &str) -> Result<&'static GossipNode, ()> { + todo!() + } + + fn refresh(&mut self) -> Result<(), std::io::Error> { + let version = self.stream.as_mut().unwrap().read_u8()? as u16; + if (version & GOSSIP_STORE_MAJOR_VERSION_MASK) != GOSSIP_STORE_MAJOR_VERSION { + return Err(Error::new( + ErrorKind::Other, + "Invalid gossip tore version {version}", + )); + } + self.version = version as u8; + + while let Ok(chunk) = self.stream.as_mut().unwrap().read_u8() { + match chunk as u16 { + // channel announcement! + 256 => todo!(), + WIRE_GOSSIP_STORE_PRIVATE_CHANNEL => todo!("parsing the private channel"), + WIRE_GOSSIP_STORE_CHANNEL_AMOUNT => todo!("channel ammount"), + WIRE_GOSSIP_STORE_PRIVATE_UPDATE => todo!("private update"), + WIRE_GOSSIP_STORE_DELETE_CHAN => todo!("channel deleted"), + WIRE_GOSSIP_STORE_ENDED => todo!("need to be reimplemented the open store"), + 257 => todo!("node announcment"), + 258 => todo!("channel update"), + _ => continue, + } + } + Ok(()) } } diff --git a/gossip_map/src/types.rs b/gossip_map/src/types.rs index 56b475d..7be86f4 100644 --- a/gossip_map/src/types.rs +++ b/gossip_map/src/types.rs @@ -1,16 +1,136 @@ //! Gossip map types implementations. +use byteorder::{BigEndian, ReadBytesExt}; +use std::{collections::HashMap, io::BufRead, ops::Deref, str, str::Bytes, vec::Vec}; -/// TODO: implement it. -struct GossipNodeId; +use crate::flags::{GOSSIP_STORE_LEN_DELETED_BIT, GOSSIP_STORE_LEN_MASK}; -/// TODO: implement it. -struct GossipNode; +trait GossipType { + /// Decode the gossip message from a sequence of bytes. + fn decode(stream: &mut dyn BufRead) -> Result + where + Self: Sized; -/// TODO: implement it -struct GossipChannel; + /// Encode the gossip message in a sequence of bytes. + fn encode(&self) -> Bytes; +} -/// TODO: implement it -struct GossipPartialChannel; +/// Node Id encoded for the gossip map +pub struct GossipNodeId { + node_id: String, +} -// TODO: implementing it -struct GossipStoredHeader; +impl GossipNodeId { + pub(crate) fn from_bytes(buff: &[u8; 32]) -> GossipNodeId { + GossipNodeId { + node_id: String::from_utf8(buff.to_vec()).unwrap(), + } + } +} + +impl GossipType for GossipNodeId { + fn decode(stream: &mut dyn BufRead) -> Result + where + Self: Sized, + { + let mut node_id: String = String::new(); + stream.read_to_string(&mut node_id)?; + + // FIXME: missed sanity check! + let res = GossipNodeId { node_id }; + Ok(res) + } + + fn encode(&self) -> Bytes { + todo!() + } +} + +pub struct GossipNode<'a> { + node_id: GossipNodeId, + announce_fileds: Option>, + announce_offset: Option, + channels: Vec<&'a GossipChannel<'a>>, +} + +impl<'a> GossipNode<'a> { + /// add a gossip channel inside the gossip map. + pub fn add_channel(&'a mut self, channel: &'a GossipChannel) { + self.channels.push(channel); + } +} + +impl GossipType for GossipNode<'_> { + fn decode(stream: &mut dyn BufRead) -> Result + where + Self: Sized, + { + let mut buff = [0; 32]; + stream.read_exact(&mut buff)?; + let node_id = GossipNodeId::from_bytes(&buff); + todo!() + } + + fn encode(&self) -> Bytes { + todo!() + } +} + +/// Channel Information stored inside the Gossip Map. +pub struct GossipChannel<'a> { + fileds: HashMap, + annound_offset: u32, + scid: String, + node_one: &'a GossipNode<'a>, + node_two: &'a GossipNode<'a>, + update_fields: Vec>, + update_offset: Vec, + satoshi: Option, + half_channels: Vec<&'a GossipPartialChannel>, +} + +impl GossipType for GossipChannel<'_> { + fn decode(stream: &mut dyn BufRead) -> Result + where + Self: Sized, + { + todo!() + } + + fn encode(&self) -> Bytes { + todo!() + } +} + +/// One direction gossip map channel +pub struct GossipPartialChannel {} + +/// Gossip map header, that contains the version +/// of the gossip map. +pub struct GossipStoredHeader { + flags: bool, + len: u16, + crc: u32, + timestamp: u32, +} + +impl GossipType for GossipStoredHeader { + fn decode(stream: &mut dyn BufRead) -> Result + where + Self: Sized, + { + let len = stream.read_u16::()?; + let crc = stream.read_u32::()?; + let timestamp = stream.read_u32::()?; + + Ok(GossipStoredHeader { + timestamp, + crc, + flags: (len as u32 & GOSSIP_STORE_LEN_DELETED_BIT) != 0, + len: (len & GOSSIP_STORE_LEN_MASK), + }) + } + + fn encode(&self) -> Bytes { + todo!() + } +}