mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-29 18:27:56 +00:00
Rework the event system of sc-network (#1370)
This commit introduces a new concept called `NotificationService` which allows Polkadot protocols to communicate with the underlying notification protocol implementation directly, without routing events through `NetworkWorker`. This implies that each protocol has its own service which it uses to communicate with remote peers and that each `NotificationService` is unique with respect to the underlying notification protocol, meaning `NotificationService` for the transaction protocol can only be used to send and receive transaction-related notifications. The `NotificationService` concept introduces two additional benefits: * allow protocols to start using custom handshakes * allow protocols to accept/reject inbound peers Previously the validation of inbound connections was solely the responsibility of `ProtocolController`. This caused issues with light peers and `SyncingEngine` as `ProtocolController` would accept more peers than `SyncingEngine` could accept which caused peers to have differing views of their own states. `SyncingEngine` would reject excess peers but these rejections were not properly communicated to those peers causing them to assume that they were accepted. With `NotificationService`, the local handshake is not sent to remote peer if peer is rejected which allows it to detect that it was rejected. This commit also deprecates the use of `NetworkEventStream` for all notification-related events and going forward only DHT events are provided through `NetworkEventStream`. If protocols wish to follow each other's events, they must introduce additional abtractions, as is done for GRANDPA and transactions protocols by following the syncing protocol through `SyncEventStream`. Fixes https://github.com/paritytech/polkadot-sdk/issues/512 Fixes https://github.com/paritytech/polkadot-sdk/issues/514 Fixes https://github.com/paritytech/polkadot-sdk/issues/515 Fixes https://github.com/paritytech/polkadot-sdk/issues/554 Fixes https://github.com/paritytech/polkadot-sdk/issues/556 --- These changes are transferred from https://github.com/paritytech/substrate/pull/14197 but there are no functional changes compared to that PR --------- Co-authored-by: Dmitry Markin <dmitry@markin.tech> Co-authored-by: Alexandru Vasile <60601340+lexnv@users.noreply.github.com>
This commit is contained in:
@@ -21,8 +21,8 @@
|
||||
//! Usage:
|
||||
//!
|
||||
//! - Use [`TransactionsHandlerPrototype::new`] to create a prototype.
|
||||
//! - Pass the return value of [`TransactionsHandlerPrototype::set_config`] to the network
|
||||
//! configuration as an extra peers set.
|
||||
//! - Pass the `NonDefaultSetConfig` returned from [`TransactionsHandlerPrototype::new`] to the
|
||||
//! network configuration as an extra peers set.
|
||||
//! - Use [`TransactionsHandlerPrototype::build`] then [`TransactionsHandler::run`] to obtain a
|
||||
//! `Future` that processes transactions.
|
||||
|
||||
@@ -37,7 +37,7 @@ use prometheus_endpoint::{register, Counter, PrometheusError, Registry, U64};
|
||||
use sc_network::{
|
||||
config::{NonDefaultSetConfig, NonReservedPeerMode, ProtocolId, SetConfig},
|
||||
error,
|
||||
event::Event,
|
||||
service::traits::{NotificationEvent, NotificationService, ValidationResult},
|
||||
types::ProtocolName,
|
||||
utils::{interval, LruHashSet},
|
||||
NetworkEventStream, NetworkNotification, NetworkPeers,
|
||||
@@ -115,8 +115,11 @@ impl<H: ExHashT> Future for PendingTransaction<H> {
|
||||
|
||||
/// Prototype for a [`TransactionsHandler`].
|
||||
pub struct TransactionsHandlerPrototype {
|
||||
/// Name of the transaction protocol.
|
||||
protocol_name: ProtocolName,
|
||||
fallback_protocol_names: Vec<ProtocolName>,
|
||||
|
||||
/// Handle that is used to communicate with `sc_network::Notifications`.
|
||||
notification_service: Box<dyn NotificationService>,
|
||||
}
|
||||
|
||||
impl TransactionsHandlerPrototype {
|
||||
@@ -125,35 +128,28 @@ impl TransactionsHandlerPrototype {
|
||||
protocol_id: ProtocolId,
|
||||
genesis_hash: Hash,
|
||||
fork_id: Option<&str>,
|
||||
) -> Self {
|
||||
) -> (Self, NonDefaultSetConfig) {
|
||||
let genesis_hash = genesis_hash.as_ref();
|
||||
let protocol_name = if let Some(fork_id) = fork_id {
|
||||
let protocol_name: ProtocolName = if let Some(fork_id) = fork_id {
|
||||
format!("/{}/{}/transactions/1", array_bytes::bytes2hex("", genesis_hash), fork_id)
|
||||
} else {
|
||||
format!("/{}/transactions/1", array_bytes::bytes2hex("", genesis_hash))
|
||||
};
|
||||
let legacy_protocol_name = format!("/{}/transactions/1", protocol_id.as_ref());
|
||||
|
||||
Self {
|
||||
protocol_name: protocol_name.into(),
|
||||
fallback_protocol_names: iter::once(legacy_protocol_name.into()).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the configuration of the set to put in the network configuration.
|
||||
pub fn set_config(&self) -> NonDefaultSetConfig {
|
||||
NonDefaultSetConfig {
|
||||
notifications_protocol: self.protocol_name.clone(),
|
||||
fallback_names: self.fallback_protocol_names.clone(),
|
||||
max_notification_size: MAX_TRANSACTIONS_SIZE,
|
||||
handshake: None,
|
||||
set_config: SetConfig {
|
||||
.into();
|
||||
let (config, notification_service) = NonDefaultSetConfig::new(
|
||||
protocol_name.clone(),
|
||||
vec![format!("/{}/transactions/1", protocol_id.as_ref()).into()],
|
||||
MAX_TRANSACTIONS_SIZE,
|
||||
None,
|
||||
SetConfig {
|
||||
in_peers: 0,
|
||||
out_peers: 0,
|
||||
reserved_nodes: Vec::new(),
|
||||
non_reserved_mode: NonReservedPeerMode::Deny,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
(Self { protocol_name, notification_service }, config)
|
||||
}
|
||||
|
||||
/// Turns the prototype into the actual handler. Returns a controller that allows controlling
|
||||
@@ -173,12 +169,12 @@ impl TransactionsHandlerPrototype {
|
||||
transaction_pool: Arc<dyn TransactionPool<H, B>>,
|
||||
metrics_registry: Option<&Registry>,
|
||||
) -> error::Result<(TransactionsHandler<B, H, N, S>, TransactionsHandlerController<H>)> {
|
||||
let net_event_stream = network.event_stream("transactions-handler-net");
|
||||
let sync_event_stream = sync.event_stream("transactions-handler-sync");
|
||||
let (to_handler, from_controller) = tracing_unbounded("mpsc_transactions_handler", 100_000);
|
||||
|
||||
let handler = TransactionsHandler {
|
||||
protocol_name: self.protocol_name,
|
||||
notification_service: self.notification_service,
|
||||
propagate_timeout: (Box::pin(interval(PROPAGATE_TIMEOUT))
|
||||
as Pin<Box<dyn Stream<Item = ()> + Send>>)
|
||||
.fuse(),
|
||||
@@ -186,7 +182,6 @@ impl TransactionsHandlerPrototype {
|
||||
pending_transactions_peers: HashMap::new(),
|
||||
network,
|
||||
sync,
|
||||
net_event_stream: net_event_stream.fuse(),
|
||||
sync_event_stream: sync_event_stream.fuse(),
|
||||
peers: HashMap::new(),
|
||||
transaction_pool,
|
||||
@@ -253,8 +248,6 @@ pub struct TransactionsHandler<
|
||||
network: N,
|
||||
/// Syncing service.
|
||||
sync: S,
|
||||
/// Stream of networking events.
|
||||
net_event_stream: stream::Fuse<Pin<Box<dyn Stream<Item = Event> + Send>>>,
|
||||
/// Receiver for syncing-related events.
|
||||
sync_event_stream: stream::Fuse<Pin<Box<dyn Stream<Item = SyncEvent> + Send>>>,
|
||||
// All connected peers
|
||||
@@ -263,6 +256,8 @@ pub struct TransactionsHandler<
|
||||
from_controller: TracingUnboundedReceiver<ToHandler<H>>,
|
||||
/// Prometheus metrics.
|
||||
metrics: Option<Metrics>,
|
||||
/// Handle that is used to communicate with `sc_network::Notifications`.
|
||||
notification_service: Box<dyn NotificationService>,
|
||||
}
|
||||
|
||||
/// Peer information
|
||||
@@ -295,14 +290,6 @@ where
|
||||
warn!(target: "sub-libp2p", "Inconsistent state, no peers for pending transaction!");
|
||||
}
|
||||
},
|
||||
network_event = self.net_event_stream.next() => {
|
||||
if let Some(network_event) = network_event {
|
||||
self.handle_network_event(network_event).await;
|
||||
} else {
|
||||
// Networking has seemingly closed. Closing as well.
|
||||
return;
|
||||
}
|
||||
},
|
||||
sync_event = self.sync_event_stream.next() => {
|
||||
if let Some(sync_event) = sync_event {
|
||||
self.handle_sync_event(sync_event);
|
||||
@@ -317,10 +304,61 @@ where
|
||||
ToHandler::PropagateTransactions => self.propagate_transactions(),
|
||||
}
|
||||
},
|
||||
event = self.notification_service.next_event().fuse() => {
|
||||
if let Some(event) = event {
|
||||
self.handle_notification_event(event)
|
||||
} else {
|
||||
// `Notifications` has seemingly closed. Closing as well.
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_notification_event(&mut self, event: NotificationEvent) {
|
||||
match event {
|
||||
NotificationEvent::ValidateInboundSubstream { peer, handshake, result_tx, .. } => {
|
||||
// only accept peers whose role can be determined
|
||||
let result = self
|
||||
.network
|
||||
.peer_role(peer, handshake)
|
||||
.map_or(ValidationResult::Reject, |_| ValidationResult::Accept);
|
||||
let _ = result_tx.send(result);
|
||||
},
|
||||
NotificationEvent::NotificationStreamOpened { peer, handshake, .. } => {
|
||||
let Some(role) = self.network.peer_role(peer, handshake) else {
|
||||
log::debug!(target: "sub-libp2p", "role for {peer} couldn't be determined");
|
||||
return
|
||||
};
|
||||
|
||||
let _was_in = self.peers.insert(
|
||||
peer,
|
||||
Peer {
|
||||
known_transactions: LruHashSet::new(
|
||||
NonZeroUsize::new(MAX_KNOWN_TRANSACTIONS).expect("Constant is nonzero"),
|
||||
),
|
||||
role,
|
||||
},
|
||||
);
|
||||
debug_assert!(_was_in.is_none());
|
||||
},
|
||||
NotificationEvent::NotificationStreamClosed { peer } => {
|
||||
let _peer = self.peers.remove(&peer);
|
||||
debug_assert!(_peer.is_some());
|
||||
},
|
||||
NotificationEvent::NotificationReceived { peer, notification } => {
|
||||
if let Ok(m) =
|
||||
<Transactions<B::Extrinsic> as Decode>::decode(&mut notification.as_ref())
|
||||
{
|
||||
self.on_transactions(peer, m);
|
||||
} else {
|
||||
warn!(target: "sub-libp2p", "Failed to decode transactions list");
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_sync_event(&mut self, event: SyncEvent) {
|
||||
match event {
|
||||
SyncEvent::PeerConnected(remote) => {
|
||||
@@ -346,51 +384,6 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_network_event(&mut self, event: Event) {
|
||||
match event {
|
||||
Event::Dht(_) => {},
|
||||
Event::NotificationStreamOpened { remote, protocol, role, .. }
|
||||
if protocol == self.protocol_name =>
|
||||
{
|
||||
let _was_in = self.peers.insert(
|
||||
remote,
|
||||
Peer {
|
||||
known_transactions: LruHashSet::new(
|
||||
NonZeroUsize::new(MAX_KNOWN_TRANSACTIONS).expect("Constant is nonzero"),
|
||||
),
|
||||
role,
|
||||
},
|
||||
);
|
||||
debug_assert!(_was_in.is_none());
|
||||
},
|
||||
Event::NotificationStreamClosed { remote, protocol }
|
||||
if protocol == self.protocol_name =>
|
||||
{
|
||||
let _peer = self.peers.remove(&remote);
|
||||
debug_assert!(_peer.is_some());
|
||||
},
|
||||
|
||||
Event::NotificationsReceived { remote, messages } => {
|
||||
for (protocol, message) in messages {
|
||||
if protocol != self.protocol_name {
|
||||
continue
|
||||
}
|
||||
|
||||
if let Ok(m) =
|
||||
<Transactions<B::Extrinsic> as Decode>::decode(&mut message.as_ref())
|
||||
{
|
||||
self.on_transactions(remote, m);
|
||||
} else {
|
||||
warn!(target: "sub-libp2p", "Failed to decode transactions list");
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Not our concern.
|
||||
Event::NotificationStreamOpened { .. } | Event::NotificationStreamClosed { .. } => {},
|
||||
}
|
||||
}
|
||||
|
||||
/// Called when peer sends us new transactions
|
||||
fn on_transactions(&mut self, who: PeerId, transactions: Transactions<B::Extrinsic>) {
|
||||
// Accept transactions only when node is not major syncing
|
||||
@@ -482,8 +475,7 @@ where
|
||||
propagated_to.entry(hash).or_default().push(who.to_base58());
|
||||
}
|
||||
trace!(target: "sync", "Sending {} transactions to {}", to_send.len(), who);
|
||||
self.network
|
||||
.write_notification(*who, self.protocol_name.clone(), to_send.encode());
|
||||
let _ = self.notification_service.send_sync_notification(who, to_send.encode());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user