diff --git a/polkadot/Cargo.lock b/polkadot/Cargo.lock index 78c2bbfe3f..f343b2ac8a 100644 --- a/polkadot/Cargo.lock +++ b/polkadot/Cargo.lock @@ -5461,6 +5461,7 @@ dependencies = [ "polkadot-primitives", "sp-core", "sp-keyring", + "sp-runtime", "thiserror", "tracing", ] diff --git a/polkadot/node/network/bridge/src/lib.rs b/polkadot/node/network/bridge/src/lib.rs index 81ef905f93..9516ecd797 100644 --- a/polkadot/node/network/bridge/src/lib.rs +++ b/polkadot/node/network/bridge/src/lib.rs @@ -1306,7 +1306,8 @@ mod tests { // peer A gets reported for sending a collation message. let collator_protocol_message = protocol_v1::CollatorProtocolMessage::Declare( - Sr25519Keyring::Alice.public().into() + Sr25519Keyring::Alice.public().into(), + Default::default(), ); let message = protocol_v1::CollationProtocol::CollatorProtocol( @@ -1572,7 +1573,8 @@ mod tests { { let collator_protocol_message = protocol_v1::CollatorProtocolMessage::Declare( - Sr25519Keyring::Alice.public().into() + Sr25519Keyring::Alice.public().into(), + Default::default(), ); let message = protocol_v1::CollationProtocol::CollatorProtocol( diff --git a/polkadot/node/network/collator-protocol/Cargo.toml b/polkadot/node/network/collator-protocol/Cargo.toml index 98efa7d240..c8820c1955 100644 --- a/polkadot/node/network/collator-protocol/Cargo.toml +++ b/polkadot/node/network/collator-protocol/Cargo.toml @@ -5,17 +5,20 @@ authors = ["Parity Technologies "] edition = "2018" [dependencies] +always-assert = "0.1.2" futures = "0.3.12" -tracing = "0.1.25" -thiserror = "1.0.23" futures-timer = "3" +thiserror = "1.0.23" +tracing = "0.1.25" + +sp-core = { package = "sp-core", git = "https://github.com/paritytech/substrate", branch = "master" } +sp-runtime = { package = "sp-runtime", git = "https://github.com/paritytech/substrate", branch = "master" } polkadot-primitives = { path = "../../../primitives" } polkadot-node-network-protocol = { path = "../../network/protocol" } polkadot-node-primitives = { path = "../../primitives" } polkadot-node-subsystem-util = { path = "../../subsystem-util" } polkadot-subsystem = { package = "polkadot-node-subsystem", path = "../../subsystem" } -always-assert = "0.1.2" [dev-dependencies] log = "0.4.13" diff --git a/polkadot/node/network/collator-protocol/src/collator_side.rs b/polkadot/node/network/collator-protocol/src/collator_side.rs index db42453e59..a46da32b12 100644 --- a/polkadot/node/network/collator-protocol/src/collator_side.rs +++ b/polkadot/node/network/collator-protocol/src/collator_side.rs @@ -19,11 +19,11 @@ use std::collections::{HashMap, HashSet}; use super::{LOG_TARGET, Result}; use futures::{select, FutureExt, channel::oneshot}; +use sp_core::Pair; use polkadot_primitives::v1::{ - CandidateHash, CandidateReceipt, CollatorId, CompressedPoV, CoreIndex, - CoreState, Hash, Id as ParaId, - PoV, ValidatorId + CandidateHash, CandidateReceipt, CollatorPair, CompressedPoV, CoreIndex, CoreState, Hash, + Id as ParaId, PoV, ValidatorId }; use polkadot_subsystem::{ jaeger, PerLeafSpan, @@ -209,10 +209,12 @@ struct Collation { status: CollationStatus, } -#[derive(Default)] struct State { - /// Our id. - our_id: CollatorId, + /// Our network peer id. + local_peer_id: PeerId, + + /// Our collator pair. + collator_pair: CollatorPair, /// The para this collator is collating on. /// Starts as `None` and is updated with every `CollateOn` message. @@ -250,6 +252,25 @@ struct State { } impl State { + /// Creates a new `State` instance with the given parameters and setting all remaining + /// state fields to their default values (i.e. empty). + fn new(local_peer_id: PeerId, collator_pair: CollatorPair, metrics: Metrics) -> State { + State { + local_peer_id, + collator_pair, + metrics, + collating_on: Default::default(), + peer_views: Default::default(), + view: Default::default(), + span_per_relay_parent: Default::default(), + collations: Default::default(), + collation_result_senders: Default::default(), + our_validators_groups: Default::default(), + declared_at: Default::default(), + connection_requests: Default::default(), + } + } + /// Returns `true` if the given `peer` is interested in the leaf that is represented by `relay_parent`. fn peer_interested_in_leaf(&self, peer: &PeerId, relay_parent: &Hash) -> bool { self.peer_views.get(peer).map(|v| v.contains(relay_parent)).unwrap_or(false) @@ -407,7 +428,12 @@ async fn declare( state: &mut State, peer: PeerId, ) { - let wire_message = protocol_v1::CollatorProtocolMessage::Declare(state.our_id.clone()); + let declare_signature_payload = protocol_v1::declare_signature_payload(&state.local_peer_id); + + let wire_message = protocol_v1::CollatorProtocolMessage::Declare( + state.collator_pair.public(), + state.collator_pair.sign(&declare_signature_payload), + ); ctx.send_message(AllMessages::NetworkBridge( NetworkBridgeMessage::SendCollationMessage( @@ -490,7 +516,10 @@ async fn advertise_collation( }, } - let wire_message = protocol_v1::CollatorProtocolMessage::AdvertiseCollation(relay_parent, collating_on); + let wire_message = protocol_v1::CollatorProtocolMessage::AdvertiseCollation( + relay_parent, + collating_on, + ); ctx.send_message(AllMessages::NetworkBridge( NetworkBridgeMessage::SendCollationMessage( @@ -676,7 +705,7 @@ async fn handle_incoming_peer_message( use protocol_v1::CollatorProtocolMessage::*; match msg { - Declare(_) => { + Declare(_, _) => { tracing::warn!( target: LOG_TARGET, ?origin, @@ -861,20 +890,17 @@ async fn handle_our_view_change( } /// The collator protocol collator side main loop. -#[tracing::instrument(skip(ctx, metrics), fields(subsystem = LOG_TARGET))] +#[tracing::instrument(skip(ctx, collator_pair, metrics), fields(subsystem = LOG_TARGET))] pub(crate) async fn run( mut ctx: impl SubsystemContext, - our_id: CollatorId, + local_peer_id: PeerId, + collator_pair: CollatorPair, metrics: Metrics, ) -> Result<()> { use FromOverseer::*; use OverseerSignal::*; - let mut state = State { - metrics, - our_id, - ..Default::default() - }; + let mut state = State::new(local_peer_id, collator_pair, metrics); loop { select! { @@ -907,27 +933,31 @@ pub(crate) async fn run( mod tests { use super::*; - use std::{time::Duration, sync::Arc}; + use std::{sync::Arc, time::Duration}; use assert_matches::assert_matches; - use futures::{executor, future, Future, channel::mpsc}; + use futures::{channel::mpsc, executor, future, Future}; use sp_core::{crypto::Pair, Decode}; use sp_keyring::Sr25519Keyring; + use sp_runtime::traits::AppVerify; - use polkadot_primitives::v1::{ - BlockData, CandidateDescriptor, CollatorPair, ScheduledCore, - ValidatorIndex, GroupRotationInfo, AuthorityDiscoveryId, - SessionIndex, SessionInfo, - }; - use polkadot_subsystem::{ActiveLeavesUpdate, messages::{RuntimeApiMessage, RuntimeApiRequest}, jaeger}; - use polkadot_node_subsystem_util::TimeoutExt; - use polkadot_subsystem_testhelpers as test_helpers; use polkadot_node_network_protocol::{ our_view, view, request_response::request::IncomingRequest, }; + use polkadot_node_subsystem_util::TimeoutExt; + use polkadot_primitives::v1::{ + AuthorityDiscoveryId, BlockData, CandidateDescriptor, CollatorPair, GroupRotationInfo, + ScheduledCore, SessionIndex, SessionInfo, ValidatorIndex, + }; + use polkadot_subsystem::{ + jaeger, + messages::{RuntimeApiMessage, RuntimeApiRequest}, + ActiveLeavesUpdate, + }; + use polkadot_subsystem_testhelpers as test_helpers; #[derive(Default)] struct TestCandidateBuilder { @@ -961,7 +991,8 @@ mod tests { validator_groups: (Vec>, GroupRotationInfo), relay_parent: Hash, availability_core: CoreState, - our_collator_pair: CollatorPair, + local_peer_id: PeerId, + collator_pair: CollatorPair, session_index: SessionIndex, } @@ -1008,7 +1039,8 @@ mod tests { let relay_parent = Hash::random(); - let our_collator_pair = CollatorPair::generate().0; + let local_peer_id = PeerId::random(); + let collator_pair = CollatorPair::generate().0; Self { para_id, @@ -1019,7 +1051,8 @@ mod tests { validator_groups, relay_parent, availability_core, - our_collator_pair, + local_peer_id, + collator_pair, session_index: 1, } } @@ -1094,7 +1127,8 @@ mod tests { } fn test_harness>( - collator_id: CollatorId, + local_peer_id: PeerId, + collator_pair: CollatorPair, test: impl FnOnce(TestHarness) -> T, ) { let _ = env_logger::builder() @@ -1113,7 +1147,7 @@ mod tests { let (context, virtual_overseer) = test_helpers::make_subsystem_context(pool.clone()); - let subsystem = run(context, collator_id, Metrics::default()); + let subsystem = run(context, local_peer_id, collator_pair, Metrics::default()); let test_fut = test(TestHarness { virtual_overseer }); @@ -1355,8 +1389,12 @@ mod tests { assert_eq!(to[0], *peer); assert_matches!( wire_message, - protocol_v1::CollatorProtocolMessage::Declare(collator_id) => { - assert_eq!(collator_id, test_state.our_collator_pair.public()); + protocol_v1::CollatorProtocolMessage::Declare(collator_id, signature) => { + assert!(signature.verify( + &*protocol_v1::declare_signature_payload(&test_state.local_peer_id), + &collator_id), + ); + assert_eq!(collator_id, test_state.collator_pair.public()); } ); } @@ -1406,8 +1444,10 @@ mod tests { #[test] fn advertise_and_send_collation() { let mut test_state = TestState::default(); + let local_peer_id = test_state.local_peer_id.clone(); + let collator_pair = test_state.collator_pair.clone(); - test_harness(test_state.our_collator_pair.public(), |test_harness| async move { + test_harness(local_peer_id, collator_pair, |test_harness| async move { let mut virtual_overseer = test_harness.virtual_overseer; setup_system(&mut virtual_overseer, &test_state).await; @@ -1520,8 +1560,10 @@ mod tests { #[test] fn collators_are_registered_correctly_at_validators() { let test_state = TestState::default(); + let local_peer_id = test_state.local_peer_id.clone(); + let collator_pair = test_state.collator_pair.clone(); - test_harness(test_state.our_collator_pair.public(), |test_harness| async move { + test_harness(local_peer_id, collator_pair, |test_harness| async move { let mut virtual_overseer = test_harness.virtual_overseer; let peer = test_state.validator_peer_id[0].clone(); @@ -1542,8 +1584,10 @@ mod tests { #[test] fn collations_are_only_advertised_to_validators_with_correct_view() { let test_state = TestState::default(); + let local_peer_id = test_state.local_peer_id.clone(); + let collator_pair = test_state.collator_pair.clone(); - test_harness(test_state.our_collator_pair.public(), |test_harness| async move { + test_harness(local_peer_id, collator_pair, |test_harness| async move { let mut virtual_overseer = test_harness.virtual_overseer; let peer = test_state.current_group_validator_peer_ids()[0].clone(); @@ -1583,8 +1627,10 @@ mod tests { #[test] fn collate_on_two_different_relay_chain_blocks() { let mut test_state = TestState::default(); + let local_peer_id = test_state.local_peer_id.clone(); + let collator_pair = test_state.collator_pair.clone(); - test_harness(test_state.our_collator_pair.public(), |test_harness| async move { + test_harness(local_peer_id, collator_pair, |test_harness| async move { let mut virtual_overseer = test_harness.virtual_overseer; let peer = test_state.current_group_validator_peer_ids()[0].clone(); @@ -1628,8 +1674,10 @@ mod tests { #[test] fn validator_reconnect_does_not_advertise_a_second_time() { let test_state = TestState::default(); + let local_peer_id = test_state.local_peer_id.clone(); + let collator_pair = test_state.collator_pair.clone(); - test_harness(test_state.our_collator_pair.public(), |test_harness| async move { + test_harness(local_peer_id, collator_pair, |test_harness| async move { let mut virtual_overseer = test_harness.virtual_overseer; let peer = test_state.current_group_validator_peer_ids()[0].clone(); diff --git a/polkadot/node/network/collator-protocol/src/lib.rs b/polkadot/node/network/collator-protocol/src/lib.rs index 8bd7995c5f..62d264a8bb 100644 --- a/polkadot/node/network/collator-protocol/src/lib.rs +++ b/polkadot/node/network/collator-protocol/src/lib.rs @@ -25,20 +25,13 @@ use std::time::Duration; use futures::{channel::oneshot, FutureExt, TryFutureExt}; use thiserror::Error; +use polkadot_node_network_protocol::{PeerId, UnifiedReputationChange as Rep}; +use polkadot_node_subsystem_util::{self as util, metrics::prometheus}; +use polkadot_primitives::v1::CollatorPair; use polkadot_subsystem::{ - Subsystem, SubsystemContext, SubsystemError, SpawnedSubsystem, errors::RuntimeApiError, - messages::{ - AllMessages, CollatorProtocolMessage, NetworkBridgeMessage, - }, -}; -use polkadot_node_network_protocol::{ - PeerId, UnifiedReputationChange as Rep, -}; -use polkadot_primitives::v1::CollatorId; -use polkadot_node_subsystem_util::{ - self as util, - metrics::prometheus, + messages::{AllMessages, CollatorProtocolMessage, NetworkBridgeMessage}, + SpawnedSubsystem, Subsystem, SubsystemContext, SubsystemError, }; mod collator_side; @@ -77,7 +70,7 @@ pub enum ProtocolSide { /// Validators operate on the relay chain. Validator(CollatorEvictionPolicy, validator_side::Metrics), /// Collators operate on a parachain. - Collator(CollatorId, collator_side::Metrics), + Collator(PeerId, CollatorPair, collator_side::Metrics), } /// The collator protocol subsystem. @@ -107,9 +100,10 @@ impl CollatorProtocolSubsystem { policy, metrics, ).await, - ProtocolSide::Collator(id, metrics) => collator_side::run( + ProtocolSide::Collator(local_peer_id, collator_pair, metrics) => collator_side::run( ctx, - id, + local_peer_id, + collator_pair, metrics, ).await, }.map_err(|e| { diff --git a/polkadot/node/network/collator-protocol/src/validator_side.rs b/polkadot/node/network/collator-protocol/src/validator_side.rs index 3678e858b3..5d66dd8256 100644 --- a/polkadot/node/network/collator-protocol/src/validator_side.rs +++ b/polkadot/node/network/collator-protocol/src/validator_side.rs @@ -16,34 +16,35 @@ use std::{collections::{HashMap, HashSet}, sync::Arc, task::Poll}; use std::time::{Duration, Instant}; - -use futures::{FutureExt, channel::oneshot, future::{Fuse, FusedFuture, BoxFuture, Either}}; -use futures::StreamExt; -use futures_timer::Delay; use always_assert::never; +use futures::{ + channel::oneshot, future::{BoxFuture, Either, Fuse, FusedFuture}, FutureExt, StreamExt, +}; +use futures_timer::Delay; -use polkadot_primitives::v1::{ - Id as ParaId, CandidateReceipt, CollatorId, Hash, PoV, -}; -use polkadot_subsystem::{ - jaeger, PerLeafSpan, - FromOverseer, OverseerSignal, SubsystemContext, - messages::{ - AllMessages, CandidateSelectionMessage, CollatorProtocolMessage, NetworkBridgeMessage, - NetworkBridgeEvent, IfDisconnected, - }, -}; use polkadot_node_network_protocol::{ - OurView, PeerId, UnifiedReputationChange as Rep, View, + request_response as req_res, v1 as protocol_v1, peer_set::PeerSet, - request_response::{OutgoingRequest, Requests, request::{Recipient, RequestError}}, v1 as protocol_v1 + request_response::{ + request::{Recipient, RequestError}, + v1::{CollationFetchingRequest, CollationFetchingResponse}, + OutgoingRequest, Requests, + }, + OurView, PeerId, UnifiedReputationChange as Rep, View, }; -use polkadot_node_network_protocol::request_response::v1::{CollationFetchingRequest, CollationFetchingResponse}; -use polkadot_node_network_protocol::request_response as req_res; +use polkadot_node_primitives::{SignedFullStatement, Statement}; use polkadot_node_subsystem_util::metrics::{self, prometheus}; -use polkadot_node_primitives::{Statement, SignedFullStatement}; +use polkadot_primitives::v1::{CandidateReceipt, CollatorId, Hash, Id as ParaId, PoV}; +use polkadot_subsystem::{ + jaeger, + messages::{ + AllMessages, CandidateSelectionMessage, CollatorProtocolMessage, IfDisconnected, + NetworkBridgeEvent, NetworkBridgeMessage, + }, + FromOverseer, OverseerSignal, PerLeafSpan, SubsystemContext, +}; -use super::{modify_reputation, LOG_TARGET, Result}; +use super::{modify_reputation, Result, LOG_TARGET}; const COST_UNEXPECTED_MESSAGE: Rep = Rep::CostMinor("An unexpected message"); /// Message could not be decoded properly. @@ -51,6 +52,7 @@ const COST_CORRUPTED_MESSAGE: Rep = Rep::CostMinor("Message was corrupt"); /// Network errors that originated at the remote host should have same cost as timeout. const COST_NETWORK_ERROR: Rep = Rep::CostMinor("Some network error"); const COST_REQUEST_TIMED_OUT: Rep = Rep::CostMinor("A collation request has timed out"); +const COST_INVALID_SIGNATURE: Rep = Rep::Malicious("Invalid network message signature"); const COST_REPORT_BAD: Rep = Rep::Malicious("A collator was reported by another subsystem"); const BENEFIT_NOTIFY_GOOD: Rep = Rep::BenefitMinor("A collator was noted good by another subsystem"); @@ -422,13 +424,19 @@ where Context: SubsystemContext { use protocol_v1::CollatorProtocolMessage::*; + use sp_runtime::traits::AppVerify; if let Some(d) = state.peer_data.get_mut(&origin) { d.note_active(); } match msg { - Declare(id) => { + Declare(id, signature) => { + if !signature.verify(&*protocol_v1::declare_signature_payload(&origin), &id) { + modify_reputation(ctx, origin, COST_INVALID_SIGNATURE).await; + return; + } + tracing::debug!( target: LOG_TARGET, peer_id = ?origin, @@ -1015,7 +1023,6 @@ mod tests { ) ).await; - let peer_b = PeerId::random(); overseer_send( @@ -1023,7 +1030,10 @@ mod tests { CollatorProtocolMessage::NetworkBridgeUpdateV1( NetworkBridgeEvent::PeerMessage( peer_b.clone(), - protocol_v1::CollatorProtocolMessage::Declare(pair.public()), + protocol_v1::CollatorProtocolMessage::Declare( + pair.public(), + pair.sign(&protocol_v1::declare_signature_payload(&peer_b)), + ) ) ) ).await; @@ -1083,6 +1093,7 @@ mod tests { peer_b.clone(), protocol_v1::CollatorProtocolMessage::Declare( test_state.collators[0].public(), + test_state.collators[0].sign(&protocol_v1::declare_signature_payload(&peer_b)), ), ) ) @@ -1095,6 +1106,7 @@ mod tests { peer_c.clone(), protocol_v1::CollatorProtocolMessage::Declare( test_state.collators[1].public(), + test_state.collators[1].sign(&protocol_v1::declare_signature_payload(&peer_c)), ), ) ) @@ -1132,6 +1144,44 @@ mod tests { }); } + // Test that we verify the signatures on `Declare` and `AdvertiseCollation` messages. + #[test] + fn collator_authentication_verification_works() { + let test_state = TestState::default(); + + test_harness(|test_harness| async move { + let TestHarness { + mut virtual_overseer, + } = test_harness; + + let peer_b = PeerId::random(); + + // the peer sends a declare message but sign the wrong payload + overseer_send( + &mut virtual_overseer, + CollatorProtocolMessage::NetworkBridgeUpdateV1(NetworkBridgeEvent::PeerMessage( + peer_b.clone(), + protocol_v1::CollatorProtocolMessage::Declare( + test_state.collators[0].public(), + test_state.collators[0].sign(&[42]), + ), + )), + ) + .await; + + // it should be reported for sending a message with an invalid signature + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::NetworkBridge( + NetworkBridgeMessage::ReportPeer(peer, rep), + ) => { + assert_eq!(peer, peer_b); + assert_eq!(rep, COST_INVALID_SIGNATURE); + } + ); + }); + } + // A test scenario that takes the following steps // - Two collators connect, declare themselves and advertise a collation relevant to // our view. @@ -1167,6 +1217,7 @@ mod tests { peer_b.clone(), protocol_v1::CollatorProtocolMessage::Declare( test_state.collators[0].public(), + test_state.collators[0].sign(&protocol_v1::declare_signature_payload(&peer_b)), ) ) ) @@ -1179,6 +1230,7 @@ mod tests { peer_c.clone(), protocol_v1::CollatorProtocolMessage::Declare( test_state.collators[1].public(), + test_state.collators[1].sign(&protocol_v1::declare_signature_payload(&peer_c)), ) ) ) @@ -1366,7 +1418,10 @@ mod tests { CollatorProtocolMessage::NetworkBridgeUpdateV1( NetworkBridgeEvent::PeerMessage( peer_b.clone(), - protocol_v1::CollatorProtocolMessage::Declare(pair.public()), + protocol_v1::CollatorProtocolMessage::Declare( + pair.public(), + pair.sign(&protocol_v1::declare_signature_payload(&peer_b)), + ) ) ) ).await; @@ -1455,7 +1510,10 @@ mod tests { CollatorProtocolMessage::NetworkBridgeUpdateV1( NetworkBridgeEvent::PeerMessage( peer_b.clone(), - protocol_v1::CollatorProtocolMessage::Declare(pair.public()), + protocol_v1::CollatorProtocolMessage::Declare( + pair.public(), + pair.sign(&protocol_v1::declare_signature_payload(&peer_b)), + ) ) ) ).await; diff --git a/polkadot/node/network/protocol/src/lib.rs b/polkadot/node/network/protocol/src/lib.rs index 5f89173a58..664ff25bd2 100644 --- a/polkadot/node/network/protocol/src/lib.rs +++ b/polkadot/node/network/protocol/src/lib.rs @@ -288,15 +288,21 @@ impl View { /// v1 protocol types. pub mod v1 { - use polkadot_primitives::v1::{AvailableData, CandidateHash, CandidateIndex, CollatorId, CompressedPoV, ErasureChunk, Hash, Id as ParaId, SignedAvailabilityBitfield, ValidatorIndex}; - use polkadot_node_primitives::{ - SignedFullStatement, - approval::{IndirectAssignmentCert, IndirectSignedApprovalVote}, - }; - use parity_scale_codec::{Encode, Decode}; - use super::RequestId; use std::convert::TryFrom; + use parity_scale_codec::{Decode, Encode}; + + use super::RequestId; + use polkadot_node_primitives::{ + approval::{IndirectAssignmentCert, IndirectSignedApprovalVote}, + SignedFullStatement, + }; + use polkadot_primitives::v1::{ + AvailableData, CandidateHash, CandidateIndex, CollatorId, CompressedPoV, + CollatorSignature, ErasureChunk, Hash, Id as ParaId, SignedAvailabilityBitfield, + ValidatorIndex, + }; + /// Network messages used by the availability recovery subsystem. #[derive(Debug, Clone, Encode, Decode, PartialEq, Eq)] pub enum AvailabilityRecoveryMessage { @@ -357,11 +363,12 @@ pub mod v1 { /// Network messages used by the collator protocol subsystem #[derive(Debug, Clone, Encode, Decode, PartialEq, Eq)] pub enum CollatorProtocolMessage { - /// Declare the intent to advertise collations under a collator ID. + /// Declare the intent to advertise collations under a collator ID, attaching a + /// signature of the `PeerId` of the node using the given collator ID key. #[codec(index = 0)] - Declare(CollatorId), - /// Advertise a collation to a validator. Can only be sent once the peer has declared - /// that they are a collator with given ID. + Declare(CollatorId, CollatorSignature), + /// Advertise a collation to a validator. Can only be sent once the peer has + /// declared that they are a collator with given ID. #[codec(index = 1)] AdvertiseCollation(Hash, ParaId), /// A collation sent to a validator was seconded. @@ -404,4 +411,14 @@ pub mod v1 { } impl_try_from!(CollationProtocol, CollatorProtocol, CollatorProtocolMessage); + + /// Get the payload that should be signed and included in a `Declare` message. + /// + /// The payload is the local peer id of the node, which serves to prove that it + /// controls the collator key it is declaring an intention to collate under. + pub fn declare_signature_payload(peer_id: &sc_network::PeerId) -> Vec { + let mut payload = peer_id.to_bytes(); + payload.extend_from_slice(b"COLL"); + payload + } } diff --git a/polkadot/node/service/src/lib.rs b/polkadot/node/service/src/lib.rs index d475a39060..cefe716298 100644 --- a/polkadot/node/service/src/lib.rs +++ b/polkadot/node/service/src/lib.rs @@ -60,7 +60,7 @@ pub use self::client::{AbstractClient, Client, ClientHandle, ExecuteWithClient, pub use chain_spec::{PolkadotChainSpec, KusamaChainSpec, WestendChainSpec, RococoChainSpec}; pub use consensus_common::{Proposal, SelectChain, BlockImport, block_validation::Chain}; pub use polkadot_parachain::wasm_executor::IsolationStrategy; -pub use polkadot_primitives::v1::{Block, BlockId, CollatorId, Hash, Id as ParaId}; +pub use polkadot_primitives::v1::{Block, BlockId, CollatorPair, Hash, Id as ParaId}; pub use sc_client_api::{Backend, ExecutionStrategy, CallExecutor}; pub use sc_consensus::LongestChain; pub use sc_executor::NativeExecutionDispatch; @@ -502,7 +502,11 @@ where ), collator_protocol: { let side = match is_collator { - IsCollator::Yes(id) => ProtocolSide::Collator(id, Metrics::register(registry)?), + IsCollator::Yes(collator_pair) => ProtocolSide::Collator( + network_service.local_peer_id().clone(), + collator_pair, + Metrics::register(registry)?, + ), IsCollator::No => ProtocolSide::Validator(Default::default(),Metrics::register(registry)?), }; CollatorProtocolSubsystem::new( @@ -578,14 +582,25 @@ impl NewFull { /// Is this node a collator? #[cfg(feature = "full-node")] -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(Clone)] pub enum IsCollator { /// This node is a collator. - Yes(CollatorId), + Yes(CollatorPair), /// This node is not a collator. No, } +#[cfg(feature = "full-node")] +impl std::fmt::Debug for IsCollator { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + use sp_core::Pair; + match self { + IsCollator::Yes(pair) => write!(fmt, "Yes({})", pair.public()), + IsCollator::No => write!(fmt, "No"), + } + } +} + #[cfg(feature = "full-node")] impl IsCollator { /// Is this a collator? diff --git a/polkadot/node/test/service/src/lib.rs b/polkadot/node/test/service/src/lib.rs index 81911fbef9..153c4590b2 100644 --- a/polkadot/node/test/service/src/lib.rs +++ b/polkadot/node/test/service/src/lib.rs @@ -24,7 +24,7 @@ pub use chain_spec::*; use futures::future::Future; use polkadot_overseer::OverseerHandler; use polkadot_primitives::v1::{ - Id as ParaId, HeadData, ValidationCode, Balance, CollatorPair, CollatorId, + Id as ParaId, HeadData, ValidationCode, Balance, CollatorPair, }; use polkadot_runtime_common::BlockHashCount; use polkadot_service::{ @@ -250,12 +250,19 @@ pub fn run_collator_node( key: Sr25519Keyring, storage_update_func: impl Fn(), boot_nodes: Vec, - collator_id: CollatorId, + collator_pair: CollatorPair, ) -> PolkadotTestNode { let config = node_config(storage_update_func, task_executor, key, boot_nodes, false); let multiaddr = config.network.listen_addresses[0].clone(); - let NewFull { task_manager, client, network, rpc_handlers, overseer_handler, .. } = - new_full(config, IsCollator::Yes(collator_id)).expect("could not create Polkadot test service"); + let NewFull { + task_manager, + client, + network, + rpc_handlers, + overseer_handler, + .. + } = new_full(config, IsCollator::Yes(collator_pair)) + .expect("could not create Polkadot test service"); let overseer_handler = overseer_handler.expect("test node must have an overseer handler"); let peer_id = network.local_peer_id().clone(); diff --git a/polkadot/parachain/test-parachains/adder/collator/src/main.rs b/polkadot/parachain/test-parachains/adder/collator/src/main.rs index 382bc89ecd..adf4ed8d98 100644 --- a/polkadot/parachain/test-parachains/adder/collator/src/main.rs +++ b/polkadot/parachain/test-parachains/adder/collator/src/main.rs @@ -60,7 +60,7 @@ fn main() -> Result<()> { let full_node = polkadot_service::build_full( config, - polkadot_service::IsCollator::Yes(collator.collator_id()), + polkadot_service::IsCollator::Yes(collator.collator_key()), None, None, None, diff --git a/polkadot/parachain/test-parachains/adder/collator/tests/integration.rs b/polkadot/parachain/test-parachains/adder/collator/tests/integration.rs index fbc2086752..0051302777 100644 --- a/polkadot/parachain/test-parachains/adder/collator/tests/integration.rs +++ b/polkadot/parachain/test-parachains/adder/collator/tests/integration.rs @@ -60,7 +60,7 @@ async fn collating_using_adder_collator(task_executor: sc_service::TaskExecutor) Charlie, || {}, vec![alice.addr.clone(), bob.addr.clone()], - collator.collator_id(), + collator.collator_key(), ); charlie.register_collator( diff --git a/polkadot/roadmap/implementers-guide/src/types/network.md b/polkadot/roadmap/implementers-guide/src/types/network.md index cee03f70e0..d7404ae446 100644 --- a/polkadot/roadmap/implementers-guide/src/types/network.md +++ b/polkadot/roadmap/implementers-guide/src/types/network.md @@ -102,10 +102,11 @@ enum StatementDistributionV1Message { ```rust enum CollatorProtocolV1Message { - /// Declare the intent to advertise collations under a collator ID. - Declare(CollatorId), - /// Advertise a collation to a validator. Can only be sent once the peer has declared - /// that they are a collator with given ID. + /// Declare the intent to advertise collations under a collator ID, attaching a + /// signature of the `PeerId` of the node using the given collator ID key. + Declare(CollatorId, CollatorSignature), + /// Advertise a collation to a validator. Can only be sent once the peer has + /// declared that they are a collator with given ID. AdvertiseCollation(Hash, ParaId), /// Request the advertised collation at that relay-parent. RequestCollation(RequestId, Hash, ParaId),