proto.rs 41 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2026 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. //! P2P protocol handlers for the Event Graph.
  19. //!
  20. //! Each peer connection spawns a [`ProtocolEventGraph`] instance that
  21. //! manages message subscriptions and handles incoming events, sync
  22. //! requests, and bidirectional range queries.
  23. use std::{
  24. collections::{BTreeMap, HashSet, VecDeque},
  25. str::FromStr,
  26. sync::{
  27. atomic::{AtomicUsize, Ordering::SeqCst},
  28. Arc,
  29. },
  30. };
  31. use darkfi_sdk::{crypto::poseidon_hash, pasta::pallas};
  32. use darkfi_serial::{
  33. async_trait, deserialize_async_partial, serialize_async, FutAsyncWriteExt, SerialDecodable,
  34. SerialEncodable,
  35. };
  36. use smol::Executor;
  37. use tracing::{error, warn};
  38. use super::{
  39. event::Header,
  40. filter_requested_event_rep,
  41. rln::{self, create_slash_proof, sss_recover, RLNNode, SlashBlob},
  42. Event, EventGraphPtr, LayerUTips, NULL_ID, NULL_PARENTS,
  43. };
  44. use crate::{
  45. impl_p2p_message,
  46. net::{
  47. metering::{MeteringConfiguration, DEFAULT_METERING_CONFIGURATION},
  48. ChannelPtr, Message, MessageSubscription, ProtocolBase, ProtocolBasePtr,
  49. ProtocolJobsManager, ProtocolJobsManagerPtr,
  50. },
  51. system::msleep,
  52. util::time::NanoTimestamp,
  53. Error, Result,
  54. };
  55. /// After this many malicious-looking messages from a single peer, we
  56. /// drop the connection.
  57. const MALICIOUS_THRESHOLD: usize = 5;
  58. /// If a peer sends more than this many unique events within
  59. /// [`WINDOW_EXPIRY_TIME`], we ban them.
  60. const WINDOW_MAXSIZE: usize = 200;
  61. /// Rolling window length for the flood-detection counter.
  62. const WINDOW_EXPIRY_TIME: NanoTimestamp = NanoTimestamp::from_secs(60);
  63. /// Rolling window length for the outbound broadcast rate limiter.
  64. const RATELIMIT_EXPIRY_TIME: NanoTimestamp = NanoTimestamp::from_secs(10);
  65. /// Rate limiter activates above this many broadcasts in the window.
  66. const RATELIMIT_MIN_COUNT: usize = 6;
  67. /// Reference point for computing sleep time: when count = this value...
  68. const RATELIMIT_SAMPLE_IDX: usize = 10;
  69. /// Sleep this many milliseconds before broadcasting.
  70. const RATELIMIT_SAMPLE_SLEEP: usize = 1000;
  71. /// Maximum number of recursive round-trips when fetching missing
  72. /// parent events from a peer during `handle_event_put`.
  73. ///
  74. /// # Why this limit exists
  75. ///
  76. /// When we receive a new event whose parents we don't have, we ask
  77. /// the sender for them. Those parents may themselves reference unknown
  78. /// grandparents, so we ask again, and so on. A malicious peer can exploit
  79. /// this by fabricating an arbitrarily deep chain, forcing us into an
  80. /// unbounded loop of network requests.
  81. ///
  82. /// # What happens when the limit is hit
  83. ///
  84. /// The event (and its unresolvable ancestry) is dropped, and the
  85. /// peer's malicious counter is incremented. This is safe because:
  86. ///
  87. /// * **Legitimate DAGs** rarely reach this depth. With 5 parents
  88. /// per event and concurrent users, cross-references keep the
  89. /// effective depth well below 1000.
  90. /// * **The header-sync path** (`dag_sync`) is unaffected - it
  91. /// fetches all headers in bulk by layer, with no recursion.
  92. /// A node that's 1000+ layers behind should be using `dag_sync`
  93. /// rather than relying on `EventPut` catch-up.
  94. /// * **After a full sync**, subsequent `EventPut` events will
  95. /// typically reference parents that are already known, so the
  96. /// depth stays near 1.
  97. const MAX_PARENT_FETCH_DEPTH: usize = 1000;
  98. /// Capacity of the bounded broadcast channel. When the channel is
  99. /// full, new relay events are dropped rather than blocking the
  100. /// event processing loop - this provides backpressure and prevents
  101. /// unbounded memory growth under sustained load.
  102. const BROADCASTER_CAPACITY: usize = 256;
  103. /// Maximum event bodies a peer may request in one `EventReq`.
  104. pub const MAX_EVENT_REQ_IDS: usize = 128;
  105. /// Maximum event bodies accepted in one `EventRep`.
  106. pub const MAX_EVENT_REP_EVENTS: usize = MAX_EVENT_REQ_IDS;
  107. /// Maximum tips a peer may include in one `HeaderReq`.
  108. pub const MAX_HEADER_REQ_TIPS: usize = 1024;
  109. /// Maximum headers returned in one `HeaderRep`.
  110. pub const MAX_HEADER_REP_HEADERS: usize = 4096;
  111. /// Maximum tips returned in one `TipRep`.
  112. pub const MAX_TIP_REP_TIPS: usize = 1024;
  113. /// Maximum events served for one paginated range request.
  114. pub const MAX_RANGE_PAGE_SIZE: usize = 100;
  115. pub(crate) fn count_layer_tips(tips: &LayerUTips) -> usize {
  116. tips.values().map(HashSet::len).sum()
  117. }
  118. pub(crate) fn cap_layer_tips(tips: &LayerUTips, limit: usize) -> LayerUTips {
  119. let mut out = BTreeMap::new();
  120. let mut remaining = limit;
  121. for (layer, hashes) in tips {
  122. if remaining == 0 {
  123. break
  124. }
  125. let mut sorted: Vec<_> = hashes.iter().copied().collect();
  126. sorted.sort_unstable_by(|a, b| a.as_bytes().cmp(b.as_bytes()));
  127. let take = sorted.len().min(remaining);
  128. if take > 0 {
  129. out.insert(*layer, sorted.into_iter().take(take).collect());
  130. remaining -= take;
  131. }
  132. }
  133. out
  134. }
  135. /// Match one parent-fetch response against the exact request and update the
  136. /// outstanding frontier only if the response resolves at least one missing ID.
  137. pub(crate) fn filter_parent_event_rep(
  138. requested: &[blake3::Hash],
  139. missing: &mut HashSet<blake3::Hash>,
  140. known: &mut HashSet<blake3::Hash>,
  141. events: Vec<Event>,
  142. blobs: Vec<Vec<u8>>,
  143. ) -> Result<Vec<(Event, Vec<u8>)>> {
  144. let (matched_events, matched_blobs, _) = filter_requested_event_rep(requested, events, blobs)?;
  145. let resolves_missing = matched_events.iter().any(|event| {
  146. let id = event.id();
  147. missing.contains(&id) && !known.contains(&id)
  148. });
  149. if !resolves_missing {
  150. return Err(Error::DagSyncFailed)
  151. }
  152. let mut resolved = Vec::with_capacity(matched_events.len());
  153. for (event, blob) in matched_events.into_iter().zip(matched_blobs) {
  154. let id = event.id();
  155. if missing.remove(&id) && known.insert(id) {
  156. resolved.push((event, blob));
  157. }
  158. }
  159. if resolved.is_empty() {
  160. return Err(Error::DagSyncFailed)
  161. }
  162. Ok(resolved)
  163. }
  164. struct MovingWindow {
  165. times: VecDeque<NanoTimestamp>,
  166. expiry_time: NanoTimestamp,
  167. }
  168. impl MovingWindow {
  169. fn new(expiry: NanoTimestamp) -> Self {
  170. Self { times: VecDeque::new(), expiry_time: expiry }
  171. }
  172. fn clean(&mut self) {
  173. while let Some(ts) = self.times.front() {
  174. match ts.elapsed() {
  175. Ok(elapsed) if elapsed >= self.expiry_time => {
  176. self.times.pop_front();
  177. }
  178. Err(_) => {
  179. self.times.pop_front();
  180. } // future timestamp - remove
  181. _ => break,
  182. }
  183. }
  184. }
  185. fn ticktock(&mut self) {
  186. self.clean();
  187. self.times.push_back(NanoTimestamp::current_time());
  188. }
  189. fn count(&self) -> usize {
  190. self.times.len()
  191. }
  192. }
  193. /// Broadcast a new event (header + content + optional RLN blob).
  194. #[derive(Clone, SerialEncodable, SerialDecodable)]
  195. pub struct EventPut(pub Event, pub Vec<u8>);
  196. impl_p2p_message!(EventPut, "EventGraph::EventPut", 0, 0, DEFAULT_METERING_CONFIGURATION);
  197. /// Broadcast a static-DAG event (RLN registration / slashing).
  198. #[derive(Clone, SerialEncodable, SerialDecodable)]
  199. pub struct StaticPut(pub Event, pub Vec<u8>);
  200. impl_p2p_message!(StaticPut, "EventGraph::StaticPut", 0, 0, DEFAULT_METERING_CONFIGURATION);
  201. /// Request full events by their IDs.
  202. #[derive(Clone, SerialEncodable, SerialDecodable)]
  203. pub struct EventReq(pub Vec<blake3::Hash>);
  204. impl_p2p_message!(EventReq, "EventGraph::EventReq", 0, 0, DEFAULT_METERING_CONFIGURATION);
  205. /// Reply with full events, plus optional aligned blobs.
  206. ///
  207. /// `events` and `blobs` are aligned by index: `blobs[i]` is the
  208. /// original RLN blob for `events[i]`. For non-genesis events,
  209. /// peers MUST supply a non-empty blob - the recipient's
  210. /// `dag_insert_with_blobs` rejects events without one. An empty
  211. /// blob is acceptable only for genesis-shaped events.
  212. ///
  213. /// `blobs.len() != events.len()` is wire-compatible: missing
  214. /// trailing entries are treated as empty, which on non-genesis
  215. /// events means rejection.
  216. #[derive(Clone, SerialEncodable, SerialDecodable)]
  217. pub struct EventRep(pub Vec<Event>, pub Vec<Vec<u8>>);
  218. impl_p2p_message!(EventRep, "EventGraph::EventRep", 0, 0, DEFAULT_METERING_CONFIGURATION);
  219. /// Broadcast a single header (unused in current flow, reserved).
  220. #[derive(Clone, SerialEncodable, SerialDecodable)]
  221. pub struct HeaderPut(pub Header);
  222. impl_p2p_message!(HeaderPut, "EventGraph::HeaderPut", 0, 0, DEFAULT_METERING_CONFIGURATION);
  223. /// Request headers that the peer has but we don't, given our tips.
  224. #[derive(Clone, SerialEncodable, SerialDecodable)]
  225. pub struct HeaderReq(pub String, pub LayerUTips);
  226. impl_p2p_message!(HeaderReq, "EventGraph::HeaderReq", 0, 0, DEFAULT_METERING_CONFIGURATION);
  227. /// Reply with headers.
  228. #[derive(Clone, SerialEncodable, SerialDecodable)]
  229. pub struct HeaderRep(pub Vec<Header>);
  230. impl_p2p_message!(HeaderRep, "EventGraph::HeaderRep", 0, 0, DEFAULT_METERING_CONFIGURATION);
  231. /// Request a peer's current unreferenced tips for a DAG.
  232. #[derive(Clone, SerialEncodable, SerialDecodable)]
  233. pub struct TipReq(pub String);
  234. impl_p2p_message!(TipReq, "EventGraph::TipReq", 0, 0, DEFAULT_METERING_CONFIGURATION);
  235. /// Reply with unreferenced tips.
  236. #[derive(Clone, SerialEncodable, SerialDecodable)]
  237. pub struct TipRep(pub LayerUTips);
  238. impl_p2p_message!(TipRep, "EventGraph::TipRep", 0, 0, DEFAULT_METERING_CONFIGURATION);
  239. /// Pagination direction for [`RangeReq`].
  240. #[derive(Clone, Debug, SerialEncodable, SerialDecodable)]
  241. pub enum SyncDirection {
  242. /// Ascending timestamps (older -> newer).
  243. /// Used for catching up from a known position.
  244. Forward,
  245. /// Descending timestamps (newer -> older).
  246. /// Used for loading the latest messages first and scrolling backward.
  247. Backward,
  248. }
  249. /// Bidirectional content pagination request.
  250. ///
  251. /// The responder uses its [`TimeIndex`] to serve events around
  252. /// `cursor_ts` in the requested direction, up to `limit` events.
  253. /// This is the primary message for lazy content fetching - the
  254. /// requester already has headers (DAG structure) and wants bodies.
  255. #[derive(Clone, SerialEncodable, SerialDecodable)]
  256. pub struct RangeReq {
  257. /// Which DAG to query (genesis timestamp as string).
  258. pub dag_name: String,
  259. /// Timestamp cursor. Use `u64::MAX` for "start from newest"
  260. /// or `0` for "start from oldest".
  261. pub cursor_ts: u64,
  262. /// Which direction to paginate.
  263. pub direction: SyncDirection,
  264. /// Maximum number of events to return.
  265. pub limit: u32,
  266. }
  267. impl_p2p_message!(RangeReq, "EventGraph::RangeReq", 0, 0, DEFAULT_METERING_CONFIGURATION);
  268. /// Reply to a [`RangeReq`] with events in the requested order.
  269. #[derive(Clone, SerialEncodable, SerialDecodable)]
  270. pub struct RangeRep(pub Vec<Event>);
  271. impl_p2p_message!(RangeRep, "EventGraph::RangeRep", 0, 0, DEFAULT_METERING_CONFIGURATION);
  272. /// Per-connection protocol handler for the Event Graph.
  273. ///
  274. /// One instance is created for each peer connection. It subscribes
  275. /// to all Event Graph P2P message types and spawns async tasks for:
  276. ///
  277. /// * `handle_event_put` - real-time ingestion of new events,
  278. /// including RLN proof verification and recursive parent fetching
  279. /// (bounded by [`MAX_PARENT_FETCH_DEPTH`]).
  280. /// * `handle_static_put` - RLN registration and slashing events.
  281. /// * `handle_event_req` - serving event content to peers (only
  282. /// for IDs we've previously broadcast, to prevent DAG enumeration).
  283. /// * `handle_header_req` - serving headers the peer is missing.
  284. /// * `handle_tip_req` - serving our unreferenced tips.
  285. /// * `handle_range_req` - serving bidirectional paginated content
  286. /// (the primary mechanism for lazy content fetching).
  287. /// * `broadcast_rate_limiter` - rate-limiting outbound event
  288. /// relay through a bounded channel with adaptive sleep.
  289. ///
  290. /// RLN share metadata is **not** stored on this struct - it lives on
  291. /// [`EventGraph::rln_state`] so that duplicate/reuse detection works
  292. /// across all peer connections, not just the one that relayed a
  293. /// particular event.
  294. pub struct ProtocolEventGraph {
  295. channel: ChannelPtr,
  296. event_graph: EventGraphPtr,
  297. ev_put_sub: MessageSubscription<EventPut>,
  298. st_put_sub: MessageSubscription<StaticPut>,
  299. ev_req_sub: MessageSubscription<EventReq>,
  300. ev_rep_sub: MessageSubscription<EventRep>,
  301. _hdr_put_sub: MessageSubscription<HeaderPut>,
  302. hdr_req_sub: MessageSubscription<HeaderReq>,
  303. _hdr_rep_sub: MessageSubscription<HeaderRep>,
  304. tip_req_sub: MessageSubscription<TipReq>,
  305. _tip_rep_sub: MessageSubscription<TipRep>,
  306. range_req_sub: MessageSubscription<RangeReq>,
  307. _range_rep_sub: MessageSubscription<RangeRep>,
  308. malicious_count: AtomicUsize,
  309. jobsman: ProtocolJobsManagerPtr,
  310. broadcaster_push: smol::channel::Sender<EventPut>,
  311. broadcaster_pull: smol::channel::Receiver<EventPut>,
  312. }
  313. #[async_trait]
  314. impl ProtocolBase for ProtocolEventGraph {
  315. async fn start(self: Arc<Self>, ex: Arc<Executor<'_>>) -> Result<()> {
  316. self.jobsman.clone().start(ex.clone());
  317. self.jobsman.clone().spawn(self.clone().handle_event_put(), ex.clone()).await;
  318. self.jobsman.clone().spawn(self.clone().handle_static_put(), ex.clone()).await;
  319. self.jobsman.clone().spawn(self.clone().handle_event_req(), ex.clone()).await;
  320. self.jobsman.clone().spawn(self.clone().handle_header_req(), ex.clone()).await;
  321. self.jobsman.clone().spawn(self.clone().handle_tip_req(), ex.clone()).await;
  322. self.jobsman.clone().spawn(self.clone().handle_range_req(), ex.clone()).await;
  323. self.jobsman.clone().spawn(self.clone().broadcast_rate_limiter(), ex.clone()).await;
  324. Ok(())
  325. }
  326. fn name(&self) -> &'static str {
  327. "ProtocolEventGraph"
  328. }
  329. }
  330. impl ProtocolEventGraph {
  331. /// Register message dispatchers and subscribe to all channels.
  332. pub async fn init(eg: EventGraphPtr, channel: ChannelPtr) -> Result<ProtocolBasePtr> {
  333. let msg_subsystem = channel.message_subsystem();
  334. msg_subsystem.add_dispatch::<EventPut>().await;
  335. msg_subsystem.add_dispatch::<StaticPut>().await;
  336. msg_subsystem.add_dispatch::<EventReq>().await;
  337. msg_subsystem.add_dispatch::<EventRep>().await;
  338. msg_subsystem.add_dispatch::<HeaderPut>().await;
  339. msg_subsystem.add_dispatch::<HeaderReq>().await;
  340. msg_subsystem.add_dispatch::<HeaderRep>().await;
  341. msg_subsystem.add_dispatch::<TipReq>().await;
  342. msg_subsystem.add_dispatch::<TipRep>().await;
  343. msg_subsystem.add_dispatch::<RangeReq>().await;
  344. msg_subsystem.add_dispatch::<RangeRep>().await;
  345. let (push, pull) = smol::channel::bounded(BROADCASTER_CAPACITY);
  346. Ok(Arc::new(Self {
  347. channel: channel.clone(),
  348. event_graph: eg,
  349. ev_put_sub: channel.subscribe_msg().await?,
  350. st_put_sub: channel.subscribe_msg().await?,
  351. ev_req_sub: channel.subscribe_msg().await?,
  352. ev_rep_sub: channel.subscribe_msg().await?,
  353. _hdr_put_sub: channel.subscribe_msg().await?,
  354. hdr_req_sub: channel.subscribe_msg().await?,
  355. _hdr_rep_sub: channel.subscribe_msg().await?,
  356. tip_req_sub: channel.subscribe_msg().await?,
  357. _tip_rep_sub: channel.subscribe_msg().await?,
  358. range_req_sub: channel.subscribe_msg().await?,
  359. _range_rep_sub: channel.subscribe_msg().await?,
  360. malicious_count: AtomicUsize::new(0),
  361. jobsman: ProtocolJobsManager::new("ProtocolEventGraph", channel),
  362. broadcaster_push: push,
  363. broadcaster_pull: pull,
  364. }))
  365. }
  366. /// Increment the malicious counter; drop peer if threshold reached.
  367. async fn strike(self: Arc<Self>) -> Result<()> {
  368. let n = self.malicious_count.fetch_add(1, SeqCst);
  369. if n + 1 >= MALICIOUS_THRESHOLD {
  370. error!(
  371. target: "event_graph::protocol",
  372. "[EVENTGRAPH] Peer {} reached malicious threshold",
  373. self.channel.display_address(),
  374. );
  375. self.channel.stop().await;
  376. return Err(Error::ChannelStopped)
  377. }
  378. warn!(
  379. target: "event_graph::protocol",
  380. "[EVENTGRAPH] Peer {} sent malicious data ({}/{})",
  381. self.channel.display_address(), n + 1, MALICIOUS_THRESHOLD,
  382. );
  383. Ok(())
  384. }
  385. async fn handle_event_put(self: Arc<Self>) -> Result<()> {
  386. let mut bantimes = MovingWindow::new(WINDOW_EXPIRY_TIME);
  387. loop {
  388. let (event, blob) = match self.ev_put_sub.receive().await {
  389. Ok(v) => (v.0.clone(), v.1.clone()),
  390. Err(_) => continue,
  391. };
  392. if !self.event_graph.is_synced() {
  393. continue
  394. }
  395. _ = self.ev_rep_sub.clean().await;
  396. // Extract genesis info and immediately release the lock.
  397. let genesis_ts = self.event_graph.current_genesis.read().await.header.timestamp;
  398. let dag_name = genesis_ts.to_string();
  399. let eid = event.id();
  400. // Already known?
  401. {
  402. let store = self.event_graph.dag_store.read().await;
  403. if let Some(slot) = store.get_slot(&genesis_ts) {
  404. if slot.header_tree.contains_key(eid.as_bytes()).unwrap_or(false) {
  405. continue
  406. }
  407. }
  408. }
  409. // Flood protection.
  410. bantimes.ticktock();
  411. if bantimes.count() > WINDOW_MAXSIZE {
  412. self.channel.ban().await;
  413. return Err(Error::MaliciousFlood)
  414. }
  415. // Reject events from before the current rotation period.
  416. if event.header.timestamp < genesis_ts {
  417. continue
  418. }
  419. // Cheap structural validation happens before RLN proof verification
  420. // so malformed content, timestamps, or parent sets cannot force
  421. // expensive proof work or mutate RLN metadata.
  422. if !event.validate_new() {
  423. self.clone().strike().await?;
  424. continue
  425. }
  426. // RLN: every non-genesis event MUST carry a signal proof. The only
  427. // exception is genesis-shaped events (parents == NULL_PARENTS),
  428. // which are deterministic consensus inputs and never carry blobs.
  429. if event.header.parents != NULL_PARENTS {
  430. if blob.is_empty() {
  431. self.clone().strike().await?;
  432. continue
  433. }
  434. } else if !blob.is_empty() {
  435. self.clone().strike().await?;
  436. continue
  437. }
  438. // Fetch missing parents (depth-bounded)
  439. // See MAX_PARENT_FETCH_DEPTH doc for why this is bounded.
  440. let mut missing = HashSet::new();
  441. {
  442. let store = self.event_graph.dag_store.read().await;
  443. if let Some(slot) = store.get_slot(&genesis_ts) {
  444. for pid in event.header.parents.iter() {
  445. if *pid != NULL_ID &&
  446. !slot.header_tree.contains_key(pid.as_bytes()).unwrap_or(true)
  447. {
  448. missing.insert(*pid);
  449. }
  450. }
  451. }
  452. }
  453. if !missing.is_empty() &&
  454. !self.clone().fetch_parents(&mut missing, &dag_name, genesis_ts).await
  455. {
  456. // Depth exceeded or peer misbehaved
  457. continue
  458. }
  459. let structurally_valid = {
  460. let store = self.event_graph.dag_store.read().await;
  461. match store.get_slot(&genesis_ts) {
  462. Some(slot) => match event
  463. .dag_validate(&slot.header_tree, &self.event_graph.config, genesis_ts)
  464. .await
  465. {
  466. Ok(valid) => valid,
  467. Err(e) => {
  468. error!(
  469. target: "event_graph::protocol",
  470. "[EVENTGRAPH] Failed validating event {} before RLN verification: {e}",
  471. event.id(),
  472. );
  473. false
  474. }
  475. },
  476. None => false,
  477. }
  478. };
  479. if !structurally_valid {
  480. self.clone().strike().await?;
  481. continue
  482. }
  483. if event.header.parents != NULL_PARENTS && self.verify_rln_signal(&event, &blob).await {
  484. continue
  485. }
  486. // Commit the already-verified signal without re-running RLN proof
  487. // verification. `verify_rln_signal` above recorded the share, so
  488. // the post-verification helper stores the blob, inserts the header,
  489. // and then inserts the event body without calling the verifier a
  490. // second time.
  491. if self.event_graph.insert_verified_signal(&event, &blob, &dag_name).await.is_err() {
  492. self.clone().strike().await?;
  493. continue
  494. }
  495. // Relay to other peers (bounded - drops if channel full)
  496. let _ = self.broadcaster_push.try_send(EventPut(event, blob));
  497. }
  498. }
  499. /// Recursively fetch missing parent events, up to
  500. /// [`MAX_PARENT_FETCH_DEPTH`] rounds.
  501. /// Returns `true` on success.
  502. async fn fetch_parents(
  503. self: Arc<Self>,
  504. missing: &mut HashSet<blake3::Hash>,
  505. dag_name: &str,
  506. dag_ts: u64,
  507. ) -> bool {
  508. // received[layer] = Vec<(event, blob)> - keeping events
  509. // paired with their blobs through the layer ordering so we
  510. // can re-verify proofs at insert time. Non-genesis parents
  511. // without blobs are rejected by dag_insert_with_blobs.
  512. let mut received: BTreeMap<u64, Vec<(Event, Vec<u8>)>> = BTreeMap::new();
  513. let mut known = HashSet::new();
  514. let mut depth = 0usize;
  515. while !missing.is_empty() {
  516. depth += 1;
  517. if depth > MAX_PARENT_FETCH_DEPTH {
  518. error!(
  519. target: "event_graph::protocol",
  520. "[EVENTGRAPH] Parent fetch depth exceeded ({})",
  521. MAX_PARENT_FETCH_DEPTH,
  522. );
  523. let _ = self.clone().strike().await;
  524. return false
  525. }
  526. let requested: Vec<_> = missing.iter().copied().collect();
  527. if self.channel.send(&EventReq(requested.clone())).await.is_err() {
  528. return false
  529. }
  530. let timeout =
  531. self.event_graph.p2p.settings().read().await.outbound_connect_timeout_max();
  532. let Ok(rep) = self.ev_rep_sub.receive_with_timeout(timeout).await else {
  533. self.channel.stop().await;
  534. return false
  535. };
  536. let parents = match filter_parent_event_rep(
  537. &requested,
  538. missing,
  539. &mut known,
  540. rep.0.clone(),
  541. rep.1.clone(),
  542. ) {
  543. Ok(parents) => parents,
  544. Err(e) => {
  545. warn!(
  546. target: "event_graph::protocol",
  547. "[EVENTGRAPH] parent fetch made no progress or returned invalid data: {e}",
  548. );
  549. let _ = self.clone().strike().await;
  550. return false
  551. }
  552. };
  553. for (parent, blob) in parents {
  554. received.entry(parent.header.layer).or_default().push((parent.clone(), blob));
  555. // Check for more unknown grandparents.
  556. let store = self.event_graph.dag_store.read().await;
  557. if let Some(slot) = store.get_slot(&dag_ts) {
  558. for gp in parent.header.parents.iter() {
  559. if *gp != NULL_ID &&
  560. !missing.contains(gp) &&
  561. !known.contains(gp) &&
  562. !slot.header_tree.contains_key(gp.as_bytes()).unwrap_or(true)
  563. {
  564. missing.insert(*gp);
  565. }
  566. }
  567. }
  568. }
  569. }
  570. // Flatten in layer order so parents are inserted before
  571. // children (dag_insert structurally validates parents).
  572. let pairs: Vec<(Event, Vec<u8>)> = received.into_values().flatten().collect();
  573. let events: Vec<Event> = pairs.iter().map(|(e, _)| e.clone()).collect();
  574. let blobs: Vec<Vec<u8>> = pairs.iter().map(|(_, b)| b.clone()).collect();
  575. let headers: Vec<Header> = events.iter().map(|e| e.header.clone()).collect();
  576. if self.event_graph.header_dag_insert(headers, dag_name).await.is_err() {
  577. return false
  578. }
  579. // dag_insert_with_blobs verifies each event's blob (when
  580. // present) and skips events that fail RLN re-verification.
  581. // This closes sync-time injection via fetch_parents.
  582. if self.event_graph.dag_insert_with_blobs(&events, &blobs, dag_name).await.is_err() {
  583. return false
  584. }
  585. true
  586. }
  587. /// Verify an RLN signal proof. Returns `true` if the event
  588. /// should be rejected (proof invalid, duplicate, or slashable).
  589. ///
  590. /// The actual verification logic lives on
  591. /// [`EventGraph::rln_verify_signal`] - this method is a thin
  592. /// wrapper that translates the [`rln::SignalCheck`] outcome
  593. /// into "accept or reject" plus the slash side effect.
  594. async fn verify_rln_signal(&self, event: &Event, blob: &[u8]) -> bool {
  595. match self.event_graph.rln_verify_signal(event, blob).await {
  596. rln::SignalCheck::Accepted => false,
  597. rln::SignalCheck::Rejected => true,
  598. rln::SignalCheck::Slashable(shares) => {
  599. self.slash(shares).await;
  600. true
  601. }
  602. }
  603. }
  604. /// Execute the slashing procedure: SSS-recover `identity_secret_hash`
  605. /// from the conflicting shares, derive the corresponding commitment
  606. /// directly, and broadcast the slash proof.
  607. ///
  608. /// With the spec-aligned signal circuit (`a_0 = identity_secret_hash`),
  609. /// the recovered value uniquely determines the commitment via a
  610. /// single Poseidon hash. The previous brute-force loop over
  611. /// `1..=MAX_MSG_LIMIT` is gone - `user_message_limit` is already
  612. /// baked into `identity_secret_hash`, so the verifier doesn't
  613. /// need to know it explicitly.
  614. async fn slash(&self, shares: Vec<(pallas::Base, pallas::Base)>) {
  615. let identity_secret_hash = match sss_recover(&shares) {
  616. Ok(s) => s,
  617. Err(e) => {
  618. error!(target: "event_graph::protocol", "[RLN] SSS recovery failed: {e}");
  619. return
  620. }
  621. };
  622. let commitment = poseidon_hash([identity_secret_hash]);
  623. // Sanity check: the commitment we recovered must be in the
  624. // tree. If it isn't, something has gone wrong (the proofs
  625. // were against a stale root we no longer have, or we have a
  626. // bug). Either way, do not broadcast a bogus slash.
  627. {
  628. let id_state = self.event_graph.identity_state.read().await;
  629. if !id_state.contains(&commitment) {
  630. warn!(
  631. target: "event_graph::protocol",
  632. "[RLN] Recovered commitment is not a current tree leaf; skipping slash",
  633. );
  634. return
  635. }
  636. }
  637. let slash_pk = match self.event_graph.zk_keys.load_slash_pk() {
  638. Ok(pk) => pk,
  639. Err(e) => {
  640. error!(target: "event_graph::protocol", "[RLN] Failed to load slash PK: {e}");
  641. return
  642. }
  643. };
  644. let mut id = self.event_graph.identity_state.write().await;
  645. let (proof, root) = match create_slash_proof(identity_secret_hash, &mut id, &slash_pk) {
  646. Ok(v) => v,
  647. Err(e) => {
  648. error!(target: "event_graph::protocol", "[RLN] Slash proof creation failed: {e}");
  649. return
  650. }
  651. };
  652. // Note: create_slash_proof itself does NOT mutate the SMT -
  653. // it only reads the membership path. The actual removal happens
  654. // when `static_insert` propagates the slashing event through
  655. // `handle_static_put`, the same code path remote slashes use.
  656. drop(id);
  657. let slash_blob = SlashBlob { proof, identity_secret_hash, merkle_root: root };
  658. let blob = serialize_async(&slash_blob).await;
  659. let node = RLNNode::Slashing(commitment);
  660. let ev = match Event::new_static(serialize_async(&node).await, &self.event_graph).await {
  661. Ok(ev) => ev,
  662. Err(e) => {
  663. error!(target: "event_graph::protocol", "[RLN] Slash event creation failed: {e}");
  664. return
  665. }
  666. };
  667. if let Err(e) = self.event_graph.commit_verified_static_event(&ev, &blob, &node).await {
  668. error!(target: "event_graph::protocol", "[RLN] Slash static commit failed: {e}");
  669. return
  670. }
  671. let _ = self.event_graph.static_broadcast(ev, blob).await;
  672. }
  673. async fn handle_static_put(self: Arc<Self>) -> Result<()> {
  674. let mut bantimes = MovingWindow::new(WINDOW_EXPIRY_TIME);
  675. loop {
  676. let (event, blob) = match self.st_put_sub.receive().await {
  677. Ok(v) => (v.0.clone(), v.1.clone()),
  678. Err(_) => continue,
  679. };
  680. if !self.event_graph.is_synced() {
  681. continue
  682. }
  683. let eid = event.id();
  684. if self.event_graph.static_dag.contains_key(eid.as_bytes())? {
  685. continue
  686. }
  687. // Validate event structure and parents BEFORE touching
  688. // the identity tree.
  689. bantimes.ticktock();
  690. if bantimes.count() > WINDOW_MAXSIZE {
  691. self.channel.ban().await;
  692. return Err(Error::MaliciousFlood)
  693. }
  694. if !event.validate_new_static() {
  695. self.clone().strike().await?;
  696. continue
  697. }
  698. let mut orphan = false;
  699. for p in event.header.parents.iter() {
  700. if *p != NULL_ID && !self.event_graph.static_dag.contains_key(p.as_bytes())? {
  701. orphan = true;
  702. break
  703. }
  704. }
  705. if orphan {
  706. self.clone().strike().await?;
  707. continue
  708. }
  709. let rln_node: RLNNode = match deserialize_async_partial(event.content()).await {
  710. Ok((v, _)) => v,
  711. Err(_) => continue,
  712. };
  713. if blob.is_empty() {
  714. continue
  715. }
  716. // Decision is made by EventGraph::rln_verify_static_event,
  717. // a pure verification function (no state mutation). Accepted events
  718. // are committed through one helper so the static DAG is durable
  719. // before RLN state, while subscribers are notified only after the
  720. // RLN apply step.
  721. match self
  722. .event_graph
  723. .rln_verify_static_event(&rln_node, &blob, event.header.timestamp)
  724. .await
  725. {
  726. rln::StaticEventCheck::AcceptedRegistration(_) |
  727. rln::StaticEventCheck::AcceptedSlash(_) => {
  728. if let Err(e) = self
  729. .event_graph
  730. .commit_verified_static_event(&event, &blob, &rln_node)
  731. .await
  732. {
  733. warn!(
  734. target: "event_graph::protocol",
  735. "[RLN] commit_verified_static_event failed: {e}",
  736. );
  737. continue
  738. }
  739. }
  740. rln::StaticEventCheck::Rejected => continue,
  741. rln::StaticEventCheck::Malicious => {
  742. self.clone().strike().await?;
  743. continue
  744. }
  745. }
  746. self.event_graph.static_broadcast(event, blob).await?;
  747. }
  748. }
  749. async fn handle_event_req(self: Arc<Self>) -> Result<()> {
  750. loop {
  751. let ids = match self.ev_req_sub.receive().await {
  752. Ok(v) => v.0.clone(),
  753. Err(_) => continue,
  754. };
  755. if !self.event_graph.is_synced() {
  756. continue
  757. }
  758. if ids.len() > MAX_EVENT_REQ_IDS {
  759. self.clone().strike().await?;
  760. continue
  761. }
  762. // Only serve IDs we've previously broadcast (prevents
  763. // arbitrary DAG enumeration by malicious peers). The
  764. // static DAG is exempt from this check because its
  765. // contents are public consensus state (RLN registrations
  766. // and slashes) - serving those freely has no privacy
  767. // cost, and it's required so that a peer performing
  768. // `static_sync` can walk ancestry via EventReq.
  769. //
  770. // For both static and rotating-DAG events, we include
  771. // the original RLN blob (proof + public inputs + ...)
  772. // so the requester can re-verify the proof at sync time.
  773. // The blobs vector is index-aligned with `events`; an
  774. // empty entry means we don't have the blob, which can
  775. // legitimately happen for events inserted before the
  776. // current blob-storage code paths existed.
  777. let bcast = self.event_graph.broadcasted_ids.read().await;
  778. let mut events = vec![];
  779. let mut blobs: Vec<Vec<u8>> = vec![];
  780. for id in &ids {
  781. let in_static =
  782. self.event_graph.static_dag.contains_key(id.as_bytes()).unwrap_or(false);
  783. if !in_static && !bcast.contains(id) {
  784. self.clone().strike().await?;
  785. continue
  786. }
  787. if let Some(ev) = self.event_graph.fetch_event_from_dags(id).await? {
  788. let blob = if in_static {
  789. self.event_graph.static_blob_fetch(id).unwrap_or(None).unwrap_or_default()
  790. } else {
  791. self.event_graph.dag_blob_fetch(id).unwrap_or(None).unwrap_or_default()
  792. };
  793. if !in_static && blob.is_empty() {
  794. // Rotating-DAG event without a blob. Don't ship
  795. // it, just log loudly.
  796. warn!(
  797. target: "event_graph::handle_event_req",
  798. "[EVENTGRAPH] declining to serve event {} - missing blob in \
  799. local dag_blobs",
  800. id,
  801. );
  802. continue
  803. }
  804. events.push(ev);
  805. blobs.push(blob);
  806. }
  807. }
  808. drop(bcast);
  809. if !events.is_empty() {
  810. let mut b = self.event_graph.broadcasted_ids.write().await;
  811. for ev in &events {
  812. for p in ev.header.parents.iter() {
  813. if *p != NULL_ID {
  814. b.insert(*p);
  815. }
  816. }
  817. }
  818. drop(b);
  819. self.channel.send(&EventRep(events, blobs)).await?;
  820. }
  821. }
  822. }
  823. async fn handle_header_req(self: Arc<Self>) -> Result<()> {
  824. loop {
  825. let Ok(v) = self.hdr_req_sub.receive().await else { continue };
  826. if !self.event_graph.is_synced() {
  827. continue
  828. }
  829. let (dag_name, tips) = (&v.0, &v.1);
  830. if count_layer_tips(tips) > MAX_HEADER_REQ_TIPS {
  831. self.clone().strike().await?;
  832. continue
  833. }
  834. let dag_ts = match u64::from_str(dag_name) {
  835. Ok(v) => v,
  836. Err(_) => continue,
  837. };
  838. {
  839. let s = self.event_graph.dag_store.read().await;
  840. if s.get_slot(&dag_ts).is_none() {
  841. continue
  842. }
  843. }
  844. let hdrs = self.event_graph.fetch_headers_with_tips(dag_name, tips).await?;
  845. // Register every header ID we are about to reveal as
  846. // "broadcasted." A peer that just received these headers
  847. // via HeaderRep will follow up with EventReq for the
  848. // bodies. `handle_event_req` strikes any peer that asks
  849. // for an ID not present in `broadcasted_ids` (the check
  850. // exists to prevent arbitrary DAG enumeration). Without
  851. // this insert the legitimate header-then-body sync flow
  852. // racks up MALICIOUS_THRESHOLD strikes per batch and the
  853. // peer gets dropped.
  854. //
  855. // `handle_tip_req` already does the analogous insert for
  856. // the tip IDs it ships, and `handle_event_req` does it
  857. // for the parents of events it serves. This fills the
  858. // missing third side of the same pattern.
  859. {
  860. let mut b = self.event_graph.broadcasted_ids.write().await;
  861. for h in &hdrs {
  862. b.insert(h.id());
  863. }
  864. }
  865. self.channel.send(&HeaderRep(hdrs)).await?;
  866. }
  867. }
  868. async fn handle_tip_req(self: Arc<Self>) -> Result<()> {
  869. loop {
  870. let dag_name = match self.tip_req_sub.receive().await {
  871. Ok(v) => v.0.clone(),
  872. Err(_) => continue,
  873. };
  874. if !self.event_graph.is_synced() {
  875. continue
  876. }
  877. let layers = match dag_name.as_str() {
  878. "static-dag" => match self.event_graph.static_unreferenced_tips().await {
  879. Ok(tips) => tips,
  880. Err(e) => {
  881. warn!(target: "event_graph::protocol", "failed to scan static DAG tips: {e}");
  882. continue
  883. }
  884. },
  885. _ => {
  886. let ts = match u64::from_str(&dag_name) {
  887. Ok(v) => v,
  888. Err(_) => continue,
  889. };
  890. let store = self.event_graph.dag_store.read().await;
  891. match store.get_slot(&ts) {
  892. Some(s) => s.tips.clone(),
  893. None => continue,
  894. }
  895. }
  896. };
  897. let layers = cap_layer_tips(&layers, MAX_TIP_REP_TIPS);
  898. let mut b = self.event_graph.broadcasted_ids.write().await;
  899. for tips in layers.values() {
  900. for t in tips {
  901. b.insert(*t);
  902. }
  903. }
  904. drop(b);
  905. self.channel.send(&TipRep(layers)).await?;
  906. }
  907. }
  908. /// Serve a paginated content request. Uses the local
  909. /// [`TimeIndex`] to find events around the cursor, then
  910. /// returns their full content.
  911. async fn handle_range_req(self: Arc<Self>) -> Result<()> {
  912. loop {
  913. let req = match self.range_req_sub.receive().await {
  914. Ok(v) => v,
  915. Err(_) => continue,
  916. };
  917. if !self.event_graph.is_synced() {
  918. continue
  919. }
  920. let limit = (req.limit as usize).min(MAX_RANGE_PAGE_SIZE);
  921. let events =
  922. self.event_graph.fetch_page(req.cursor_ts, req.direction.clone(), limit).await?;
  923. self.channel.send(&RangeRep(events)).await?;
  924. }
  925. }
  926. async fn broadcast_rate_limiter(self: Arc<Self>) -> Result<()> {
  927. let mut rl = MovingWindow::new(RATELIMIT_EXPIRY_TIME);
  928. loop {
  929. let ep = self.broadcaster_pull.recv().await.expect("broadcaster closed");
  930. rl.ticktock();
  931. if rl.count() > RATELIMIT_MIN_COUNT {
  932. let ms = ((rl.count() - RATELIMIT_MIN_COUNT) * RATELIMIT_SAMPLE_SLEEP /
  933. (RATELIMIT_SAMPLE_IDX - RATELIMIT_MIN_COUNT)) as u64;
  934. msleep(ms).await;
  935. }
  936. self.event_graph
  937. .p2p
  938. .broadcast_with_exclude(&ep, &[self.channel.address().clone()])
  939. .await;
  940. }
  941. }
  942. }