From e19f5818deee90a2dd66f2c39427d2d0ead7f183 Mon Sep 17 00:00:00 2001 From: erhant Date: Mon, 3 Aug 2026 21:47:13 +0300 Subject: [PATCH] feat(gossip): add `accredited_keys` placeholder (for future Join/Leave) --- .../core/src/gossip/keys_provider.rs | 82 +++++++++++++++++++ lez/sequencer/core/src/gossip/mod.rs | 1 + 2 files changed, 83 insertions(+) create mode 100644 lez/sequencer/core/src/gossip/keys_provider.rs diff --git a/lez/sequencer/core/src/gossip/keys_provider.rs b/lez/sequencer/core/src/gossip/keys_provider.rs new file mode 100644 index 000000000..41e8cd1a8 --- /dev/null +++ b/lez/sequencer/core/src/gossip/keys_provider.rs @@ -0,0 +1,82 @@ +//! Source of the channel's current accredited key set, polled by the +//! gossip layer to validate announcements. +//! +//! FIXME: `NodeKeysProvider` will be replaced by an L2 Join/Leave-derived provider in a follow-up. + +use std::{collections::HashSet, future::Future}; + +use anyhow::{Context as _, Result}; +use logos_blockchain_core::mantle::ops::channel::ChannelId; +use logos_blockchain_zone_sdk::{ + CommonHttpClient, + adapter::{Node as _, NodeHttpClient}, +}; + +use crate::config::BedrockConfig; + +pub trait AccreditedKeysProvider: Send + 'static { + /// The channel's current accredited Ed25519 keys. An empty set is valid + /// (channel does not exist yet); errors keep the caller's last set. + fn accredited_keys(&self) -> impl Future>> + Send; +} + +/// Reads accredited keys from the bedrock node's channel state, on its own +/// HTTP connection (no coupling to the publisher's drive task). +pub struct NodeKeysProvider { + node: NodeHttpClient, + channel_id: ChannelId, +} + +impl NodeKeysProvider { + #[must_use] + pub fn new(config: &BedrockConfig) -> Self { + let node = NodeHttpClient::new( + CommonHttpClient::new(config.auth.clone().map(Into::into)), + config.node_url.clone(), + ); + Self { + node, + channel_id: config.channel_id, + } + } +} + +impl AccreditedKeysProvider for NodeKeysProvider { + async fn accredited_keys(&self) -> Result> { + let state = self + .node + .channel_state(self.channel_id) + .await + .context("Failed to read channel state for accredited keys")?; + Ok(state + .map(|state| { + state + .accredited_keys + .iter() + .map(|key| key.to_bytes()) + .collect() + }) + .unwrap_or_default()) + } +} + +/// Fixed key set, for tests. +pub struct StaticKeysProvider(pub HashSet<[u8; 32]>); + +impl AccreditedKeysProvider for StaticKeysProvider { + async fn accredited_keys(&self) -> Result> { + Ok(self.0.clone()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[tokio::test] + async fn static_provider_returns_its_set() { + let keys = HashSet::from([[1; 32], [2; 32]]); + let provider = StaticKeysProvider(keys.clone()); + assert_eq!(provider.accredited_keys().await.unwrap(), keys); + } +} diff --git a/lez/sequencer/core/src/gossip/mod.rs b/lez/sequencer/core/src/gossip/mod.rs index cd5b144ec..0d2fd16be 100644 --- a/lez/sequencer/core/src/gossip/mod.rs +++ b/lez/sequencer/core/src/gossip/mod.rs @@ -7,3 +7,4 @@ pub mod announcement; pub mod directory; +pub mod keys_provider;