feat: initialize Kurdistan SDK - independent fork of Polkadot SDK

This commit is contained in:
2025-12-13 15:44:15 +03:00
commit 286de54384
6841 changed files with 1848356 additions and 0 deletions
+400
View File
@@ -0,0 +1,400 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezkuwi.
// Pezkuwi is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Pezkuwi is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Pezkuwi. If not, see <http://www.gnu.org/licenses/>.
//! The Network Bridge Subsystem - handles _outgoing_ messages, from subsystem to the network.
use super::*;
use pezkuwi_node_network_protocol::{
peer_set::PeerSetProtocolNames, request_response::ReqProtocolNames, CollationProtocols,
ValidationProtocols,
};
use pezkuwi_node_subsystem::{
errors::SubsystemError,
messages::{NetworkBridgeTxMessage, ReportPeerMessage},
overseer, FromOrchestra, OverseerSignal, SpawnedSubsystem,
};
use pezkuwi_node_network_protocol::request_response::Requests;
use sc_network::{MessageSink, ReputationChange};
use crate::validator_discovery;
/// Actual interfacing to the network based on the `Network` trait.
///
/// Defines the `Network` trait with an implementation for an `Arc<NetworkService>`.
use crate::network::{
send_collation_message_v1, send_collation_message_v2, send_validation_message_v3, Network,
};
use crate::metrics::Metrics;
#[cfg(test)]
mod tests;
// network bridge log target
const LOG_TARGET: &'static str = "teyrchain::network-bridge-tx";
/// The network bridge subsystem.
pub struct NetworkBridgeTx<N, AD> {
/// `Network` trait implementing type.
network_service: N,
authority_discovery_service: AD,
metrics: Metrics,
req_protocol_names: ReqProtocolNames,
peerset_protocol_names: PeerSetProtocolNames,
notification_sinks: Arc<Mutex<HashMap<(PeerSet, PeerId), Box<dyn MessageSink>>>>,
}
impl<N, AD> NetworkBridgeTx<N, AD> {
/// Create a new network bridge subsystem with underlying network service and authority
/// discovery service.
///
/// This assumes that the network service has had the notifications protocol for the network
/// bridge already registered. See [`peer_sets_info`].
pub fn new(
network_service: N,
authority_discovery_service: AD,
metrics: Metrics,
req_protocol_names: ReqProtocolNames,
peerset_protocol_names: PeerSetProtocolNames,
notification_sinks: Arc<Mutex<HashMap<(PeerSet, PeerId), Box<dyn MessageSink>>>>,
) -> Self {
Self {
network_service,
authority_discovery_service,
metrics,
req_protocol_names,
peerset_protocol_names,
notification_sinks,
}
}
}
#[overseer::subsystem(NetworkBridgeTx, error = SubsystemError, prefix = self::overseer)]
impl<Net, AD, Context> NetworkBridgeTx<Net, AD>
where
Net: Network + Sync,
AD: validator_discovery::AuthorityDiscovery + Clone + Sync,
{
fn start(self, ctx: Context) -> SpawnedSubsystem {
let future = run_network_out(self, ctx)
.map_err(|e| SubsystemError::with_origin("network-bridge", e))
.boxed();
SpawnedSubsystem { name: "network-bridge-tx-subsystem", future }
}
}
#[overseer::contextbounds(NetworkBridgeTx, prefix = self::overseer)]
async fn handle_subsystem_messages<Context, N, AD>(
mut ctx: Context,
mut network_service: N,
mut authority_discovery_service: AD,
metrics: Metrics,
req_protocol_names: ReqProtocolNames,
peerset_protocol_names: PeerSetProtocolNames,
notification_sinks: Arc<Mutex<HashMap<(PeerSet, PeerId), Box<dyn MessageSink>>>>,
) -> Result<(), Error>
where
N: Network,
AD: validator_discovery::AuthorityDiscovery + Clone,
{
let mut validator_discovery =
validator_discovery::Service::<N, AD>::new(peerset_protocol_names.clone());
loop {
match ctx.recv().fuse().await? {
FromOrchestra::Signal(OverseerSignal::Conclude) => return Ok(()),
FromOrchestra::Signal(_) => { /* handled by incoming */ },
FromOrchestra::Communication { msg } => {
(network_service, authority_discovery_service) =
handle_incoming_subsystem_communication(
&mut ctx,
network_service,
&mut validator_discovery,
authority_discovery_service.clone(),
msg,
&metrics,
&req_protocol_names,
&peerset_protocol_names,
&notification_sinks,
)
.await;
},
}
}
}
#[overseer::contextbounds(NetworkBridgeTx, prefix = self::overseer)]
async fn handle_incoming_subsystem_communication<Context, N, AD>(
_ctx: &mut Context,
network_service: N,
validator_discovery: &mut validator_discovery::Service<N, AD>,
mut authority_discovery_service: AD,
msg: NetworkBridgeTxMessage,
metrics: &Metrics,
req_protocol_names: &ReqProtocolNames,
peerset_protocol_names: &PeerSetProtocolNames,
notification_sinks: &Arc<Mutex<HashMap<(PeerSet, PeerId), Box<dyn MessageSink>>>>,
) -> (N, AD)
where
N: Network,
AD: validator_discovery::AuthorityDiscovery + Clone,
{
match msg {
NetworkBridgeTxMessage::ReportPeer(ReportPeerMessage::Single(peer, rep)) => {
if !rep.value.is_positive() {
gum::debug!(target: LOG_TARGET, ?peer, ?rep, action = "ReportPeer");
}
metrics.on_report_event();
network_service.report_peer(peer, rep);
},
NetworkBridgeTxMessage::ReportPeer(ReportPeerMessage::Batch(batch)) => {
for (peer, score) in batch {
let rep = ReputationChange::new(score, "Aggregated reputation change");
if !rep.value.is_positive() {
gum::debug!(target: LOG_TARGET, ?peer, ?rep, action = "ReportPeer");
}
metrics.on_report_event();
network_service.report_peer(peer, rep);
}
},
NetworkBridgeTxMessage::DisconnectPeers(peers, peer_set) => {
gum::trace!(
target: LOG_TARGET,
action = "DisconnectPeers",
?peers,
peer_set = ?peer_set,
);
// [`NetworkService`] keeps track of the protocols by their main name.
let protocol = peerset_protocol_names.get_main_name(peer_set);
for peer in peers {
network_service.disconnect_peer(peer, protocol.clone());
}
},
NetworkBridgeTxMessage::SendValidationMessage(peers, msg) => {
gum::trace!(
target: LOG_TARGET,
action = "SendValidationMessages",
?msg,
num_messages = 1usize,
);
match msg {
ValidationProtocols::V3(msg) => send_validation_message_v3(
peers,
WireMessage::ProtocolMessage(msg),
&metrics,
notification_sinks,
),
}
},
NetworkBridgeTxMessage::SendValidationMessages(msgs) => {
gum::trace!(
target: LOG_TARGET,
action = "SendValidationMessages",
num_messages = %msgs.len(),
?msgs,
);
for (peers, msg) in msgs {
match msg {
ValidationProtocols::V3(msg) => send_validation_message_v3(
peers,
WireMessage::ProtocolMessage(msg),
&metrics,
notification_sinks,
),
}
}
},
NetworkBridgeTxMessage::SendCollationMessage(peers, msg) => {
gum::trace!(
target: LOG_TARGET,
action = "SendCollationMessages",
num_messages = 1usize,
);
match msg {
CollationProtocols::V1(msg) => send_collation_message_v1(
peers,
WireMessage::ProtocolMessage(msg),
&metrics,
notification_sinks,
),
CollationProtocols::V2(msg) => send_collation_message_v2(
peers,
WireMessage::ProtocolMessage(msg),
&metrics,
notification_sinks,
),
}
},
NetworkBridgeTxMessage::SendCollationMessages(msgs) => {
gum::trace!(
target: LOG_TARGET,
action = "SendCollationMessages",
num_messages = %msgs.len(),
);
for (peers, msg) in msgs {
match msg {
CollationProtocols::V1(msg) => send_collation_message_v1(
peers,
WireMessage::ProtocolMessage(msg),
&metrics,
notification_sinks,
),
CollationProtocols::V2(msg) => send_collation_message_v2(
peers,
WireMessage::ProtocolMessage(msg),
&metrics,
notification_sinks,
),
}
}
},
NetworkBridgeTxMessage::SendRequests(reqs, if_disconnected) => {
gum::trace!(
target: LOG_TARGET,
action = "SendRequests",
num_requests = %reqs.len(),
);
for req in reqs {
match req {
Requests::ChunkFetching(ref req) => {
// This is not the actual request that will succeed, as we don't know yet
// what that will be. It's only the primary request we tried.
if req.fallback_request.is_some() {
metrics.on_message("chunk_fetching_v2")
} else {
metrics.on_message("chunk_fetching_v1")
}
},
Requests::AvailableDataFetchingV1(_) =>
metrics.on_message("available_data_fetching_v1"),
Requests::CollationFetchingV1(_) => metrics.on_message("collation_fetching_v1"),
Requests::CollationFetchingV2(_) => metrics.on_message("collation_fetching_v2"),
Requests::PoVFetchingV1(_) => metrics.on_message("pov_fetching_v1"),
Requests::DisputeSendingV1(_) => metrics.on_message("dispute_sending_v1"),
Requests::AttestedCandidateV2(_) => metrics.on_message("attested_candidate_v2"),
}
network_service
.start_request(
&mut authority_discovery_service,
req,
req_protocol_names,
if_disconnected,
)
.await;
}
},
NetworkBridgeTxMessage::ConnectToValidators { validator_ids, peer_set, failed } => {
gum::trace!(
target: LOG_TARGET,
action = "ConnectToValidators",
peer_set = ?peer_set,
ids = ?validator_ids,
"Received a validator connection request",
);
metrics.note_desired_peer_count(peer_set, validator_ids.len());
let (network_service, ads) = validator_discovery
.on_request(
validator_ids,
peer_set,
failed,
network_service,
authority_discovery_service,
)
.await;
return (network_service, ads);
},
NetworkBridgeTxMessage::ConnectToResolvedValidators { validator_addrs, peer_set } => {
gum::trace!(
target: LOG_TARGET,
action = "ConnectToPeers",
peer_set = ?peer_set,
?validator_addrs,
"Received a resolved validator connection request",
);
metrics.note_desired_peer_count(peer_set, validator_addrs.len());
let all_addrs = validator_addrs.into_iter().flatten().collect();
let network_service = validator_discovery
.on_resolved_request(all_addrs, peer_set, network_service)
.await;
return (network_service, authority_discovery_service);
},
NetworkBridgeTxMessage::AddToResolvedValidators { validator_addrs, peer_set } => {
gum::trace!(
target: LOG_TARGET,
action = "AddToResolvedValidators",
peer_set = ?peer_set,
?validator_addrs,
"Received a resolved validator connection request",
);
let all_addrs = validator_addrs.into_iter().flatten().collect();
let network_service = validator_discovery
.on_add_to_resolved_request(all_addrs, peer_set, network_service)
.await;
return (network_service, authority_discovery_service);
},
}
(network_service, authority_discovery_service)
}
#[overseer::contextbounds(NetworkBridgeTx, prefix = self::overseer)]
async fn run_network_out<N, AD, Context>(
bridge: NetworkBridgeTx<N, AD>,
ctx: Context,
) -> Result<(), Error>
where
N: Network,
AD: validator_discovery::AuthorityDiscovery + Clone + Sync,
{
let NetworkBridgeTx {
network_service,
authority_discovery_service,
metrics,
req_protocol_names,
peerset_protocol_names,
notification_sinks,
} = bridge;
handle_subsystem_messages(
ctx,
network_service,
authority_discovery_service,
metrics,
req_protocol_names,
peerset_protocol_names,
notification_sinks,
)
.await?;
Ok(())
}
+371
View File
@@ -0,0 +1,371 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezkuwi.
// Pezkuwi is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Pezkuwi is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Pezkuwi. If not, see <http://www.gnu.org/licenses/>.
use super::*;
use futures::executor;
use pezkuwi_node_subsystem_util::TimeoutExt;
use async_trait::async_trait;
use parking_lot::Mutex;
use std::collections::HashSet;
use sc_network::{
IfDisconnected, ObservedRole as SubstrateObservedRole, ProtocolName, ReputationChange, Roles,
};
use codec::DecodeAll;
use pezkuwi_node_network_protocol::{
peer_set::PeerSetProtocolNames,
request_response::{outgoing::Requests, ReqProtocolNames},
v1 as protocol_v1, v3 as protocol_v3, CollationProtocols, ObservedRole, ValidationProtocols,
};
use pezkuwi_node_subsystem::{FromOrchestra, OverseerSignal};
use pezkuwi_node_subsystem_test_helpers::TestSubsystemContextHandle;
use pezkuwi_node_subsystem_util::metered;
use pezkuwi_primitives::{AuthorityDiscoveryId, Hash};
use pezkuwi_primitives_test_helpers::dummy_collator_signature;
use sc_network::Multiaddr;
use sp_keyring::Sr25519Keyring;
const TIMEOUT: std::time::Duration = pezkuwi_node_subsystem_test_helpers::TestSubsystemContextHandle::<NetworkBridgeTxMessage>::TIMEOUT;
use crate::{network::Network, validator_discovery::AuthorityDiscovery};
#[derive(Debug, PartialEq)]
pub enum NetworkAction {
/// Note a change in reputation for a peer.
ReputationChange(PeerId, ReputationChange),
/// Disconnect a peer from the given peer-set.
DisconnectPeer(PeerId, PeerSet),
/// Write a notification to a given peer on the given peer-set.
WriteNotification(PeerId, PeerSet, Vec<u8>),
}
// The subsystem's view of the network.
#[derive(Clone)]
struct TestNetwork {
action_tx: Arc<Mutex<metered::UnboundedMeteredSender<NetworkAction>>>,
peerset_protocol_names: Arc<PeerSetProtocolNames>,
}
#[derive(Clone, Debug)]
struct TestAuthorityDiscovery;
// The test's view of the network. This receives updates from the subsystem in the form
// of `NetworkAction`s.
struct TestNetworkHandle {
action_rx: metered::UnboundedMeteredReceiver<NetworkAction>,
_peerset_protocol_names: PeerSetProtocolNames,
notification_sinks: Arc<Mutex<HashMap<(PeerSet, PeerId), Box<dyn MessageSink>>>>,
action_tx: Arc<Mutex<metered::UnboundedMeteredSender<NetworkAction>>>,
}
struct TestMessageSink {
peer: PeerId,
peer_set: PeerSet,
action_tx: Arc<Mutex<metered::UnboundedMeteredSender<NetworkAction>>>,
}
impl TestMessageSink {
fn new(
peer: PeerId,
peer_set: PeerSet,
action_tx: Arc<Mutex<metered::UnboundedMeteredSender<NetworkAction>>>,
) -> TestMessageSink {
Self { peer, peer_set, action_tx }
}
}
#[async_trait::async_trait]
impl MessageSink for TestMessageSink {
fn send_sync_notification(&self, notification: Vec<u8>) {
self.action_tx
.lock()
.unbounded_send(NetworkAction::WriteNotification(
self.peer,
self.peer_set,
notification,
))
.unwrap();
}
async fn send_async_notification(
&self,
_notification: Vec<u8>,
) -> Result<(), sc_network::error::Error> {
unimplemented!();
}
}
fn new_test_network(
peerset_protocol_names: PeerSetProtocolNames,
) -> (
TestNetwork,
TestNetworkHandle,
TestAuthorityDiscovery,
Arc<Mutex<HashMap<(PeerSet, PeerId), Box<dyn MessageSink>>>>,
) {
let (action_tx, action_rx) = metered::unbounded();
let notification_sinks = Arc::new(Mutex::new(HashMap::new()));
let action_tx = Arc::new(Mutex::new(action_tx));
(
TestNetwork {
action_tx: action_tx.clone(),
peerset_protocol_names: Arc::new(peerset_protocol_names.clone()),
},
TestNetworkHandle {
action_rx,
_peerset_protocol_names: peerset_protocol_names,
action_tx,
notification_sinks: notification_sinks.clone(),
},
TestAuthorityDiscovery,
notification_sinks,
)
}
#[async_trait]
impl Network for TestNetwork {
async fn set_reserved_peers(
&mut self,
_protocol: ProtocolName,
_: HashSet<Multiaddr>,
) -> Result<(), String> {
Ok(())
}
async fn add_peers_to_reserved_set(
&mut self,
_protocol: ProtocolName,
_: HashSet<Multiaddr>,
) -> Result<(), String> {
Ok(())
}
async fn remove_from_peers_set(
&mut self,
_protocol: ProtocolName,
_: Vec<PeerId>,
) -> Result<(), String> {
Ok(())
}
async fn start_request<AD: AuthorityDiscovery>(
&self,
_: &mut AD,
_: Requests,
_: &ReqProtocolNames,
_: IfDisconnected,
) {
}
fn report_peer(&self, who: PeerId, rep: ReputationChange) {
self.action_tx
.lock()
.unbounded_send(NetworkAction::ReputationChange(who, rep))
.unwrap();
}
fn disconnect_peer(&self, who: PeerId, protocol: ProtocolName) {
let (peer_set, version) = self.peerset_protocol_names.try_get_protocol(&protocol).unwrap();
assert_eq!(version, peer_set.get_main_version());
self.action_tx
.lock()
.unbounded_send(NetworkAction::DisconnectPeer(who, peer_set))
.unwrap();
}
fn peer_role(&self, _peer_id: PeerId, handshake: Vec<u8>) -> Option<SubstrateObservedRole> {
Roles::decode_all(&mut &handshake[..])
.ok()
.and_then(|role| Some(SubstrateObservedRole::from(role)))
}
}
#[async_trait]
impl validator_discovery::AuthorityDiscovery for TestAuthorityDiscovery {
async fn get_addresses_by_authority_id(
&mut self,
_authority: AuthorityDiscoveryId,
) -> Option<HashSet<Multiaddr>> {
None
}
async fn get_authority_ids_by_peer_id(
&mut self,
_peer_id: PeerId,
) -> Option<HashSet<AuthorityDiscoveryId>> {
None
}
}
impl TestNetworkHandle {
// Get the next network action.
async fn next_network_action(&mut self) -> NetworkAction {
self.action_rx.next().await.expect("subsystem concluded early")
}
async fn connect_peer(&mut self, peer: PeerId, peer_set: PeerSet, _role: ObservedRole) {
self.notification_sinks.lock().insert(
(peer_set, peer),
Box::new(TestMessageSink::new(peer, peer_set, self.action_tx.clone())),
);
}
}
type VirtualOverseer = TestSubsystemContextHandle<NetworkBridgeTxMessage>;
struct TestHarness {
network_handle: TestNetworkHandle,
virtual_overseer: VirtualOverseer,
}
fn test_harness<T: Future<Output = VirtualOverseer>>(test: impl FnOnce(TestHarness) -> T) {
let genesis_hash = Hash::repeat_byte(0xff);
let fork_id = None;
let req_protocol_names = ReqProtocolNames::new(genesis_hash, fork_id);
let peerset_protocol_names = PeerSetProtocolNames::new(genesis_hash, fork_id);
let pool = sp_core::testing::TaskExecutor::new();
let (network, network_handle, discovery, network_notification_sinks) =
new_test_network(peerset_protocol_names.clone());
let (context, virtual_overseer) =
pezkuwi_node_subsystem_test_helpers::make_subsystem_context(pool);
let bridge_out = NetworkBridgeTx::new(
network,
discovery,
Metrics(None),
req_protocol_names,
peerset_protocol_names,
network_notification_sinks,
);
let network_bridge_out_fut = run_network_out(bridge_out, context)
.map_err(|e| panic!("bridge-out subsystem execution failed {:?}", e))
.map(|_| ());
let test_fut = test(TestHarness { network_handle, virtual_overseer });
futures::pin_mut!(test_fut);
futures::pin_mut!(network_bridge_out_fut);
let _ = executor::block_on(future::join(
async move {
let mut virtual_overseer = test_fut.await;
virtual_overseer.send(FromOrchestra::Signal(OverseerSignal::Conclude)).await;
},
network_bridge_out_fut,
));
}
#[test]
fn send_messages_to_peers() {
test_harness(|test_harness| async move {
let TestHarness { mut network_handle, mut virtual_overseer } = test_harness;
let peer = PeerId::random();
network_handle
.connect_peer(peer, PeerSet::Validation, ObservedRole::Full)
.timeout(TIMEOUT)
.await
.expect("Timeout does not occur");
// the outgoing side does not consume network messages
// so the single item sink has to be free explicitly
network_handle
.connect_peer(peer, PeerSet::Collation, ObservedRole::Full)
.timeout(TIMEOUT)
.await
.expect("Timeout does not occur");
// send a validation protocol message.
{
let approval_distribution_message =
protocol_v3::ApprovalDistributionMessage::Approvals(Vec::new());
let message_v1 = protocol_v3::ValidationProtocol::ApprovalDistribution(
approval_distribution_message.clone(),
);
virtual_overseer
.send(FromOrchestra::Communication {
msg: NetworkBridgeTxMessage::SendValidationMessage(
vec![peer],
ValidationProtocols::V3(message_v1.clone()),
),
})
.timeout(TIMEOUT)
.await
.expect("Timeout does not occur");
assert_eq!(
network_handle
.next_network_action()
.timeout(TIMEOUT)
.await
.expect("Timeout does not occur"),
NetworkAction::WriteNotification(
peer,
PeerSet::Validation,
WireMessage::ProtocolMessage(message_v1).encode(),
)
);
}
// send a collation protocol message.
{
let collator_protocol_message = protocol_v1::CollatorProtocolMessage::Declare(
Sr25519Keyring::Alice.public().into(),
0_u32.into(),
dummy_collator_signature(),
);
let message_v1 =
protocol_v1::CollationProtocol::CollatorProtocol(collator_protocol_message.clone());
virtual_overseer
.send(FromOrchestra::Communication {
msg: NetworkBridgeTxMessage::SendCollationMessage(
vec![peer],
CollationProtocols::V1(message_v1.clone()),
),
})
.await;
assert_eq!(
network_handle
.next_network_action()
.timeout(TIMEOUT)
.await
.expect("Timeout does not occur"),
NetworkAction::WriteNotification(
peer,
PeerSet::Collation,
WireMessage::ProtocolMessage(message_v1).encode(),
)
);
}
virtual_overseer
});
}