From 08c238186dcc23b1b67f6c662ea1bb4e367d57cc Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe Date: Thu, 27 Jul 2023 08:31:16 +0300 Subject: [PATCH 01/54] Approve multiple candidates with a single signature Signed-off-by: Alexandru Gheorghe --- .../src/approval_db/v2/migration_helpers.rs | 1 + .../approval-voting/src/approval_db/v2/mod.rs | 14 +- .../src/approval_db/v2/tests.rs | 1 + node/core/approval-voting/src/import.rs | 2 + node/core/approval-voting/src/lib.rs | 539 +++++++++++++----- .../approval-voting/src/persisted_entries.rs | 34 +- node/core/approval-voting/src/tests.rs | 105 +++- node/core/dispute-coordinator/src/import.rs | 21 +- .../dispute-coordinator/src/initialized.rs | 4 +- node/core/dispute-coordinator/src/lib.rs | 2 +- node/core/dispute-coordinator/src/tests.rs | 11 +- .../src/disputes/prioritized_selection/mod.rs | 2 +- node/network/approval-distribution/src/lib.rs | 409 +++++++------ .../approval-distribution/src/tests.rs | 77 ++- node/network/protocol/src/lib.rs | 7 +- node/primitives/src/approval.rs | 62 +- node/primitives/src/disputes/message.rs | 2 +- node/primitives/src/disputes/mod.rs | 5 +- node/subsystem-types/src/messages.rs | 12 +- primitives/src/lib.rs | 5 +- primitives/src/v5/mod.rs | 44 +- runtime/parachains/src/disputes.rs | 13 +- .../functional/0001-parachains-pvf.zndsl | 2 + 23 files changed, 974 insertions(+), 400 deletions(-) diff --git a/node/core/approval-voting/src/approval_db/v2/migration_helpers.rs b/node/core/approval-voting/src/approval_db/v2/migration_helpers.rs index 33aecf5e1b0d..841c9eb79849 100644 --- a/node/core/approval-voting/src/approval_db/v2/migration_helpers.rs +++ b/node/core/approval-voting/src/approval_db/v2/migration_helpers.rs @@ -51,6 +51,7 @@ fn make_block_entry( approved_bitfield: make_bitvec(candidates.len()), candidates, children: Vec::new(), + candidates_pending_signature: Default::default(), } } diff --git a/node/core/approval-voting/src/approval_db/v2/mod.rs b/node/core/approval-voting/src/approval_db/v2/mod.rs index 714ca8985fc2..7791c3631b5b 100644 --- a/node/core/approval-voting/src/approval_db/v2/mod.rs +++ b/node/core/approval-voting/src/approval_db/v2/mod.rs @@ -21,8 +21,8 @@ use polkadot_node_primitives::approval::{v1::DelayTranche, v2::AssignmentCertV2} use polkadot_node_subsystem::{SubsystemError, SubsystemResult}; use polkadot_node_subsystem_util::database::{DBTransaction, Database}; use polkadot_primitives::{ - BlockNumber, CandidateHash, CandidateReceipt, CoreIndex, GroupIndex, Hash, SessionIndex, - ValidatorIndex, ValidatorSignature, + BlockNumber, CandidateHash, CandidateIndex, CandidateReceipt, CoreIndex, GroupIndex, Hash, + SessionIndex, ValidatorIndex, ValidatorSignature, }; use sp_consensus_slots::Slot; @@ -238,6 +238,16 @@ pub struct BlockEntry { // block. The block can be considered approved if the bitfield has all bits set to `true`. pub approved_bitfield: Bitfield, pub children: Vec, + // A list of candidates that has been approved, but we didn't not sign and + // advertise the vote yet. + pub candidates_pending_signature: BTreeMap, +} + +#[derive(Encode, Decode, Debug, Clone, PartialEq)] + +/// Context needed for creating an approval signature for a given candidate. +pub struct CandidateSigningContext { + pub candidate_hash: CandidateHash, } impl From for Tick { diff --git a/node/core/approval-voting/src/approval_db/v2/tests.rs b/node/core/approval-voting/src/approval_db/v2/tests.rs index dfcf56ccccc2..01225e9a6ca0 100644 --- a/node/core/approval-voting/src/approval_db/v2/tests.rs +++ b/node/core/approval-voting/src/approval_db/v2/tests.rs @@ -56,6 +56,7 @@ fn make_block_entry( approved_bitfield: make_bitvec(candidates.len()), candidates, children: Vec::new(), + candidates_pending_signature: Default::default(), } } diff --git a/node/core/approval-voting/src/import.rs b/node/core/approval-voting/src/import.rs index cf2a90eac34b..d3cd864ea317 100644 --- a/node/core/approval-voting/src/import.rs +++ b/node/core/approval-voting/src/import.rs @@ -511,6 +511,7 @@ pub(crate) async fn handle_new_head( .collect(), approved_bitfield, children: Vec::new(), + candidates_pending_signature: Default::default(), }; gum::trace!( @@ -1264,6 +1265,7 @@ pub(crate) mod tests { candidates: Vec::new(), approved_bitfield: Default::default(), children: Vec::new(), + candidates_pending_signature: Default::default(), } .into(), ); diff --git a/node/core/approval-voting/src/lib.rs b/node/core/approval-voting/src/lib.rs index 4bbc86462e21..98a0b2a260a0 100644 --- a/node/core/approval-voting/src/lib.rs +++ b/node/core/approval-voting/src/lib.rs @@ -21,14 +21,16 @@ //! of others. It uses this information to determine when candidates and blocks have //! been sufficiently approved to finalize. +use futures_timer::Delay; +use itertools::Itertools; use jaeger::{hash_to_trace_identifier, PerLeafSpan}; use polkadot_node_jaeger as jaeger; use polkadot_node_primitives::{ approval::{ - v1::{BlockApprovalMeta, DelayTranche, IndirectSignedApprovalVote}, + v1::{BlockApprovalMeta, DelayTranche}, v2::{ AssignmentCertKindV2, BitfieldError, CandidateBitfield, CoreBitfield, - IndirectAssignmentCertV2, + IndirectAssignmentCertV2, IndirectSignedApprovalVoteV2, }, }, ValidationResult, DISPUTE_WINDOW, @@ -53,9 +55,10 @@ use polkadot_node_subsystem_util::{ TimeoutExt, }; use polkadot_primitives::{ - ApprovalVote, BlockNumber, CandidateHash, CandidateIndex, CandidateReceipt, DisputeStatement, - GroupIndex, Hash, PvfExecTimeoutKind, SessionIndex, SessionInfo, ValidDisputeStatementKind, - ValidatorId, ValidatorIndex, ValidatorPair, ValidatorSignature, + vstaging::ApprovalVoteMultipleCandidates, BlockNumber, CandidateHash, CandidateIndex, + CandidateReceipt, DisputeStatement, GroupIndex, Hash, PvfExecTimeoutKind, SessionIndex, + SessionInfo, ValidDisputeStatementKind, ValidatorId, ValidatorIndex, ValidatorPair, + ValidatorSignature, MAX_APPROVAL_COALESCE_COUNT, MAX_APPROVAL_COALESCE_WAIT_MILLIS, }; use sc_keystore::LocalKeystore; use sp_application_crypto::Pair; @@ -66,7 +69,7 @@ use futures::{ channel::oneshot, future::{BoxFuture, RemoteHandle}, prelude::*, - stream::FuturesUnordered, + stream::{FuturesOrdered, FuturesUnordered}, }; use std::{ @@ -97,6 +100,7 @@ use crate::{ approval_db::v2::{Config as DatabaseConfig, DbBackend}, backend::{Backend, OverlayedBackend}, criteria::InvalidAssignmentReason, + persisted_entries::CandidateSigningContext, }; #[cfg(test)] @@ -636,6 +640,13 @@ impl CurrentlyCheckingSet { } } +// A list of delayed futures that gets triggered when the waiting time has expired and it is +// time to sign the candidate. +#[derive(Default)] +struct SignApprovalsTimers { + pub timers: FuturesOrdered>, +} + async fn get_session_info<'a, Sender>( runtime_info: &'a mut RuntimeInfo, sender: &mut Sender, @@ -779,6 +790,7 @@ where let mut wakeups = Wakeups::default(); let mut currently_checking_set = CurrentlyCheckingSet::default(); let mut approvals_cache = lru::LruCache::new(APPROVAL_CACHE_SIZE); + let mut sign_approvals_timers = SignApprovalsTimers::default(); let mut last_finalized_height: Option = { let (tx, rx) = oneshot::channel(); @@ -856,6 +868,30 @@ where } actions + }, + (block_hash, validator_index, candidate_hash) = sign_approvals_timers.timers.select_next_some() => { + gum::debug!( + target: LOG_TARGET, + ?candidate_hash, + "Sign approval for candidate", + ); + if let Err(err) = maybe_create_signature( + &mut overlayed_db, + &mut session_info_provider, + &state, &mut ctx, + block_hash, + validator_index, + candidate_hash, + &subsystem.metrics, + ).await { + gum::error!( + target: LOG_TARGET, + ?err, + ?candidate_hash, + "Failed to create signature", + ); + } + vec![] } }; @@ -868,6 +904,7 @@ where &mut wakeups, &mut currently_checking_set, &mut approvals_cache, + &mut sign_approvals_timers, &mut subsystem.mode, actions, ) @@ -915,6 +952,7 @@ async fn handle_actions( wakeups: &mut Wakeups, currently_checking_set: &mut CurrentlyCheckingSet, approvals_cache: &mut lru::LruCache, + sign_approvals_timers: &mut SignApprovalsTimers, mode: &mut Mode, actions: Vec, ) -> SubsystemResult { @@ -944,6 +982,7 @@ async fn handle_actions( session_info_provider, metrics, candidate_hash, + sign_approvals_timers, approval_request, ) .await? @@ -1066,7 +1105,7 @@ fn cores_to_candidate_indices( .iter() .position(|(core_index, _)| core_index.0 == claimed_core_index as u32) { - candidate_indices.push(candidate_index as CandidateIndex); + candidate_indices.push(candidate_index as _); } } @@ -1233,9 +1272,9 @@ fn distribution_messages_for_activation( } messages.push(ApprovalDistributionMessage::DistributeApproval( - IndirectSignedApprovalVote { + IndirectSignedApprovalVoteV2 { block_hash, - candidate_index: i as _, + candidate_indices: (i as CandidateIndex).into(), validator: assignment.validator_index(), signature: approval_sig, }, @@ -1442,7 +1481,7 @@ async fn get_approval_signatures_for_candidate( ctx: &mut Context, db: &OverlayedBackend<'_, impl Backend>, candidate_hash: CandidateHash, - tx: oneshot::Sender>, + tx: oneshot::Sender, ValidatorSignature)>>, ) -> SubsystemResult<()> { let send_votes = |votes| { if let Err(_) = tx.send(votes) { @@ -1468,6 +1507,11 @@ async fn get_approval_signatures_for_candidate( let relay_hashes = entry.block_assignments.keys(); let mut candidate_indices = HashSet::new(); + let mut candidate_indices_to_candidate_hashes: HashMap< + Hash, + HashMap, + > = HashMap::new(); + // Retrieve `CoreIndices`/`CandidateIndices` as required by approval-distribution: for hash in relay_hashes { let entry = match db.load_block_entry(hash)? { @@ -1485,8 +1529,11 @@ async fn get_approval_signatures_for_candidate( for (candidate_index, (_core_index, c_hash)) in entry.candidates().iter().enumerate() { if c_hash == &candidate_hash { candidate_indices.insert((*hash, candidate_index as u32)); - break } + candidate_indices_to_candidate_hashes + .entry(*hash) + .or_default() + .insert(candidate_index as _, *c_hash); } } @@ -1511,7 +1558,24 @@ async fn get_approval_signatures_for_candidate( target: LOG_TARGET, "Request for approval signatures got cancelled by `approval-distribution`." ), - Some(Ok(votes)) => send_votes(votes), + Some(Ok(votes)) => { + let votes = votes + .into_iter() + .map(|(validator_index, (hash, signed_candidates_indices, signature))| { + let candidates_hashes = + candidate_indices_to_candidate_hashes.get(&hash).expect("Can't fail because it is the same hash we sent to approval-distribution; qed"); + let signed_candidates_hashes: Vec = + signed_candidates_indices + .into_iter() + .map(|candidate_index| { + *(candidates_hashes.get(&candidate_index).expect("Can't fail because we already checked the signature was valid, so we should be able to find the hash; qed")) + }) + .collect(); + (validator_index, (signed_candidates_hashes, signature)) + }) + .collect(); + send_votes(votes) + }, } }; @@ -2108,7 +2172,7 @@ async fn check_and_import_approval( db: &mut OverlayedBackend<'_, impl Backend>, session_info_provider: &mut RuntimeInfo, metrics: &Metrics, - approval: IndirectSignedApprovalVote, + approval: IndirectSignedApprovalVoteV2, with_response: impl FnOnce(ApprovalCheckResult) -> T, ) -> SubsystemResult<(Vec, T)> where @@ -2120,13 +2184,12 @@ where return Ok((Vec::new(), t)) }}; } - let mut span = state .spans .get(&approval.block_hash) .map(|span| span.child("check-and-import-approval")) .unwrap_or_else(|| jaeger::Span::new(approval.block_hash, "check-and-import-approval")) - .with_uint_tag("candidate-index", approval.candidate_index as u64) + .with_string_fmt_debug_tag("candidate-index", approval.candidate_indices.clone()) .with_relay_parent(approval.block_hash) .with_stage(jaeger::Stage::ApprovalChecking); @@ -2139,105 +2202,157 @@ where }, }; - let session_info = match get_session_info( - session_info_provider, - sender, - approval.block_hash, - block_entry.session(), - ) - .await - { - Some(s) => s, - None => { - respond_early!(ApprovalCheckResult::Bad(ApprovalCheckError::UnknownSessionIndex( - block_entry.session() - ),)) - }, - }; + let approved_candidates_info: Result, ApprovalCheckError> = + approval + .candidate_indices + .iter_ones() + .map(|candidate_index| { + block_entry + .candidate(candidate_index) + .ok_or(ApprovalCheckError::InvalidCandidateIndex(candidate_index as _)) + .map(|candidate| (candidate_index as _, candidate.1)) + }) + .collect(); - let approved_candidate_hash = match block_entry.candidate(approval.candidate_index as usize) { - Some((_, h)) => *h, - None => respond_early!(ApprovalCheckResult::Bad( - ApprovalCheckError::InvalidCandidateIndex(approval.candidate_index), - )), + let approved_candidates_info = match approved_candidates_info { + Ok(approved_candidates_info) => approved_candidates_info, + Err(err) => { + respond_early!(ApprovalCheckResult::Bad(err)) + }, }; - span.add_string_tag("candidate-hash", format!("{:?}", approved_candidate_hash)); + span.add_string_tag("candidate-hashes", format!("{:?}", approved_candidates_info)); span.add_string_tag( - "traceID", - format!("{:?}", hash_to_trace_identifier(approved_candidate_hash.0)), + "traceIDs", + format!( + "{:?}", + approved_candidates_info + .iter() + .map(|(_, approved_candidate_hash)| hash_to_trace_identifier( + approved_candidate_hash.0 + )) + .collect_vec() + ), ); - let pubkey = match session_info.validators.get(approval.validator) { - Some(k) => k, - None => respond_early!(ApprovalCheckResult::Bad( - ApprovalCheckError::InvalidValidatorIndex(approval.validator), - )), - }; + { + let session_info = match get_session_info( + session_info_provider, + sender, + approval.block_hash, + block_entry.session(), + ) + .await + { + Some(s) => s, + None => { + respond_early!(ApprovalCheckResult::Bad(ApprovalCheckError::UnknownSessionIndex( + block_entry.session() + ),)) + }, + }; - // Signature check: - match DisputeStatement::Valid(ValidDisputeStatementKind::ApprovalChecking).check_signature( - &pubkey, - approved_candidate_hash, - block_entry.session(), - &approval.signature, - ) { - Err(_) => respond_early!(ApprovalCheckResult::Bad(ApprovalCheckError::InvalidSignature( - approval.validator - ),)), - Ok(()) => {}, - }; + let pubkey = match session_info.validators.get(approval.validator) { + Some(k) => k, + None => respond_early!(ApprovalCheckResult::Bad( + ApprovalCheckError::InvalidValidatorIndex(approval.validator), + )), + }; - let candidate_entry = match db.load_candidate_entry(&approved_candidate_hash)? { - Some(c) => c, - None => { - respond_early!(ApprovalCheckResult::Bad(ApprovalCheckError::InvalidCandidate( - approval.candidate_index, - approved_candidate_hash - ),)) - }, - }; + gum::debug!( + target: LOG_TARGET, + "Received approval for num_candidates {:}", + approval.candidate_indices.count_ones() + ); - // Don't accept approvals until assignment. - match candidate_entry.approval_entry(&approval.block_hash) { - None => { - respond_early!(ApprovalCheckResult::Bad(ApprovalCheckError::Internal( - approval.block_hash, - approved_candidate_hash - ),)) - }, - Some(e) if !e.is_assigned(approval.validator) => { - respond_early!(ApprovalCheckResult::Bad(ApprovalCheckError::NoAssignment( - approval.validator - ),)) - }, - _ => {}, + let candidate_hashes: Vec = + approved_candidates_info.iter().map(|candidate| candidate.1).collect(); + // Signature check: + match DisputeStatement::Valid(ValidDisputeStatementKind::ApprovalCheckingV2( + candidate_hashes.clone(), + )) + .check_signature( + &pubkey, + *candidate_hashes.first().expect("Checked above this is not empty; qed"), + block_entry.session(), + &approval.signature, + ) { + Err(_) => { + gum::error!( + target: LOG_TARGET, + "Error while checking signature {:}", + approval.candidate_indices.count_ones() + ); + respond_early!(ApprovalCheckResult::Bad(ApprovalCheckError::InvalidSignature( + approval.validator + ),)) + }, + Ok(()) => {}, + }; } - // importing the approval can be heavy as it may trigger acceptance for a series of blocks. - let t = with_response(ApprovalCheckResult::Accepted); + let mut actions = Vec::new(); + for (approval_candidate_index, approved_candidate_hash) in approved_candidates_info { + let block_entry = match db.load_block_entry(&approval.block_hash)? { + Some(b) => b, + None => { + respond_early!(ApprovalCheckResult::Bad(ApprovalCheckError::UnknownBlock( + approval.block_hash + ),)) + }, + }; - gum::trace!( - target: LOG_TARGET, - validator_index = approval.validator.0, - validator = ?pubkey, - candidate_hash = ?approved_candidate_hash, - para_id = ?candidate_entry.candidate_receipt().descriptor.para_id, - "Importing approval vote", - ); + let candidate_entry = match db.load_candidate_entry(&approved_candidate_hash)? { + Some(c) => c, + None => { + respond_early!(ApprovalCheckResult::Bad(ApprovalCheckError::InvalidCandidate( + approval_candidate_index, + approved_candidate_hash + ),)) + }, + }; - let actions = advance_approval_state( - sender, - state, - db, - session_info_provider, - &metrics, - block_entry, - approved_candidate_hash, - candidate_entry, - ApprovalStateTransition::RemoteApproval(approval.validator), - ) - .await; + // Don't accept approvals until assignment. + match candidate_entry.approval_entry(&approval.block_hash) { + None => { + respond_early!(ApprovalCheckResult::Bad(ApprovalCheckError::Internal( + approval.block_hash, + approved_candidate_hash + ),)) + }, + Some(e) if !e.is_assigned(approval.validator) => { + respond_early!(ApprovalCheckResult::Bad(ApprovalCheckError::NoAssignment( + approval.validator + ),)) + }, + _ => {}, + } + + gum::debug!( + target: LOG_TARGET, + validator_index = approval.validator.0, + candidate_hash = ?approved_candidate_hash, + para_id = ?candidate_entry.candidate_receipt().descriptor.para_id, + "Importing approval vote", + ); + + let new_actions = advance_approval_state( + sender, + state, + db, + session_info_provider, + &metrics, + block_entry, + approved_candidate_hash, + candidate_entry, + ApprovalStateTransition::RemoteApproval(approval.validator), + ) + .await; + actions.extend(new_actions); + } + + // importing the approval can be heavy as it may trigger acceptance for a series of blocks. + let t = with_response(ApprovalCheckResult::Accepted); Ok((actions, t)) } @@ -2245,7 +2360,7 @@ where #[derive(Debug)] enum ApprovalStateTransition { RemoteApproval(ValidatorIndex), - LocalApproval(ValidatorIndex, ValidatorSignature), + LocalApproval(ValidatorIndex), WakeupProcessed, } @@ -2253,7 +2368,7 @@ impl ApprovalStateTransition { fn validator_index(&self) -> Option { match *self { ApprovalStateTransition::RemoteApproval(v) | - ApprovalStateTransition::LocalApproval(v, _) => Some(v), + ApprovalStateTransition::LocalApproval(v) => Some(v), ApprovalStateTransition::WakeupProcessed => None, } } @@ -2261,7 +2376,7 @@ impl ApprovalStateTransition { fn is_local_approval(&self) -> bool { match *self { ApprovalStateTransition::RemoteApproval(_) => false, - ApprovalStateTransition::LocalApproval(_, _) => true, + ApprovalStateTransition::LocalApproval(_) => true, ApprovalStateTransition::WakeupProcessed => false, } } @@ -2353,6 +2468,10 @@ where actions.push(Action::NoteApprovedInChainSelection(block_hash)); } + db.write_block_entry(block_entry.into()); + } else if transition.is_local_approval() { + // Local approvals always update the block_entry, so we need to flush it to + // the database. db.write_block_entry(block_entry.into()); } @@ -2381,10 +2500,6 @@ where approval_entry.mark_approved(); } - if let ApprovalStateTransition::LocalApproval(_, ref sig) = transition { - approval_entry.import_approval_sig(sig.clone()); - } - actions.extend(schedule_wakeup_action( &approval_entry, block_hash, @@ -2826,6 +2941,7 @@ async fn issue_approval( session_info_provider: &mut RuntimeInfo, metrics: &Metrics, candidate_hash: CandidateHash, + sign_approvals_timers: &mut SignApprovalsTimers, ApprovalVoteRequest { validator_index, block_hash }: ApprovalVoteRequest, ) -> SubsystemResult> { let mut issue_approval_span = state @@ -2839,7 +2955,7 @@ async fn issue_approval( .with_validator_index(validator_index) .with_stage(jaeger::Stage::ApprovalChecking); - let block_entry = match db.load_block_entry(&block_hash)? { + let mut block_entry = match db.load_block_entry(&block_hash)? { Some(b) => b, None => { // not a cause for alarm - just lost a race with pruning, most likely. @@ -2865,21 +2981,6 @@ async fn issue_approval( }; issue_approval_span.add_int_tag("candidate_index", candidate_index as i64); - let session_info = match get_session_info( - session_info_provider, - ctx.sender(), - block_entry.parent_hash(), - block_entry.session(), - ) - .await - { - Some(s) => s, - None => { - metrics.on_approval_error(); - return Ok(Vec::new()) - }, - }; - let candidate_hash = match block_entry.candidate(candidate_index as usize) { Some((_, h)) => *h, None => { @@ -2910,10 +3011,115 @@ async fn issue_approval( }, }; + block_entry + .candidates_pending_signature + .insert(candidate_index as _, CandidateSigningContext { candidate_hash }); + + let should_create_signature = + block_entry.candidates_pending_signature.len() >= MAX_APPROVAL_COALESCE_COUNT as usize; + + let delay = Delay::new(Duration::from_millis(MAX_APPROVAL_COALESCE_WAIT_MILLIS)); + + sign_approvals_timers.timers.push_back(Box::pin(async move { + delay.await; + (block_hash, validator_index, candidate_hash) + })); + + gum::info!( + target: LOG_TARGET, + ?candidate_hash, + ?block_hash, + validator_index = validator_index.0, + "Issuing approval vote", + ); + + let actions = advance_approval_state( + ctx.sender(), + state, + db, + session_info_provider, + metrics, + block_entry, + candidate_hash, + candidate_entry, + ApprovalStateTransition::LocalApproval(validator_index as _), + ) + .await; + + if should_create_signature { + maybe_create_signature( + db, + session_info_provider, + state, + ctx, + block_hash, + validator_index, + candidate_hash, + metrics, + ) + .await?; + } + Ok(actions) +} + +// Create signature for the approved candidates pending signatures +#[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] +async fn maybe_create_signature( + db: &mut OverlayedBackend<'_, impl Backend>, + session_info_provider: &mut RuntimeInfo, + state: &State, + ctx: &mut Context, + block_hash: Hash, + validator_index: ValidatorIndex, + current_candidate_hash: CandidateHash, + metrics: &Metrics, +) -> SubsystemResult<()> { + let mut block_entry = match db.load_block_entry(&block_hash)? { + Some(b) => b, + None => { + gum::error!( + target: LOG_TARGET, + "Could not find block that needs signature {:}", block_hash + ); + return Ok(()) + }, + }; + + // If the candidate that woke us is not pending do nothing and let one of the wakeup of the + // pending candidates create the + // signature. + if !block_entry + .candidates_pending_signature + .values() + .map(|val| val.candidate_hash) + .contains(¤t_candidate_hash) + { + return Ok(()) + } + + let session_info = match get_session_info( + session_info_provider, + ctx.sender(), + block_entry.parent_hash(), + block_entry.session(), + ) + .await + { + Some(s) => s, + None => { + metrics.on_approval_error(); + gum::error!( + target: LOG_TARGET, + "Could not retrieve the session" + ); + return Ok(()) + }, + }; + let validator_pubkey = match session_info.validators.get(validator_index) { Some(p) => p, None => { - gum::warn!( + gum::error!( target: LOG_TARGET, "Validator index {} out of bounds in session {}", validator_index.0, @@ -2921,72 +3127,89 @@ async fn issue_approval( ); metrics.on_approval_error(); - return Ok(Vec::new()) + return Ok(()) }, }; - let session = block_entry.session(); - let sig = match sign_approval(&state.keystore, &validator_pubkey, candidate_hash, session) { + let candidate_hashes: Vec = block_entry + .candidates_pending_signature + .values() + .map(|unsigned_approval| unsigned_approval.candidate_hash) + .collect(); + + let signature = match sign_approval( + &state.keystore, + &validator_pubkey, + candidate_hashes, + block_entry.session(), + ) { Some(sig) => sig, None => { - gum::warn!( + gum::error!( target: LOG_TARGET, validator_index = ?validator_index, - session, + session = ?block_entry.session(), "Could not issue approval signature. Assignment key present but not validator key?", ); metrics.on_approval_error(); - return Ok(Vec::new()) + return Ok(()) }, }; - gum::trace!( - target: LOG_TARGET, - ?candidate_hash, - ?block_hash, - validator_index = validator_index.0, - "Issuing approval vote", - ); + let candidate_entries = block_entry + .candidates_pending_signature + .values() + .map(|unsigned_approval| db.load_candidate_entry(&unsigned_approval.candidate_hash)) + .collect::>>>()?; - let actions = advance_approval_state( - ctx.sender(), - state, - db, - session_info_provider, - metrics, - block_entry, - candidate_hash, - candidate_entry, - ApprovalStateTransition::LocalApproval(validator_index as _, sig.clone()), - ) - .await; + for candidate_entry in candidate_entries { + let mut candidate_entry = candidate_entry + .expect("Candidate was scheduled to be signed entry in db should exist; qed"); + let approval_entry = candidate_entry + .approval_entry_mut(&block_entry.block_hash()) + .expect("Candidate was scheduled to be signed entry in db should exist; qed"); + approval_entry.import_approval_sig(signature.clone()); + db.write_candidate_entry(candidate_entry); + } + + let candidate_indexes: Vec = + block_entry.candidates_pending_signature.keys().map(|val| *val).collect(); metrics.on_approval_produced(); - // dispatch to approval distribution. ctx.send_unbounded_message(ApprovalDistributionMessage::DistributeApproval( - IndirectSignedApprovalVote { - block_hash, - candidate_index: candidate_index as _, + IndirectSignedApprovalVoteV2 { + block_hash: block_entry.block_hash(), + candidate_indices: candidate_indexes + .try_into() + .expect("Fails only of array empty, it can't be, qed"), validator: validator_index, - signature: sig, + signature, }, )); - Ok(actions) + gum::debug!( + target: LOG_TARGET, + ?block_hash, + "Approval entry for num_candidates was sent {:}", + block_entry.candidates_pending_signature.len() + ); + block_entry.candidates_pending_signature.clear(); + db.write_block_entry(block_entry.into()); + Ok(()) } // Sign an approval vote. Fails if the key isn't present in the store. fn sign_approval( keystore: &LocalKeystore, public: &ValidatorId, - candidate_hash: CandidateHash, + candidate_hashes: Vec, session_index: SessionIndex, ) -> Option { let key = keystore.key_pair::(public).ok().flatten()?; - let payload = ApprovalVote(candidate_hash).signing_payload(session_index); + let payload = ApprovalVoteMultipleCandidates(candidate_hashes).signing_payload(session_index); Some(key.sign(&payload[..])) } diff --git a/node/core/approval-voting/src/persisted_entries.rs b/node/core/approval-voting/src/persisted_entries.rs index 3f5c2766154d..2b1cbaed027e 100644 --- a/node/core/approval-voting/src/persisted_entries.rs +++ b/node/core/approval-voting/src/persisted_entries.rs @@ -25,8 +25,8 @@ use polkadot_node_primitives::approval::{ v2::AssignmentCertV2, }; use polkadot_primitives::{ - BlockNumber, CandidateHash, CandidateReceipt, CoreIndex, GroupIndex, Hash, SessionIndex, - ValidatorIndex, ValidatorSignature, + BlockNumber, CandidateHash, CandidateIndex, CandidateReceipt, CoreIndex, GroupIndex, Hash, + SessionIndex, ValidatorIndex, ValidatorSignature, }; use sp_consensus_slots::Slot; @@ -358,6 +358,14 @@ pub struct BlockEntry { // block. The block can be considered approved if the bitfield has all bits set to `true`. pub approved_bitfield: Bitfield, pub children: Vec, + // A list of candidates that has been approved, but we didn't not sign and + // advertise the vote yet. + pub candidates_pending_signature: BTreeMap, +} + +#[derive(Debug, Clone, PartialEq)] +pub struct CandidateSigningContext { + pub candidate_hash: CandidateHash, } impl BlockEntry { @@ -446,6 +454,11 @@ impl From for BlockEntry { candidates: entry.candidates, approved_bitfield: entry.approved_bitfield, children: entry.children, + candidates_pending_signature: entry + .candidates_pending_signature + .into_iter() + .map(|(candidate_index, signing_context)| (candidate_index, signing_context.into())) + .collect(), } } } @@ -462,10 +475,27 @@ impl From for crate::approval_db::v2::BlockEntry { candidates: entry.candidates, approved_bitfield: entry.approved_bitfield, children: entry.children, + candidates_pending_signature: entry + .candidates_pending_signature + .into_iter() + .map(|(candidate_index, signing_context)| (candidate_index, signing_context.into())) + .collect(), } } } +impl From for CandidateSigningContext { + fn from(signing_context: crate::approval_db::v2::CandidateSigningContext) -> Self { + Self { candidate_hash: signing_context.candidate_hash } + } +} + +impl From for crate::approval_db::v2::CandidateSigningContext { + fn from(signing_context: CandidateSigningContext) -> Self { + Self { candidate_hash: signing_context.candidate_hash } + } +} + /// Migration helpers. impl From for CandidateEntry { fn from(value: crate::approval_db::v1::CandidateEntry) -> Self { diff --git a/node/core/approval-voting/src/tests.rs b/node/core/approval-voting/src/tests.rs index 8d9679915176..c37850d0466f 100644 --- a/node/core/approval-voting/src/tests.rs +++ b/node/core/approval-voting/src/tests.rs @@ -35,8 +35,8 @@ use polkadot_node_subsystem_test_helpers as test_helpers; use polkadot_node_subsystem_util::TimeoutExt; use polkadot_overseer::HeadSupportsParachains; use polkadot_primitives::{ - CandidateCommitments, CandidateEvent, CoreIndex, GroupIndex, Header, Id as ParaId, IndexedVec, - ValidationCode, ValidatorSignature, + ApprovalVote, CandidateCommitments, CandidateEvent, CoreIndex, GroupIndex, Header, + Id as ParaId, IndexedVec, ValidationCode, ValidatorSignature, }; use std::time::Duration; @@ -427,6 +427,15 @@ fn sign_approval( key.sign(&ApprovalVote(candidate_hash).signing_payload(session_index)).into() } +fn sign_approval_multiple_candidates( + key: Sr25519Keyring, + candidate_hashes: Vec, + session_index: SessionIndex, +) -> ValidatorSignature { + key.sign(&ApprovalVoteMultipleCandidates(candidate_hashes).signing_payload(session_index)) + .into() +} + type VirtualOverseer = test_helpers::TestSubsystemContextHandle; #[derive(Default)] @@ -616,7 +625,12 @@ async fn check_and_import_approval( overseer, FromOrchestra::Communication { msg: ApprovalVotingMessage::CheckAndImportApproval( - IndirectSignedApprovalVote { block_hash, candidate_index, validator, signature }, + IndirectSignedApprovalVoteV2 { + block_hash, + candidate_indices: candidate_index.into(), + validator, + signature, + }, tx, ), }, @@ -1915,6 +1929,91 @@ fn forkful_import_at_same_height_act_on_leaf() { }); } +#[test] +fn test_signing_a_single_candidate_is_backwards_compatible() { + let session_index = 1; + let block_hash = Hash::repeat_byte(0x01); + let candidate_descriptors = (1..10) + .into_iter() + .map(|val| make_candidate(ParaId::from(val as u32), &block_hash)) + .collect::>(); + + let candidate_hashes = candidate_descriptors + .iter() + .map(|candidate_descriptor| candidate_descriptor.hash()) + .collect_vec(); + + let first_descriptor = candidate_descriptors.first().expect("TODO"); + + let candidate_hash = first_descriptor.hash(); + + let sig_a = sign_approval(Sr25519Keyring::Alice, candidate_hash, session_index); + + let sig_b = sign_approval(Sr25519Keyring::Alice, candidate_hash, session_index); + + assert!(DisputeStatement::Valid(ValidDisputeStatementKind::ApprovalChecking) + .check_signature( + &Sr25519Keyring::Alice.public().into(), + candidate_hash, + session_index, + &sig_a, + ) + .is_ok()); + + assert!(DisputeStatement::Valid(ValidDisputeStatementKind::ApprovalChecking) + .check_signature( + &Sr25519Keyring::Alice.public().into(), + candidate_hash, + session_index, + &sig_b, + ) + .is_ok()); + + let sig_c = sign_approval_multiple_candidates( + Sr25519Keyring::Alice, + vec![candidate_hash], + session_index, + ); + + assert!(DisputeStatement::Valid(ValidDisputeStatementKind::ApprovalCheckingV2(vec![ + candidate_hash + ])) + .check_signature(&Sr25519Keyring::Alice.public().into(), candidate_hash, session_index, &sig_c,) + .is_ok()); + + assert!(DisputeStatement::Valid(ValidDisputeStatementKind::ApprovalChecking) + .check_signature( + &Sr25519Keyring::Alice.public().into(), + candidate_hash, + session_index, + &sig_c, + ) + .is_ok()); + + assert!(DisputeStatement::Valid(ValidDisputeStatementKind::ApprovalCheckingV2(vec![ + candidate_hash + ])) + .check_signature(&Sr25519Keyring::Alice.public().into(), candidate_hash, session_index, &sig_a,) + .is_ok()); + + let sig_all = sign_approval_multiple_candidates( + Sr25519Keyring::Alice, + candidate_hashes.clone(), + session_index, + ); + + assert!(DisputeStatement::Valid(ValidDisputeStatementKind::ApprovalCheckingV2( + candidate_hashes.clone() + )) + .check_signature( + &Sr25519Keyring::Alice.public().into(), + *candidate_hashes.first().expect("test"), + session_index, + &sig_all, + ) + .is_ok()); +} + #[test] fn import_checked_approval_updates_entries_and_schedules() { let config = HarnessConfig::default(); diff --git a/node/core/dispute-coordinator/src/import.rs b/node/core/dispute-coordinator/src/import.rs index 3c50e8c14186..dbe4f69ec66d 100644 --- a/node/core/dispute-coordinator/src/import.rs +++ b/node/core/dispute-coordinator/src/import.rs @@ -34,7 +34,7 @@ use polkadot_node_primitives::{ use polkadot_node_subsystem::overseer; use polkadot_node_subsystem_util::runtime::RuntimeInfo; use polkadot_primitives::{ - CandidateReceipt, DisputeStatement, Hash, IndexedVec, SessionIndex, SessionInfo, + CandidateHash, CandidateReceipt, DisputeStatement, Hash, IndexedVec, SessionIndex, SessionInfo, ValidDisputeStatementKind, ValidatorId, ValidatorIndex, ValidatorPair, ValidatorSignature, }; use sc_keystore::LocalKeystore; @@ -118,7 +118,9 @@ impl OwnVoteState { let our_valid_votes = controlled_indices .iter() .filter_map(|i| votes.valid.raw().get_key_value(i)) - .map(|(index, (kind, sig))| (*index, (DisputeStatement::Valid(*kind), sig.clone()))); + .map(|(index, (kind, sig))| { + (*index, (DisputeStatement::Valid(kind.clone()), sig.clone())) + }); let our_invalid_votes = controlled_indices .iter() .filter_map(|i| votes.invalid.get_key_value(i)) @@ -297,7 +299,7 @@ impl CandidateVoteState { DisputeStatement::Valid(valid_kind) => { let fresh = votes.valid.insert_vote( val_index, - *valid_kind, + valid_kind.clone(), statement.into_validator_signature(), ); if fresh { @@ -503,7 +505,7 @@ impl ImportResult { pub fn import_approval_votes( self, env: &CandidateEnvironment, - approval_votes: HashMap, + approval_votes: HashMap, ValidatorSignature)>, now: Timestamp, ) -> Self { let Self { @@ -517,14 +519,17 @@ impl ImportResult { let (mut votes, _) = new_state.into_old_state(); - for (index, sig) in approval_votes.into_iter() { + for (index, (candidate_hashes, sig)) in approval_votes.into_iter() { + gum::debug!( + target: LOG_TARGET, + "Imported votes for {:?}", candidate_hashes + ); debug_assert!( { let pub_key = &env.session_info().validators.get(index).expect("indices are validated by approval-voting subsystem; qed"); - let candidate_hash = votes.candidate_receipt.hash(); let session_index = env.session_index(); - DisputeStatement::Valid(ValidDisputeStatementKind::ApprovalChecking) - .check_signature(pub_key, candidate_hash, session_index, &sig) + candidate_hashes.contains(&votes.candidate_receipt.hash()) && DisputeStatement::Valid(ValidDisputeStatementKind::ApprovalCheckingV2(candidate_hashes.clone())) + .check_signature(pub_key, *candidate_hashes.first().expect("Valid votes have at least one candidate; qed"), session_index, &sig) .is_ok() }, "Signature check for imported approval votes failed! This is a serious bug. Session: {:?}, candidate hash: {:?}, validator index: {:?}", env.session_index(), votes.candidate_receipt.hash(), index diff --git a/node/core/dispute-coordinator/src/initialized.rs b/node/core/dispute-coordinator/src/initialized.rs index 7d64c91fb63f..f4d0f947a2fc 100644 --- a/node/core/dispute-coordinator/src/initialized.rs +++ b/node/core/dispute-coordinator/src/initialized.rs @@ -633,7 +633,7 @@ impl Initialized { }; debug_assert!( SignedDisputeStatement::new_checked( - DisputeStatement::Valid(valid_statement_kind), + DisputeStatement::Valid(valid_statement_kind.clone()), candidate_hash, session, validator_public.clone(), @@ -647,7 +647,7 @@ impl Initialized { ); let signed_dispute_statement = SignedDisputeStatement::new_unchecked_from_trusted_source( - DisputeStatement::Valid(valid_statement_kind), + DisputeStatement::Valid(valid_statement_kind.clone()), candidate_hash, session, validator_public, diff --git a/node/core/dispute-coordinator/src/lib.rs b/node/core/dispute-coordinator/src/lib.rs index 02bb6ef9ecda..c6c35b789fb6 100644 --- a/node/core/dispute-coordinator/src/lib.rs +++ b/node/core/dispute-coordinator/src/lib.rs @@ -574,7 +574,7 @@ pub fn make_dispute_message( .next() .ok_or(DisputeMessageCreationError::NoOppositeVote)?; let other_vote = SignedDisputeStatement::new_checked( - DisputeStatement::Valid(*statement_kind), + DisputeStatement::Valid(statement_kind.clone()), *our_vote.candidate_hash(), our_vote.session_index(), validators diff --git a/node/core/dispute-coordinator/src/tests.rs b/node/core/dispute-coordinator/src/tests.rs index f2590aea1511..064175fd59a0 100644 --- a/node/core/dispute-coordinator/src/tests.rs +++ b/node/core/dispute-coordinator/src/tests.rs @@ -651,7 +651,7 @@ fn make_candidate_included_event(candidate_receipt: CandidateReceipt) -> Candida pub async fn handle_approval_vote_request( ctx_handle: &mut VirtualOverseer, expected_hash: &CandidateHash, - votes_to_send: HashMap, + votes_to_send: HashMap, ValidatorSignature)>, ) { assert_matches!( ctx_handle.recv().await, @@ -857,9 +857,12 @@ fn approval_vote_import_works() { .await; gum::trace!("After sending `ImportStatements`"); - let approval_votes = [(ValidatorIndex(4), approval_vote.into_validator_signature())] - .into_iter() - .collect(); + let approval_votes = [( + ValidatorIndex(4), + (vec![candidate_receipt1.hash()], approval_vote.into_validator_signature()), + )] + .into_iter() + .collect(); handle_approval_vote_request(&mut virtual_overseer, &candidate_hash1, approval_votes) .await; diff --git a/node/core/provisioner/src/disputes/prioritized_selection/mod.rs b/node/core/provisioner/src/disputes/prioritized_selection/mod.rs index 5c8aaad422f2..24a61190fe7e 100644 --- a/node/core/provisioner/src/disputes/prioritized_selection/mod.rs +++ b/node/core/provisioner/src/disputes/prioritized_selection/mod.rs @@ -217,7 +217,7 @@ where votes.valid.retain(|validator_idx, (statement_kind, _)| { is_vote_worth_to_keep( validator_idx, - DisputeStatement::Valid(*statement_kind), + DisputeStatement::Valid(statement_kind.clone()), &onchain_state, ) }); diff --git a/node/network/approval-distribution/src/lib.rs b/node/network/approval-distribution/src/lib.rs index 5a5e1339d5a7..587ec2c97e4c 100644 --- a/node/network/approval-distribution/src/lib.rs +++ b/node/network/approval-distribution/src/lib.rs @@ -34,7 +34,7 @@ use polkadot_node_network_protocol::{ }; use polkadot_node_primitives::approval::{ v1::{BlockApprovalMeta, IndirectSignedApprovalVote}, - v2::{AsBitIndex, CandidateBitfield, IndirectAssignmentCertV2}, + v2::{AsBitIndex, CandidateBitfield, IndirectAssignmentCertV2, IndirectSignedApprovalVoteV2}, }; use polkadot_node_subsystem::{ messages::{ @@ -109,7 +109,7 @@ struct ApprovalEntry { // The candidates claimed by the certificate. A mapping between bit index and candidate index. candidates: CandidateBitfield, // The approval signatures for each `CandidateIndex` claimed by the assignment certificate. - approvals: HashMap, + approvals: HashMap, // The validator index of the assignment signer. validator_index: ValidatorIndex, // Information required for gossiping to other peers using the grid topology. @@ -177,7 +177,8 @@ impl ApprovalEntry { // Records a new approval. Returns false if the claimed candidate is not found or we already have received the approval. pub fn note_approval( &mut self, - approval: IndirectSignedApprovalVote, + approval: IndirectSignedApprovalVoteV2, + candidate_index: CandidateIndex, ) -> Result<(), ApprovalEntryError> { // First do some sanity checks: // - check validator index matches @@ -187,19 +188,18 @@ impl ApprovalEntry { return Err(ApprovalEntryError::InvalidValidatorIndex) } - if self.candidates.len() <= approval.candidate_index as usize { + if self.candidates.len() <= candidate_index as usize { return Err(ApprovalEntryError::CandidateIndexOutOfBounds) } - - if !self.candidates.bit_at(approval.candidate_index.as_bit_index()) { + if !self.candidates.bit_at((candidate_index).as_bit_index()) { return Err(ApprovalEntryError::InvalidCandidateIndex) } - if self.approvals.contains_key(&approval.candidate_index) { + if self.approvals.contains_key(&candidate_index) { return Err(ApprovalEntryError::DuplicateApproval) } - self.approvals.insert(approval.candidate_index, approval); + self.approvals.insert(candidate_index, approval.clone()); Ok(()) } @@ -209,7 +209,7 @@ impl ApprovalEntry { } // Get all approvals for all candidates claimed by the assignment. - pub fn get_approvals(&self) -> Vec { + pub fn get_approvals(&self) -> Vec { self.approvals.values().cloned().collect::>() } @@ -217,7 +217,7 @@ impl ApprovalEntry { pub fn get_approval( &self, candidate_index: CandidateIndex, - ) -> Option { + ) -> Option { self.approvals.get(&candidate_index).cloned() } @@ -552,7 +552,7 @@ impl MessageSource { enum PendingMessage { Assignment(IndirectAssignmentCertV2, CandidateBitfield), - Approval(IndirectSignedApprovalVote), + Approval(IndirectSignedApprovalVoteV2), } #[overseer::contextbounds(ApprovalDistribution, prefix = self::overseer)] @@ -822,6 +822,48 @@ impl State { } } + async fn process_incoming_approvals( + &mut self, + ctx: &mut Context, + metrics: &Metrics, + peer_id: PeerId, + approvals: Vec, + ) { + gum::trace!( + target: LOG_TARGET, + peer_id = %peer_id, + num = approvals.len(), + "Processing approvals from a peer", + ); + for approval_vote in approvals.into_iter() { + if let Some(pending) = self.pending_known.get_mut(&approval_vote.block_hash) { + let block_hash = approval_vote.block_hash; + let validator_index = approval_vote.validator; + + gum::trace!( + target: LOG_TARGET, + %peer_id, + ?block_hash, + ?validator_index, + "Pending assignment candidates {:?}", + approval_vote.candidate_indices, + ); + + pending.push((peer_id, PendingMessage::Approval(approval_vote))); + + continue + } + + self.import_and_circulate_approval( + ctx, + metrics, + MessageSource::Peer(peer_id), + approval_vote, + ) + .await; + } + } + async fn process_incoming_peer_message( &mut self, ctx: &mut Context, @@ -869,42 +911,17 @@ impl State { }, Versioned::VStaging(protocol_vstaging::ApprovalDistributionMessage::Approvals( approvals, - )) | + )) => { + self.process_incoming_approvals(ctx, metrics, peer_id, approvals).await; + }, Versioned::V1(protocol_v1::ApprovalDistributionMessage::Approvals(approvals)) => { - gum::trace!( - target: LOG_TARGET, - peer_id = %peer_id, - num = approvals.len(), - "Processing approvals from a peer", - ); - for approval_vote in approvals.into_iter() { - if let Some(pending) = self.pending_known.get_mut(&approval_vote.block_hash) { - let block_hash = approval_vote.block_hash; - let candidate_index = approval_vote.candidate_index; - let validator_index = approval_vote.validator; - - gum::trace!( - target: LOG_TARGET, - %peer_id, - ?block_hash, - ?candidate_index, - ?validator_index, - "Pending assignment", - ); - - pending.push((peer_id, PendingMessage::Approval(approval_vote))); - - continue - } - - self.import_and_circulate_approval( - ctx, - metrics, - MessageSource::Peer(peer_id), - approval_vote, - ) - .await; - } + self.process_incoming_approvals( + ctx, + metrics, + peer_id, + approvals.into_iter().map(|approval| approval.into()).collect::>(), + ) + .await; }, } } @@ -1287,7 +1304,7 @@ impl State { ctx: &mut Context, metrics: &Metrics, source: MessageSource, - vote: IndirectSignedApprovalVote, + vote: IndirectSignedApprovalVoteV2, ) { let _span = self .spans @@ -1306,10 +1323,13 @@ impl State { let block_hash = vote.block_hash; let validator_index = vote.validator; - let candidate_index = vote.candidate_index; - + let candidate_indices = &vote.candidate_indices; let entry = match self.blocks.get_mut(&block_hash) { - Some(entry) if entry.contains_approval_entry(candidate_index, validator_index) => entry, + Some(entry) + if vote.candidate_indices.iter_ones().fold(true, |result, candidate_index| { + entry.contains_approval_entry(candidate_index as _, validator_index) && result + }) => + entry, _ => { if let Some(peer_id) = source.peer_id() { if !self.recent_outdated_blocks.is_recent_outdated(&block_hash) { @@ -1321,60 +1341,74 @@ impl State { }; // compute metadata on the assignment. - let message_subject = MessageSubject(block_hash, candidate_index.into(), validator_index); + let message_subjects = candidate_indices + .iter_ones() + .map(|candidate_index| { + MessageSubject( + block_hash, + (candidate_index as CandidateIndex).into(), + validator_index, + ) + }) + .collect_vec(); let message_kind = MessageKind::Approval; if let Some(peer_id) = source.peer_id() { - if !entry.knowledge.contains(&message_subject, MessageKind::Assignment) { - gum::debug!( - target: LOG_TARGET, - ?peer_id, - ?message_subject, - "Unknown approval assignment", - ); - modify_reputation(ctx.sender(), peer_id, COST_UNEXPECTED_MESSAGE).await; - return - } - - // check if our knowledge of the peer already contains this approval - match entry.known_by.entry(peer_id) { - hash_map::Entry::Occupied(mut knowledge) => { - let peer_knowledge = knowledge.get_mut(); - if peer_knowledge.contains(&message_subject, message_kind) { - if !peer_knowledge.received.insert(message_subject.clone(), message_kind) { - gum::debug!( - target: LOG_TARGET, - ?peer_id, - ?message_subject, - "Duplicate approval", - ); - - modify_reputation(ctx.sender(), peer_id, COST_DUPLICATE_MESSAGE).await; - } - return - } - }, - hash_map::Entry::Vacant(_) => { + for message_subject in &message_subjects { + if !entry.knowledge.contains(&message_subject, MessageKind::Assignment) { gum::debug!( target: LOG_TARGET, ?peer_id, ?message_subject, - "Approval from a peer is out of view", + "Unknown approval assignment", ); modify_reputation(ctx.sender(), peer_id, COST_UNEXPECTED_MESSAGE).await; - }, - } + return + } - // if the approval is known to be valid, reward the peer - if entry.knowledge.contains(&message_subject, message_kind) { - gum::trace!(target: LOG_TARGET, ?peer_id, ?message_subject, "Known approval"); - modify_reputation(ctx.sender(), peer_id, BENEFIT_VALID_MESSAGE).await; - if let Some(peer_knowledge) = entry.known_by.get_mut(&peer_id) { - peer_knowledge.received.insert(message_subject.clone(), message_kind); + // check if our knowledge of the peer already contains this approval + match entry.known_by.entry(peer_id) { + hash_map::Entry::Occupied(mut knowledge) => { + let peer_knowledge = knowledge.get_mut(); + if peer_knowledge.contains(&message_subject, message_kind) { + if !peer_knowledge + .received + .insert(message_subject.clone(), message_kind) + { + gum::debug!( + target: LOG_TARGET, + ?peer_id, + ?message_subject, + "Duplicate approval", + ); + + modify_reputation(ctx.sender(), peer_id, COST_DUPLICATE_MESSAGE) + .await; + } + return + } + }, + hash_map::Entry::Vacant(_) => { + gum::debug!( + target: LOG_TARGET, + ?peer_id, + ?message_subject, + "Approval from a peer is out of view", + ); + modify_reputation(ctx.sender(), peer_id, COST_UNEXPECTED_MESSAGE).await; + }, } - return - } + // if the approval is known to be valid, reward the peer + if entry.knowledge.contains(&message_subject, message_kind) { + gum::trace!(target: LOG_TARGET, ?peer_id, ?message_subject, "Known approval"); + modify_reputation(ctx.sender(), peer_id, BENEFIT_VALID_MESSAGE).await; + if let Some(peer_knowledge) = entry.known_by.get_mut(&peer_id) { + peer_knowledge.received.insert(message_subject.clone(), message_kind); + } + return + } + } let (tx, rx) = oneshot::channel(); ctx.send_message(ApprovalVotingMessage::CheckAndImportApproval(vote.clone(), tx)) @@ -1393,7 +1427,6 @@ impl State { gum::trace!( target: LOG_TARGET, ?peer_id, - ?message_subject, ?result, "Checked approval", ); @@ -1401,9 +1434,11 @@ impl State { ApprovalCheckResult::Accepted => { modify_reputation(ctx.sender(), peer_id, BENEFIT_VALID_MESSAGE_FIRST).await; - entry.knowledge.insert(message_subject.clone(), message_kind); - if let Some(peer_knowledge) = entry.known_by.get_mut(&peer_id) { - peer_knowledge.received.insert(message_subject.clone(), message_kind); + for message_subject in &message_subjects { + entry.knowledge.insert(message_subject.clone(), message_kind); + if let Some(peer_knowledge) = entry.known_by.get_mut(&peer_id) { + peer_knowledge.received.insert(message_subject.clone(), message_kind); + } } }, ApprovalCheckResult::Bad(error) => { @@ -1418,69 +1453,71 @@ impl State { }, } } else { - if !entry.knowledge.insert(message_subject.clone(), message_kind) { - // if we already imported an approval, there is no need to distribute it again + let all_approvals_imported_already = + message_subjects.iter().fold(true, |result, message_subject| { + !entry.knowledge.insert(message_subject.clone(), message_kind) && result + }); + if all_approvals_imported_already { + // if we already imported all approvals, there is no need to distribute it again gum::warn!( target: LOG_TARGET, - ?message_subject, "Importing locally an already known approval", ); return } else { gum::debug!( target: LOG_TARGET, - ?message_subject, "Importing locally a new approval", ); } } + let mut required_routing = RequiredRouting::None; + + for candidate_index in candidate_indices.iter_ones() { + // The entry is created when assignment is imported, so we assume this exists. + let approval_entry = entry.get_approval_entry(candidate_index as _, validator_index); + if approval_entry.is_none() { + let peer_id = source.peer_id(); + // This indicates a bug in approval-distribution, since we check the knowledge at the begining of the function. + gum::warn!( + target: LOG_TARGET, + ?peer_id, + "Unknown approval assignment", + ); + // No rep change as this is caused by an issue + return + } - // The entry is created when assignment is imported, so we assume this exists. - let approval_entry = entry.get_approval_entry(candidate_index, validator_index); - if approval_entry.is_none() { - let peer_id = source.peer_id(); - // This indicates a bug in approval-distribution, since we check the knowledge at the begining of the function. - gum::warn!( - target: LOG_TARGET, - ?peer_id, - ?message_subject, - "Unknown approval assignment", - ); - // No rep change as this is caused by an issue - return - } + let approval_entry = approval_entry.expect("Just checked above; qed"); - let approval_entry = approval_entry.expect("Just checked above; qed"); + if let Err(err) = approval_entry.note_approval(vote.clone(), candidate_index as _) { + // this would indicate a bug in approval-voting: + // - validator index mismatch + // - candidate index mismatch + // - duplicate approval + gum::warn!( + target: LOG_TARGET, + hash = ?block_hash, + ?candidate_index, + ?validator_index, + ?err, + "Possible bug: Vote import failed", + ); + return + } + required_routing = approval_entry.routing_info().required_routing; + } // Invariant: to our knowledge, none of the peers except for the `source` know about the approval. metrics.on_approval_imported(); - if let Err(err) = approval_entry.note_approval(vote.clone()) { - // this would indicate a bug in approval-voting: - // - validator index mismatch - // - candidate index mismatch - // - duplicate approval - gum::warn!( - target: LOG_TARGET, - hash = ?block_hash, - ?candidate_index, - ?validator_index, - ?err, - "Possible bug: Vote import failed", - ); - - return - } - - let required_routing = approval_entry.routing_info().required_routing; - // Dispatch a ApprovalDistributionV1Message::Approval(vote) // to all peers required by the topology, with the exception of the source peer. let topology = self.topologies.get_topology(entry.session); let source_peer = source.peer_id(); - let message_subject = &message_subject; + let message_subjects_clone = message_subjects.clone(); let peer_filter = move |peer, knowledge: &PeerKnowledge| { if Some(peer) == source_peer.as_ref() { return false @@ -1497,7 +1534,10 @@ impl State { // 3. Any randomly selected peers have been sent the assignment already. let in_topology = topology .map_or(false, |t| t.local_grid_neighbors().route_to_peer(required_routing, peer)); - in_topology || knowledge.sent.contains(message_subject, MessageKind::Assignment) + in_topology || + message_subjects_clone.iter().fold(false, |result, message_subject| { + result || knowledge.sent.contains(message_subject, MessageKind::Assignment) + }) }; let peers = entry @@ -1511,7 +1551,9 @@ impl State { for peer in peers.iter() { // we already filtered peers above, so this should always be Some if let Some(entry) = entry.known_by.get_mut(&peer.0) { - entry.sent.insert(message_subject.clone(), message_kind); + for message_subject in &message_subjects { + entry.sent.insert(message_subject.clone(), message_kind); + } } } @@ -1520,7 +1562,6 @@ impl State { gum::trace!( target: LOG_TARGET, ?block_hash, - ?candidate_index, local = source.peer_id().is_none(), num_peers = peers.len(), "Sending an approval to peers", @@ -1533,7 +1574,18 @@ impl State { ctx.send_message(NetworkBridgeTxMessage::SendValidationMessage( v1_peers, Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Approvals(approvals.clone()), + protocol_v1::ApprovalDistributionMessage::Approvals( + approvals + .clone() + .into_iter() + .filter(|approval| approval.candidate_indices.count_ones() == 1) + .map(|approval| { + approval + .try_into() + .expect("We checked len() == 1 so it should not fail; qed") + }) + .collect::>(), + ), )), )) .await; @@ -1557,7 +1609,7 @@ impl State { fn get_approval_signatures( &mut self, indices: HashSet<(Hash, CandidateIndex)>, - ) -> HashMap { + ) -> HashMap, ValidatorSignature)> { let mut all_sigs = HashMap::new(); for (hash, index) in indices { let _span = self @@ -1584,8 +1636,22 @@ impl State { .get_approval_entries(index) .into_iter() .filter_map(|approval_entry| approval_entry.get_approval(index)) - .map(|approval| (approval.validator, approval.signature)) - .collect::>(); + .map(|approval| { + ( + approval.validator, + ( + hash, + approval + .candidate_indices + .iter_ones() + .map(|val| val as CandidateIndex) + .collect_vec(), + approval.signature, + ), + ) + }) + .collect::, ValidatorSignature)>>( + ); all_sigs.extend(sigs); } all_sigs @@ -1670,12 +1736,20 @@ impl State { // Filter approval votes. for approval_message in approval_messages { - let (approval_knowledge, message_kind) = approval_entry - .create_approval_knowledge(block, approval_message.candidate_index); - - if !peer_knowledge.contains(&approval_knowledge, message_kind) { - peer_knowledge.sent.insert(approval_knowledge, message_kind); - approvals_to_send.push(approval_message); + let mut queued_to_be_sent = false; + for approval_candidate_index in + approval_message.candidate_indices.iter_ones() + { + let (approval_knowledge, message_kind) = approval_entry + .create_approval_knowledge(block, approval_candidate_index as _); + + if !peer_knowledge.contains(&approval_knowledge, message_kind) { + peer_knowledge.sent.insert(approval_knowledge, message_kind); + if !queued_to_be_sent { + approvals_to_send.push(approval_message.clone()); + queued_to_be_sent = true; + } + } } } } @@ -1901,15 +1975,21 @@ async fn adjust_required_routing_and_propagate { gum::debug!( target: LOG_TARGET, - "Distributing our approval vote on candidate (block={}, index={})", + "Distributing our approval vote on candidate (block={}, index={:?})", vote.block_hash, - vote.candidate_index, + vote.candidate_indices, ); state @@ -2121,7 +2201,7 @@ pub const MAX_ASSIGNMENT_BATCH_SIZE: usize = ensure_size_not_zero( /// The maximum amount of approvals per batch is 33% of maximum allowed by protocol. pub const MAX_APPROVAL_BATCH_SIZE: usize = ensure_size_not_zero( - MAX_NOTIFICATION_SIZE as usize / std::mem::size_of::() / 3, + MAX_NOTIFICATION_SIZE as usize / std::mem::size_of::() / 3, ); // Low level helper for sending assignments. @@ -2214,14 +2294,19 @@ pub(crate) async fn send_assignments_batched( /// Send approvals while honoring the `max_notification_size` of the protocol and peer version. pub(crate) async fn send_approvals_batched( sender: &mut impl overseer::ApprovalDistributionSenderTrait, - approvals: impl IntoIterator + Clone, + approvals: impl IntoIterator + Clone, peers: &[(PeerId, ProtocolVersion)], ) { let v1_peers = filter_by_peer_version(peers, ValidationVersion::V1.into()); let v2_peers = filter_by_peer_version(peers, ValidationVersion::VStaging.into()); if !v1_peers.is_empty() { - let mut batches = approvals.clone().into_iter().peekable(); + let mut batches = approvals + .clone() + .into_iter() + .filter(|approval| approval.candidate_indices.count_ones() == 1) + .map(|val| val.try_into().expect("We checked conversion should succeed; qed")) + .peekable(); while batches.peek().is_some() { let batch: Vec<_> = batches.by_ref().take(MAX_APPROVAL_BATCH_SIZE).collect(); diff --git a/node/network/approval-distribution/src/tests.rs b/node/network/approval-distribution/src/tests.rs index 626b8a6e21a6..7dc5152c2f06 100644 --- a/node/network/approval-distribution/src/tests.rs +++ b/node/network/approval-distribution/src/tests.rs @@ -746,14 +746,14 @@ fn import_approval_happy_path() { ); // send the an approval from peer_b - let approval = IndirectSignedApprovalVote { + let approval = IndirectSignedApprovalVoteV2 { block_hash: hash, - candidate_index, + candidate_indices: candidate_index.into(), validator: validator_index, signature: dummy_signature(), }; - let msg = protocol_v1::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); - send_message_from_peer(overseer, &peer_b, msg).await; + let msg = protocol_vstaging::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); + send_message_from_peer_v2(overseer, &peer_b, msg).await; assert_matches!( overseer_recv(overseer).await, @@ -814,14 +814,14 @@ fn import_approval_bad() { let cert = fake_assignment_cert(hash, validator_index); // send the an approval from peer_b, we don't have an assignment yet - let approval = IndirectSignedApprovalVote { + let approval = IndirectSignedApprovalVoteV2 { block_hash: hash, - candidate_index, + candidate_indices: candidate_index.into(), validator: validator_index, signature: dummy_signature(), }; - let msg = protocol_v1::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); - send_message_from_peer(overseer, &peer_b, msg).await; + let msg = protocol_vstaging::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); + send_message_from_peer_v2(overseer, &peer_b, msg).await; expect_reputation_change(overseer, &peer_b, COST_UNEXPECTED_MESSAGE).await; @@ -846,8 +846,8 @@ fn import_approval_bad() { expect_reputation_change(overseer, &peer_b, BENEFIT_VALID_MESSAGE_FIRST).await; // and try again - let msg = protocol_v1::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); - send_message_from_peer(overseer, &peer_b, msg).await; + let msg = protocol_vstaging::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); + send_message_from_peer_v2(overseer, &peer_b, msg).await; assert_matches!( overseer_recv(overseer).await, @@ -1162,14 +1162,14 @@ fn import_remotely_then_locally() { assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); // send the approval remotely - let approval = IndirectSignedApprovalVote { + let approval = IndirectSignedApprovalVoteV2 { block_hash: hash, - candidate_index, + candidate_indices: candidate_index.into(), validator: validator_index, signature: dummy_signature(), }; - let msg = protocol_v1::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); - send_message_from_peer(overseer, peer, msg).await; + let msg = protocol_vstaging::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); + send_message_from_peer_v2(overseer, peer, msg).await; assert_matches!( overseer_recv(overseer).await, @@ -1234,8 +1234,11 @@ fn sends_assignments_even_when_state_is_approved() { ) .await; - overseer_send(overseer, ApprovalDistributionMessage::DistributeApproval(approval.clone())) - .await; + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeApproval(approval.clone().into()), + ) + .await; // connect the peer. setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; @@ -1313,9 +1316,9 @@ fn sends_assignments_even_when_state_is_approved_v2() { // Assumes candidate index == core index. let approvals = cores .iter() - .map(|core| IndirectSignedApprovalVote { + .map(|core| IndirectSignedApprovalVoteV2 { block_hash: hash, - candidate_index: *core, + candidate_indices: (*core).into(), validator: validator_index, signature: dummy_signature(), }) @@ -1366,8 +1369,8 @@ fn sends_assignments_even_when_state_is_approved_v2() { )) => { // Construct a hashmaps of approvals for comparison. Approval distribution reorders messages because they are kept in a // hashmap as well. - let sent_approvals = sent_approvals.into_iter().map(|approval| (approval.candidate_index, approval)).collect::>(); - let approvals = approvals.into_iter().map(|approval| (approval.candidate_index, approval)).collect::>(); + let sent_approvals = sent_approvals.into_iter().map(|approval| (approval.candidate_indices.clone(), approval)).collect::>(); + let approvals = approvals.into_iter().map(|approval| (approval.candidate_indices.clone(), approval)).collect::>(); assert_eq!(peers, vec![peer.clone()]); assert_eq!(sent_approvals, approvals); @@ -1520,8 +1523,11 @@ fn propagates_locally_generated_assignment_to_both_dimensions() { ) .await; - overseer_send(overseer, ApprovalDistributionMessage::DistributeApproval(approval.clone())) - .await; + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeApproval(approval.clone().into()), + ) + .await; let assignments = vec![(cert.clone(), candidate_index)]; let approvals = vec![approval.clone()]; @@ -1771,8 +1777,11 @@ fn propagates_to_required_after_connect() { ) .await; - overseer_send(overseer, ApprovalDistributionMessage::DistributeApproval(approval.clone())) - .await; + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeApproval(approval.clone().into()), + ) + .await; let assignments = vec![(cert.clone(), candidate_index)]; let approvals = vec![approval.clone()]; @@ -1899,8 +1908,11 @@ fn sends_to_more_peers_after_getting_topology() { ) .await; - overseer_send(overseer, ApprovalDistributionMessage::DistributeApproval(approval.clone())) - .await; + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeApproval(approval.clone().into()), + ) + .await; let assignments = vec![(cert.clone(), candidate_index)]; let approvals = vec![approval.clone()]; @@ -2061,8 +2073,11 @@ fn originator_aggression_l1() { ) .await; - overseer_send(overseer, ApprovalDistributionMessage::DistributeApproval(approval.clone())) - .await; + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeApproval(approval.clone().into()), + ) + .await; let assignments = vec![(cert.clone(), candidate_index)]; let approvals = vec![approval.clone()]; @@ -2600,9 +2615,9 @@ fn batch_test_round(message_count: usize) { .collect(); let approvals: Vec<_> = validators - .map(|index| IndirectSignedApprovalVote { + .map(|index| IndirectSignedApprovalVoteV2 { block_hash: Hash::zero(), - candidate_index: 0, + candidate_indices: 0u32.into(), validator: ValidatorIndex(index as u32), signature: dummy_signature(), }) @@ -2669,7 +2684,7 @@ fn batch_test_round(message_count: usize) { assert_eq!(peers.len(), 1); for (message_index, approval) in sent_approvals.iter().enumerate() { - assert_eq!(approval, &approvals[approval_index + message_index]); + assert_eq!(approval, &approvals[approval_index + message_index].clone().try_into().unwrap()); } } ); diff --git a/node/network/protocol/src/lib.rs b/node/network/protocol/src/lib.rs index 009d44689c2e..b70c5b2495c0 100644 --- a/node/network/protocol/src/lib.rs +++ b/node/network/protocol/src/lib.rs @@ -427,9 +427,8 @@ impl_versioned_try_from!( /// - assignment cert type changed, see `IndirectAssignmentCertV2`. pub mod vstaging { use parity_scale_codec::{Decode, Encode}; - use polkadot_node_primitives::approval::{ - v1::IndirectSignedApprovalVote, - v2::{CandidateBitfield, IndirectAssignmentCertV2}, + use polkadot_node_primitives::approval::v2::{ + CandidateBitfield, IndirectAssignmentCertV2, IndirectSignedApprovalVoteV2, }; // Re-export stuff that has not changed since v1. @@ -468,7 +467,7 @@ pub mod vstaging { Assignments(Vec<(IndirectAssignmentCertV2, CandidateBitfield)>), /// Approvals for candidates in some recent, unfinalized block. #[codec(index = 1)] - Approvals(Vec), + Approvals(Vec), } } diff --git a/node/primitives/src/approval.rs b/node/primitives/src/approval.rs index dda7d0e4a845..9a6274c4f1ee 100644 --- a/node/primitives/src/approval.rs +++ b/node/primitives/src/approval.rs @@ -219,7 +219,9 @@ pub mod v2 { use std::ops::BitOr; use bitvec::{prelude::Lsb0, vec::BitVec}; - use polkadot_primitives::{CandidateIndex, CoreIndex, Hash, ValidatorIndex}; + use polkadot_primitives::{ + CandidateIndex, CoreIndex, Hash, ValidatorIndex, ValidatorSignature, + }; /// A static context associated with producing randomness for a core. pub const CORE_RANDOMNESS_CONTEXT: &[u8] = b"A&V CORE v2"; @@ -361,14 +363,16 @@ pub mod v2 { /// candidates were included. /// /// The context is [`v2::RELAY_VRF_MODULO_CONTEXT`] + #[codec(index = 0)] RelayVRFModuloCompact { /// A bitfield representing the core indices claimed by this assignment. core_bitfield: CoreBitfield, - } = 0, + }, /// An assignment story based on the VRF that authorized the relay-chain block where the /// candidate was included combined with the index of a particular core. /// /// The context is [`v2::RELAY_VRF_DELAY_CONTEXT`] + #[codec(index = 1)] RelayVRFDelay { /// The core index chosen in this cert. core_index: CoreIndex, @@ -378,6 +382,7 @@ pub mod v2 { /// candidate was included combined with a sample number. /// /// The context used to produce bytes is [`v1::RELAY_VRF_MODULO_CONTEXT`] + #[codec(index = 2)] RelayVRFModulo { /// The sample number used in this cert. sample: u32, @@ -465,6 +470,59 @@ pub mod v2 { }) } } + + impl From for IndirectSignedApprovalVoteV2 { + fn from(value: super::v1::IndirectSignedApprovalVote) -> Self { + Self { + block_hash: value.block_hash, + validator: value.validator, + candidate_indices: value.candidate_index.into(), + signature: value.signature, + } + } + } + + /// Errors that can occur when trying to convert to/from approvals v1/v2 + #[derive(Debug)] + pub enum ApprovalConversionError { + /// More than one candidate was signed. + MoreThanOneCandidate(usize), + } + + impl TryFrom for super::v1::IndirectSignedApprovalVote { + type Error = ApprovalConversionError; + + fn try_from(value: IndirectSignedApprovalVoteV2) -> Result { + if value.candidate_indices.count_ones() != 1 { + return Err(ApprovalConversionError::MoreThanOneCandidate( + value.candidate_indices.count_ones(), + )) + } + Ok(Self { + block_hash: value.block_hash, + validator: value.validator, + candidate_index: value.candidate_indices.first_one().expect("Qed we checked above") + as u32, + signature: value.signature, + }) + } + } + + /// A signed approval vote which references the candidate indirectly via the block. + /// + /// In practice, we have a look-up from block hash and candidate index to candidate hash, + /// so this can be transformed into a `SignedApprovalVote`. + #[derive(Debug, Clone, Encode, Decode, PartialEq, Eq)] + pub struct IndirectSignedApprovalVoteV2 { + /// A block hash where the candidate appears. + pub block_hash: Hash, + /// The index of the candidate in the list of candidates fully included as-of the block. + pub candidate_indices: CandidateBitfield, + /// The validator index. + pub validator: ValidatorIndex, + /// The signature by the validator. + pub signature: ValidatorSignature, + } } #[cfg(test)] diff --git a/node/primitives/src/disputes/message.rs b/node/primitives/src/disputes/message.rs index 992d70ba1324..5120c6d2fa2f 100644 --- a/node/primitives/src/disputes/message.rs +++ b/node/primitives/src/disputes/message.rs @@ -170,7 +170,7 @@ impl DisputeMessage { let valid_vote = ValidDisputeVote { validator_index: valid_index, signature: valid_statement.validator_signature().clone(), - kind: *valid_kind, + kind: valid_kind.clone(), }; let invalid_vote = InvalidDisputeVote { diff --git a/node/primitives/src/disputes/mod.rs b/node/primitives/src/disputes/mod.rs index 5e8e5815258d..0871f4d766f4 100644 --- a/node/primitives/src/disputes/mod.rs +++ b/node/primitives/src/disputes/mod.rs @@ -107,8 +107,9 @@ impl ValidCandidateVotes { ValidDisputeStatementKind::BackingValid(_) | ValidDisputeStatementKind::BackingSeconded(_) => false, ValidDisputeStatementKind::Explicit | - ValidDisputeStatementKind::ApprovalChecking => { - occupied.insert((kind, sig)); + ValidDisputeStatementKind::ApprovalChecking | + ValidDisputeStatementKind::ApprovalCheckingV2(_) => { + occupied.insert((kind.clone(), sig)); kind != occupied.get().0 }, }, diff --git a/node/subsystem-types/src/messages.rs b/node/subsystem-types/src/messages.rs index 27191639053a..94d59af8eba5 100644 --- a/node/subsystem-types/src/messages.rs +++ b/node/subsystem-types/src/messages.rs @@ -34,8 +34,8 @@ use polkadot_node_network_protocol::{ }; use polkadot_node_primitives::{ approval::{ - v1::{BlockApprovalMeta, IndirectSignedApprovalVote}, - v2::{CandidateBitfield, IndirectAssignmentCertV2}, + v1::BlockApprovalMeta, + v2::{CandidateBitfield, IndirectAssignmentCertV2, IndirectSignedApprovalVoteV2}, }, AvailableData, BabeEpoch, BlockWeight, CandidateVotes, CollationGenerationConfig, CollationSecondedSignal, DisputeMessage, DisputeStatus, ErasureChunk, PoV, @@ -812,7 +812,7 @@ pub enum ApprovalVotingMessage { /// protocol. /// /// Should not be sent unless the block hash within the indirect vote is known. - CheckAndImportApproval(IndirectSignedApprovalVote, oneshot::Sender), + CheckAndImportApproval(IndirectSignedApprovalVoteV2, oneshot::Sender), /// Returns the highest possible ancestor hash of the provided block hash which is /// acceptable to vote on finality for. /// The `BlockNumber` provided is the number of the block's ancestor which is the @@ -828,7 +828,7 @@ pub enum ApprovalVotingMessage { /// requires calling into `approval-distribution`: Calls should be infrequent and bounded. GetApprovalSignaturesForCandidate( CandidateHash, - oneshot::Sender>, + oneshot::Sender, ValidatorSignature)>>, ), } @@ -844,7 +844,7 @@ pub enum ApprovalDistributionMessage { /// Distribute an approval vote for the local validator. The approval vote is assumed to be /// valid, relevant, and the corresponding approval already issued. /// If not, the subsystem is free to drop the message. - DistributeApproval(IndirectSignedApprovalVote), + DistributeApproval(IndirectSignedApprovalVoteV2), /// An update from the network bridge. #[from] NetworkBridgeUpdate(NetworkBridgeEvent), @@ -852,7 +852,7 @@ pub enum ApprovalDistributionMessage { /// Get all approval signatures for all chains a candidate appeared in. GetApprovalSignatures( HashSet<(Hash, CandidateIndex)>, - oneshot::Sender>, + oneshot::Sender, ValidatorSignature)>>, ), /// Approval checking lag update measured in blocks. ApprovalCheckingLagUpdate(BlockNumber), diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs index 1c8ef1eae73b..8018f3a4dde6 100644 --- a/primitives/src/lib.rs +++ b/primitives/src/lib.rs @@ -55,8 +55,9 @@ pub use v5::{ UncheckedSignedAvailabilityBitfields, UncheckedSignedStatement, UpgradeGoAhead, UpgradeRestriction, UpwardMessage, ValidDisputeStatementKind, ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature, ValidityAttestation, - ValidityError, ASSIGNMENT_KEY_TYPE_ID, LOWEST_PUBLIC_ID, MAX_CODE_SIZE, MAX_HEAD_DATA_SIZE, - MAX_POV_SIZE, PARACHAINS_INHERENT_IDENTIFIER, PARACHAIN_KEY_TYPE_ID, + ValidityError, ASSIGNMENT_KEY_TYPE_ID, LOWEST_PUBLIC_ID, MAX_APPROVAL_COALESCE_COUNT, + MAX_APPROVAL_COALESCE_WAIT_MILLIS, MAX_CODE_SIZE, MAX_HEAD_DATA_SIZE, MAX_POV_SIZE, + PARACHAINS_INHERENT_IDENTIFIER, PARACHAIN_KEY_TYPE_ID, }; #[cfg(feature = "std")] diff --git a/primitives/src/v5/mod.rs b/primitives/src/v5/mod.rs index 6c6258b2b805..c1c765244f06 100644 --- a/primitives/src/v5/mod.rs +++ b/primitives/src/v5/mod.rs @@ -353,6 +353,13 @@ pub mod well_known_keys { /// Unique identifier for the Parachains Inherent pub const PARACHAINS_INHERENT_IDENTIFIER: InherentIdentifier = *b"parachn0"; +/// TODO: Make this two a parachain host configuration. +/// Maximum allowed candidates to be signed withing a signle approval votes. +pub const MAX_APPROVAL_COALESCE_COUNT: u64 = 6; +/// The maximum time we await for an approval to be coalesced with other approvals +/// before we sign it and distribute to our peers +pub const MAX_APPROVAL_COALESCE_WAIT_MILLIS: u64 = 500; + /// The key type ID for parachain assignment key. pub const ASSIGNMENT_KEY_TYPE_ID: KeyTypeId = KeyTypeId(*b"asgn"); @@ -1060,6 +1067,26 @@ impl ApprovalVote { } } +/// A vote of approvalf for multiple candidates. +#[derive(Clone, RuntimeDebug)] +pub struct ApprovalVoteMultipleCandidates(pub Vec); + +impl ApprovalVoteMultipleCandidates { + /// Yields the signing payload for this approval vote. + pub fn signing_payload(&self, session_index: SessionIndex) -> Vec { + const MAGIC: [u8; 4] = *b"APPR"; + // Make this backwards compatible with `ApprovalVote` so if we have just on candidate the signature + // will look the same. + // This gives us the nice benefit that old nodes can still check signatures when len is 1 and the + // new node can check the signature coming from old nodes. + if self.0.len() == 1 { + (MAGIC, self.0.first().expect("QED: we just checked"), session_index).encode() + } else { + (MAGIC, &self.0, session_index).encode() + } + } +} + /// Custom validity errors used in Polkadot while validating transactions. #[repr(u8)] pub enum ValidityError { @@ -1234,22 +1261,25 @@ pub enum DisputeStatement { impl DisputeStatement { /// Get the payload data for this type of dispute statement. pub fn payload_data(&self, candidate_hash: CandidateHash, session: SessionIndex) -> Vec { - match *self { + match self { DisputeStatement::Valid(ValidDisputeStatementKind::Explicit) => ExplicitDisputeStatement { valid: true, candidate_hash, session }.signing_payload(), DisputeStatement::Valid(ValidDisputeStatementKind::BackingSeconded( inclusion_parent, )) => CompactStatement::Seconded(candidate_hash).signing_payload(&SigningContext { session_index: session, - parent_hash: inclusion_parent, + parent_hash: *inclusion_parent, }), DisputeStatement::Valid(ValidDisputeStatementKind::BackingValid(inclusion_parent)) => CompactStatement::Valid(candidate_hash).signing_payload(&SigningContext { session_index: session, - parent_hash: inclusion_parent, + parent_hash: *inclusion_parent, }), DisputeStatement::Valid(ValidDisputeStatementKind::ApprovalChecking) => ApprovalVote(candidate_hash).signing_payload(session), + DisputeStatement::Valid(ValidDisputeStatementKind::ApprovalCheckingV2( + candidate_hashes, + )) => ApprovalVoteMultipleCandidates(candidate_hashes.clone()).signing_payload(session), DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit) => ExplicitDisputeStatement { valid: false, candidate_hash, session }.signing_payload(), } @@ -1295,13 +1325,14 @@ impl DisputeStatement { Self::Valid(ValidDisputeStatementKind::BackingValid(_)) => true, Self::Valid(ValidDisputeStatementKind::Explicit) | Self::Valid(ValidDisputeStatementKind::ApprovalChecking) | + Self::Valid(ValidDisputeStatementKind::ApprovalCheckingV2(_)) | Self::Invalid(_) => false, } } } /// Different kinds of statements of validity on a candidate. -#[derive(Encode, Decode, Copy, Clone, PartialEq, RuntimeDebug, TypeInfo)] +#[derive(Encode, Decode, Clone, PartialEq, RuntimeDebug, TypeInfo)] pub enum ValidDisputeStatementKind { /// An explicit statement issued as part of a dispute. #[codec(index = 0)] @@ -1315,6 +1346,11 @@ pub enum ValidDisputeStatementKind { /// An approval vote from the approval checking phase. #[codec(index = 3)] ApprovalChecking, + /// An approval vote from the new version. + /// TODO: Fixme this probably means we can't create this version + /// untill all nodes have been updated to support it. + #[codec(index = 4)] + ApprovalCheckingV2(Vec), } /// Different kinds of statements of invalidity on a candidate. diff --git a/runtime/parachains/src/disputes.rs b/runtime/parachains/src/disputes.rs index 6a3d61ff8047..5e253005ad17 100644 --- a/runtime/parachains/src/disputes.rs +++ b/runtime/parachains/src/disputes.rs @@ -25,11 +25,11 @@ use frame_system::pallet_prelude::*; use parity_scale_codec::{Decode, Encode}; use polkadot_runtime_metrics::get_current_time; use primitives::{ - byzantine_threshold, supermajority_threshold, ApprovalVote, CandidateHash, - CheckedDisputeStatementSet, CheckedMultiDisputeStatementSet, CompactStatement, ConsensusLog, - DisputeState, DisputeStatement, DisputeStatementSet, ExplicitDisputeStatement, - InvalidDisputeStatementKind, MultiDisputeStatementSet, SessionIndex, SigningContext, - ValidDisputeStatementKind, ValidatorId, ValidatorIndex, ValidatorSignature, + byzantine_threshold, supermajority_threshold, vstaging::ApprovalVoteMultipleCandidates, + ApprovalVote, CandidateHash, CheckedDisputeStatementSet, CheckedMultiDisputeStatementSet, + CompactStatement, ConsensusLog, DisputeState, DisputeStatement, DisputeStatementSet, + ExplicitDisputeStatement, InvalidDisputeStatementKind, MultiDisputeStatementSet, SessionIndex, + SigningContext, ValidDisputeStatementKind, ValidatorId, ValidatorIndex, ValidatorSignature, }; use scale_info::TypeInfo; use sp_runtime::{ @@ -1345,6 +1345,9 @@ fn check_signature( }), DisputeStatement::Valid(ValidDisputeStatementKind::ApprovalChecking) => ApprovalVote(candidate_hash).signing_payload(session), + DisputeStatement::Valid(ValidDisputeStatementKind::ApprovalCheckingV2(_)) => + // TODO: Fixme + ApprovalVoteMultipleCandidates(vec![candidate_hash]).signing_payload(session), DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit) => ExplicitDisputeStatement { valid: false, candidate_hash, session }.signing_payload(), }; diff --git a/zombienet_tests/functional/0001-parachains-pvf.zndsl b/zombienet_tests/functional/0001-parachains-pvf.zndsl index 46bb8bcdf72b..7859dd6d1156 100644 --- a/zombienet_tests/functional/0001-parachains-pvf.zndsl +++ b/zombienet_tests/functional/0001-parachains-pvf.zndsl @@ -32,6 +32,8 @@ alice: parachain 2005 block height is at least 10 within 300 seconds alice: parachain 2006 block height is at least 10 within 300 seconds alice: parachain 2007 block height is at least 10 within 300 seconds +alice: reports substrate_block_height{status="finalized"} is at least 30 within 400 seconds + # Check preparation time is under 10s. # Check all buckets <= 10. alice: reports histogram polkadot_pvf_preparation_time has at least 1 samples in buckets ["0.1", "0.5", "1", "2", "3", "10"] within 10 seconds From 4901feeb9c002c1b59a8b264dc0953fe8d5b841f Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe Date: Fri, 28 Jul 2023 18:43:00 +0300 Subject: [PATCH 02/54] Add Runtime configuration for max_approval_coalesce_count/wait_millis TODO: Migration is not correctly handled, should be done before versi testing. Signed-off-by: Alexandru Gheorghe --- node/core/approval-voting/src/lib.rs | 50 ++++++++++++++++--- node/core/runtime-api/src/cache.rs | 31 +++++++++--- node/core/runtime-api/src/lib.rs | 7 +++ node/subsystem-types/src/messages.rs | 17 ++++--- node/subsystem-types/src/runtime_client.rs | 21 +++++--- primitives/src/lib.rs | 5 +- primitives/src/runtime_api.rs | 12 +++-- primitives/src/v5/mod.rs | 12 ++--- primitives/src/vstaging/mod.rs | 23 +++++++++ runtime/kusama/src/lib.rs | 16 +++--- runtime/parachains/src/configuration.rs | 30 +++++++++-- .../src/configuration/migration/v6.rs | 6 ++- runtime/parachains/src/runtime_api_impl/v5.rs | 11 ++-- runtime/polkadot/src/lib.rs | 17 ++++--- runtime/rococo/src/lib.rs | 15 ++++-- runtime/westend/src/lib.rs | 17 ++++--- 16 files changed, 220 insertions(+), 70 deletions(-) diff --git a/node/core/approval-voting/src/lib.rs b/node/core/approval-voting/src/lib.rs index 98a0b2a260a0..d0eb855c35b1 100644 --- a/node/core/approval-voting/src/lib.rs +++ b/node/core/approval-voting/src/lib.rs @@ -55,10 +55,10 @@ use polkadot_node_subsystem_util::{ TimeoutExt, }; use polkadot_primitives::{ - vstaging::ApprovalVoteMultipleCandidates, BlockNumber, CandidateHash, CandidateIndex, - CandidateReceipt, DisputeStatement, GroupIndex, Hash, PvfExecTimeoutKind, SessionIndex, - SessionInfo, ValidDisputeStatementKind, ValidatorId, ValidatorIndex, ValidatorPair, - ValidatorSignature, MAX_APPROVAL_COALESCE_COUNT, MAX_APPROVAL_COALESCE_WAIT_MILLIS, + vstaging::{ApprovalVoteMultipleCandidates, ApprovalVotingParams}, + BlockNumber, CandidateHash, CandidateIndex, CandidateReceipt, DisputeStatement, GroupIndex, + Hash, PvfExecTimeoutKind, SessionIndex, SessionInfo, ValidDisputeStatementKind, ValidatorId, + ValidatorIndex, ValidatorPair, ValidatorSignature, }; use sc_keystore::LocalKeystore; use sp_application_crypto::Pair; @@ -2964,6 +2964,28 @@ async fn issue_approval( }, }; + let (s_tx, s_rx) = oneshot::channel(); + + ctx.send_message(RuntimeApiMessage::Request( + block_hash, + RuntimeApiRequest::ApprovalVotingParams(s_tx), + )) + .await; + + let approval_params = match s_rx.await { + Ok(Ok(s)) => s, + _ => { + gum::error!( + target: LOG_TARGET, + "Could not request approval voting params from runtime using defaults" + ); + ApprovalVotingParams { + max_approval_coalesce_count: 1, + max_approval_coalesce_wait_millis: 0, + } + }, + }; + let candidate_index = match block_entry.candidates().iter().position(|e| e.1 == candidate_hash) { None => { @@ -3015,10 +3037,16 @@ async fn issue_approval( .candidates_pending_signature .insert(candidate_index as _, CandidateSigningContext { candidate_hash }); - let should_create_signature = - block_entry.candidates_pending_signature.len() >= MAX_APPROVAL_COALESCE_COUNT as usize; + let should_create_signature = block_entry.candidates_pending_signature.len() >= + approval_params.max_approval_coalesce_count as usize; + + gum::debug!( + target: LOG_TARGET, + "Approval coalese params{:?}", approval_params + ); - let delay = Delay::new(Duration::from_millis(MAX_APPROVAL_COALESCE_WAIT_MILLIS)); + let delay = + Delay::new(Duration::from_millis(approval_params.max_approval_coalesce_wait_millis as _)); sign_approvals_timers.timers.push_back(Box::pin(async move { delay.await; @@ -3077,7 +3105,9 @@ async fn maybe_create_signature( let mut block_entry = match db.load_block_entry(&block_hash)? { Some(b) => b, None => { - gum::error!( + // not a cause for alarm - just lost a race with pruning, most likely. + metrics.on_approval_stale(); + gum::trace!( target: LOG_TARGET, "Could not find block that needs signature {:}", block_hash ); @@ -3088,6 +3118,10 @@ async fn maybe_create_signature( // If the candidate that woke us is not pending do nothing and let one of the wakeup of the // pending candidates create the // signature. + gum::debug!( + target: LOG_TARGET, + "Candidates pending signatures {:}", block_entry.candidates_pending_signature.len() + ); if !block_entry .candidates_pending_signature .values() diff --git a/node/core/runtime-api/src/cache.rs b/node/core/runtime-api/src/cache.rs index 4c23ce2fa3c7..d74ae8062804 100644 --- a/node/core/runtime-api/src/cache.rs +++ b/node/core/runtime-api/src/cache.rs @@ -20,12 +20,12 @@ use lru::LruCache; use sp_consensus_babe::Epoch; use polkadot_primitives::{ - vstaging, AuthorityDiscoveryId, BlockNumber, CandidateCommitments, CandidateEvent, - CandidateHash, CommittedCandidateReceipt, CoreState, DisputeState, ExecutorParams, - GroupRotationInfo, Hash, Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, - OccupiedCoreAssumption, PersistedValidationData, PvfCheckStatement, ScrapedOnChainVotes, - SessionIndex, SessionInfo, ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, - ValidatorSignature, + vstaging::{self, ApprovalVotingParams}, + AuthorityDiscoveryId, BlockNumber, CandidateCommitments, CandidateEvent, CandidateHash, + CommittedCandidateReceipt, CoreState, DisputeState, ExecutorParams, GroupRotationInfo, Hash, + Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, OccupiedCoreAssumption, + PersistedValidationData, PvfCheckStatement, ScrapedOnChainVotes, SessionIndex, SessionInfo, + ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature, }; /// For consistency we have the same capacity for all caches. We use 128 as we'll only need that @@ -64,6 +64,8 @@ pub(crate) struct RequestResultCache { LruCache<(Hash, ParaId, OccupiedCoreAssumption), Option>, version: LruCache, disputes: LruCache)>>, + approval_voting_params: LruCache, + unapplied_slashes: LruCache>, key_ownership_proof: @@ -97,6 +99,7 @@ impl Default for RequestResultCache { disputes: LruCache::new(DEFAULT_CACHE_CAP), unapplied_slashes: LruCache::new(DEFAULT_CACHE_CAP), key_ownership_proof: LruCache::new(DEFAULT_CACHE_CAP), + approval_voting_params: LruCache::new(DEFAULT_CACHE_CAP), } } } @@ -393,6 +396,21 @@ impl RequestResultCache { self.disputes.put(relay_parent, value); } + pub(crate) fn approval_voting_params( + &mut self, + relay_parent: &Hash, + ) -> Option<&ApprovalVotingParams> { + self.approval_voting_params.get(relay_parent) + } + + pub(crate) fn cache_approval_voting_params( + &mut self, + relay_parent: Hash, + value: ApprovalVotingParams, + ) { + self.approval_voting_params.put(relay_parent, value); + } + pub(crate) fn unapplied_slashes( &mut self, relay_parent: &Hash, @@ -476,4 +494,5 @@ pub(crate) enum RequestResult { vstaging::slashing::OpaqueKeyOwnershipProof, Option<()>, ), + ApprovalVotingParams(Hash, ApprovalVotingParams), } diff --git a/node/core/runtime-api/src/lib.rs b/node/core/runtime-api/src/lib.rs index 252bb21b0edb..17e7f456bd6b 100644 --- a/node/core/runtime-api/src/lib.rs +++ b/node/core/runtime-api/src/lib.rs @@ -162,6 +162,8 @@ where KeyOwnershipProof(relay_parent, validator_id, key_ownership_proof) => self .requests_cache .cache_key_ownership_proof((relay_parent, validator_id), key_ownership_proof), + RequestResult::ApprovalVotingParams(relay_parent, params) => + self.requests_cache.cache_approval_voting_params(relay_parent, params), SubmitReportDisputeLost(_, _, _, _) => {}, } } @@ -288,6 +290,8 @@ where Request::SubmitReportDisputeLost(dispute_proof, key_ownership_proof, sender) }, ), + Request::ApprovalVotingParams(sender) => query!(approval_voting_params(), sender) + .map(|sender| Request::ApprovalVotingParams(sender)), } } @@ -531,6 +535,9 @@ where ver = Request::KEY_OWNERSHIP_PROOF_RUNTIME_REQUIREMENT, sender ), + Request::ApprovalVotingParams(sender) => { + query!(ApprovalVotingParams, approval_voting_params(), ver = 5, sender) + }, Request::SubmitReportDisputeLost(dispute_proof, key_ownership_proof, sender) => query!( SubmitReportDisputeLost, submit_report_dispute_lost(dispute_proof, key_ownership_proof), diff --git a/node/subsystem-types/src/messages.rs b/node/subsystem-types/src/messages.rs index 94d59af8eba5..0d5ec421bd85 100644 --- a/node/subsystem-types/src/messages.rs +++ b/node/subsystem-types/src/messages.rs @@ -42,13 +42,14 @@ use polkadot_node_primitives::{ SignedDisputeStatement, SignedFullStatement, ValidationResult, }; use polkadot_primitives::{ - slashing, AuthorityDiscoveryId, BackedCandidate, BlockNumber, CandidateEvent, CandidateHash, - CandidateIndex, CandidateReceipt, CollatorId, CommittedCandidateReceipt, CoreState, - DisputeState, ExecutorParams, GroupIndex, GroupRotationInfo, Hash, Header as BlockHeader, - Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, MultiDisputeStatementSet, - OccupiedCoreAssumption, PersistedValidationData, PvfCheckStatement, PvfExecTimeoutKind, - SessionIndex, SessionInfo, SignedAvailabilityBitfield, SignedAvailabilityBitfields, - ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature, + slashing, vstaging::ApprovalVotingParams, AuthorityDiscoveryId, BackedCandidate, BlockNumber, + CandidateEvent, CandidateHash, CandidateIndex, CandidateReceipt, CollatorId, + CommittedCandidateReceipt, CoreState, DisputeState, ExecutorParams, GroupIndex, + GroupRotationInfo, Hash, Header as BlockHeader, Id as ParaId, InboundDownwardMessage, + InboundHrmpMessage, MultiDisputeStatementSet, OccupiedCoreAssumption, PersistedValidationData, + PvfCheckStatement, PvfExecTimeoutKind, SessionIndex, SessionInfo, SignedAvailabilityBitfield, + SignedAvailabilityBitfields, ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, + ValidatorSignature, }; use polkadot_statement_table::v2::Misbehavior; use std::{ @@ -624,6 +625,8 @@ pub enum RuntimeApiRequest { slashing::OpaqueKeyOwnershipProof, RuntimeApiSender>, ), + /// Approval voting params + ApprovalVotingParams(RuntimeApiSender), } impl RuntimeApiRequest { diff --git a/node/subsystem-types/src/runtime_client.rs b/node/subsystem-types/src/runtime_client.rs index 2d6d7afcfd08..fea0c08d1cda 100644 --- a/node/subsystem-types/src/runtime_client.rs +++ b/node/subsystem-types/src/runtime_client.rs @@ -16,12 +16,13 @@ use async_trait::async_trait; use polkadot_primitives::{ - runtime_api::ParachainHost, vstaging, Block, BlockNumber, CandidateCommitments, CandidateEvent, - CandidateHash, CommittedCandidateReceipt, CoreState, DisputeState, ExecutorParams, - GroupRotationInfo, Hash, Id, InboundDownwardMessage, InboundHrmpMessage, - OccupiedCoreAssumption, PersistedValidationData, PvfCheckStatement, ScrapedOnChainVotes, - SessionIndex, SessionInfo, ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, - ValidatorSignature, + runtime_api::ParachainHost, + vstaging::{self, ApprovalVotingParams}, + Block, BlockNumber, CandidateCommitments, CandidateEvent, CandidateHash, + CommittedCandidateReceipt, CoreState, DisputeState, ExecutorParams, GroupRotationInfo, Hash, + Id, InboundDownwardMessage, InboundHrmpMessage, OccupiedCoreAssumption, + PersistedValidationData, PvfCheckStatement, ScrapedOnChainVotes, SessionIndex, SessionInfo, + ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature, }; use sp_api::{ApiError, ApiExt, ProvideRuntimeApi}; use sp_authority_discovery::AuthorityDiscoveryApi; @@ -229,6 +230,9 @@ pub trait RuntimeApiSubsystemClient { &self, at: Hash, ) -> std::result::Result, ApiError>; + + /// Approval voting configuration parameters + async fn approval_voting_params(&self, at: Hash) -> Result; } #[async_trait] @@ -427,4 +431,9 @@ where self.runtime_api() .submit_report_dispute_lost(at, dispute_proof, key_ownership_proof) } + + /// Approval voting configuration parameters + async fn approval_voting_params(&self, at: Hash) -> Result { + self.runtime_api().approval_voting_params(at) + } } diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs index 8018f3a4dde6..1c8ef1eae73b 100644 --- a/primitives/src/lib.rs +++ b/primitives/src/lib.rs @@ -55,9 +55,8 @@ pub use v5::{ UncheckedSignedAvailabilityBitfields, UncheckedSignedStatement, UpgradeGoAhead, UpgradeRestriction, UpwardMessage, ValidDisputeStatementKind, ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature, ValidityAttestation, - ValidityError, ASSIGNMENT_KEY_TYPE_ID, LOWEST_PUBLIC_ID, MAX_APPROVAL_COALESCE_COUNT, - MAX_APPROVAL_COALESCE_WAIT_MILLIS, MAX_CODE_SIZE, MAX_HEAD_DATA_SIZE, MAX_POV_SIZE, - PARACHAINS_INHERENT_IDENTIFIER, PARACHAIN_KEY_TYPE_ID, + ValidityError, ASSIGNMENT_KEY_TYPE_ID, LOWEST_PUBLIC_ID, MAX_CODE_SIZE, MAX_HEAD_DATA_SIZE, + MAX_POV_SIZE, PARACHAINS_INHERENT_IDENTIFIER, PARACHAIN_KEY_TYPE_ID, }; #[cfg(feature = "std")] diff --git a/primitives/src/runtime_api.rs b/primitives/src/runtime_api.rs index aea069db7694..176171690340 100644 --- a/primitives/src/runtime_api.rs +++ b/primitives/src/runtime_api.rs @@ -111,10 +111,11 @@ //! from the stable primitives. use crate::{ - vstaging, BlockNumber, CandidateCommitments, CandidateEvent, CandidateHash, - CommittedCandidateReceipt, CoreState, DisputeState, ExecutorParams, GroupRotationInfo, - OccupiedCoreAssumption, PersistedValidationData, PvfCheckStatement, ScrapedOnChainVotes, - SessionIndex, SessionInfo, ValidatorId, ValidatorIndex, ValidatorSignature, + vstaging::{self, ApprovalVotingParams}, + BlockNumber, CandidateCommitments, CandidateEvent, CandidateHash, CommittedCandidateReceipt, + CoreState, DisputeState, ExecutorParams, GroupRotationInfo, OccupiedCoreAssumption, + PersistedValidationData, PvfCheckStatement, ScrapedOnChainVotes, SessionIndex, SessionInfo, + ValidatorId, ValidatorIndex, ValidatorSignature, }; use parity_scale_codec::{Decode, Encode}; use polkadot_core_primitives as pcp; @@ -236,5 +237,8 @@ sp_api::decl_runtime_apis! { dispute_proof: vstaging::slashing::DisputeProof, key_ownership_proof: vstaging::slashing::OpaqueKeyOwnershipProof, ) -> Option<()>; + + /// Approval voting configuration parameters + fn approval_voting_params() -> ApprovalVotingParams; } } diff --git a/primitives/src/v5/mod.rs b/primitives/src/v5/mod.rs index c1c765244f06..d3280f8be525 100644 --- a/primitives/src/v5/mod.rs +++ b/primitives/src/v5/mod.rs @@ -353,12 +353,12 @@ pub mod well_known_keys { /// Unique identifier for the Parachains Inherent pub const PARACHAINS_INHERENT_IDENTIFIER: InherentIdentifier = *b"parachn0"; -/// TODO: Make this two a parachain host configuration. -/// Maximum allowed candidates to be signed withing a signle approval votes. -pub const MAX_APPROVAL_COALESCE_COUNT: u64 = 6; -/// The maximum time we await for an approval to be coalesced with other approvals -/// before we sign it and distribute to our peers -pub const MAX_APPROVAL_COALESCE_WAIT_MILLIS: u64 = 500; +// /// TODO: Make this two a parachain host configuration. +// /// Maximum allowed candidates to be signed withing a signle approval votes. +// pub const MAX_APPROVAL_COALESCE_COUNT: u64 = 6; +// /// The maximum time we await for an approval to be coalesced with other approvals +// /// before we sign it and distribute to our peers +// pub const MAX_APPROVAL_COALESCE_WAIT_MILLIS: u64 = 500; /// The key type ID for parachain assignment key. pub const ASSIGNMENT_KEY_TYPE_ID: KeyTypeId = KeyTypeId(*b"asgn"); diff --git a/primitives/src/vstaging/mod.rs b/primitives/src/vstaging/mod.rs index 0dbfa8c34cf6..aad7208355e1 100644 --- a/primitives/src/vstaging/mod.rs +++ b/primitives/src/vstaging/mod.rs @@ -50,3 +50,26 @@ pub struct AsyncBackingParams { /// When async backing is disabled, the only valid value is 0. pub allowed_ancestry_len: u32, } + +/// Approval voting configuration parameters +#[derive( + RuntimeDebug, + Copy, + Clone, + PartialEq, + Encode, + Decode, + TypeInfo, + serde::Serialize, + serde::Deserialize, +)] +pub struct ApprovalVotingParams { + /// The maximum number of candidates `approval-voting` can vote for with + /// a single signatures. + /// + /// Setting it to 1, means we send the approval as soon as we have it available. + pub max_approval_coalesce_count: u32, + /// The maximum time we await for a candidate approval to be coalesced with + /// the ones for other candidate before we sign it and distribute to our peers + pub max_approval_coalesce_wait_millis: u32, +} diff --git a/runtime/kusama/src/lib.rs b/runtime/kusama/src/lib.rs index 7954ba0326ef..d8c6cb3f7c4f 100644 --- a/runtime/kusama/src/lib.rs +++ b/runtime/kusama/src/lib.rs @@ -23,12 +23,12 @@ use pallet_nis::WithMaximumOf; use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use primitives::{ - slashing, AccountId, AccountIndex, Balance, BlockNumber, CandidateEvent, CandidateHash, - CommittedCandidateReceipt, CoreState, DisputeState, ExecutorParams, GroupRotationInfo, Hash, - Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, Moment, Nonce, - OccupiedCoreAssumption, PersistedValidationData, ScrapedOnChainVotes, SessionInfo, Signature, - ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, LOWEST_PUBLIC_ID, - PARACHAIN_KEY_TYPE_ID, + slashing, vstaging::ApprovalVotingParams, AccountId, AccountIndex, Balance, BlockNumber, + CandidateEvent, CandidateHash, CommittedCandidateReceipt, CoreState, DisputeState, + ExecutorParams, GroupRotationInfo, Hash, Id as ParaId, InboundDownwardMessage, + InboundHrmpMessage, Moment, Nonce, OccupiedCoreAssumption, PersistedValidationData, + ScrapedOnChainVotes, SessionInfo, Signature, ValidationCode, ValidationCodeHash, ValidatorId, + ValidatorIndex, LOWEST_PUBLIC_ID, PARACHAIN_KEY_TYPE_ID, }; use runtime_common::{ auctions, claims, crowdloan, impl_runtime_weights, impls::DealWithFees, paras_registrar, @@ -1854,6 +1854,10 @@ sp_api::impl_runtime_apis! { key_ownership_proof, ) } + /// Approval voting configuration parameters + fn approval_voting_params() -> ApprovalVotingParams { + parachains_runtime_api_impl::approval_voting_params::() + } } impl beefy_primitives::BeefyApi for Runtime { diff --git a/runtime/parachains/src/configuration.rs b/runtime/parachains/src/configuration.rs index 5317c18f8962..52172f9acf5a 100644 --- a/runtime/parachains/src/configuration.rs +++ b/runtime/parachains/src/configuration.rs @@ -24,8 +24,8 @@ use frame_system::pallet_prelude::*; use parity_scale_codec::{Decode, Encode}; use polkadot_parachain::primitives::{MAX_HORIZONTAL_MESSAGE_NUM, MAX_UPWARD_MESSAGE_NUM}; use primitives::{ - vstaging::AsyncBackingParams, Balance, ExecutorParams, SessionIndex, MAX_CODE_SIZE, - MAX_HEAD_DATA_SIZE, MAX_POV_SIZE, + vstaging::{ApprovalVotingParams, AsyncBackingParams}, + Balance, ExecutorParams, SessionIndex, MAX_CODE_SIZE, MAX_HEAD_DATA_SIZE, MAX_POV_SIZE, }; use sp_runtime::traits::Zero; use sp_std::prelude::*; @@ -246,6 +246,10 @@ pub struct HostConfiguration { /// This value should be greater than [`chain_availability_period`] and /// [`thread_availability_period`]. pub minimum_validation_upgrade_delay: BlockNumber, + + /// Params used by approval-voting + /// TODO: fixme this is not correctly migrated + pub approval_voting_params: ApprovalVotingParams, } impl> Default for HostConfiguration { @@ -295,6 +299,10 @@ impl> Default for HostConfiguration crate::hrmp::HRMP_MAX_INBOUND_CHANNELS_BOUND { return Err(MaxHrmpInboundChannelsExceeded) } - + // TODO: add consistency check for approval-voting-params Ok(()) } @@ -1157,6 +1165,22 @@ pub mod pallet { config.executor_params = new; }) } + + /// Set PVF executor parameters. + #[pallet::call_index(47)] + #[pallet::weight(( + T::WeightInfo::set_config_with_executor_params(), + DispatchClass::Operational, + ))] + pub fn set_approval_voting_params( + origin: OriginFor, + new: ApprovalVotingParams, + ) -> DispatchResult { + ensure_root(origin)?; + Self::schedule_config_update(|config| { + config.approval_voting_params = new; + }) + } } #[pallet::hooks] diff --git a/runtime/parachains/src/configuration/migration/v6.rs b/runtime/parachains/src/configuration/migration/v6.rs index 76ff788eb54d..e14ff1965c78 100644 --- a/runtime/parachains/src/configuration/migration/v6.rs +++ b/runtime/parachains/src/configuration/migration/v6.rs @@ -26,7 +26,7 @@ use frame_system::pallet_prelude::BlockNumberFor; use sp_std::vec::Vec; use frame_support::traits::OnRuntimeUpgrade; -use primitives::SessionIndex; +use primitives::{vstaging::ApprovalVotingParams, SessionIndex}; #[cfg(feature = "try-runtime")] use sp_std::prelude::*; @@ -151,6 +151,10 @@ pvf_voting_ttl : pre.pvf_voting_ttl, minimum_validation_upgrade_delay : pre.minimum_validation_upgrade_delay, async_backing_params : pre.async_backing_params, executor_params : pre.executor_params, +approval_voting_params : ApprovalVotingParams { + max_approval_coalesce_count: 1, + max_approval_coalesce_wait_millis: 0, + } } }; diff --git a/runtime/parachains/src/runtime_api_impl/v5.rs b/runtime/parachains/src/runtime_api_impl/v5.rs index 72425995b4b0..77144c35d469 100644 --- a/runtime/parachains/src/runtime_api_impl/v5.rs +++ b/runtime/parachains/src/runtime_api_impl/v5.rs @@ -22,9 +22,9 @@ use crate::{ session_info, shared, }; use primitives::{ - slashing, AuthorityDiscoveryId, CandidateEvent, CandidateHash, CommittedCandidateReceipt, - CoreIndex, CoreOccupied, CoreState, DisputeState, ExecutorParams, GroupIndex, - GroupRotationInfo, Hash, Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, + slashing, vstaging::ApprovalVotingParams, AuthorityDiscoveryId, CandidateEvent, CandidateHash, + CommittedCandidateReceipt, CoreIndex, CoreOccupied, CoreState, DisputeState, ExecutorParams, + GroupIndex, GroupRotationInfo, Hash, Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, OccupiedCore, OccupiedCoreAssumption, PersistedValidationData, PvfCheckStatement, ScheduledCore, ScrapedOnChainVotes, SessionIndex, SessionInfo, ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature, @@ -48,6 +48,11 @@ pub fn validator_groups( (groups, rotation_info) } +pub fn approval_voting_params() -> ApprovalVotingParams { + let config = >::config(); + config.approval_voting_params +} + /// Implementation for the `availability_cores` function of the runtime API. pub fn availability_cores() -> Vec> { let cores = >::availability_cores(); diff --git a/runtime/polkadot/src/lib.rs b/runtime/polkadot/src/lib.rs index c24f87ce8e80..860a8aa8b182 100644 --- a/runtime/polkadot/src/lib.rs +++ b/runtime/polkadot/src/lib.rs @@ -57,12 +57,12 @@ use pallet_session::historical as session_historical; use pallet_transaction_payment::{FeeDetails, RuntimeDispatchInfo}; use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use primitives::{ - slashing, AccountId, AccountIndex, Balance, BlockNumber, CandidateEvent, CandidateHash, - CommittedCandidateReceipt, CoreState, DisputeState, ExecutorParams, GroupRotationInfo, Hash, - Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, Moment, Nonce, - OccupiedCoreAssumption, PersistedValidationData, ScrapedOnChainVotes, SessionInfo, Signature, - ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, LOWEST_PUBLIC_ID, - PARACHAIN_KEY_TYPE_ID, + slashing, vstaging::ApprovalVotingParams, AccountId, AccountIndex, Balance, BlockNumber, + CandidateEvent, CandidateHash, CommittedCandidateReceipt, CoreState, DisputeState, + ExecutorParams, GroupRotationInfo, Hash, Id as ParaId, InboundDownwardMessage, + InboundHrmpMessage, Moment, Nonce, OccupiedCoreAssumption, PersistedValidationData, + ScrapedOnChainVotes, SessionInfo, Signature, ValidationCode, ValidationCodeHash, ValidatorId, + ValidatorIndex, LOWEST_PUBLIC_ID, PARACHAIN_KEY_TYPE_ID, }; use sp_core::OpaqueMetadata; use sp_mmr_primitives as mmr; @@ -1863,6 +1863,11 @@ sp_api::impl_runtime_apis! { key_ownership_proof, ) } + + /// Approval voting configuration parameters + fn approval_voting_params() -> ApprovalVotingParams { + parachains_runtime_api_impl::approval_voting_params::() + } } impl beefy_primitives::BeefyApi for Runtime { diff --git a/runtime/rococo/src/lib.rs b/runtime/rococo/src/lib.rs index 0b97cf56744c..69fc3526d772 100644 --- a/runtime/rococo/src/lib.rs +++ b/runtime/rococo/src/lib.rs @@ -23,11 +23,12 @@ use pallet_nis::WithMaximumOf; use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use primitives::{ - slashing, AccountId, AccountIndex, Balance, BlockNumber, CandidateEvent, CandidateHash, - CommittedCandidateReceipt, CoreState, DisputeState, ExecutorParams, GroupRotationInfo, Hash, - Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, Moment, Nonce, - OccupiedCoreAssumption, PersistedValidationData, ScrapedOnChainVotes, SessionInfo, Signature, - ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, PARACHAIN_KEY_TYPE_ID, + slashing, vstaging::ApprovalVotingParams, AccountId, AccountIndex, Balance, BlockNumber, + CandidateEvent, CandidateHash, CommittedCandidateReceipt, CoreState, DisputeState, + ExecutorParams, GroupRotationInfo, Hash, Id as ParaId, InboundDownwardMessage, + InboundHrmpMessage, Moment, Nonce, OccupiedCoreAssumption, PersistedValidationData, + ScrapedOnChainVotes, SessionInfo, Signature, ValidationCode, ValidationCodeHash, ValidatorId, + ValidatorIndex, PARACHAIN_KEY_TYPE_ID, }; use runtime_common::{ assigned_slots, auctions, claims, crowdloan, impl_runtime_weights, impls::ToAuthor, @@ -1944,6 +1945,10 @@ sp_api::impl_runtime_apis! { key_ownership_proof, ) } + /// Approval voting configuration parameters + fn approval_voting_params() -> ApprovalVotingParams { + parachains_runtime_api_impl::approval_voting_params::() + } } #[api_version(2)] diff --git a/runtime/westend/src/lib.rs b/runtime/westend/src/lib.rs index 6f89547c7a83..c05b8be9d777 100644 --- a/runtime/westend/src/lib.rs +++ b/runtime/westend/src/lib.rs @@ -39,12 +39,12 @@ use pallet_session::historical as session_historical; use pallet_transaction_payment::{CurrencyAdapter, FeeDetails, RuntimeDispatchInfo}; use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use primitives::{ - slashing, AccountId, AccountIndex, Balance, BlockNumber, CandidateEvent, CandidateHash, - CommittedCandidateReceipt, CoreState, DisputeState, ExecutorParams, GroupRotationInfo, Hash, - Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, Moment, Nonce, - OccupiedCoreAssumption, PersistedValidationData, PvfCheckStatement, ScrapedOnChainVotes, - SessionInfo, Signature, ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, - ValidatorSignature, PARACHAIN_KEY_TYPE_ID, + slashing, vstaging::ApprovalVotingParams, AccountId, AccountIndex, Balance, BlockNumber, + CandidateEvent, CandidateHash, CommittedCandidateReceipt, CoreState, DisputeState, + ExecutorParams, GroupRotationInfo, Hash, Id as ParaId, InboundDownwardMessage, + InboundHrmpMessage, Moment, Nonce, OccupiedCoreAssumption, PersistedValidationData, + PvfCheckStatement, ScrapedOnChainVotes, SessionInfo, Signature, ValidationCode, + ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature, PARACHAIN_KEY_TYPE_ID, }; use runtime_common::{ assigned_slots, auctions, crowdloan, elections::OnChainAccuracy, impl_runtime_weights, @@ -1609,6 +1609,11 @@ sp_api::impl_runtime_apis! { key_ownership_proof, ) } + + /// Approval voting configuration parameters + fn approval_voting_params() -> ApprovalVotingParams { + parachains_runtime_api_impl::approval_voting_params::() + } } impl beefy_primitives::BeefyApi for Runtime { From 1e3b5115d3959ec223bf7498a7f6dbedb3739165 Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe Date: Fri, 28 Jul 2023 18:45:07 +0300 Subject: [PATCH 03/54] Add zombienet for approval-coalescing Signed-off-by: Alexandru Gheorghe --- .../0006-approval-voting-coalescing.toml | 140 ++++++++++++++++++ .../0006-approval-voting-coalescing.zndsl | 51 +++++++ 2 files changed, 191 insertions(+) create mode 100644 zombienet_tests/functional/0006-approval-voting-coalescing.toml create mode 100644 zombienet_tests/functional/0006-approval-voting-coalescing.zndsl diff --git a/zombienet_tests/functional/0006-approval-voting-coalescing.toml b/zombienet_tests/functional/0006-approval-voting-coalescing.toml new file mode 100644 index 000000000000..89d337a8aa39 --- /dev/null +++ b/zombienet_tests/functional/0006-approval-voting-coalescing.toml @@ -0,0 +1,140 @@ +[settings] +timeout = 1000 + +[relaychain] +default_image = "{{ZOMBIENET_INTEGRATION_TEST_IMAGE}}" +chain = "rococo-local" +chain_spec_command = "polkadot build-spec --chain rococo-local --disable-default-bootnode" + +[relaychain.genesis.runtime.runtime_genesis_config.configuration.config.approval_voting_params] + max_approval_coalesce_count = {{MAX_APPROVAL_COALESCE_COUNT}} + max_approval_coalesce_wait_millis = {{MAX_APPROVAL_COALESCE_WAIT_MILLIS}} + +[relaychain.default_resources] +limits = { memory = "4G", cpu = "2" } +requests = { memory = "2G", cpu = "1" } + + [[relaychain.nodes]] + name = "alice" + args = [ "--alice", "-lparachain=debug,runtime=debug" ] + + [[relaychain.nodes]] + name = "bob" + args = [ "--bob", "-lparachain=debug,runtime=debug"] + + [[relaychain.nodes]] + name = "charlie" + args = [ "--charlie", "-lparachain=debug,runtime=debug" ] + + [[relaychain.nodes]] + name = "dave" + args = [ "--dave", "-lparachain=debug,runtime=debug"] + + [[relaychain.nodes]] + name = "ferdie" + args = [ "--ferdie", "-lparachain=debug,runtime=debug" ] + + [[relaychain.nodes]] + name = "eve" + args = [ "--eve", "-lparachain=debug,runtime=debug"] + + [[relaychain.nodes]] + name = "one" + args = [ "--one", "-lparachain=debug,runtime=debug" ] + + [[relaychain.nodes]] + name = "two" + args = [ "--two", "-lparachain=debug,runtime=debug"] + +[[parachains]] +id = 2000 +addToGenesis = true +genesis_state_generator = "undying-collator export-genesis-state --pov-size=100000 --pvf-complexity=1" + + [parachains.collator] + name = "collator01" + image = "{{COL_IMAGE}}" + command = "undying-collator" + args = ["-lparachain=debug", "--pov-size=100000", "--pvf-complexity=1", "--parachain-id=2000"] + +[[parachains]] +id = 2001 +addToGenesis = true +genesis_state_generator = "undying-collator export-genesis-state --pov-size=100000 --pvf-complexity=10" + + [parachains.collator] + name = "collator02" + image = "{{COL_IMAGE}}" + command = "undying-collator" + args = ["-lparachain=debug", "--pov-size=100000", "--parachain-id=2001", "--pvf-complexity=10"] + +[[parachains]] +id = 2002 +addToGenesis = true +genesis_state_generator = "undying-collator export-genesis-state --pov-size=100000 --pvf-complexity=100" + + [parachains.collator] + name = "collator03" + image = "{{COL_IMAGE}}" + command = "undying-collator" + args = ["-lparachain=debug", "--pov-size=100000", "--parachain-id=2002", "--pvf-complexity=100"] + +[[parachains]] +id = 2003 +addToGenesis = true +genesis_state_generator = "undying-collator export-genesis-state --pov-size=20000 --pvf-complexity=300" + + [parachains.collator] + name = "collator04" + image = "{{COL_IMAGE}}" + command = "undying-collator" + args = ["-lparachain=debug", "--pov-size=20000", "--parachain-id=2003", "--pvf-complexity=300"] + +[[parachains]] +id = 2004 +addToGenesis = true +genesis_state_generator = "undying-collator export-genesis-state --pov-size=100000 --pvf-complexity=300" + + [parachains.collator] + name = "collator05" + image = "{{COL_IMAGE}}" + command = "undying-collator" + args = ["-lparachain=debug", "--pov-size=100000", "--parachain-id=2004", "--pvf-complexity=300"] + +[[parachains]] +id = 2005 +addToGenesis = true +genesis_state_generator = "undying-collator export-genesis-state --pov-size=20000 --pvf-complexity=400" + + [parachains.collator] + name = "collator06" + image = "{{COL_IMAGE}}" + command = "undying-collator" + args = ["-lparachain=debug", "--pov-size=20000", "--pvf-complexity=400", "--parachain-id=2005"] + +[[parachains]] +id = 2006 +addToGenesis = true +genesis_state_generator = "undying-collator export-genesis-state --pov-size=100000 --pvf-complexity=300" + + [parachains.collator] + name = "collator07" + image = "{{COL_IMAGE}}" + command = "undying-collator" + args = ["-lparachain=debug", "--pov-size=100000", "--pvf-complexity=300", "--parachain-id=2006"] + +[[parachains]] +id = 2007 +addToGenesis = true +genesis_state_generator = "undying-collator export-genesis-state --pov-size=100000 --pvf-complexity=300" + + [parachains.collator] + name = "collator08" + image = "{{COL_IMAGE}}" + command = "undying-collator" + args = ["-lparachain=debug", "--pov-size=100000", "--pvf-complexity=300", "--parachain-id=2007"] + +[types.Header] +number = "u64" +parent_hash = "Hash" +post_state = "Hash" \ No newline at end of file diff --git a/zombienet_tests/functional/0006-approval-voting-coalescing.zndsl b/zombienet_tests/functional/0006-approval-voting-coalescing.zndsl new file mode 100644 index 000000000000..272ac4fa2640 --- /dev/null +++ b/zombienet_tests/functional/0006-approval-voting-coalescing.zndsl @@ -0,0 +1,51 @@ +Description: Approval voting coalescing does not lag finality +Network: ./0006-approval-voting-coalescing.toml +Creds: config + +# Check authority status. +alice: reports node_roles is 4 +bob: reports node_roles is 4 +charlie: reports node_roles is 4 +dave: reports node_roles is 4 +eve: reports node_roles is 4 +ferdie: reports node_roles is 4 +one: reports node_roles is 4 +two: reports node_roles is 4 + +# Ensure parachains are registered. +alice: parachain 2000 is registered within 60 seconds +bob: parachain 2001 is registered within 60 seconds +charlie: parachain 2002 is registered within 60 seconds +dave: parachain 2003 is registered within 60 seconds +ferdie: parachain 2004 is registered within 60 seconds +eve: parachain 2005 is registered within 60 seconds +one: parachain 2006 is registered within 60 seconds +two: parachain 2007 is registered within 60 seconds + +# Ensure parachains made progress. +alice: parachain 2000 block height is at least 10 within 300 seconds +alice: parachain 2001 block height is at least 10 within 300 seconds +alice: parachain 2002 block height is at least 10 within 300 seconds +alice: parachain 2003 block height is at least 10 within 300 seconds +alice: parachain 2004 block height is at least 10 within 300 seconds +alice: parachain 2005 block height is at least 10 within 300 seconds +alice: parachain 2006 block height is at least 10 within 300 seconds +alice: parachain 2007 block height is at least 10 within 300 seconds + +alice: reports substrate_block_height{status="finalized"} is at least 30 within 400 seconds +bob: reports substrate_block_height{status="finalized"} is at least 30 within 400 seconds +charlie: reports substrate_block_height{status="finalized"} is at least 30 within 400 seconds +dave: reports substrate_block_height{status="finalized"} is at least 30 within 400 seconds +eve: reports substrate_block_height{status="finalized"} is at least 30 within 400 seconds +ferdie: reports substrate_block_height{status="finalized"} is at least 30 within 400 seconds +one: reports substrate_block_height{status="finalized"} is at least 30 within 400 seconds +two: reports substrate_block_height{status="finalized"} is at least 30 within 400 seconds + +alice: reports polkadot_parachain_approval_checking_finality_lag is 0 +bob: reports polkadot_parachain_approval_checking_finality_lag is 0 +charlie: reports polkadot_parachain_approval_checking_finality_lag is 0 +dave: reports polkadot_parachain_approval_checking_finality_lag is 0 +ferdie: reports polkadot_parachain_approval_checking_finality_lag is 0 +eve: reports polkadot_parachain_approval_checking_finality_lag is 0 +one: reports polkadot_parachain_approval_checking_finality_lag is 0 +two: reports polkadot_parachain_approval_checking_finality_lag is 0 \ No newline at end of file From cf68a108286482ef1a308ad1507fd14fed88507b Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe Date: Mon, 31 Jul 2023 18:53:32 +0300 Subject: [PATCH 04/54] Refactor timers to address review comments Signed-off-by: Alexandru Gheorghe --- .../approval-voting/src/approval_db/v2/mod.rs | 1 + .../approval-voting/src/approvals_timers.rs | 80 ++++++++ node/core/approval-voting/src/lib.rs | 178 +++++++++--------- .../approval-voting/src/persisted_entries.rs | 11 +- 4 files changed, 183 insertions(+), 87 deletions(-) create mode 100644 node/core/approval-voting/src/approvals_timers.rs diff --git a/node/core/approval-voting/src/approval_db/v2/mod.rs b/node/core/approval-voting/src/approval_db/v2/mod.rs index 7791c3631b5b..f33f17b4386c 100644 --- a/node/core/approval-voting/src/approval_db/v2/mod.rs +++ b/node/core/approval-voting/src/approval_db/v2/mod.rs @@ -248,6 +248,7 @@ pub struct BlockEntry { /// Context needed for creating an approval signature for a given candidate. pub struct CandidateSigningContext { pub candidate_hash: CandidateHash, + pub approved_time_since_unix_epoch: u128, } impl From for Tick { diff --git a/node/core/approval-voting/src/approvals_timers.rs b/node/core/approval-voting/src/approvals_timers.rs new file mode 100644 index 000000000000..9225fad375f3 --- /dev/null +++ b/node/core/approval-voting/src/approvals_timers.rs @@ -0,0 +1,80 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! A simple implementation of a timer per block hash . +//! +use std::{collections::HashSet, task::Poll, time::Duration}; + +use futures::{ + future::BoxFuture, + stream::{FusedStream, FuturesUnordered}, + Stream, StreamExt, +}; +use futures_timer::Delay; +use polkadot_primitives::{Hash, ValidatorIndex}; +// A list of delayed futures that gets triggered when the waiting time has expired and it is +// time to sign the candidate. +// We have a timer per relay-chain block. +#[derive(Default)] +pub struct SignApprovalsTimers { + timers: FuturesUnordered>, + blocks: HashSet, +} + +impl SignApprovalsTimers { + /// Starts a single timer per block hash + /// + /// Guarantees that if a timer already exits for the give block hash, + /// no additional timer is started. + pub fn maybe_start_timer_for_block( + &mut self, + timer_duration_ms: u32, + block_hash: Hash, + validator_index: ValidatorIndex, + ) { + if self.blocks.insert(block_hash) { + let delay = Delay::new(Duration::from_millis(timer_duration_ms as _)); + self.timers.push(Box::pin(async move { + delay.await; + (block_hash, validator_index) + })); + } + } +} + +impl Stream for SignApprovalsTimers { + type Item = (Hash, ValidatorIndex); + + fn poll_next( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + let poll_result = self.timers.poll_next_unpin(cx); + match poll_result { + Poll::Ready(Some(result)) => { + self.blocks.remove(&result.0); + Poll::Ready(Some(result)) + }, + _ => poll_result, + } + } +} + +impl FusedStream for SignApprovalsTimers { + fn is_terminated(&self) -> bool { + self.timers.is_terminated() + } +} diff --git a/node/core/approval-voting/src/lib.rs b/node/core/approval-voting/src/lib.rs index d0eb855c35b1..f6380826fc2f 100644 --- a/node/core/approval-voting/src/lib.rs +++ b/node/core/approval-voting/src/lib.rs @@ -21,7 +21,7 @@ //! of others. It uses this information to determine when candidates and blocks have //! been sufficiently approved to finalize. -use futures_timer::Delay; +use approvals_timers::SignApprovalsTimers; use itertools::Itertools; use jaeger::{hash_to_trace_identifier, PerLeafSpan}; use polkadot_node_jaeger as jaeger; @@ -69,7 +69,8 @@ use futures::{ channel::oneshot, future::{BoxFuture, RemoteHandle}, prelude::*, - stream::{FuturesOrdered, FuturesUnordered}, + stream::FuturesUnordered, + StreamExt, }; use std::{ @@ -78,7 +79,7 @@ use std::{ }, num::NonZeroUsize, sync::Arc, - time::Duration, + time::{Duration, SystemTime, UNIX_EPOCH}, }; use approval_checking::RequiredTranches; @@ -89,6 +90,7 @@ use time::{slot_number_to_tick, Clock, ClockExt, SystemClock, Tick}; mod approval_checking; pub mod approval_db; +pub mod approvals_timers; mod backend; mod criteria; mod import; @@ -640,13 +642,6 @@ impl CurrentlyCheckingSet { } } -// A list of delayed futures that gets triggered when the waiting time has expired and it is -// time to sign the candidate. -#[derive(Default)] -struct SignApprovalsTimers { - pub timers: FuturesOrdered>, -} - async fn get_session_info<'a, Sender>( runtime_info: &'a mut RuntimeInfo, sender: &mut Sender, @@ -869,27 +864,30 @@ where actions }, - (block_hash, validator_index, candidate_hash) = sign_approvals_timers.timers.select_next_some() => { + (block_hash, validator_index) = sign_approvals_timers.select_next_some() => { gum::debug!( target: LOG_TARGET, - ?candidate_hash, - "Sign approval for candidate", + "Sign approval for multiple candidates", ); - if let Err(err) = maybe_create_signature( + match maybe_create_signature( &mut overlayed_db, &mut session_info_provider, &state, &mut ctx, block_hash, validator_index, - candidate_hash, &subsystem.metrics, ).await { - gum::error!( - target: LOG_TARGET, - ?err, - ?candidate_hash, - "Failed to create signature", - ); + Ok(Some(duration)) => { + sign_approvals_timers.maybe_start_timer_for_block(duration.as_millis() as u32, block_hash, validator_index); + }, + Ok(None) => {} + Err(err) => { + gum::error!( + target: LOG_TARGET, + ?err, + "Failed to create signature", + ); + } } vec![] } @@ -2964,28 +2962,6 @@ async fn issue_approval( }, }; - let (s_tx, s_rx) = oneshot::channel(); - - ctx.send_message(RuntimeApiMessage::Request( - block_hash, - RuntimeApiRequest::ApprovalVotingParams(s_tx), - )) - .await; - - let approval_params = match s_rx.await { - Ok(Ok(s)) => s, - _ => { - gum::error!( - target: LOG_TARGET, - "Could not request approval voting params from runtime using defaults" - ); - ApprovalVotingParams { - max_approval_coalesce_count: 1, - max_approval_coalesce_wait_millis: 0, - } - }, - }; - let candidate_index = match block_entry.candidates().iter().position(|e| e.1 == candidate_hash) { None => { @@ -3033,26 +3009,17 @@ async fn issue_approval( }, }; - block_entry - .candidates_pending_signature - .insert(candidate_index as _, CandidateSigningContext { candidate_hash }); - - let should_create_signature = block_entry.candidates_pending_signature.len() >= - approval_params.max_approval_coalesce_count as usize; - - gum::debug!( - target: LOG_TARGET, - "Approval coalese params{:?}", approval_params + block_entry.candidates_pending_signature.insert( + candidate_index as _, + CandidateSigningContext { + candidate_hash, + approved_time_since_unix_epoch: SystemTime::now() + .duration_since(UNIX_EPOCH) + .map(|duration| duration.as_millis()) + .unwrap_or(0), + }, ); - let delay = - Delay::new(Duration::from_millis(approval_params.max_approval_coalesce_wait_millis as _)); - - sign_approvals_timers.timers.push_back(Box::pin(async move { - delay.await; - (block_hash, validator_index, candidate_hash) - })); - gum::info!( target: LOG_TARGET, ?candidate_hash, @@ -3074,18 +3041,22 @@ async fn issue_approval( ) .await; - if should_create_signature { - maybe_create_signature( - db, - session_info_provider, - state, - ctx, + if let Some(timer_duration) = maybe_create_signature( + db, + session_info_provider, + state, + ctx, + block_hash, + validator_index, + metrics, + ) + .await? + { + sign_approvals_timers.maybe_start_timer_for_block( + timer_duration.as_millis() as u32, block_hash, validator_index, - candidate_hash, - metrics, - ) - .await?; + ); } Ok(actions) } @@ -3099,9 +3070,8 @@ async fn maybe_create_signature( ctx: &mut Context, block_hash: Hash, validator_index: ValidatorIndex, - current_candidate_hash: CandidateHash, metrics: &Metrics, -) -> SubsystemResult<()> { +) -> SubsystemResult> { let mut block_entry = match db.load_block_entry(&block_hash)? { Some(b) => b, None => { @@ -3111,24 +3081,62 @@ async fn maybe_create_signature( target: LOG_TARGET, "Could not find block that needs signature {:}", block_hash ); - return Ok(()) + return Ok(None) }, }; - // If the candidate that woke us is not pending do nothing and let one of the wakeup of the - // pending candidates create the - // signature. gum::debug!( target: LOG_TARGET, "Candidates pending signatures {:}", block_entry.candidates_pending_signature.len() ); - if !block_entry + + let oldest_candidate_to_sign = match block_entry .candidates_pending_signature .values() - .map(|val| val.candidate_hash) - .contains(¤t_candidate_hash) + .min_by(|a, b| a.approved_time_since_unix_epoch.cmp(&b.approved_time_since_unix_epoch)) { - return Ok(()) + Some(candidate) => candidate, + None => return Ok(None), + }; + + let (s_tx, s_rx) = oneshot::channel(); + + ctx.send_message(RuntimeApiMessage::Request( + block_hash, + RuntimeApiRequest::ApprovalVotingParams(s_tx), + )) + .await; + + let approval_params = match s_rx.await { + Ok(Ok(s)) => s, + _ => { + gum::error!( + target: LOG_TARGET, + "Could not request approval voting params from runtime using defaults" + ); + ApprovalVotingParams { + max_approval_coalesce_count: 1, + max_approval_coalesce_wait_millis: 100, + } + }, + }; + + let passed_since_approved = SystemTime::now() + .duration_since(UNIX_EPOCH) + .map(|duration| duration.as_millis()) + .map(|now| now.checked_sub(oldest_candidate_to_sign.approved_time_since_unix_epoch)); + + match passed_since_approved { + Ok(Some(passed_since_approved)) + if passed_since_approved < + approval_params.max_approval_coalesce_wait_millis as u128 && + (block_entry.candidates_pending_signature.len() as u32) < + approval_params.max_approval_coalesce_count => + return Ok(Some(Duration::from_millis( + (approval_params.max_approval_coalesce_wait_millis as u128 - passed_since_approved) + as u64, + ))), + _ => {}, } let session_info = match get_session_info( @@ -3146,7 +3154,7 @@ async fn maybe_create_signature( target: LOG_TARGET, "Could not retrieve the session" ); - return Ok(()) + return Ok(None) }, }; @@ -3161,7 +3169,7 @@ async fn maybe_create_signature( ); metrics.on_approval_error(); - return Ok(()) + return Ok(None) }, }; @@ -3187,7 +3195,7 @@ async fn maybe_create_signature( ); metrics.on_approval_error(); - return Ok(()) + return Ok(None) }, }; @@ -3231,7 +3239,7 @@ async fn maybe_create_signature( ); block_entry.candidates_pending_signature.clear(); db.write_block_entry(block_entry.into()); - Ok(()) + Ok(None) } // Sign an approval vote. Fails if the key isn't present in the store. diff --git a/node/core/approval-voting/src/persisted_entries.rs b/node/core/approval-voting/src/persisted_entries.rs index 2b1cbaed027e..bf3d52956998 100644 --- a/node/core/approval-voting/src/persisted_entries.rs +++ b/node/core/approval-voting/src/persisted_entries.rs @@ -366,6 +366,7 @@ pub struct BlockEntry { #[derive(Debug, Clone, PartialEq)] pub struct CandidateSigningContext { pub candidate_hash: CandidateHash, + pub approved_time_since_unix_epoch: u128, } impl BlockEntry { @@ -486,13 +487,19 @@ impl From for crate::approval_db::v2::BlockEntry { impl From for CandidateSigningContext { fn from(signing_context: crate::approval_db::v2::CandidateSigningContext) -> Self { - Self { candidate_hash: signing_context.candidate_hash } + Self { + candidate_hash: signing_context.candidate_hash, + approved_time_since_unix_epoch: signing_context.approved_time_since_unix_epoch, + } } } impl From for crate::approval_db::v2::CandidateSigningContext { fn from(signing_context: CandidateSigningContext) -> Self { - Self { candidate_hash: signing_context.candidate_hash } + Self { + candidate_hash: signing_context.candidate_hash, + approved_time_since_unix_epoch: signing_context.approved_time_since_unix_epoch, + } } } From fd4b90646c95b2393838af47143edfa127186967 Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe Date: Tue, 1 Aug 2023 09:52:07 +0300 Subject: [PATCH 05/54] Make wait time for caching relative to no-show ... additionally computed in ticks as it is done everywhere else. And added some tests to make sure approval-voting behaves the way we intended to. Signed-off-by: Alexandru Gheorghe --- .../approval-voting/src/approval_db/v2/mod.rs | 2 +- .../approval-voting/src/approvals_timers.rs | 80 --- node/core/approval-voting/src/lib.rs | 162 +++--- .../approval-voting/src/persisted_entries.rs | 6 +- node/core/approval-voting/src/tests.rs | 477 ++++++++++++++++++ node/core/approval-voting/src/time.rs | 165 +++++- primitives/src/vstaging/mod.rs | 4 +- runtime/parachains/src/configuration.rs | 2 +- .../src/configuration/migration/v6.rs | 2 +- 9 files changed, 752 insertions(+), 148 deletions(-) delete mode 100644 node/core/approval-voting/src/approvals_timers.rs diff --git a/node/core/approval-voting/src/approval_db/v2/mod.rs b/node/core/approval-voting/src/approval_db/v2/mod.rs index f33f17b4386c..20260c3e842e 100644 --- a/node/core/approval-voting/src/approval_db/v2/mod.rs +++ b/node/core/approval-voting/src/approval_db/v2/mod.rs @@ -248,7 +248,7 @@ pub struct BlockEntry { /// Context needed for creating an approval signature for a given candidate. pub struct CandidateSigningContext { pub candidate_hash: CandidateHash, - pub approved_time_since_unix_epoch: u128, + pub send_no_later_than_tick: Tick, } impl From for Tick { diff --git a/node/core/approval-voting/src/approvals_timers.rs b/node/core/approval-voting/src/approvals_timers.rs deleted file mode 100644 index 9225fad375f3..000000000000 --- a/node/core/approval-voting/src/approvals_timers.rs +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot 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. - -// Polkadot 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 Polkadot. If not, see . - -//! A simple implementation of a timer per block hash . -//! -use std::{collections::HashSet, task::Poll, time::Duration}; - -use futures::{ - future::BoxFuture, - stream::{FusedStream, FuturesUnordered}, - Stream, StreamExt, -}; -use futures_timer::Delay; -use polkadot_primitives::{Hash, ValidatorIndex}; -// A list of delayed futures that gets triggered when the waiting time has expired and it is -// time to sign the candidate. -// We have a timer per relay-chain block. -#[derive(Default)] -pub struct SignApprovalsTimers { - timers: FuturesUnordered>, - blocks: HashSet, -} - -impl SignApprovalsTimers { - /// Starts a single timer per block hash - /// - /// Guarantees that if a timer already exits for the give block hash, - /// no additional timer is started. - pub fn maybe_start_timer_for_block( - &mut self, - timer_duration_ms: u32, - block_hash: Hash, - validator_index: ValidatorIndex, - ) { - if self.blocks.insert(block_hash) { - let delay = Delay::new(Duration::from_millis(timer_duration_ms as _)); - self.timers.push(Box::pin(async move { - delay.await; - (block_hash, validator_index) - })); - } - } -} - -impl Stream for SignApprovalsTimers { - type Item = (Hash, ValidatorIndex); - - fn poll_next( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - let poll_result = self.timers.poll_next_unpin(cx); - match poll_result { - Poll::Ready(Some(result)) => { - self.blocks.remove(&result.0); - Poll::Ready(Some(result)) - }, - _ => poll_result, - } - } -} - -impl FusedStream for SignApprovalsTimers { - fn is_terminated(&self) -> bool { - self.timers.is_terminated() - } -} diff --git a/node/core/approval-voting/src/lib.rs b/node/core/approval-voting/src/lib.rs index f6380826fc2f..b212874d356c 100644 --- a/node/core/approval-voting/src/lib.rs +++ b/node/core/approval-voting/src/lib.rs @@ -21,7 +21,6 @@ //! of others. It uses this information to determine when candidates and blocks have //! been sufficiently approved to finalize. -use approvals_timers::SignApprovalsTimers; use itertools::Itertools; use jaeger::{hash_to_trace_identifier, PerLeafSpan}; use polkadot_node_jaeger as jaeger; @@ -74,23 +73,23 @@ use futures::{ }; use std::{ + cmp::min, collections::{ btree_map::Entry as BTMEntry, hash_map::Entry as HMEntry, BTreeMap, HashMap, HashSet, }, num::NonZeroUsize, sync::Arc, - time::{Duration, SystemTime, UNIX_EPOCH}, + time::Duration, }; use approval_checking::RequiredTranches; use bitvec::{order::Lsb0, vec::BitVec}; use criteria::{AssignmentCriteria, RealAssignmentCriteria}; use persisted_entries::{ApprovalEntry, BlockEntry, CandidateEntry}; -use time::{slot_number_to_tick, Clock, ClockExt, SystemClock, Tick}; +use time::{slot_number_to_tick, Clock, ClockExt, DelayedApprovalTimer, SystemClock, Tick}; mod approval_checking; pub mod approval_db; -pub mod approvals_timers; mod backend; mod criteria; mod import; @@ -785,7 +784,7 @@ where let mut wakeups = Wakeups::default(); let mut currently_checking_set = CurrentlyCheckingSet::default(); let mut approvals_cache = lru::LruCache::new(APPROVAL_CACHE_SIZE); - let mut sign_approvals_timers = SignApprovalsTimers::default(); + let mut delayed_approvals_timers = DelayedApprovalTimer::default(); let mut last_finalized_height: Option = { let (tx, rx) = oneshot::channel(); @@ -864,21 +863,25 @@ where actions }, - (block_hash, validator_index) = sign_approvals_timers.select_next_some() => { + (block_hash, validator_index) = delayed_approvals_timers.select_next_some() => { gum::debug!( target: LOG_TARGET, "Sign approval for multiple candidates", ); + + let approval_params = get_approval_voting_params_or_default(&mut ctx, block_hash).await; + match maybe_create_signature( &mut overlayed_db, &mut session_info_provider, + approval_params, &state, &mut ctx, block_hash, validator_index, &subsystem.metrics, ).await { - Ok(Some(duration)) => { - sign_approvals_timers.maybe_start_timer_for_block(duration.as_millis() as u32, block_hash, validator_index); + Ok(Some(next_wakeup)) => { + delayed_approvals_timers.maybe_arm_timer(next_wakeup, state.clock.as_ref(), block_hash, validator_index); }, Ok(None) => {} Err(err) => { @@ -902,7 +905,7 @@ where &mut wakeups, &mut currently_checking_set, &mut approvals_cache, - &mut sign_approvals_timers, + &mut delayed_approvals_timers, &mut subsystem.mode, actions, ) @@ -950,7 +953,7 @@ async fn handle_actions( wakeups: &mut Wakeups, currently_checking_set: &mut CurrentlyCheckingSet, approvals_cache: &mut lru::LruCache, - sign_approvals_timers: &mut SignApprovalsTimers, + delayed_approvals_timers: &mut DelayedApprovalTimer, mode: &mut Mode, actions: Vec, ) -> SubsystemResult { @@ -980,7 +983,7 @@ async fn handle_actions( session_info_provider, metrics, candidate_hash, - sign_approvals_timers, + delayed_approvals_timers, approval_request, ) .await? @@ -1019,7 +1022,6 @@ async fn handle_actions( let block_hash = indirect_cert.block_hash; launch_approval_span.add_string_tag("block-hash", format!("{:?}", block_hash)); let validator_index = indirect_cert.validator; - ctx.send_unbounded_message(ApprovalDistributionMessage::DistributeAssignment( indirect_cert, claimed_candidate_indices, @@ -2939,7 +2941,7 @@ async fn issue_approval( session_info_provider: &mut RuntimeInfo, metrics: &Metrics, candidate_hash: CandidateHash, - sign_approvals_timers: &mut SignApprovalsTimers, + delayed_approvals_timers: &mut DelayedApprovalTimer, ApprovalVoteRequest { validator_index, block_hash }: ApprovalVoteRequest, ) -> SubsystemResult> { let mut issue_approval_span = state @@ -3009,14 +3011,30 @@ async fn issue_approval( }, }; + let session_info = match get_session_info( + session_info_provider, + ctx.sender(), + block_entry.parent_hash(), + block_entry.session(), + ) + .await + { + Some(s) => s, + None => return Ok(Vec::new()), + }; + + let approval_params = get_approval_voting_params_or_default(ctx, block_hash).await; + block_entry.candidates_pending_signature.insert( candidate_index as _, CandidateSigningContext { candidate_hash, - approved_time_since_unix_epoch: SystemTime::now() - .duration_since(UNIX_EPOCH) - .map(|duration| duration.as_millis()) - .unwrap_or(0), + send_no_later_than_tick: compute_delayed_approval_sending_tick( + state, + &block_entry, + session_info, + &approval_params, + ), }, ); @@ -3041,9 +3059,10 @@ async fn issue_approval( ) .await; - if let Some(timer_duration) = maybe_create_signature( + if let Some(next_wakeup) = maybe_create_signature( db, session_info_provider, + approval_params, state, ctx, block_hash, @@ -3052,8 +3071,9 @@ async fn issue_approval( ) .await? { - sign_approvals_timers.maybe_start_timer_for_block( - timer_duration.as_millis() as u32, + delayed_approvals_timers.maybe_arm_timer( + next_wakeup, + state.clock.as_ref(), block_hash, validator_index, ); @@ -3066,12 +3086,13 @@ async fn issue_approval( async fn maybe_create_signature( db: &mut OverlayedBackend<'_, impl Backend>, session_info_provider: &mut RuntimeInfo, + approval_params: ApprovalVotingParams, state: &State, ctx: &mut Context, block_hash: Hash, validator_index: ValidatorIndex, metrics: &Metrics, -) -> SubsystemResult> { +) -> SubsystemResult> { let mut block_entry = match db.load_block_entry(&block_hash)? { Some(b) => b, None => { @@ -3093,50 +3114,21 @@ async fn maybe_create_signature( let oldest_candidate_to_sign = match block_entry .candidates_pending_signature .values() - .min_by(|a, b| a.approved_time_since_unix_epoch.cmp(&b.approved_time_since_unix_epoch)) + .min_by(|a, b| a.send_no_later_than_tick.cmp(&b.send_no_later_than_tick)) { Some(candidate) => candidate, + // No cached candidates, nothing to do here, this just means the timer fired, + // but the signatures were already sent because we gathered more than max_approval_coalesce_count. None => return Ok(None), }; - let (s_tx, s_rx) = oneshot::channel(); - - ctx.send_message(RuntimeApiMessage::Request( - block_hash, - RuntimeApiRequest::ApprovalVotingParams(s_tx), - )) - .await; - - let approval_params = match s_rx.await { - Ok(Ok(s)) => s, - _ => { - gum::error!( - target: LOG_TARGET, - "Could not request approval voting params from runtime using defaults" - ); - ApprovalVotingParams { - max_approval_coalesce_count: 1, - max_approval_coalesce_wait_millis: 100, - } - }, - }; + let tick_now = state.clock.tick_now(); - let passed_since_approved = SystemTime::now() - .duration_since(UNIX_EPOCH) - .map(|duration| duration.as_millis()) - .map(|now| now.checked_sub(oldest_candidate_to_sign.approved_time_since_unix_epoch)); - - match passed_since_approved { - Ok(Some(passed_since_approved)) - if passed_since_approved < - approval_params.max_approval_coalesce_wait_millis as u128 && - (block_entry.candidates_pending_signature.len() as u32) < - approval_params.max_approval_coalesce_count => - return Ok(Some(Duration::from_millis( - (approval_params.max_approval_coalesce_wait_millis as u128 - passed_since_approved) - as u64, - ))), - _ => {}, + if oldest_candidate_to_sign.send_no_later_than_tick > tick_now && + (block_entry.candidates_pending_signature.len() as u32) < + approval_params.max_approval_coalesce_count + { + return Ok(Some(oldest_candidate_to_sign.send_no_later_than_tick)) } let session_info = match get_session_info( @@ -3281,3 +3273,55 @@ fn issue_local_invalid_statement( false, )); } + +#[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] +async fn get_approval_voting_params_or_default( + ctx: &mut Context, + block_hash: Hash, +) -> ApprovalVotingParams { + let (s_tx, s_rx) = oneshot::channel(); + + ctx.send_message(RuntimeApiMessage::Request( + block_hash, + RuntimeApiRequest::ApprovalVotingParams(s_tx), + )) + .await; + + match s_rx.await { + Ok(Ok(s)) => s, + _ => { + gum::error!( + target: LOG_TARGET, + "Could not request approval voting params from runtime using defaults" + ); + ApprovalVotingParams { + max_approval_coalesce_count: 1, + max_approval_coalesce_wait_ticks: 2, + } + }, + } +} + +fn compute_delayed_approval_sending_tick( + state: &State, + block_entry: &BlockEntry, + session_info: &SessionInfo, + approval_params: &ApprovalVotingParams, +) -> Tick { + let current_block_tick = slot_number_to_tick(state.slot_duration_millis, block_entry.slot()); + + let no_show_duration_ticks = slot_number_to_tick( + state.slot_duration_millis, + Slot::from(u64::from(session_info.no_show_slots)), + ); + let tick_now = state.clock.tick_now(); + + min( + tick_now + approval_params.max_approval_coalesce_wait_ticks as Tick, + // We don't want to accidentally cause, no-shows so if we are past + // the 2 thirds of the no show time, force the sending of the + // approval immediately. + // TODO: TBD the right value here, so that we don't accidentally create no-shows. + current_block_tick + (no_show_duration_ticks * 2) / 3, + ) +} diff --git a/node/core/approval-voting/src/persisted_entries.rs b/node/core/approval-voting/src/persisted_entries.rs index bf3d52956998..ac1dea98ebda 100644 --- a/node/core/approval-voting/src/persisted_entries.rs +++ b/node/core/approval-voting/src/persisted_entries.rs @@ -366,7 +366,7 @@ pub struct BlockEntry { #[derive(Debug, Clone, PartialEq)] pub struct CandidateSigningContext { pub candidate_hash: CandidateHash, - pub approved_time_since_unix_epoch: u128, + pub send_no_later_than_tick: Tick, } impl BlockEntry { @@ -489,7 +489,7 @@ impl From for CandidateSigningC fn from(signing_context: crate::approval_db::v2::CandidateSigningContext) -> Self { Self { candidate_hash: signing_context.candidate_hash, - approved_time_since_unix_epoch: signing_context.approved_time_since_unix_epoch, + send_no_later_than_tick: signing_context.send_no_later_than_tick.into(), } } } @@ -498,7 +498,7 @@ impl From for crate::approval_db::v2::CandidateSigningC fn from(signing_context: CandidateSigningContext) -> Self { Self { candidate_hash: signing_context.candidate_hash, - approved_time_since_unix_epoch: signing_context.approved_time_since_unix_epoch, + send_no_later_than_tick: signing_context.send_no_later_than_tick.into(), } } } diff --git a/node/core/approval-voting/src/tests.rs b/node/core/approval-voting/src/tests.rs index c37850d0466f..3ad5457aace7 100644 --- a/node/core/approval-voting/src/tests.rs +++ b/node/core/approval-voting/src/tests.rs @@ -2595,11 +2595,31 @@ async fn handle_double_assignment_import( } ); + assert_matches!( + overseer_recv(virtual_overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request(_, RuntimeApiRequest::ApprovalVotingParams(sender))) => { + let _ = sender.send(Ok(ApprovalVotingParams { + max_approval_coalesce_count: 1, + max_approval_coalesce_wait_ticks: 0, + })); + } + ); + assert_matches!( overseer_recv(virtual_overseer).await, AllMessages::ApprovalDistribution(ApprovalDistributionMessage::DistributeApproval(_)) ); + assert_matches!( + overseer_recv(virtual_overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request(_, RuntimeApiRequest::ApprovalVotingParams(sender))) => { + let _ = sender.send(Ok(ApprovalVotingParams { + max_approval_coalesce_count: 1, + max_approval_coalesce_wait_ticks: 0, + })); + } + ); + assert_matches!( overseer_recv(virtual_overseer).await, AllMessages::ApprovalDistribution(ApprovalDistributionMessage::DistributeApproval(_)) @@ -3334,3 +3354,460 @@ fn waits_until_approving_assignments_are_old_enough() { virtual_overseer }); } + +#[test] +fn test_approval_is_sent_on_max_approval_coalesce_count() { + let assignment_criteria = Box::new(MockAssignmentCriteria( + || { + let mut assignments = HashMap::new(); + let _ = assignments.insert( + CoreIndex(0), + approval_db::v2::OurAssignment { + cert: garbage_assignment_cert(AssignmentCertKind::RelayVRFModulo { sample: 0 }) + .into(), + tranche: 0, + validator_index: ValidatorIndex(0), + triggered: false, + } + .into(), + ); + + let assignments_cert = + garbage_assignment_cert_v2(AssignmentCertKindV2::RelayVRFModuloCompact { + core_bitfield: vec![CoreIndex(0), CoreIndex(1), CoreIndex(2)] + .try_into() + .unwrap(), + }); + let _ = assignments.insert( + CoreIndex(0), + approval_db::v2::OurAssignment { + cert: assignments_cert.clone(), + tranche: 0, + validator_index: ValidatorIndex(0), + triggered: false, + } + .into(), + ); + + let _ = assignments.insert( + CoreIndex(1), + approval_db::v2::OurAssignment { + cert: assignments_cert.clone(), + tranche: 0, + validator_index: ValidatorIndex(0), + triggered: false, + } + .into(), + ); + assignments + }, + |_| Ok(0), + )); + + let config = HarnessConfigBuilder::default().assignment_criteria(assignment_criteria).build(); + let store = config.backend(); + + test_harness(config, |test_harness| async move { + let TestHarness { mut virtual_overseer, clock, sync_oracle_handle: _sync_oracle_handle } = + test_harness; + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::ChainApi(ChainApiMessage::FinalizedBlockNumber(rx)) => { + rx.send(Ok(0)).unwrap(); + } + ); + + let block_hash = Hash::repeat_byte(0x01); + + let candidate_commitments = CandidateCommitments::default(); + + let candidate_receipt1 = { + let mut receipt = dummy_candidate_receipt(block_hash); + receipt.descriptor.para_id = ParaId::from(1_u32); + receipt.commitments_hash = candidate_commitments.hash(); + receipt + }; + + let candidate_hash1 = candidate_receipt1.hash(); + + let candidate_receipt2 = { + let mut receipt = dummy_candidate_receipt(block_hash); + receipt.descriptor.para_id = ParaId::from(2_u32); + receipt.commitments_hash = candidate_commitments.hash(); + receipt + }; + + let slot = Slot::from(1); + let candidate_index1 = 0; + let candidate_index2 = 1; + + let validators = vec![ + Sr25519Keyring::Alice, + Sr25519Keyring::Bob, + Sr25519Keyring::Charlie, + Sr25519Keyring::Dave, + Sr25519Keyring::Eve, + ]; + let session_info = SessionInfo { + validator_groups: IndexedVec::>::from(vec![ + vec![ValidatorIndex(0), ValidatorIndex(1)], + vec![ValidatorIndex(2)], + vec![ValidatorIndex(3), ValidatorIndex(4)], + ]), + ..session_info(&validators) + }; + + let candidates = Some(vec![ + (candidate_receipt1.clone(), CoreIndex(0), GroupIndex(0)), + (candidate_receipt2.clone(), CoreIndex(1), GroupIndex(1)), + ]); + ChainBuilder::new() + .add_block( + block_hash, + ChainBuilder::GENESIS_HASH, + 1, + BlockConfig { + slot, + candidates: candidates.clone(), + session_info: Some(session_info.clone()), + }, + ) + .build(&mut virtual_overseer) + .await; + + assert!(!clock.inner.lock().current_wakeup_is(1)); + clock.inner.lock().wakeup_all(1); + + assert!(clock.inner.lock().current_wakeup_is(slot_to_tick(slot))); + clock.inner.lock().wakeup_all(slot_to_tick(slot)); + + futures_timer::Delay::new(Duration::from_millis(200)).await; + + clock.inner.lock().wakeup_all(slot_to_tick(slot + 2)); + + assert_eq!(clock.inner.lock().wakeups.len(), 0); + + futures_timer::Delay::new(Duration::from_millis(200)).await; + + let candidate_entry = store.load_candidate_entry(&candidate_hash1).unwrap().unwrap(); + let our_assignment = + candidate_entry.approval_entry(&block_hash).unwrap().our_assignment().unwrap(); + assert!(our_assignment.triggered()); + + handle_approval_on_max_coalesce_count( + &mut virtual_overseer, + vec![candidate_index1, candidate_index2], + ) + .await; + + virtual_overseer + }); +} + +async fn handle_approval_on_max_coalesce_count( + virtual_overseer: &mut VirtualOverseer, + candidate_indicies: Vec, +) { + for _ in &candidate_indicies { + assert_matches!( + overseer_recv(virtual_overseer).await, + AllMessages::ApprovalDistribution(ApprovalDistributionMessage::DistributeAssignment( + _, + c_indices, + )) => { + assert_eq!(TryInto::::try_into(candidate_indicies.clone()).unwrap(), c_indices); + } + ); + + recover_available_data(virtual_overseer).await; + fetch_validation_code(virtual_overseer).await; + } + + for _ in &candidate_indicies { + assert_matches!( + overseer_recv(virtual_overseer).await, + AllMessages::CandidateValidation(CandidateValidationMessage::ValidateFromExhaustive(_, _, _, _, timeout, tx)) if timeout == PvfExecTimeoutKind::Approval => { + tx.send(Ok(ValidationResult::Valid(Default::default(), Default::default()))) + .unwrap(); + } + ); + } + + assert_matches!( + overseer_recv(virtual_overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request(_, RuntimeApiRequest::ApprovalVotingParams(sender))) => { + let _ = sender.send(Ok(ApprovalVotingParams { + max_approval_coalesce_count: 2, + max_approval_coalesce_wait_ticks: 10000, + })); + } + ); + + assert_matches!( + overseer_recv(virtual_overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request(_, RuntimeApiRequest::ApprovalVotingParams(sender))) => { + let _ = sender.send(Ok(ApprovalVotingParams { + max_approval_coalesce_count: 2, + max_approval_coalesce_wait_ticks: 10000, + })); + } + ); + + assert_matches!( + overseer_recv(virtual_overseer).await, + AllMessages::ApprovalDistribution(ApprovalDistributionMessage::DistributeApproval(vote)) => { + assert_eq!(TryInto::::try_into(candidate_indicies).unwrap(), vote.candidate_indices); + } + ); + + // Assert that there are no more messages being sent by the subsystem + assert!(overseer_recv(virtual_overseer).timeout(TIMEOUT / 2).await.is_none()); +} + +async fn handle_approval_on_max_wait_time( + virtual_overseer: &mut VirtualOverseer, + candidate_indicies: Vec, + clock: Box, +) { + const TICK_NOW_BEGIN: u64 = 1; + const MAX_COALESCE_COUNT: u32 = 3; + const MAX_APPROVAL_COALESCE_WAIT_TICKS: u32 = 4; + + clock.inner.lock().set_tick(TICK_NOW_BEGIN); + + for _ in &candidate_indicies { + assert_matches!( + overseer_recv(virtual_overseer).await, + AllMessages::ApprovalDistribution(ApprovalDistributionMessage::DistributeAssignment( + _, + c_indices, + )) => { + assert_eq!(TryInto::::try_into(candidate_indicies.clone()).unwrap(), c_indices); + } + ); + + recover_available_data(virtual_overseer).await; + fetch_validation_code(virtual_overseer).await; + } + + for _ in &candidate_indicies { + assert_matches!( + overseer_recv(virtual_overseer).await, + AllMessages::CandidateValidation(CandidateValidationMessage::ValidateFromExhaustive(_, _, _, _, timeout, tx)) if timeout == PvfExecTimeoutKind::Approval => { + tx.send(Ok(ValidationResult::Valid(Default::default(), Default::default()))) + .unwrap(); + } + ); + } + + // First time we fetch the configuration when we are ready to approve the first candidate + assert_matches!( + overseer_recv(virtual_overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request(_, RuntimeApiRequest::ApprovalVotingParams(sender))) => { + let _ = sender.send(Ok(ApprovalVotingParams { + max_approval_coalesce_count: MAX_COALESCE_COUNT, + max_approval_coalesce_wait_ticks: MAX_APPROVAL_COALESCE_WAIT_TICKS, + })); + } + ); + + // Second time we fetch the configuration when we are ready to approve the second candidate + assert_matches!( + overseer_recv(virtual_overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request(_, RuntimeApiRequest::ApprovalVotingParams(sender))) => { + let _ = sender.send(Ok(ApprovalVotingParams { + max_approval_coalesce_count: MAX_COALESCE_COUNT, + max_approval_coalesce_wait_ticks: MAX_APPROVAL_COALESCE_WAIT_TICKS, + })); + } + ); + + assert!(overseer_recv(virtual_overseer).timeout(TIMEOUT / 2).await.is_none()); + + // Move the clock just before we should send the approval + clock + .inner + .lock() + .set_tick(MAX_APPROVAL_COALESCE_WAIT_TICKS as Tick + TICK_NOW_BEGIN - 1); + + assert!(overseer_recv(virtual_overseer).timeout(TIMEOUT / 2).await.is_none()); + + // Move the clock tick, so we can trigger a force sending of the approvals + clock + .inner + .lock() + .set_tick(MAX_APPROVAL_COALESCE_WAIT_TICKS as Tick + TICK_NOW_BEGIN); + + // Third time we fetch the configuration when timer expires and we are ready to sent the approval + assert_matches!( + overseer_recv(virtual_overseer).await, + AllMessages::RuntimeApi(RuntimeApiMessage::Request(_, RuntimeApiRequest::ApprovalVotingParams(sender))) => { + let _ = sender.send(Ok(ApprovalVotingParams { + max_approval_coalesce_count: 3, + max_approval_coalesce_wait_ticks: 4, + })); + } + ); + + assert_matches!( + overseer_recv(virtual_overseer).await, + AllMessages::ApprovalDistribution(ApprovalDistributionMessage::DistributeApproval(vote)) => { + assert_eq!(TryInto::::try_into(candidate_indicies).unwrap(), vote.candidate_indices); + } + ); + + // Assert that there are no more messages being sent by the subsystem + assert!(overseer_recv(virtual_overseer).timeout(TIMEOUT / 2).await.is_none()); +} + +#[test] +fn test_approval_is_sent_on_max_approval_coalesce_wait() { + let assignment_criteria = Box::new(MockAssignmentCriteria( + || { + let mut assignments = HashMap::new(); + let _ = assignments.insert( + CoreIndex(0), + approval_db::v2::OurAssignment { + cert: garbage_assignment_cert(AssignmentCertKind::RelayVRFModulo { sample: 0 }) + .into(), + tranche: 0, + validator_index: ValidatorIndex(0), + triggered: false, + } + .into(), + ); + + let assignments_cert = + garbage_assignment_cert_v2(AssignmentCertKindV2::RelayVRFModuloCompact { + core_bitfield: vec![CoreIndex(0), CoreIndex(1), CoreIndex(2)] + .try_into() + .unwrap(), + }); + let _ = assignments.insert( + CoreIndex(0), + approval_db::v2::OurAssignment { + cert: assignments_cert.clone(), + tranche: 0, + validator_index: ValidatorIndex(0), + triggered: false, + } + .into(), + ); + + let _ = assignments.insert( + CoreIndex(1), + approval_db::v2::OurAssignment { + cert: assignments_cert.clone(), + tranche: 0, + validator_index: ValidatorIndex(0), + triggered: false, + } + .into(), + ); + assignments + }, + |_| Ok(0), + )); + + let config = HarnessConfigBuilder::default().assignment_criteria(assignment_criteria).build(); + let store = config.backend(); + + test_harness(config, |test_harness| async move { + let TestHarness { mut virtual_overseer, clock, sync_oracle_handle: _sync_oracle_handle } = + test_harness; + + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::ChainApi(ChainApiMessage::FinalizedBlockNumber(rx)) => { + rx.send(Ok(0)).unwrap(); + } + ); + + let block_hash = Hash::repeat_byte(0x01); + + let candidate_commitments = CandidateCommitments::default(); + + let candidate_receipt1 = { + let mut receipt = dummy_candidate_receipt(block_hash); + receipt.descriptor.para_id = ParaId::from(1_u32); + receipt.commitments_hash = candidate_commitments.hash(); + receipt + }; + + let candidate_hash1 = candidate_receipt1.hash(); + + let candidate_receipt2 = { + let mut receipt = dummy_candidate_receipt(block_hash); + receipt.descriptor.para_id = ParaId::from(2_u32); + receipt.commitments_hash = candidate_commitments.hash(); + receipt + }; + + let slot = Slot::from(1); + let candidate_index1 = 0; + let candidate_index2 = 1; + + let validators = vec![ + Sr25519Keyring::Alice, + Sr25519Keyring::Bob, + Sr25519Keyring::Charlie, + Sr25519Keyring::Dave, + Sr25519Keyring::Eve, + ]; + let session_info = SessionInfo { + validator_groups: IndexedVec::>::from(vec![ + vec![ValidatorIndex(0), ValidatorIndex(1)], + vec![ValidatorIndex(2)], + vec![ValidatorIndex(3), ValidatorIndex(4)], + ]), + ..session_info(&validators) + }; + + let candidates = Some(vec![ + (candidate_receipt1.clone(), CoreIndex(0), GroupIndex(0)), + (candidate_receipt2.clone(), CoreIndex(1), GroupIndex(1)), + ]); + ChainBuilder::new() + .add_block( + block_hash, + ChainBuilder::GENESIS_HASH, + 1, + BlockConfig { + slot, + candidates: candidates.clone(), + session_info: Some(session_info.clone()), + }, + ) + .build(&mut virtual_overseer) + .await; + + assert!(!clock.inner.lock().current_wakeup_is(1)); + clock.inner.lock().wakeup_all(1); + + assert!(clock.inner.lock().current_wakeup_is(slot_to_tick(slot))); + clock.inner.lock().wakeup_all(slot_to_tick(slot)); + + futures_timer::Delay::new(Duration::from_millis(200)).await; + + clock.inner.lock().wakeup_all(slot_to_tick(slot + 2)); + + assert_eq!(clock.inner.lock().wakeups.len(), 0); + + futures_timer::Delay::new(Duration::from_millis(200)).await; + + let candidate_entry = store.load_candidate_entry(&candidate_hash1).unwrap().unwrap(); + let our_assignment = + candidate_entry.approval_entry(&block_hash).unwrap().our_assignment().unwrap(); + assert!(our_assignment.triggered()); + + handle_approval_on_max_wait_time( + &mut virtual_overseer, + vec![candidate_index1, candidate_index2], + clock, + ) + .await; + + virtual_overseer + }); +} diff --git a/node/core/approval-voting/src/time.rs b/node/core/approval-voting/src/time.rs index a45866402c82..ca6ba288d161 100644 --- a/node/core/approval-voting/src/time.rs +++ b/node/core/approval-voting/src/time.rs @@ -16,14 +16,23 @@ //! Time utilities for approval voting. -use futures::prelude::*; +use futures::{ + future::BoxFuture, + prelude::*, + stream::{FusedStream, FuturesUnordered}, + Stream, StreamExt, +}; + use polkadot_node_primitives::approval::v1::DelayTranche; use sp_consensus_slots::Slot; use std::{ + collections::HashSet, pin::Pin, + task::Poll, time::{Duration, SystemTime}, }; +use polkadot_primitives::{Hash, ValidatorIndex}; const TICK_DURATION_MILLIS: u64 = 500; /// A base unit of time, starting from the Unix epoch, split into half-second intervals. @@ -88,3 +97,157 @@ pub(crate) fn slot_number_to_tick(slot_duration_millis: u64, slot: Slot) -> Tick let ticks_per_slot = slot_duration_millis / TICK_DURATION_MILLIS; u64::from(slot) * ticks_per_slot } + +// A list of delayed futures that gets triggered when the waiting time has expired and it is +// time to sign the candidate. +// We have a timer per relay-chain block. +#[derive(Default)] +pub struct DelayedApprovalTimer { + timers: FuturesUnordered>, + blocks: HashSet, +} + +impl DelayedApprovalTimer { + /// Starts a single timer per block hash + /// + /// Guarantees that if a timer already exits for the give block hash, + /// no additional timer is started. + pub(crate) fn maybe_arm_timer( + &mut self, + wait_untill: Tick, + clock: &dyn Clock, + block_hash: Hash, + validator_index: ValidatorIndex, + ) { + if self.blocks.insert(block_hash) { + let clock_wait = clock.wait(wait_untill); + self.timers.push(Box::pin(async move { + clock_wait.await; + (block_hash, validator_index) + })); + } + } +} + +impl Stream for DelayedApprovalTimer { + type Item = (Hash, ValidatorIndex); + + fn poll_next( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + let poll_result = self.timers.poll_next_unpin(cx); + match poll_result { + Poll::Ready(Some(result)) => { + self.blocks.remove(&result.0); + Poll::Ready(Some(result)) + }, + _ => poll_result, + } + } +} + +impl FusedStream for DelayedApprovalTimer { + fn is_terminated(&self) -> bool { + self.timers.is_terminated() + } +} + +#[cfg(test)] +mod tests { + use std::time::Duration; + + use futures::{executor::block_on, FutureExt, StreamExt}; + use futures_timer::Delay; + use polkadot_primitives::{Hash, ValidatorIndex}; + + use crate::time::{Clock, SystemClock}; + + use super::DelayedApprovalTimer; + + #[test] + fn test_select_empty_timer() { + block_on(async move { + let mut timer = DelayedApprovalTimer::default(); + + for _ in 1..10 { + let result = futures::select!( + _ = timer.select_next_some() => { + 0 + } + // Only this arm should fire + _ = Delay::new(Duration::from_millis(100)).fuse() => { + 1 + } + ); + + assert_eq!(result, 1); + } + }); + } + + #[test] + fn test_timer_functionality() { + block_on(async move { + let mut timer = DelayedApprovalTimer::default(); + let test_hashes = + vec![Hash::repeat_byte(0x01), Hash::repeat_byte(0x02), Hash::repeat_byte(0x03)]; + for (index, hash) in test_hashes.iter().enumerate() { + timer.maybe_arm_timer( + SystemClock.tick_now() + index as u64, + &SystemClock, + *hash, + ValidatorIndex::from(2), + ); + timer.maybe_arm_timer( + SystemClock.tick_now() + index as u64, + &SystemClock, + *hash, + ValidatorIndex::from(2), + ); + } + let timeout_hash = Hash::repeat_byte(0x02); + for i in 0..test_hashes.len() * 2 { + let result = futures::select!( + (hash, _) = timer.select_next_some() => { + hash + } + // Timers should fire only once, so for the rest of the iterations we should timeout through here. + _ = Delay::new(Duration::from_secs(2)).fuse() => { + timeout_hash + } + ); + assert_eq!(test_hashes.get(i).cloned().unwrap_or(timeout_hash), result); + } + + // Now check timer can be restarted if already fired + for (index, hash) in test_hashes.iter().enumerate() { + timer.maybe_arm_timer( + SystemClock.tick_now() + index as u64, + &SystemClock, + *hash, + ValidatorIndex::from(2), + ); + timer.maybe_arm_timer( + SystemClock.tick_now() + index as u64, + &SystemClock, + *hash, + ValidatorIndex::from(2), + ); + } + + for i in 0..test_hashes.len() * 2 { + let result = futures::select!( + (hash, _) = timer.select_next_some() => { + hash + } + // Timers should fire only once, so for the rest of the iterations we should timeout through here. + _ = Delay::new(Duration::from_secs(2)).fuse() => { + timeout_hash + } + ); + assert_eq!(test_hashes.get(i).cloned().unwrap_or(timeout_hash), result); + } + }); + } +} diff --git a/primitives/src/vstaging/mod.rs b/primitives/src/vstaging/mod.rs index aad7208355e1..aa652f433f66 100644 --- a/primitives/src/vstaging/mod.rs +++ b/primitives/src/vstaging/mod.rs @@ -69,7 +69,7 @@ pub struct ApprovalVotingParams { /// /// Setting it to 1, means we send the approval as soon as we have it available. pub max_approval_coalesce_count: u32, - /// The maximum time we await for a candidate approval to be coalesced with + /// The maximum ticks we await for a candidate approval to be coalesced with /// the ones for other candidate before we sign it and distribute to our peers - pub max_approval_coalesce_wait_millis: u32, + pub max_approval_coalesce_wait_ticks: u32, } diff --git a/runtime/parachains/src/configuration.rs b/runtime/parachains/src/configuration.rs index 52172f9acf5a..79990c1ac5f0 100644 --- a/runtime/parachains/src/configuration.rs +++ b/runtime/parachains/src/configuration.rs @@ -301,7 +301,7 @@ impl> Default for HostConfiguration Date: Wed, 2 Aug 2023 13:08:12 +0300 Subject: [PATCH 06/54] Enforce target candidate is part of the signature Signed-off-by: Alexandru Gheorghe --- node/core/approval-voting/src/lib.rs | 8 ++-- node/core/approval-voting/src/tests.rs | 18 ++++----- node/core/dispute-coordinator/src/import.rs | 2 +- node/primitives/src/disputes/mod.rs | 20 ++++++++-- primitives/src/v5/mod.rs | 43 +++++++++++++-------- runtime/parachains/src/disputes.rs | 17 +++++--- 6 files changed, 68 insertions(+), 40 deletions(-) diff --git a/node/core/approval-voting/src/lib.rs b/node/core/approval-voting/src/lib.rs index b212874d356c..3c6bb5f2a142 100644 --- a/node/core/approval-voting/src/lib.rs +++ b/node/core/approval-voting/src/lib.rs @@ -2268,9 +2268,9 @@ where let candidate_hashes: Vec = approved_candidates_info.iter().map(|candidate| candidate.1).collect(); // Signature check: - match DisputeStatement::Valid(ValidDisputeStatementKind::ApprovalCheckingV2( - candidate_hashes.clone(), - )) + match DisputeStatement::Valid( + ValidDisputeStatementKind::ApprovalCheckingMultipleCandidates(candidate_hashes.clone()), + ) .check_signature( &pubkey, *candidate_hashes.first().expect("Checked above this is not empty; qed"), @@ -3243,7 +3243,7 @@ fn sign_approval( ) -> Option { let key = keystore.key_pair::(public).ok().flatten()?; - let payload = ApprovalVoteMultipleCandidates(candidate_hashes).signing_payload(session_index); + let payload = ApprovalVoteMultipleCandidates(&candidate_hashes).signing_payload(session_index); Some(key.sign(&payload[..])) } diff --git a/node/core/approval-voting/src/tests.rs b/node/core/approval-voting/src/tests.rs index 3ad5457aace7..ed9cd9791e25 100644 --- a/node/core/approval-voting/src/tests.rs +++ b/node/core/approval-voting/src/tests.rs @@ -1975,9 +1975,9 @@ fn test_signing_a_single_candidate_is_backwards_compatible() { session_index, ); - assert!(DisputeStatement::Valid(ValidDisputeStatementKind::ApprovalCheckingV2(vec![ - candidate_hash - ])) + assert!(DisputeStatement::Valid( + ValidDisputeStatementKind::ApprovalCheckingMultipleCandidates(vec![candidate_hash]) + ) .check_signature(&Sr25519Keyring::Alice.public().into(), candidate_hash, session_index, &sig_c,) .is_ok()); @@ -1990,9 +1990,9 @@ fn test_signing_a_single_candidate_is_backwards_compatible() { ) .is_ok()); - assert!(DisputeStatement::Valid(ValidDisputeStatementKind::ApprovalCheckingV2(vec![ - candidate_hash - ])) + assert!(DisputeStatement::Valid( + ValidDisputeStatementKind::ApprovalCheckingMultipleCandidates(vec![candidate_hash]) + ) .check_signature(&Sr25519Keyring::Alice.public().into(), candidate_hash, session_index, &sig_a,) .is_ok()); @@ -2002,9 +2002,9 @@ fn test_signing_a_single_candidate_is_backwards_compatible() { session_index, ); - assert!(DisputeStatement::Valid(ValidDisputeStatementKind::ApprovalCheckingV2( - candidate_hashes.clone() - )) + assert!(DisputeStatement::Valid( + ValidDisputeStatementKind::ApprovalCheckingMultipleCandidates(candidate_hashes.clone()) + ) .check_signature( &Sr25519Keyring::Alice.public().into(), *candidate_hashes.first().expect("test"), diff --git a/node/core/dispute-coordinator/src/import.rs b/node/core/dispute-coordinator/src/import.rs index dbe4f69ec66d..a760fe6326f3 100644 --- a/node/core/dispute-coordinator/src/import.rs +++ b/node/core/dispute-coordinator/src/import.rs @@ -528,7 +528,7 @@ impl ImportResult { { let pub_key = &env.session_info().validators.get(index).expect("indices are validated by approval-voting subsystem; qed"); let session_index = env.session_index(); - candidate_hashes.contains(&votes.candidate_receipt.hash()) && DisputeStatement::Valid(ValidDisputeStatementKind::ApprovalCheckingV2(candidate_hashes.clone())) + candidate_hashes.contains(&votes.candidate_receipt.hash()) && DisputeStatement::Valid(ValidDisputeStatementKind::ApprovalCheckingMultipleCandidates(candidate_hashes.clone())) .check_signature(pub_key, *candidate_hashes.first().expect("Valid votes have at least one candidate; qed"), session_index, &sig) .is_ok() }, diff --git a/node/primitives/src/disputes/mod.rs b/node/primitives/src/disputes/mod.rs index 0871f4d766f4..040a92575e4e 100644 --- a/node/primitives/src/disputes/mod.rs +++ b/node/primitives/src/disputes/mod.rs @@ -46,6 +46,15 @@ pub struct SignedDisputeStatement { session_index: SessionIndex, } +/// Errors encountered while signing a dispute statement +#[derive(Debug)] +pub enum SignedDisputeStatementError { + /// Encountered a keystore error while signing + KeyStoreError(KeystoreError), + /// Could not generate signing payload + PayloadError, +} + /// Tracked votes on candidates, for the purposes of dispute resolution. #[derive(Debug, Clone)] pub struct CandidateVotes { @@ -108,7 +117,7 @@ impl ValidCandidateVotes { ValidDisputeStatementKind::BackingSeconded(_) => false, ValidDisputeStatementKind::Explicit | ValidDisputeStatementKind::ApprovalChecking | - ValidDisputeStatementKind::ApprovalCheckingV2(_) => { + ValidDisputeStatementKind::ApprovalCheckingMultipleCandidates(_) => { occupied.insert((kind.clone(), sig)); kind != occupied.get().0 }, @@ -214,16 +223,19 @@ impl SignedDisputeStatement { candidate_hash: CandidateHash, session_index: SessionIndex, validator_public: ValidatorId, - ) -> Result, KeystoreError> { + ) -> Result, SignedDisputeStatementError> { let dispute_statement = if valid { DisputeStatement::Valid(ValidDisputeStatementKind::Explicit) } else { DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit) }; - let data = dispute_statement.payload_data(candidate_hash, session_index); + let data = dispute_statement + .payload_data(candidate_hash, session_index) + .map_err(|_| SignedDisputeStatementError::PayloadError)?; let signature = keystore - .sr25519_sign(ValidatorId::ID, validator_public.as_ref(), &data)? + .sr25519_sign(ValidatorId::ID, validator_public.as_ref(), &data) + .map_err(SignedDisputeStatementError::KeyStoreError)? .map(|sig| Self { dispute_statement, candidate_hash, diff --git a/primitives/src/v5/mod.rs b/primitives/src/v5/mod.rs index d3280f8be525..af169a599505 100644 --- a/primitives/src/v5/mod.rs +++ b/primitives/src/v5/mod.rs @@ -1069,9 +1069,9 @@ impl ApprovalVote { /// A vote of approvalf for multiple candidates. #[derive(Clone, RuntimeDebug)] -pub struct ApprovalVoteMultipleCandidates(pub Vec); +pub struct ApprovalVoteMultipleCandidates<'a>(pub &'a Vec); -impl ApprovalVoteMultipleCandidates { +impl<'a> ApprovalVoteMultipleCandidates<'a> { /// Yields the signing payload for this approval vote. pub fn signing_payload(&self, session_index: SessionIndex) -> Vec { const MAGIC: [u8; 4] = *b"APPR"; @@ -1260,28 +1260,39 @@ pub enum DisputeStatement { impl DisputeStatement { /// Get the payload data for this type of dispute statement. - pub fn payload_data(&self, candidate_hash: CandidateHash, session: SessionIndex) -> Vec { + pub fn payload_data( + &self, + candidate_hash: CandidateHash, + session: SessionIndex, + ) -> Result, ()> { match self { DisputeStatement::Valid(ValidDisputeStatementKind::Explicit) => - ExplicitDisputeStatement { valid: true, candidate_hash, session }.signing_payload(), + Ok(ExplicitDisputeStatement { valid: true, candidate_hash, session } + .signing_payload()), DisputeStatement::Valid(ValidDisputeStatementKind::BackingSeconded( inclusion_parent, - )) => CompactStatement::Seconded(candidate_hash).signing_payload(&SigningContext { + )) => Ok(CompactStatement::Seconded(candidate_hash).signing_payload(&SigningContext { session_index: session, parent_hash: *inclusion_parent, - }), + })), DisputeStatement::Valid(ValidDisputeStatementKind::BackingValid(inclusion_parent)) => - CompactStatement::Valid(candidate_hash).signing_payload(&SigningContext { + Ok(CompactStatement::Valid(candidate_hash).signing_payload(&SigningContext { session_index: session, parent_hash: *inclusion_parent, - }), + })), DisputeStatement::Valid(ValidDisputeStatementKind::ApprovalChecking) => - ApprovalVote(candidate_hash).signing_payload(session), - DisputeStatement::Valid(ValidDisputeStatementKind::ApprovalCheckingV2( - candidate_hashes, - )) => ApprovalVoteMultipleCandidates(candidate_hashes.clone()).signing_payload(session), + Ok(ApprovalVote(candidate_hash).signing_payload(session)), + DisputeStatement::Valid( + ValidDisputeStatementKind::ApprovalCheckingMultipleCandidates(candidate_hashes), + ) => + if candidate_hashes.contains(&candidate_hash) { + Ok(ApprovalVoteMultipleCandidates(candidate_hashes).signing_payload(session)) + } else { + Err(()) + }, DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit) => - ExplicitDisputeStatement { valid: false, candidate_hash, session }.signing_payload(), + Ok(ExplicitDisputeStatement { valid: false, candidate_hash, session } + .signing_payload()), } } @@ -1293,7 +1304,7 @@ impl DisputeStatement { session: SessionIndex, validator_signature: &ValidatorSignature, ) -> Result<(), ()> { - let payload = self.payload_data(candidate_hash, session); + let payload = self.payload_data(candidate_hash, session)?; if validator_signature.verify(&payload[..], &validator_public) { Ok(()) @@ -1325,7 +1336,7 @@ impl DisputeStatement { Self::Valid(ValidDisputeStatementKind::BackingValid(_)) => true, Self::Valid(ValidDisputeStatementKind::Explicit) | Self::Valid(ValidDisputeStatementKind::ApprovalChecking) | - Self::Valid(ValidDisputeStatementKind::ApprovalCheckingV2(_)) | + Self::Valid(ValidDisputeStatementKind::ApprovalCheckingMultipleCandidates(_)) | Self::Invalid(_) => false, } } @@ -1350,7 +1361,7 @@ pub enum ValidDisputeStatementKind { /// TODO: Fixme this probably means we can't create this version /// untill all nodes have been updated to support it. #[codec(index = 4)] - ApprovalCheckingV2(Vec), + ApprovalCheckingMultipleCandidates(Vec), } /// Different kinds of statements of invalidity on a candidate. diff --git a/runtime/parachains/src/disputes.rs b/runtime/parachains/src/disputes.rs index 5e253005ad17..fc5e4039561c 100644 --- a/runtime/parachains/src/disputes.rs +++ b/runtime/parachains/src/disputes.rs @@ -1330,24 +1330,29 @@ fn check_signature( statement: &DisputeStatement, validator_signature: &ValidatorSignature, ) -> Result<(), ()> { - let payload = match *statement { + let payload = match statement { DisputeStatement::Valid(ValidDisputeStatementKind::Explicit) => ExplicitDisputeStatement { valid: true, candidate_hash, session }.signing_payload(), DisputeStatement::Valid(ValidDisputeStatementKind::BackingSeconded(inclusion_parent)) => CompactStatement::Seconded(candidate_hash).signing_payload(&SigningContext { session_index: session, - parent_hash: inclusion_parent, + parent_hash: *inclusion_parent, }), DisputeStatement::Valid(ValidDisputeStatementKind::BackingValid(inclusion_parent)) => CompactStatement::Valid(candidate_hash).signing_payload(&SigningContext { session_index: session, - parent_hash: inclusion_parent, + parent_hash: *inclusion_parent, }), DisputeStatement::Valid(ValidDisputeStatementKind::ApprovalChecking) => ApprovalVote(candidate_hash).signing_payload(session), - DisputeStatement::Valid(ValidDisputeStatementKind::ApprovalCheckingV2(_)) => - // TODO: Fixme - ApprovalVoteMultipleCandidates(vec![candidate_hash]).signing_payload(session), + DisputeStatement::Valid(ValidDisputeStatementKind::ApprovalCheckingMultipleCandidates( + candidates, + )) => + if candidates.contains(&candidate_hash) { + ApprovalVoteMultipleCandidates(candidates).signing_payload(session) + } else { + return Err(()) + }, DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit) => ExplicitDisputeStatement { valid: false, candidate_hash, session }.signing_payload(), }; From f0f05c66bf14c4908300d8667747c30512558321 Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe Date: Wed, 2 Aug 2023 18:49:29 +0300 Subject: [PATCH 07/54] Fixup runtime configuration after rebase Signed-off-by: Alexandru Gheorghe --- node/core/runtime-api/src/lib.rs | 2 +- node/service/src/fake_runtime_api.rs | 17 +++++++++++------ node/subsystem-types/src/runtime_client.rs | 2 +- primitives/src/runtime_api.rs | 1 + runtime/kusama/src/lib.rs | 4 +++- .../src/configuration/migration/v7.rs | 6 +++++- runtime/parachains/src/runtime_api_impl/v5.rs | 11 +++-------- .../parachains/src/runtime_api_impl/vstaging.rs | 9 +++++++++ runtime/polkadot/src/lib.rs | 3 ++- runtime/rococo/src/lib.rs | 4 ++-- runtime/westend/src/lib.rs | 4 ++-- .../0006-approval-voting-coalescing.toml | 2 +- 12 files changed, 41 insertions(+), 24 deletions(-) diff --git a/node/core/runtime-api/src/lib.rs b/node/core/runtime-api/src/lib.rs index 17e7f456bd6b..c215bc52c960 100644 --- a/node/core/runtime-api/src/lib.rs +++ b/node/core/runtime-api/src/lib.rs @@ -536,7 +536,7 @@ where sender ), Request::ApprovalVotingParams(sender) => { - query!(ApprovalVotingParams, approval_voting_params(), ver = 5, sender) + query!(ApprovalVotingParams, approval_voting_params(), ver = 6, sender) }, Request::SubmitReportDisputeLost(dispute_proof, key_ownership_proof, sender) => query!( SubmitReportDisputeLost, diff --git a/node/service/src/fake_runtime_api.rs b/node/service/src/fake_runtime_api.rs index f9d7799d8262..845957028adc 100644 --- a/node/service/src/fake_runtime_api.rs +++ b/node/service/src/fake_runtime_api.rs @@ -22,12 +22,12 @@ use beefy_primitives::crypto::{AuthorityId as BeefyId, Signature as BeefySignatu use grandpa_primitives::AuthorityId as GrandpaId; use pallet_transaction_payment::{FeeDetails, RuntimeDispatchInfo}; use polkadot_primitives::{ - runtime_api, slashing, AccountId, AuthorityDiscoveryId, Balance, Block, BlockNumber, - CandidateCommitments, CandidateEvent, CandidateHash, CommittedCandidateReceipt, CoreState, - DisputeState, ExecutorParams, GroupRotationInfo, Hash, Id as ParaId, InboundDownwardMessage, - InboundHrmpMessage, Nonce, OccupiedCoreAssumption, PersistedValidationData, PvfCheckStatement, - ScrapedOnChainVotes, SessionIndex, SessionInfo, ValidationCode, ValidationCodeHash, - ValidatorId, ValidatorIndex, ValidatorSignature, + runtime_api, slashing, vstaging::ApprovalVotingParams, AccountId, AuthorityDiscoveryId, + Balance, Block, BlockNumber, CandidateCommitments, CandidateEvent, CandidateHash, + CommittedCandidateReceipt, CoreState, DisputeState, ExecutorParams, GroupRotationInfo, Hash, + Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, Nonce, OccupiedCoreAssumption, + PersistedValidationData, PvfCheckStatement, ScrapedOnChainVotes, SessionIndex, SessionInfo, + ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature, }; use sp_core::OpaqueMetadata; use sp_runtime::{ @@ -115,6 +115,7 @@ sp_api::impl_runtime_apis! { } } + #[api_version(6)] impl runtime_api::ParachainHost for Runtime { fn validators() -> Vec { unimplemented!() @@ -228,6 +229,10 @@ sp_api::impl_runtime_apis! { ) -> Option<()> { unimplemented!() } + + fn approval_voting_params() -> ApprovalVotingParams { + unimplemented!() + } } impl beefy_primitives::BeefyApi for Runtime { diff --git a/node/subsystem-types/src/runtime_client.rs b/node/subsystem-types/src/runtime_client.rs index f0e24986bf91..0508c5dca934 100644 --- a/node/subsystem-types/src/runtime_client.rs +++ b/node/subsystem-types/src/runtime_client.rs @@ -462,6 +462,6 @@ where /// Approval voting configuration parameters async fn approval_voting_params(&self, at: Hash) -> Result { - self.runtime_api().approval_voting_params(at) + self.client.runtime_api().approval_voting_params(at) } } diff --git a/primitives/src/runtime_api.rs b/primitives/src/runtime_api.rs index caa2db64c1c0..8c6714828052 100644 --- a/primitives/src/runtime_api.rs +++ b/primitives/src/runtime_api.rs @@ -243,6 +243,7 @@ sp_api::decl_runtime_apis! { ) -> Option<()>; /// Approval voting configuration parameters + #[api_version(6)] fn approval_voting_params() -> ApprovalVotingParams; } } diff --git a/runtime/kusama/src/lib.rs b/runtime/kusama/src/lib.rs index 90adcc8b06b0..a4ff6772f099 100644 --- a/runtime/kusama/src/lib.rs +++ b/runtime/kusama/src/lib.rs @@ -1684,6 +1684,7 @@ sp_api::impl_runtime_apis! { } } + #[api_version(6)] impl primitives::runtime_api::ParachainHost for Runtime { fn validators() -> Vec { parachains_runtime_api_impl::validators::() @@ -1814,9 +1815,10 @@ sp_api::impl_runtime_apis! { key_ownership_proof, ) } + /// Approval voting configuration parameters fn approval_voting_params() -> ApprovalVotingParams { - parachains_runtime_api_impl::approval_voting_params::() + runtime_parachains::runtime_api_impl::vstaging::approval_voting_params:: () } } diff --git a/runtime/parachains/src/configuration/migration/v7.rs b/runtime/parachains/src/configuration/migration/v7.rs index cdff80a31a3a..9bd7512ff119 100644 --- a/runtime/parachains/src/configuration/migration/v7.rs +++ b/runtime/parachains/src/configuration/migration/v7.rs @@ -23,7 +23,7 @@ use frame_support::{ weights::Weight, }; use frame_system::pallet_prelude::BlockNumberFor; -use primitives::SessionIndex; +use primitives::{vstaging::ApprovalVotingParams, SessionIndex}; use sp_std::vec::Vec; use frame_support::traits::OnRuntimeUpgrade; @@ -147,6 +147,10 @@ pvf_voting_ttl : pre.pvf_voting_ttl, minimum_validation_upgrade_delay : pre.minimum_validation_upgrade_delay, async_backing_params : pre.async_backing_params, executor_params : pre.executor_params, +approval_voting_params : ApprovalVotingParams { + max_approval_coalesce_count: 1, + max_approval_coalesce_wait_ticks: 0, + } } }; diff --git a/runtime/parachains/src/runtime_api_impl/v5.rs b/runtime/parachains/src/runtime_api_impl/v5.rs index 04b5930d14cc..1257c0c91702 100644 --- a/runtime/parachains/src/runtime_api_impl/v5.rs +++ b/runtime/parachains/src/runtime_api_impl/v5.rs @@ -23,9 +23,9 @@ use crate::{ }; use frame_system::pallet_prelude::*; use primitives::{ - slashing, vstaging::ApprovalVotingParams, AuthorityDiscoveryId, CandidateEvent, CandidateHash, - CommittedCandidateReceipt, CoreIndex, CoreOccupied, CoreState, DisputeState, ExecutorParams, - GroupIndex, GroupRotationInfo, Hash, Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, + slashing, AuthorityDiscoveryId, CandidateEvent, CandidateHash, CommittedCandidateReceipt, + CoreIndex, CoreOccupied, CoreState, DisputeState, ExecutorParams, GroupIndex, + GroupRotationInfo, Hash, Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, OccupiedCore, OccupiedCoreAssumption, PersistedValidationData, PvfCheckStatement, ScheduledCore, ScrapedOnChainVotes, SessionIndex, SessionInfo, ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature, @@ -49,11 +49,6 @@ pub fn validator_groups( (groups, rotation_info) } -pub fn approval_voting_params() -> ApprovalVotingParams { - let config = >::config(); - config.approval_voting_params -} - /// Implementation for the `availability_cores` function of the runtime API. pub fn availability_cores() -> Vec>> { let cores = >::availability_cores(); diff --git a/runtime/parachains/src/runtime_api_impl/vstaging.rs b/runtime/parachains/src/runtime_api_impl/vstaging.rs index d01b543630c3..2de20b0e087e 100644 --- a/runtime/parachains/src/runtime_api_impl/vstaging.rs +++ b/runtime/parachains/src/runtime_api_impl/vstaging.rs @@ -15,3 +15,12 @@ // along with Polkadot. If not, see . //! Put implementations of functions from staging APIs here. + +use primitives::vstaging::ApprovalVotingParams; + +use crate::{configuration, initializer}; + +pub fn approval_voting_params() -> ApprovalVotingParams { + let config = >::config(); + config.approval_voting_params +} diff --git a/runtime/polkadot/src/lib.rs b/runtime/polkadot/src/lib.rs index 9272468c7a17..444130a31033 100644 --- a/runtime/polkadot/src/lib.rs +++ b/runtime/polkadot/src/lib.rs @@ -1687,6 +1687,7 @@ sp_api::impl_runtime_apis! { } } + #[api_version(6)] impl primitives::runtime_api::ParachainHost for Runtime { fn validators() -> Vec { parachains_runtime_api_impl::validators::() @@ -1820,7 +1821,7 @@ sp_api::impl_runtime_apis! { /// Approval voting configuration parameters fn approval_voting_params() -> ApprovalVotingParams { - parachains_runtime_api_impl::approval_voting_params::() + runtime_parachains::runtime_api_impl::vstaging::approval_voting_params:: () } } diff --git a/runtime/rococo/src/lib.rs b/runtime/rococo/src/lib.rs index 99dc7d109acc..d9e986224457 100644 --- a/runtime/rococo/src/lib.rs +++ b/runtime/rococo/src/lib.rs @@ -1687,7 +1687,7 @@ sp_api::impl_runtime_apis! { } } - #[api_version(5)] + #[api_version(6)] impl primitives::runtime_api::ParachainHost for Runtime { fn validators() -> Vec { parachains_runtime_api_impl::validators::() @@ -1820,7 +1820,7 @@ sp_api::impl_runtime_apis! { } /// Approval voting configuration parameters fn approval_voting_params() -> ApprovalVotingParams { - parachains_runtime_api_impl::approval_voting_params::() + runtime_parachains::runtime_api_impl::vstaging::approval_voting_params:: () } } diff --git a/runtime/westend/src/lib.rs b/runtime/westend/src/lib.rs index 0a481e24686a..5291365a53ff 100644 --- a/runtime/westend/src/lib.rs +++ b/runtime/westend/src/lib.rs @@ -1421,7 +1421,7 @@ sp_api::impl_runtime_apis! { } } - #[api_version(5)] + #[api_version(6)] impl primitives::runtime_api::ParachainHost for Runtime { fn validators() -> Vec { parachains_runtime_api_impl::validators::() @@ -1555,7 +1555,7 @@ sp_api::impl_runtime_apis! { /// Approval voting configuration parameters fn approval_voting_params() -> ApprovalVotingParams { - parachains_runtime_api_impl::approval_voting_params::() + runtime_parachains::runtime_api_impl::vstaging::approval_voting_params:: () } } diff --git a/zombienet_tests/functional/0006-approval-voting-coalescing.toml b/zombienet_tests/functional/0006-approval-voting-coalescing.toml index 89d337a8aa39..6f1e2a4674cc 100644 --- a/zombienet_tests/functional/0006-approval-voting-coalescing.toml +++ b/zombienet_tests/functional/0006-approval-voting-coalescing.toml @@ -8,7 +8,7 @@ chain_spec_command = "polkadot build-spec --chain rococo-local --disable-default [relaychain.genesis.runtime.runtime_genesis_config.configuration.config.approval_voting_params] max_approval_coalesce_count = {{MAX_APPROVAL_COALESCE_COUNT}} - max_approval_coalesce_wait_millis = {{MAX_APPROVAL_COALESCE_WAIT_MILLIS}} + max_approval_coalesce_wait_ticks = {{MAX_APPROVAL_COALESCE_WAIT_TICKS}} [relaychain.default_resources] limits = { memory = "4G", cpu = "2" } From d31d1d0296b88e02673622e703a66579544244f8 Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe Date: Wed, 2 Aug 2023 19:10:21 +0300 Subject: [PATCH 08/54] Fixup build for unittests Signed-off-by: Alexandru Gheorghe --- node/core/approval-voting/src/tests.rs | 2 +- node/core/runtime-api/src/tests.rs | 17 +++++++++++------ runtime/parachains/src/builder.rs | 2 +- runtime/parachains/src/configuration/tests.rs | 4 ++++ 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/node/core/approval-voting/src/tests.rs b/node/core/approval-voting/src/tests.rs index cde8c683623f..26dcb1d24970 100644 --- a/node/core/approval-voting/src/tests.rs +++ b/node/core/approval-voting/src/tests.rs @@ -437,7 +437,7 @@ fn sign_approval_multiple_candidates( candidate_hashes: Vec, session_index: SessionIndex, ) -> ValidatorSignature { - key.sign(&ApprovalVoteMultipleCandidates(candidate_hashes).signing_payload(session_index)) + key.sign(&ApprovalVoteMultipleCandidates(&candidate_hashes).signing_payload(session_index)) .into() } diff --git a/node/core/runtime-api/src/tests.rs b/node/core/runtime-api/src/tests.rs index 27090a102ec2..9fb287b1aa63 100644 --- a/node/core/runtime-api/src/tests.rs +++ b/node/core/runtime-api/src/tests.rs @@ -20,12 +20,12 @@ use polkadot_node_primitives::{BabeAllowedSlots, BabeEpoch, BabeEpochConfigurati use polkadot_node_subsystem::SpawnGlue; use polkadot_node_subsystem_test_helpers::make_subsystem_context; use polkadot_primitives::{ - vstaging, AuthorityDiscoveryId, BlockNumber, CandidateCommitments, CandidateEvent, - CandidateHash, CommittedCandidateReceipt, CoreState, DisputeState, ExecutorParams, - GroupRotationInfo, Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, - OccupiedCoreAssumption, PersistedValidationData, PvfCheckStatement, ScrapedOnChainVotes, - SessionIndex, SessionInfo, Slot, ValidationCode, ValidationCodeHash, ValidatorId, - ValidatorIndex, ValidatorSignature, + vstaging::{self, ApprovalVotingParams}, + AuthorityDiscoveryId, BlockNumber, CandidateCommitments, CandidateEvent, CandidateHash, + CommittedCandidateReceipt, CoreState, DisputeState, ExecutorParams, GroupRotationInfo, + Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, OccupiedCoreAssumption, + PersistedValidationData, PvfCheckStatement, ScrapedOnChainVotes, SessionIndex, SessionInfo, + Slot, ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature, }; use sp_api::ApiError; use sp_core::testing::TaskExecutor; @@ -242,6 +242,11 @@ impl RuntimeApiSubsystemClient for MockSubsystemClient { todo!("Not required for tests") } + /// Approval voting configuration parameters + async fn approval_voting_params(&self, _: Hash) -> Result { + todo!("Not required for tests") + } + async fn current_epoch(&self, _: Hash) -> Result { Ok(self.babe_epoch.as_ref().unwrap().clone()) } diff --git a/runtime/parachains/src/builder.rs b/runtime/parachains/src/builder.rs index e46c9f59b957..72512a564c99 100644 --- a/runtime/parachains/src/builder.rs +++ b/runtime/parachains/src/builder.rs @@ -630,7 +630,7 @@ impl BenchBuilder { } else { DisputeStatement::Valid(ValidDisputeStatementKind::Explicit) }; - let data = dispute_statement.payload_data(candidate_hash.clone(), session); + let data = dispute_statement.payload_data(candidate_hash.clone(), session).unwrap(); let statement_sig = validator_public.sign(&data).unwrap(); (dispute_statement, ValidatorIndex(validator_index), statement_sig) diff --git a/runtime/parachains/src/configuration/tests.rs b/runtime/parachains/src/configuration/tests.rs index 0c2b5a779cb5..0374ed416dbd 100644 --- a/runtime/parachains/src/configuration/tests.rs +++ b/runtime/parachains/src/configuration/tests.rs @@ -324,6 +324,10 @@ fn setting_pending_config_members() { pvf_voting_ttl: 3, minimum_validation_upgrade_delay: 20, executor_params: Default::default(), + approval_voting_params: ApprovalVotingParams { + max_approval_coalesce_count: 1, + max_approval_coalesce_wait_ticks: 0, + }, }; Configuration::set_validation_upgrade_cooldown( From 6dfaecbef40cab3a25b8c8f54b035736320a8df9 Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe Date: Thu, 3 Aug 2023 17:22:29 +0300 Subject: [PATCH 09/54] Fix bugs discovered during zombie-testing and unittest Signed-off-by: Alexandru Gheorghe --- node/core/approval-voting/src/lib.rs | 4 +- node/core/approval-voting/src/tests.rs | 40 +++++++++---------- node/core/dispute-coordinator/src/import.rs | 16 +++++++- node/network/approval-distribution/src/lib.rs | 6 ++- runtime/parachains/src/disputes.rs | 2 + .../functional/0002-parachains-disputes.toml | 5 +++ 6 files changed, 49 insertions(+), 24 deletions(-) diff --git a/node/core/approval-voting/src/lib.rs b/node/core/approval-voting/src/lib.rs index 8fbc879b29e8..5623d959a2fe 100644 --- a/node/core/approval-voting/src/lib.rs +++ b/node/core/approval-voting/src/lib.rs @@ -1986,10 +1986,10 @@ where let n_cores = session_info.n_cores as usize; - // Early check the candidate bitfield and core bitfields lengths < `n_cores`. + // Early check the candidate bitfield and core bitfields lengths <= `n_cores`. // `approval-distribution` already checks for core and claimed candidate bitfields // to be equal in size. A check for claimed candidate bitfields should be enough here. - if candidate_indices.len() >= n_cores { + if candidate_indices.len() > n_cores { gum::debug!( target: LOG_TARGET, validator = assignment.validator.0, diff --git a/node/core/approval-voting/src/tests.rs b/node/core/approval-voting/src/tests.rs index 26dcb1d24970..35897beeae08 100644 --- a/node/core/approval-voting/src/tests.rs +++ b/node/core/approval-voting/src/tests.rs @@ -3708,17 +3708,17 @@ async fn handle_approval_on_max_coalesce_count( virtual_overseer: &mut VirtualOverseer, candidate_indicies: Vec, ) { - for _ in &candidate_indicies { - assert_matches!( - overseer_recv(virtual_overseer).await, - AllMessages::ApprovalDistribution(ApprovalDistributionMessage::DistributeAssignment( - _, - c_indices, - )) => { - assert_eq!(TryInto::::try_into(candidate_indicies.clone()).unwrap(), c_indices); - } - ); + assert_matches!( + overseer_recv(virtual_overseer).await, + AllMessages::ApprovalDistribution(ApprovalDistributionMessage::DistributeAssignment( + _, + c_indices, + )) => { + assert_eq!(TryInto::::try_into(candidate_indicies.clone()).unwrap(), c_indices); + } + ); + for _ in &candidate_indicies { recover_available_data(virtual_overseer).await; fetch_validation_code(virtual_overseer).await; } @@ -3775,17 +3775,17 @@ async fn handle_approval_on_max_wait_time( clock.inner.lock().set_tick(TICK_NOW_BEGIN); - for _ in &candidate_indicies { - assert_matches!( - overseer_recv(virtual_overseer).await, - AllMessages::ApprovalDistribution(ApprovalDistributionMessage::DistributeAssignment( - _, - c_indices, - )) => { - assert_eq!(TryInto::::try_into(candidate_indicies.clone()).unwrap(), c_indices); - } - ); + assert_matches!( + overseer_recv(virtual_overseer).await, + AllMessages::ApprovalDistribution(ApprovalDistributionMessage::DistributeAssignment( + _, + c_indices, + )) => { + assert_eq!(TryInto::::try_into(candidate_indicies.clone()).unwrap(), c_indices); + } + ); + for _ in &candidate_indicies { recover_available_data(virtual_overseer).await; fetch_validation_code(virtual_overseer).await; } diff --git a/node/core/dispute-coordinator/src/import.rs b/node/core/dispute-coordinator/src/import.rs index a760fe6326f3..c5ece106e42c 100644 --- a/node/core/dispute-coordinator/src/import.rs +++ b/node/core/dispute-coordinator/src/import.rs @@ -534,7 +534,21 @@ impl ImportResult { }, "Signature check for imported approval votes failed! This is a serious bug. Session: {:?}, candidate hash: {:?}, validator index: {:?}", env.session_index(), votes.candidate_receipt.hash(), index ); - if votes.valid.insert_vote(index, ValidDisputeStatementKind::ApprovalChecking, sig) { + if votes.valid.insert_vote( + index, + // There is a hidden dependency here between approval-voting and this subsystem. + // We should be able to start emitting ValidDisputeStatementKind::ApprovalCheckingMultipleCandidates only after: + // 1. Runtime have been upgraded to know about the new format. + // 2. All nodes have been upgraded to know about the new format. + // Once those two requirements have been met we should be able to increase max_approval_coalesce_count to values + // greater than 1. + if candidate_hashes.len() > 1 { + ValidDisputeStatementKind::ApprovalCheckingMultipleCandidates(candidate_hashes) + } else { + ValidDisputeStatementKind::ApprovalChecking + }, + sig, + ) { imported_valid_votes += 1; imported_approval_votes += 1; } diff --git a/node/network/approval-distribution/src/lib.rs b/node/network/approval-distribution/src/lib.rs index e4289445b48c..4b5f0190c9bb 100644 --- a/node/network/approval-distribution/src/lib.rs +++ b/node/network/approval-distribution/src/lib.rs @@ -1490,7 +1490,7 @@ impl State { COST_UNEXPECTED_MESSAGE, ) .await; - // TODO: is returned needed here ? + return }, } @@ -1507,8 +1507,10 @@ impl State { if let Some(peer_knowledge) = entry.known_by.get_mut(&peer_id) { peer_knowledge.received.insert(message_subject.clone(), message_kind); } + return } } + let (tx, rx) = oneshot::channel(); ctx.send_message(ApprovalVotingMessage::CheckAndImportApproval(vote.clone(), tx)) @@ -1583,6 +1585,7 @@ impl State { ); } } + let mut required_routing = RequiredRouting::None; for candidate_index in candidate_indices.iter_ones() { @@ -1620,6 +1623,7 @@ impl State { } required_routing = approval_entry.routing_info().required_routing; } + // Invariant: to our knowledge, none of the peers except for the `source` know about the approval. metrics.on_approval_imported(); diff --git a/runtime/parachains/src/disputes.rs b/runtime/parachains/src/disputes.rs index 80605818bf1b..ccc48968a281 100644 --- a/runtime/parachains/src/disputes.rs +++ b/runtime/parachains/src/disputes.rs @@ -1016,6 +1016,8 @@ impl Pallet { statement, signature, ) { + log::warn!("Failed to check dispute signature"); + importer.undo(undo); filter.remove_index(i); continue diff --git a/zombienet_tests/functional/0002-parachains-disputes.toml b/zombienet_tests/functional/0002-parachains-disputes.toml index a0a87d60d4e3..aa9602247662 100644 --- a/zombienet_tests/functional/0002-parachains-disputes.toml +++ b/zombienet_tests/functional/0002-parachains-disputes.toml @@ -5,6 +5,11 @@ timeout = 1000 max_validators_per_core = 5 needed_approvals = 8 +[relaychain.genesis.runtime.runtime_genesis_config.configuration.config.approval_voting_params] + max_approval_coalesce_count = 5 + max_approval_coalesce_wait_ticks = 2 + + [relaychain] default_image = "{{ZOMBIENET_INTEGRATION_TEST_IMAGE}}" chain = "rococo-local" From 1a2a5ec3bf9f1c75cee45a41e824e7d13689bfe2 Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe Date: Thu, 3 Aug 2023 21:24:47 +0300 Subject: [PATCH 10/54] Refactor the implementation a bit Signed-off-by: Alexandru Gheorghe --- node/network/approval-distribution/src/lib.rs | 223 ++++++++++-------- 1 file changed, 129 insertions(+), 94 deletions(-) diff --git a/node/network/approval-distribution/src/lib.rs b/node/network/approval-distribution/src/lib.rs index 4b5f0190c9bb..0f5ba633363b 100644 --- a/node/network/approval-distribution/src/lib.rs +++ b/node/network/approval-distribution/src/lib.rs @@ -1370,6 +1370,85 @@ impl State { } } + async fn check_approval_can_be_processed( + ctx: &mut Context, + message_subjects: &Vec, + message_kind: MessageKind, + entry: &mut BlockEntry, + reputation: &mut ReputationAggregator, + peer_id: PeerId, + ) -> bool { + for message_subject in message_subjects { + if !entry.knowledge.contains(&message_subject, MessageKind::Assignment) { + gum::debug!( + target: LOG_TARGET, + ?peer_id, + ?message_subject, + "Unknown approval assignment", + ); + modify_reputation(reputation, ctx.sender(), peer_id, COST_UNEXPECTED_MESSAGE).await; + return false + } + + // check if our knowledge of the peer already contains this approval + match entry.known_by.entry(peer_id) { + hash_map::Entry::Occupied(mut knowledge) => { + let peer_knowledge = knowledge.get_mut(); + if peer_knowledge.contains(&message_subject, message_kind) { + if !peer_knowledge.received.insert(message_subject.clone(), message_kind) { + gum::debug!( + target: LOG_TARGET, + ?peer_id, + ?message_subject, + "Duplicate approval", + ); + + modify_reputation( + reputation, + ctx.sender(), + peer_id, + COST_DUPLICATE_MESSAGE, + ) + .await; + } + return false + } + }, + hash_map::Entry::Vacant(_) => { + gum::debug!( + target: LOG_TARGET, + ?peer_id, + ?message_subject, + "Unknown approval assignment", + ); + modify_reputation(reputation, ctx.sender(), peer_id, COST_UNEXPECTED_MESSAGE) + .await; + return true + }, + } + } + + let good_known_approval = + message_subjects.iter().fold(false, |accumulator, message_subject| { + // if the approval is known to be valid, reward the peer + if entry.knowledge.contains(&message_subject, message_kind) { + if let Some(peer_knowledge) = entry.known_by.get_mut(&peer_id) { + peer_knowledge.received.insert(message_subject.clone(), message_kind); + } + // We already processed this approval no need + true + } else { + accumulator + } + }); + if good_known_approval { + gum::trace!(target: LOG_TARGET, ?peer_id, ?message_subjects, "Known approval"); + modify_reputation(reputation, ctx.sender(), peer_id, BENEFIT_VALID_MESSAGE).await; + } + + !good_known_approval + } + async fn import_and_circulate_approval( &mut self, ctx: &mut Context, @@ -1431,84 +1510,17 @@ impl State { let message_kind = MessageKind::Approval; if let Some(peer_id) = source.peer_id() { - for message_subject in &message_subjects { - if !entry.knowledge.contains(&message_subject, MessageKind::Assignment) { - gum::debug!( - target: LOG_TARGET, - ?peer_id, - ?message_subject, - "Unknown approval assignment", - ); - modify_reputation( - &mut self.reputation, - ctx.sender(), - peer_id, - COST_UNEXPECTED_MESSAGE, - ) - .await; - return - } - - // check if our knowledge of the peer already contains this approval - match entry.known_by.entry(peer_id) { - hash_map::Entry::Occupied(mut knowledge) => { - let peer_knowledge = knowledge.get_mut(); - if peer_knowledge.contains(&message_subject, message_kind) { - if !peer_knowledge - .received - .insert(message_subject.clone(), message_kind) - { - gum::debug!( - target: LOG_TARGET, - ?peer_id, - ?message_subject, - "Duplicate approval", - ); - - modify_reputation( - &mut self.reputation, - ctx.sender(), - peer_id, - COST_DUPLICATE_MESSAGE, - ) - .await; - } - return - } - }, - hash_map::Entry::Vacant(_) => { - gum::debug!( - target: LOG_TARGET, - ?peer_id, - ?message_subject, - "Unknown approval assignment", - ); - modify_reputation( - &mut self.reputation, - ctx.sender(), - peer_id, - COST_UNEXPECTED_MESSAGE, - ) - .await; - return - }, - } - - // if the approval is known to be valid, reward the peer - if entry.knowledge.contains(&message_subject, message_kind) { - gum::trace!(target: LOG_TARGET, ?peer_id, ?message_subject, "Known approval"); - modify_reputation( - &mut self.reputation, - ctx.sender(), - peer_id, - BENEFIT_VALID_MESSAGE, - ) - .await; - if let Some(peer_knowledge) = entry.known_by.get_mut(&peer_id) { - peer_knowledge.received.insert(message_subject.clone(), message_kind); - } - return - } + if !Self::check_approval_can_be_processed( + ctx, + &message_subjects, + message_kind, + entry, + &mut self.reputation, + peer_id, + ) + .await + { + return } let (tx, rx) = oneshot::channel(); @@ -1650,8 +1662,8 @@ impl State { let in_topology = topology .map_or(false, |t| t.local_grid_neighbors().route_to_peer(required_routing, peer)); in_topology || - message_subjects_clone.iter().fold(false, |result, message_subject| { - result || knowledge.sent.contains(message_subject, MessageKind::Assignment) + message_subjects_clone.iter().fold(true, |result, message_subject| { + result && knowledge.sent.contains(message_subject, MessageKind::Assignment) }) }; @@ -1851,20 +1863,43 @@ impl State { // Filter approval votes. for approval_message in approval_messages { - let mut queued_to_be_sent = false; - for approval_candidate_index in - approval_message.candidate_indices.iter_ones() - { - let (approval_knowledge, message_kind) = approval_entry - .create_approval_knowledge(block, approval_candidate_index as _); - - if !peer_knowledge.contains(&approval_knowledge, message_kind) { - peer_knowledge.sent.insert(approval_knowledge, message_kind); - if !queued_to_be_sent { - approvals_to_send.push(approval_message.clone()); - queued_to_be_sent = true; - } - } + let (should_forward_approval, covered_approvals) = + approval_message.candidate_indices.iter_ones().fold( + (true, Vec::new()), + |(should_forward_approval, mut new_covered_approvals), + approval_candidate_index| { + let (approval_knowledge, message_kind) = approval_entry + .create_approval_knowledge( + block, + approval_candidate_index as _, + ); + let (assignment_knowledge, assignment_kind) = + approval_entry.create_assignment_knowledge(block); + + if !peer_knowledge.contains(&approval_knowledge, message_kind) { + new_covered_approvals + .push((approval_knowledge, message_kind)) + } + + ( + // The assignments for all candidates signed in the approval should already have been sent to the peer, + // otherwise we can't send our approval and risk breaking our reputation. + should_forward_approval && + peer_knowledge.contains( + &assignment_knowledge, + assignment_kind, + ), + new_covered_approvals, + ) + }, + ); + if should_forward_approval { + approvals_to_send.push(approval_message); + covered_approvals.into_iter().for_each( + |(approval_knowledge, message_kind)| { + peer_knowledge.sent.insert(approval_knowledge, message_kind); + }, + ); } } } From 81eb942a1052c7f3b29c0f2e4029a842071af45d Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe Date: Tue, 8 Aug 2023 11:41:46 +0300 Subject: [PATCH 11/54] Make test better Signed-off-by: Alexandru Gheorghe --- .../0006-approval-voting-coalescing.toml | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/zombienet_tests/functional/0006-approval-voting-coalescing.toml b/zombienet_tests/functional/0006-approval-voting-coalescing.toml index 6f1e2a4674cc..92f08527b020 100644 --- a/zombienet_tests/functional/0006-approval-voting-coalescing.toml +++ b/zombienet_tests/functional/0006-approval-voting-coalescing.toml @@ -6,6 +6,10 @@ default_image = "{{ZOMBIENET_INTEGRATION_TEST_IMAGE}}" chain = "rococo-local" chain_spec_command = "polkadot build-spec --chain rococo-local --disable-default-bootnode" +[relaychain.genesis.runtime.runtime_genesis_config.configuration.config] + needed_approvals = 8 + relay_vrf_modulo_samples = 6 + [relaychain.genesis.runtime.runtime_genesis_config.configuration.config.approval_voting_params] max_approval_coalesce_count = {{MAX_APPROVAL_COALESCE_COUNT}} max_approval_coalesce_wait_ticks = {{MAX_APPROVAL_COALESCE_WAIT_TICKS}} @@ -46,6 +50,58 @@ requests = { memory = "2G", cpu = "1" } name = "two" args = [ "--two", "-lparachain=debug,runtime=debug"] + [[relaychain.nodes]] + name = "nine" + args = ["-lparachain=debug,runtime=debug"] + + [[relaychain.nodes]] + name = "eleven" + args = ["-lparachain=debug,runtime=debug"] + + [[relaychain.nodes]] + name = "twelve" + args = ["-lparachain=debug,runtime=debug"] + + [[relaychain.nodes]] + name = "thirteen" + args = ["-lparachain=debug,runtime=debug"] + + [[relaychain.nodes]] + name = "fourteen" + args = ["-lparachain=debug,runtime=debug"] + + [[relaychain.nodes]] + name = "fifteen" + args = ["-lparachain=debug,runtime=debug"] + + [[relaychain.nodes]] + name = "sixteen" + args = ["-lparachain=debug,runtime=debug"] + + [[relaychain.nodes]] + name = "seventeen" + args = ["-lparachain=debug,runtime=debug"] + + [[relaychain.nodes]] + name = "eithteen" + args = ["-lparachain=debug,runtime=debug"] + + [[relaychain.nodes]] + name = "nineteen" + args = ["-lparachain=debug,runtime=debug"] + + [[relaychain.nodes]] + name = "twenty" + args = ["-lparachain=debug,runtime=debug"] + + [[relaychain.nodes]] + name = "twentyone" + args = ["-lparachain=debug,runtime=debug"] + + [[relaychain.nodes]] + name = "twentytwo" + args = ["-lparachain=debug,runtime=debug"] + [[parachains]] id = 2000 addToGenesis = true From ff0b35d38350e22027e81b8bd7411fb8e7b9b212 Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe Date: Tue, 8 Aug 2023 12:32:20 +0300 Subject: [PATCH 12/54] approval-voting: fix migration from v1 to v2 Signed-off-by: Alexandru Gheorghe --- .../approval-voting/src/approval_db/v1/mod.rs | 26 ++++++++++++++++++- .../src/approval_db/v2/migration_helpers.rs | 6 ++++- .../approval-voting/src/approval_db/v2/mod.rs | 18 +++++++++++++ node/core/approval-voting/src/backend.rs | 3 +++ .../approval-voting/src/persisted_entries.rs | 18 +++++++++++++ 5 files changed, 69 insertions(+), 2 deletions(-) diff --git a/node/core/approval-voting/src/approval_db/v1/mod.rs b/node/core/approval-voting/src/approval_db/v1/mod.rs index c324156d2836..dc796c9b48e2 100644 --- a/node/core/approval-voting/src/approval_db/v1/mod.rs +++ b/node/core/approval-voting/src/approval_db/v1/mod.rs @@ -25,8 +25,10 @@ use parity_scale_codec::{Decode, Encode}; use polkadot_node_primitives::approval::v1::{AssignmentCert, DelayTranche}; use polkadot_primitives::{ - CandidateReceipt, GroupIndex, Hash, SessionIndex, ValidatorIndex, ValidatorSignature, + BlockNumber, CandidateHash, CandidateReceipt, CoreIndex, GroupIndex, Hash, SessionIndex, + ValidatorIndex, ValidatorSignature, }; +use sp_consensus_slots::Slot; use std::collections::BTreeMap; @@ -66,3 +68,25 @@ pub struct CandidateEntry { pub block_assignments: BTreeMap, pub approvals: Bitfield, } + +/// Metadata regarding approval of a particular block, by way of approval of the +/// candidates contained within it. +#[derive(Encode, Decode, Debug, Clone, PartialEq)] +pub struct BlockEntry { + pub block_hash: Hash, + pub block_number: BlockNumber, + pub parent_hash: Hash, + pub session: SessionIndex, + pub slot: Slot, + /// Random bytes derived from the VRF submitted within the block by the block + /// author as a credential and used as input to approval assignment criteria. + pub relay_vrf_story: [u8; 32], + // The candidates included as-of this block and the index of the core they are + // leaving. Sorted ascending by core index. + pub candidates: Vec<(CoreIndex, CandidateHash)>, + // A bitfield where the i'th bit corresponds to the i'th candidate in `candidates`. + // The i'th bit is `true` iff the candidate has been approved in the context of this + // block. The block can be considered approved if the bitfield has all bits set to `true`. + pub approved_bitfield: Bitfield, + pub children: Vec, +} diff --git a/node/core/approval-voting/src/approval_db/v2/migration_helpers.rs b/node/core/approval-voting/src/approval_db/v2/migration_helpers.rs index ada9685fd85a..8971df32c5d5 100644 --- a/node/core/approval-voting/src/approval_db/v2/migration_helpers.rs +++ b/node/core/approval-voting/src/approval_db/v2/migration_helpers.rs @@ -70,7 +70,10 @@ pub fn v1_to_v2(db: Arc, config: Config) -> Result<()> { .map_err(|e| Error::InternalError(e))? .iter() .filter_map(|block_hash| { - backend.load_block_entry(block_hash).map_err(|e| Error::InternalError(e)).ok()? + backend + .load_block_entry_v1(block_hash) + .map_err(|e| Error::InternalError(e)) + .ok()? }) .collect::>(); @@ -96,6 +99,7 @@ pub fn v1_to_v2(db: Arc, config: Config) -> Result<()> { counter += 1; } } + overlay.write_block_entry(block); } gum::info!(target: crate::LOG_TARGET, "Migrated {} entries", counter); diff --git a/node/core/approval-voting/src/approval_db/v2/mod.rs b/node/core/approval-voting/src/approval_db/v2/mod.rs index ac72a291a9e0..ee1d832df527 100644 --- a/node/core/approval-voting/src/approval_db/v2/mod.rs +++ b/node/core/approval-voting/src/approval_db/v2/mod.rs @@ -63,6 +63,13 @@ impl V1ReadBackend for DbBackend { load_candidate_entry_v1(&*self.inner, &self.config, candidate_hash) .map(|e| e.map(Into::into)) } + + fn load_block_entry_v1( + &self, + block_hash: &Hash, + ) -> SubsystemResult> { + load_block_entry_v1(&*self.inner, &self.config, block_hash).map(|e| e.map(Into::into)) + } } impl Backend for DbBackend { @@ -385,3 +392,14 @@ pub fn load_candidate_entry_v1( .map(|u: Option| u.map(|v| v.into())) .map_err(|e| SubsystemError::with_origin("approval-voting", e)) } + +/// Load a block entry from the aux store in v1 format. +pub fn load_block_entry_v1( + store: &dyn Database, + config: &Config, + block_hash: &Hash, +) -> SubsystemResult> { + load_decode(store, config.col_approval_data, &block_entry_key(block_hash)) + .map(|u: Option| u.map(|v| v.into())) + .map_err(|e| SubsystemError::with_origin("approval-voting", e)) +} diff --git a/node/core/approval-voting/src/backend.rs b/node/core/approval-voting/src/backend.rs index 0e3f04cc7e96..221c22e2ad48 100644 --- a/node/core/approval-voting/src/backend.rs +++ b/node/core/approval-voting/src/backend.rs @@ -73,6 +73,9 @@ pub trait V1ReadBackend: Backend { &self, candidate_hash: &CandidateHash, ) -> SubsystemResult>; + + /// Loads a block entry from the DB with scheme version 1. + fn load_block_entry_v1(&self, block_hash: &Hash) -> SubsystemResult>; } // Status of block range in the `OverlayedBackend`. diff --git a/node/core/approval-voting/src/persisted_entries.rs b/node/core/approval-voting/src/persisted_entries.rs index 229ce15a6e27..c7fd8bf9570a 100644 --- a/node/core/approval-voting/src/persisted_entries.rs +++ b/node/core/approval-voting/src/persisted_entries.rs @@ -536,6 +536,24 @@ impl From for CandidateEntry { } } +impl From for BlockEntry { + fn from(block: crate::approval_db::v1::BlockEntry) -> Self { + Self { + block_hash: block.block_hash, + parent_hash: block.parent_hash, + block_number: block.block_number, + session: block.session, + slot: block.slot, + relay_vrf_story: RelayVRFStory(block.relay_vrf_story), + candidates: block.candidates, + approved_bitfield: block.approved_bitfield, + children: block.children, + candidates_pending_signature: Default::default(), + distributed_assignments: Default::default(), + } + } +} + impl From for ApprovalEntry { fn from(value: crate::approval_db::v1::ApprovalEntry) -> Self { ApprovalEntry { From 2570c1e0dc2de8ca5a528b97eaa0a9b5ab6bfd79 Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe Date: Tue, 8 Aug 2023 12:33:40 +0300 Subject: [PATCH 13/54] Fix-up bugs in assignnments_coalescing of tranche0 Signed-off-by: Alexandru Gheorghe --- node/network/approval-distribution/src/lib.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/node/network/approval-distribution/src/lib.rs b/node/network/approval-distribution/src/lib.rs index 0f5ba633363b..1cc1f1791b16 100644 --- a/node/network/approval-distribution/src/lib.rs +++ b/node/network/approval-distribution/src/lib.rs @@ -1096,6 +1096,7 @@ impl State { COST_UNEXPECTED_MESSAGE, ) .await; + gum::error!(target: LOG_TARGET, "Received assignment for invalid block"); } } return @@ -1490,6 +1491,7 @@ impl State { COST_UNEXPECTED_MESSAGE, ) .await; + gum::error!(target: LOG_TARGET, "Received approval for invalid block"); } } return @@ -2065,9 +2067,7 @@ impl State { }; // Ensure bitfields length under hard limit. - if cert_bitfield_bits > MAX_BITFIELD_SIZE || - cert_bitfield_bits != candidate_index as usize + 1 - { + if cert_bitfield_bits > MAX_BITFIELD_SIZE { // Punish the peer for the invalid message. modify_reputation(&mut self.reputation, sender, peer_id, COST_OVERSIZED_BITFIELD) .await; @@ -2105,7 +2105,6 @@ impl State { // Ensure bitfields length under hard limit. if cert_bitfield_bits > MAX_BITFIELD_SIZE - || cert_bitfield_bits != candidate_bitfield_len // Ensure minimum bitfield size - MSB needs to be one. || !candidate_bitfield.bit_at(msb.as_bit_index()) { From 305a43a2045b3fcd3593aea5d5d79faaaf391e73 Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe Date: Tue, 8 Aug 2023 12:34:24 +0300 Subject: [PATCH 14/54] Enable v2 network protocol Signed-off-by: Alexandru Gheorghe --- node/network/protocol/src/peer_set.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/network/protocol/src/peer_set.rs b/node/network/protocol/src/peer_set.rs index ff6cb4d9ad1b..78e4ca02dce3 100644 --- a/node/network/protocol/src/peer_set.rs +++ b/node/network/protocol/src/peer_set.rs @@ -119,7 +119,7 @@ impl PeerSet { pub fn get_main_version(self) -> ProtocolVersion { #[cfg(not(feature = "network-protocol-staging"))] match self { - PeerSet::Validation => ValidationVersion::V1.into(), + PeerSet::Validation => ValidationVersion::VStaging.into(), PeerSet::Collation => CollationVersion::V1.into(), } From 5bb1e912b2dc5145d00252b15944a0c367dc5dba Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe Date: Tue, 8 Aug 2023 15:59:07 +0300 Subject: [PATCH 15/54] Fixup test builds Signed-off-by: Alexandru Gheorghe --- node/core/approval-voting/src/tests.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/node/core/approval-voting/src/tests.rs b/node/core/approval-voting/src/tests.rs index 35897beeae08..ac12287ed718 100644 --- a/node/core/approval-voting/src/tests.rs +++ b/node/core/approval-voting/src/tests.rs @@ -282,6 +282,10 @@ impl V1ReadBackend for TestStoreInner { ) -> SubsystemResult> { self.load_candidate_entry(candidate_hash) } + + fn load_block_entry_v1(&self, block_hash: &Hash) -> SubsystemResult> { + self.load_block_entry(block_hash) + } } impl Backend for TestStoreInner { @@ -362,6 +366,10 @@ impl V1ReadBackend for TestStore { ) -> SubsystemResult> { self.load_candidate_entry(candidate_hash) } + + fn load_block_entry_v1(&self, block_hash: &Hash) -> SubsystemResult> { + self.load_block_entry(block_hash) + } } impl Backend for TestStore { From a83cfb7202eca0cca5f6ed9c8fe2f627203e42cb Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe Date: Tue, 8 Aug 2023 14:07:37 +0300 Subject: [PATCH 16/54] Enable assignments v2 computing Signed-off-by: Alexandru Gheorghe --- node/core/approval-voting/src/criteria.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/core/approval-voting/src/criteria.rs b/node/core/approval-voting/src/criteria.rs index 33731a5fe3fa..692a60aa7751 100644 --- a/node/core/approval-voting/src/criteria.rs +++ b/node/core/approval-voting/src/criteria.rs @@ -278,7 +278,7 @@ impl AssignmentCriteria for RealAssignmentCriteria { config: &Config, leaving_cores: Vec<(CandidateHash, CoreIndex, GroupIndex)>, ) -> HashMap { - compute_assignments(keystore, relay_vrf_story, config, leaving_cores, false) + compute_assignments(keystore, relay_vrf_story, config, leaving_cores, true) } fn check_assignment_cert( From 98705e27d85f7a163e7a1d89957f136955c26f3e Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe Date: Thu, 10 Aug 2023 11:05:21 +0300 Subject: [PATCH 17/54] Update test params Signed-off-by: Alexandru Gheorghe --- zombienet_tests/functional/0006-approval-voting-coalescing.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zombienet_tests/functional/0006-approval-voting-coalescing.toml b/zombienet_tests/functional/0006-approval-voting-coalescing.toml index 92f08527b020..a2c8ceb72f27 100644 --- a/zombienet_tests/functional/0006-approval-voting-coalescing.toml +++ b/zombienet_tests/functional/0006-approval-voting-coalescing.toml @@ -8,7 +8,7 @@ chain_spec_command = "polkadot build-spec --chain rococo-local --disable-default [relaychain.genesis.runtime.runtime_genesis_config.configuration.config] needed_approvals = 8 - relay_vrf_modulo_samples = 6 + relay_vrf_modulo_samples = 3 [relaychain.genesis.runtime.runtime_genesis_config.configuration.config.approval_voting_params] max_approval_coalesce_count = {{MAX_APPROVAL_COALESCE_COUNT}} From aae2a4ae58dd3df8bc1990b1404084f3050e4601 Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe Date: Thu, 10 Aug 2023 16:49:50 +0300 Subject: [PATCH 18/54] Fixup logic for restarting the node Signed-off-by: Alexandru Gheorghe --- .../approval-voting/src/approval_db/v2/mod.rs | 16 ++++- node/core/approval-voting/src/lib.rs | 60 +++++++++++++------ .../approval-voting/src/persisted_entries.rs | 43 +++++++++++-- node/network/approval-distribution/src/lib.rs | 6 +- 4 files changed, 99 insertions(+), 26 deletions(-) diff --git a/node/core/approval-voting/src/approval_db/v2/mod.rs b/node/core/approval-voting/src/approval_db/v2/mod.rs index ee1d832df527..8fb2e12f57c2 100644 --- a/node/core/approval-voting/src/approval_db/v2/mod.rs +++ b/node/core/approval-voting/src/approval_db/v2/mod.rs @@ -17,7 +17,10 @@ //! Version 2 of the DB schema. use parity_scale_codec::{Decode, Encode}; -use polkadot_node_primitives::approval::{v1::DelayTranche, v2::AssignmentCertV2}; +use polkadot_node_primitives::approval::{ + v1::DelayTranche, + v2::{AssignmentCertV2, CandidateBitfield}, +}; use polkadot_node_subsystem::{SubsystemError, SubsystemResult}; use polkadot_node_subsystem_util::database::{DBTransaction, Database}; use polkadot_primitives::{ @@ -197,6 +200,15 @@ pub struct TrancheEntry { pub assignments: Vec<(ValidatorIndex, Tick)>, } +/// Metadata about our approval signature +#[derive(Encode, Decode, Debug, Clone, PartialEq)] +pub struct OurApproval { + /// The + pub signature: ValidatorSignature, + // The hashes of the candidates signed in this approval + pub signed_candidates_indices: Option, +} + /// Metadata regarding approval of a particular candidate within the context of some /// particular block. #[derive(Encode, Decode, Debug, Clone, PartialEq)] @@ -204,7 +216,7 @@ pub struct ApprovalEntry { pub tranches: Vec, pub backing_group: GroupIndex, pub our_assignment: Option, - pub our_approval_sig: Option, + pub our_approval_sig: Option, // `n_validators` bits. pub assigned_validators: Bitfield, pub approved: bool, diff --git a/node/core/approval-voting/src/lib.rs b/node/core/approval-voting/src/lib.rs index 270645e23cde..d15552aa9a03 100644 --- a/node/core/approval-voting/src/lib.rs +++ b/node/core/approval-voting/src/lib.rs @@ -101,7 +101,7 @@ use crate::{ approval_db::v2::{Config as DatabaseConfig, DbBackend}, backend::{Backend, OverlayedBackend}, criteria::InvalidAssignmentReason, - persisted_entries::CandidateSigningContext, + persisted_entries::{CandidateSigningContext, OurApproval}, }; #[cfg(test)] @@ -1084,7 +1084,11 @@ async fn handle_actions( Action::BecomeActive => { *mode = Mode::Active; - let messages = distribution_messages_for_activation(overlayed_db, state)?; + let messages = distribution_messages_for_activation( + overlayed_db, + state, + delayed_approvals_timers, + )?; ctx.send_messages(messages.into_iter()).await; }, @@ -1143,6 +1147,7 @@ fn get_assignment_core_indices( fn distribution_messages_for_activation( db: &OverlayedBackend<'_, impl Backend>, state: &State, + delayed_approvals_timers: &mut DelayedApprovalTimer, ) -> SubsystemResult> { let all_blocks: Vec = db.load_all_blocks()?; @@ -1181,7 +1186,7 @@ fn distribution_messages_for_activation( slot: block_entry.slot(), session: block_entry.session(), }); - + let mut signatures_queued = HashSet::new(); for (i, (_, candidate_hash)) in block_entry.candidates().iter().enumerate() { let _candidate_span = distribution_message_span.child("candidate").with_candidate(*candidate_hash); @@ -1209,6 +1214,15 @@ fn distribution_messages_for_activation( &candidate_hash, &block_entry, ) { + if !block_entry.candidates_pending_signature.is_empty() { + delayed_approvals_timers.maybe_arm_timer( + state.clock.tick_now(), + state.clock.as_ref(), + block_entry.block_hash(), + assignment.validator_index(), + ) + } + match cores_to_candidate_indices( &claimed_core_indices, &block_entry, @@ -1275,15 +1289,19 @@ fn distribution_messages_for_activation( continue }, } - - messages.push(ApprovalDistributionMessage::DistributeApproval( - IndirectSignedApprovalVoteV2 { - block_hash, - candidate_indices: (i as CandidateIndex).into(), - validator: assignment.validator_index(), - signature: approval_sig, - }, - )); + let candidate_indices = approval_sig + .signed_candidates_indices + .unwrap_or((i as CandidateIndex).into()); + if signatures_queued.insert(candidate_indices.clone()) { + messages.push(ApprovalDistributionMessage::DistributeApproval( + IndirectSignedApprovalVoteV2 { + block_hash, + candidate_indices, + validator: assignment.validator_index(), + signature: approval_sig.signature, + }, + )) + }; } else { gum::warn!( target: LOG_TARGET, @@ -3221,7 +3239,7 @@ async fn maybe_create_signature( let signature = match sign_approval( &state.keystore, &validator_pubkey, - candidate_hashes, + candidate_hashes.clone(), block_entry.session(), ) { Some(sig) => sig, @@ -3243,20 +3261,26 @@ async fn maybe_create_signature( .values() .map(|unsigned_approval| db.load_candidate_entry(&unsigned_approval.candidate_hash)) .collect::>>>()?; - + let candidate_indexes: Vec = + block_entry.candidates_pending_signature.keys().map(|val| *val).collect(); for candidate_entry in candidate_entries { let mut candidate_entry = candidate_entry .expect("Candidate was scheduled to be signed entry in db should exist; qed"); let approval_entry = candidate_entry .approval_entry_mut(&block_entry.block_hash()) .expect("Candidate was scheduled to be signed entry in db should exist; qed"); - approval_entry.import_approval_sig(signature.clone()); + approval_entry.import_approval_sig(OurApproval { + signature: signature.clone(), + signed_candidates_indices: Some( + candidate_indexes + .clone() + .try_into() + .expect("Fails only of array empty, it can't be, qed"), + ), + }); db.write_candidate_entry(candidate_entry); } - let candidate_indexes: Vec = - block_entry.candidates_pending_signature.keys().map(|val| *val).collect(); - metrics.on_approval_produced(); ctx.send_unbounded_message(ApprovalDistributionMessage::DistributeApproval( diff --git a/node/core/approval-voting/src/persisted_entries.rs b/node/core/approval-voting/src/persisted_entries.rs index a25939a9c69d..d599c2eac133 100644 --- a/node/core/approval-voting/src/persisted_entries.rs +++ b/node/core/approval-voting/src/persisted_entries.rs @@ -76,6 +76,39 @@ impl From for crate::approval_db::v2::TrancheEntry { } } +impl From for OurApproval { + fn from(approval: crate::approval_db::v2::OurApproval) -> Self { + Self { + signature: approval.signature, + signed_candidates_indices: approval.signed_candidates_indices, + } + } +} +impl From for crate::approval_db::v2::OurApproval { + fn from(approval: OurApproval) -> Self { + Self { + signature: approval.signature, + signed_candidates_indices: approval.signed_candidates_indices, + } + } +} + +impl From for OurApproval { + fn from(value: ValidatorSignature) -> Self { + Self { signature: value, signed_candidates_indices: Default::default() } + } +} + +/// Metadata about our approval signature +#[derive(Debug, Clone, PartialEq)] +pub struct OurApproval { + /// The + pub signature: ValidatorSignature, + /// The indices of the candidates signed in this approval, an empty value means only + /// the candidate referred by this approval entry was signed. + pub signed_candidates_indices: Option, +} + /// Metadata regarding approval of a particular candidate within the context of some /// particular block. #[derive(Debug, Clone, PartialEq)] @@ -83,7 +116,7 @@ pub struct ApprovalEntry { tranches: Vec, backing_group: GroupIndex, our_assignment: Option, - our_approval_sig: Option, + our_approval_sig: Option, // `n_validators` bits. assigned_validators: Bitfield, approved: bool, @@ -95,7 +128,7 @@ impl ApprovalEntry { tranches: Vec, backing_group: GroupIndex, our_assignment: Option, - our_approval_sig: Option, + our_approval_sig: Option, // `n_validators` bits. assigned_validators: Bitfield, approved: bool, @@ -137,7 +170,7 @@ impl ApprovalEntry { } /// Import our local approval vote signature for this candidate. - pub fn import_approval_sig(&mut self, approval_sig: ValidatorSignature) { + pub fn import_approval_sig(&mut self, approval_sig: OurApproval) { self.our_approval_sig = Some(approval_sig); } @@ -224,7 +257,7 @@ impl ApprovalEntry { /// Get the assignment cert & approval signature. /// /// The approval signature will only be `Some` if the assignment is too. - pub fn local_statements(&self) -> (Option, Option) { + pub fn local_statements(&self) -> (Option, Option) { let approval_sig = self.our_approval_sig.clone(); if let Some(our_assignment) = self.our_assignment.as_ref().filter(|a| a.triggered()) { (Some(our_assignment.clone()), approval_sig) @@ -560,7 +593,7 @@ impl From for ApprovalEntry { tranches: value.tranches.into_iter().map(|tranche| tranche.into()).collect(), backing_group: value.backing_group, our_assignment: value.our_assignment.map(|assignment| assignment.into()), - our_approval_sig: value.our_approval_sig, + our_approval_sig: value.our_approval_sig.map(Into::into), assigned_validators: value.assignments, approved: value.approved, } diff --git a/node/network/approval-distribution/src/lib.rs b/node/network/approval-distribution/src/lib.rs index 44d5cc484ab4..3e5e5eba2a73 100644 --- a/node/network/approval-distribution/src/lib.rs +++ b/node/network/approval-distribution/src/lib.rs @@ -1478,7 +1478,11 @@ impl State { let entry = match self.blocks.get_mut(&block_hash) { Some(entry) if vote.candidate_indices.iter_ones().fold(true, |result, candidate_index| { - entry.contains_approval_entry(candidate_index as _, validator_index) && result + let approval_entry_exists = entry.contains_approval_entry(candidate_index as _, validator_index); + if !approval_entry_exists { + gum::error!(target: LOG_TARGET, ?block_hash, ?candidate_index, peer_id = ?source.peer_id(), "Received approval before assignment"); + } + approval_entry_exists && result }) => entry, _ => { From 435d46a80c846ea76d8107f1deb9d20138363ccd Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe Date: Thu, 10 Aug 2023 17:52:16 +0300 Subject: [PATCH 19/54] Add metric to measure impact of coalescing of approvals Signed-off-by: Alexandru Gheorghe --- node/core/approval-voting/src/lib.rs | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/node/core/approval-voting/src/lib.rs b/node/core/approval-voting/src/lib.rs index d15552aa9a03..d644407412e9 100644 --- a/node/core/approval-voting/src/lib.rs +++ b/node/core/approval-voting/src/lib.rs @@ -166,6 +166,7 @@ struct MetricsInner { approvals_produced_total: prometheus::CounterVec, no_shows_total: prometheus::Counter, wakeups_triggered_total: prometheus::Counter, + coalesced_approvals_buckets: prometheus::Histogram, candidate_approval_time_ticks: prometheus::Histogram, block_approval_time_ticks: prometheus::Histogram, time_db_transaction: prometheus::Histogram, @@ -190,6 +191,15 @@ impl Metrics { } } + fn on_approval_coalesce(&self, num_coalesced: u32) { + if let Some(metrics) = &self.0 { + // So we count how many candidates we covered with this approvals. + for _ in 0..num_coalesced { + metrics.coalesced_approvals_buckets.observe(num_coalesced as f64) + } + } + } + fn on_approval_stale(&self) { if let Some(metrics) = &self.0 { metrics.approvals_produced_total.with_label_values(&["stale"]).inc() @@ -313,6 +323,15 @@ impl metrics::Metrics for Metrics { )?, registry, )?, + coalesced_approvals_buckets: prometheus::register( + prometheus::Histogram::with_opts( + prometheus::HistogramOpts::new( + "polkadot_parachain_approvals_coalesced_approvals_buckets", + "Number of coalesced approvals.", + ).buckets(vec![1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5]), + )?, + registry, + )?, block_approval_time_ticks: prometheus::register( prometheus::Histogram::with_opts( prometheus::HistogramOpts::new( @@ -3163,7 +3182,7 @@ async fn maybe_create_signature( None => { // not a cause for alarm - just lost a race with pruning, most likely. metrics.on_approval_stale(); - gum::trace!( + gum::info!( target: LOG_TARGET, "Could not find block that needs signature {:}", block_hash ); @@ -3281,6 +3300,8 @@ async fn maybe_create_signature( db.write_candidate_entry(candidate_entry); } + metrics.on_approval_coalesce(candidate_indexes.len() as u32); + metrics.on_approval_produced(); ctx.send_unbounded_message(ApprovalDistributionMessage::DistributeApproval( From 9016644e5e60581c5a1d0f658ee1615a504e1bcd Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe Date: Fri, 11 Aug 2023 09:06:55 +0300 Subject: [PATCH 20/54] Add extra logs to improve debugability in versi Signed-off-by: Alexandru Gheorghe --- node/network/approval-distribution/src/lib.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/node/network/approval-distribution/src/lib.rs b/node/network/approval-distribution/src/lib.rs index 3e5e5eba2a73..b77bd955c505 100644 --- a/node/network/approval-distribution/src/lib.rs +++ b/node/network/approval-distribution/src/lib.rs @@ -1478,9 +1478,13 @@ impl State { let entry = match self.blocks.get_mut(&block_hash) { Some(entry) if vote.candidate_indices.iter_ones().fold(true, |result, candidate_index| { - let approval_entry_exists = entry.contains_approval_entry(candidate_index as _, validator_index); + let approval_entry_exists = + entry.contains_approval_entry(candidate_index as _, validator_index); if !approval_entry_exists { - gum::error!(target: LOG_TARGET, ?block_hash, ?candidate_index, peer_id = ?source.peer_id(), "Received approval before assignment"); + gum::error!( + target: LOG_TARGET, ?block_hash, ?candidate_index, validator_index = ?vote.validator, + peer_id = ?source.peer_id(), "Received approval before assignment" + ); } approval_entry_exists && result }) => @@ -2078,6 +2082,7 @@ impl State { // Punish the peer for the invalid message. modify_reputation(&mut self.reputation, sender, peer_id, COST_OVERSIZED_BITFIELD) .await; + gum::error!(target: LOG_TARGET, block_hash = ?cert.block_hash, ?candidate_index, validator_index = ?cert.validator, kind = ?cert.cert.kind, "Bad assignment v1"); } else { sanitized_assignments.push((cert.into(), candidate_index.into())) } @@ -2120,6 +2125,9 @@ impl State { // Punish the peer for the invalid message. modify_reputation(&mut self.reputation, sender, peer_id, COST_OVERSIZED_BITFIELD) .await; + for candidate_index in candidate_bitfield.iter_ones() { + gum::error!(target: LOG_TARGET, block_hash = ?cert.block_hash, ?candidate_index, validator_index = ?cert.validator, "Bad assignment v2"); + } } else { sanitized_assignments.push((cert, candidate_bitfield)) } From 1662e141ed68215fdeaaf6b7e6521cd8700fd5fa Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe Date: Fri, 11 Aug 2023 16:12:55 +0300 Subject: [PATCH 21/54] Fixup sending approval before assignment Signed-off-by: Alexandru Gheorghe --- node/network/approval-distribution/src/lib.rs | 26 +++++++------------ 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/node/network/approval-distribution/src/lib.rs b/node/network/approval-distribution/src/lib.rs index b77bd955c505..81328021ce80 100644 --- a/node/network/approval-distribution/src/lib.rs +++ b/node/network/approval-distribution/src/lib.rs @@ -1878,29 +1878,21 @@ impl State { (true, Vec::new()), |(should_forward_approval, mut new_covered_approvals), approval_candidate_index| { - let (approval_knowledge, message_kind) = approval_entry + let (message_subject, message_kind) = approval_entry .create_approval_knowledge( block, approval_candidate_index as _, ); - let (assignment_knowledge, assignment_kind) = - approval_entry.create_assignment_knowledge(block); - - if !peer_knowledge.contains(&approval_knowledge, message_kind) { - new_covered_approvals - .push((approval_knowledge, message_kind)) + // The assignments for all candidates signed in the approval should already have been sent to the peer, + // otherwise we can't send our approval and risk breaking our reputation. + let should_forward_approval = should_forward_approval && + peer_knowledge + .contains(&message_subject, MessageKind::Assignment); + if !peer_knowledge.contains(&message_subject, message_kind) { + new_covered_approvals.push((message_subject, message_kind)) } - ( - // The assignments for all candidates signed in the approval should already have been sent to the peer, - // otherwise we can't send our approval and risk breaking our reputation. - should_forward_approval && - peer_knowledge.contains( - &assignment_knowledge, - assignment_kind, - ), - new_covered_approvals, - ) + (should_forward_approval, new_covered_approvals) }, ); if should_forward_approval { From af578316c2a9abf3a2266534d7f0fb8ded4e554f Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe Date: Tue, 15 Aug 2023 11:32:23 +0300 Subject: [PATCH 22/54] Count needed approvals by more than one third Signed-off-by: Alexandru Gheorghe --- .../approval-voting/src/approval_checking.rs | 2 +- node/core/approval-voting/src/lib.rs | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/node/core/approval-voting/src/approval_checking.rs b/node/core/approval-voting/src/approval_checking.rs index d661ccd80578..3e8fe651eae6 100644 --- a/node/core/approval-voting/src/approval_checking.rs +++ b/node/core/approval-voting/src/approval_checking.rs @@ -64,7 +64,7 @@ pub enum RequiredTranches { } /// The result of a check. -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq)] pub enum Check { /// The candidate is unapproved. Unapproved, diff --git a/node/core/approval-voting/src/lib.rs b/node/core/approval-voting/src/lib.rs index d644407412e9..fbe1c6c783e1 100644 --- a/node/core/approval-voting/src/lib.rs +++ b/node/core/approval-voting/src/lib.rs @@ -98,6 +98,7 @@ mod persisted_entries; mod time; use crate::{ + approval_checking::Check, approval_db::v2::{Config as DatabaseConfig, DbBackend}, backend::{Backend, OverlayedBackend}, criteria::InvalidAssignmentReason, @@ -165,6 +166,7 @@ struct MetricsInner { assignments_produced: prometheus::Histogram, approvals_produced_total: prometheus::CounterVec, no_shows_total: prometheus::Counter, + approved_by_one_third: prometheus::Counter, wakeups_triggered_total: prometheus::Counter, coalesced_approvals_buckets: prometheus::Histogram, candidate_approval_time_ticks: prometheus::Histogram, @@ -236,6 +238,12 @@ impl Metrics { } } + fn on_approved_by_one_third(&self) { + if let Some(metrics) = &self.0 { + metrics.approved_by_one_third.inc(); + } + } + fn on_wakeup(&self) { if let Some(metrics) = &self.0 { metrics.wakeups_triggered_total.inc(); @@ -332,6 +340,13 @@ impl metrics::Metrics for Metrics { )?, registry, )?, + approved_by_one_third: prometheus::register( + prometheus::Counter::new( + "polkadot_parachain_approved_by_one_third", + "Number of candidates where more than one third had to vote ", + )?, + registry, + )?, block_approval_time_ticks: prometheus::register( prometheus::Histogram::with_opts( prometheus::HistogramOpts::new( @@ -2524,6 +2539,12 @@ where if no_shows != 0 { metrics.on_no_shows(no_shows); } + if check == Check::ApprovedOneThird { + // No-shows are not counted when more than one third of validators, + // so count candidates where more than one third of validators had + // to approve it, this is indicative of something breaking. + metrics.on_approved_by_one_third() + } metrics.on_candidate_approved(status.tranche_now as _); From be65f42aa228be0320eca513b150fdc2a7a8835b Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe Date: Tue, 15 Aug 2023 14:37:04 +0300 Subject: [PATCH 23/54] Add a few more metrics to understand versi behaviour Signed-off-by: Alexandru Gheorghe --- node/network/approval-distribution/src/lib.rs | 44 +++-- .../approval-distribution/src/metrics.rs | 160 ++++++++++++++++++ 2 files changed, 194 insertions(+), 10 deletions(-) diff --git a/node/network/approval-distribution/src/lib.rs b/node/network/approval-distribution/src/lib.rs index 81328021ce80..1ecd15bc1396 100644 --- a/node/network/approval-distribution/src/lib.rs +++ b/node/network/approval-distribution/src/lib.rs @@ -1096,9 +1096,11 @@ impl State { COST_UNEXPECTED_MESSAGE, ) .await; - gum::error!(target: LOG_TARGET, "Received assignment for invalid block"); + gum::debug!(target: LOG_TARGET, "Received assignment for invalid block"); + metrics.on_assignment_recent_outdated(); } } + metrics.on_assignment_invalid_block(); return }, }; @@ -1131,6 +1133,7 @@ impl State { COST_DUPLICATE_MESSAGE, ) .await; + metrics.on_assignment_duplicate(); } else { gum::trace!( target: LOG_TARGET, @@ -1158,6 +1161,7 @@ impl State { COST_UNEXPECTED_MESSAGE, ) .await; + metrics.on_assignment_out_of_view(); }, } @@ -1174,6 +1178,7 @@ impl State { gum::trace!(target: LOG_TARGET, ?peer_id, ?message_subject, "Known assignment"); peer_knowledge.received.insert(message_subject, message_kind); } + metrics.on_assignment_good_known(); return } @@ -1230,6 +1235,8 @@ impl State { ?peer_id, "Got an `AcceptedDuplicate` assignment", ); + metrics.on_assignment_duplicatevoting(); + return }, AssignmentCheckResult::TooFarInFuture => { @@ -1246,6 +1253,8 @@ impl State { COST_ASSIGNMENT_TOO_FAR_IN_THE_FUTURE, ) .await; + metrics.on_assignment_far(); + return }, AssignmentCheckResult::Bad(error) => { @@ -1263,6 +1272,7 @@ impl State { COST_INVALID_MESSAGE, ) .await; + metrics.on_assignment_bad(); return }, } @@ -1378,16 +1388,18 @@ impl State { entry: &mut BlockEntry, reputation: &mut ReputationAggregator, peer_id: PeerId, + metrics: &Metrics, ) -> bool { for message_subject in message_subjects { if !entry.knowledge.contains(&message_subject, MessageKind::Assignment) { - gum::debug!( + gum::trace!( target: LOG_TARGET, ?peer_id, ?message_subject, "Unknown approval assignment", ); modify_reputation(reputation, ctx.sender(), peer_id, COST_UNEXPECTED_MESSAGE).await; + metrics.on_approval_unknown_assignment(); return false } @@ -1397,7 +1409,7 @@ impl State { let peer_knowledge = knowledge.get_mut(); if peer_knowledge.contains(&message_subject, message_kind) { if !peer_knowledge.received.insert(message_subject.clone(), message_kind) { - gum::debug!( + gum::trace!( target: LOG_TARGET, ?peer_id, ?message_subject, @@ -1411,6 +1423,7 @@ impl State { COST_DUPLICATE_MESSAGE, ) .await; + metrics.on_approval_duplicate(); } return false } @@ -1420,10 +1433,11 @@ impl State { target: LOG_TARGET, ?peer_id, ?message_subject, - "Unknown approval assignment", + "Approval from a peer is out of view", ); modify_reputation(reputation, ctx.sender(), peer_id, COST_UNEXPECTED_MESSAGE) .await; + metrics.on_approval_out_of_view(); return true }, } @@ -1436,7 +1450,7 @@ impl State { if let Some(peer_knowledge) = entry.known_by.get_mut(&peer_id) { peer_knowledge.received.insert(message_subject.clone(), message_kind); } - // We already processed this approval no need + // We already processed this approval no need to continue. true } else { accumulator @@ -1444,6 +1458,7 @@ impl State { }); if good_known_approval { gum::trace!(target: LOG_TARGET, ?peer_id, ?message_subjects, "Known approval"); + metrics.on_approval_good_known(); modify_reputation(reputation, ctx.sender(), peer_id, BENEFIT_VALID_MESSAGE).await; } @@ -1481,10 +1496,11 @@ impl State { let approval_entry_exists = entry.contains_approval_entry(candidate_index as _, validator_index); if !approval_entry_exists { - gum::error!( - target: LOG_TARGET, ?block_hash, ?candidate_index, validator_index = ?vote.validator, - peer_id = ?source.peer_id(), "Received approval before assignment" + gum::debug!( + target: LOG_TARGET, ?block_hash, ?candidate_index, validator_index = ?vote.validator, candidate_indices = ?vote.candidate_indices, + peer_id = ?source.peer_id(), "Received approval before assignment" ); + metrics.on_approval_entry_not_found(); } approval_entry_exists && result }) => @@ -1499,7 +1515,10 @@ impl State { COST_UNEXPECTED_MESSAGE, ) .await; - gum::error!(target: LOG_TARGET, "Received approval for invalid block"); + gum::debug!(target: LOG_TARGET, "Received approval for invalid block"); + metrics.on_approval_invalid_block(); + } else { + metrics.on_approval_recent_outdated(); } } return @@ -1527,6 +1546,7 @@ impl State { entry, &mut self.reputation, peer_id, + metrics, ) .await { @@ -1585,6 +1605,7 @@ impl State { %error, "Got a bad approval from peer", ); + metrics.on_approval_bad(); return }, } @@ -1622,6 +1643,7 @@ impl State { "Unknown approval assignment", ); // No rep change as this is caused by an issue + metrics.on_approval_unexpected(); return } @@ -1640,7 +1662,7 @@ impl State { ?err, "Possible bug: Vote import failed", ); - + metrics.on_approval_bug(); return } required_routing = approval_entry.routing_info().required_routing; @@ -1726,6 +1748,7 @@ impl State { )), )) .await; + metrics.on_approval_sent_v1(); } if !v2_peers.is_empty() { @@ -1738,6 +1761,7 @@ impl State { ), )) .await; + metrics.on_approval_sent_v2(); } } } diff --git a/node/network/approval-distribution/src/metrics.rs b/node/network/approval-distribution/src/metrics.rs index 6864259e6fdb..094874820f26 100644 --- a/node/network/approval-distribution/src/metrics.rs +++ b/node/network/approval-distribution/src/metrics.rs @@ -31,6 +31,8 @@ struct MetricsInner { time_unify_with_peer: prometheus::Histogram, time_import_pending_now_known: prometheus::Histogram, time_awaiting_approval_voting: prometheus::Histogram, + assignments_received_result: prometheus::CounterVec, + approvals_received_result: prometheus::CounterVec, } trait AsLabel { @@ -78,6 +80,144 @@ impl Metrics { .map(|metrics| metrics.time_import_pending_now_known.start_timer()) } + pub fn on_approval_already_known(&self) { + if let Some(metrics) = &self.0 { + metrics.approvals_received_result.with_label_values(&["known"]).inc() + } + } + + pub fn on_approval_entry_not_found(&self) { + if let Some(metrics) = &self.0 { + metrics.approvals_received_result.with_label_values(&["noapprovalentry"]).inc() + } + } + + pub fn on_approval_recent_outdated(&self) { + if let Some(metrics) = &self.0 { + metrics.approvals_received_result.with_label_values(&["outdated"]).inc() + } + } + + pub fn on_approval_invalid_block(&self) { + if let Some(metrics) = &self.0 { + metrics.approvals_received_result.with_label_values(&["invalidblock"]).inc() + } + } + + pub fn on_approval_unknown_assignment(&self) { + if let Some(metrics) = &self.0 { + metrics + .approvals_received_result + .with_label_values(&["unknownassignment"]) + .inc() + } + } + + pub fn on_approval_duplicate(&self) { + if let Some(metrics) = &self.0 { + metrics.approvals_received_result.with_label_values(&["duplicate"]).inc() + } + } + + pub fn on_approval_out_of_view(&self) { + if let Some(metrics) = &self.0 { + metrics.approvals_received_result.with_label_values(&["outofview"]).inc() + } + } + + pub fn on_approval_good_known(&self) { + if let Some(metrics) = &self.0 { + metrics.approvals_received_result.with_label_values(&["goodknown"]).inc() + } + } + + pub fn on_approval_bad(&self) { + if let Some(metrics) = &self.0 { + metrics.approvals_received_result.with_label_values(&["bad"]).inc() + } + } + + pub fn on_approval_unexpected(&self) { + if let Some(metrics) = &self.0 { + metrics.approvals_received_result.with_label_values(&["unexpected"]).inc() + } + } + + pub fn on_approval_bug(&self) { + if let Some(metrics) = &self.0 { + metrics.approvals_received_result.with_label_values(&["bug"]).inc() + } + } + + pub fn on_approval_sent_v1(&self) { + if let Some(metrics) = &self.0 { + metrics.approvals_received_result.with_label_values(&["v1"]).inc() + } + } + + pub fn on_approval_sent_v2(&self) { + if let Some(metrics) = &self.0 { + metrics.approvals_received_result.with_label_values(&["v2"]).inc() + } + } + + pub fn on_assignment_already_known(&self) { + if let Some(metrics) = &self.0 { + metrics.assignments_received_result.with_label_values(&["known"]).inc() + } + } + + pub fn on_assignment_recent_outdated(&self) { + if let Some(metrics) = &self.0 { + metrics.assignments_received_result.with_label_values(&["outdated"]).inc() + } + } + + pub fn on_assignment_invalid_block(&self) { + if let Some(metrics) = &self.0 { + metrics.assignments_received_result.with_label_values(&["invalidblock"]).inc() + } + } + + pub fn on_assignment_duplicate(&self) { + if let Some(metrics) = &self.0 { + metrics.assignments_received_result.with_label_values(&["duplicate"]).inc() + } + } + + pub fn on_assignment_out_of_view(&self) { + if let Some(metrics) = &self.0 { + metrics.assignments_received_result.with_label_values(&["outofview"]).inc() + } + } + + pub fn on_assignment_good_known(&self) { + if let Some(metrics) = &self.0 { + metrics.assignments_received_result.with_label_values(&["goodknown"]).inc() + } + } + + pub fn on_assignment_bad(&self) { + if let Some(metrics) = &self.0 { + metrics.assignments_received_result.with_label_values(&["bad"]).inc() + } + } + + pub fn on_assignment_duplicatevoting(&self) { + if let Some(metrics) = &self.0 { + metrics + .assignments_received_result + .with_label_values(&["duplicatevoting"]) + .inc() + } + } + + pub fn on_assignment_far(&self) { + if let Some(metrics) = &self.0 { + metrics.assignments_received_result.with_label_values(&["far"]).inc() + } + } + pub(crate) fn time_awaiting_approval_voting( &self, ) -> Option { @@ -167,6 +307,26 @@ impl MetricsTrait for Metrics { ).buckets(vec![0.0001, 0.0004, 0.0016, 0.0064, 0.0256, 0.1024, 0.4096, 1.6384, 3.2768, 4.9152, 6.5536,]))?, registry, )?, + assignments_received_result: prometheus::register( + prometheus::CounterVec::new( + prometheus::Opts::new( + "polkadot_parachain_assignments_received_result", + "Result of a processed assignement", + ), + &["status"] + )?, + registry, + )?, + approvals_received_result: prometheus::register( + prometheus::CounterVec::new( + prometheus::Opts::new( + "polkadot_parachain_approvals_received_result", + "Result of a processed approval", + ), + &["status"] + )?, + registry, + )?, }; Ok(Metrics(Some(metrics))) } From 3ef84bf4df18c55c397a155bf7ba4e4b710bc98f Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe Date: Thu, 17 Aug 2023 15:49:40 +0300 Subject: [PATCH 24/54] Don't send assignments to peers that are not part of the authorities session ... there is not point in sending the assignments to peers that are not authorities in the session(collators or validators that are not authorities yet), because they won't be gossiped, so we are just wasting them. Signed-off-by: Alexandru Gheorghe --- node/network/approval-distribution/src/lib.rs | 10 +++++++++ node/network/protocol/src/grid_topology.rs | 22 ++++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/node/network/approval-distribution/src/lib.rs b/node/network/approval-distribution/src/lib.rs index 1ecd15bc1396..cb759021b504 100644 --- a/node/network/approval-distribution/src/lib.rs +++ b/node/network/approval-distribution/src/lib.rs @@ -1340,6 +1340,9 @@ impl State { continue } + if !topology.map(|topology| topology.is_validator(&peer)).unwrap_or(false) { + continue + } // Note: at this point, we haven't received the message from any peers // other than the source peer, and we just got it, so we haven't sent it // to any peers either. @@ -1870,6 +1873,13 @@ impl State { t.local_grid_neighbors().route_to_peer(required_routing, peer_id) }); in_topology || { + if !topology + .map(|topology| topology.is_validator(peer_id)) + .unwrap_or(false) + { + return false + } + let route_random = random_routing.sample(total_peers, rng); if route_random { random_routing.inc_sent(); diff --git a/node/network/protocol/src/grid_topology.rs b/node/network/protocol/src/grid_topology.rs index 1b356f67617b..d4983990fedc 100644 --- a/node/network/protocol/src/grid_topology.rs +++ b/node/network/protocol/src/grid_topology.rs @@ -32,6 +32,7 @@ use crate::PeerId; use polkadot_primitives::{AuthorityDiscoveryId, SessionIndex, ValidatorIndex}; use rand::{CryptoRng, Rng}; +use sc_network::network_state::Peer; use std::{ collections::{hash_map, HashMap, HashSet}, fmt::Debug, @@ -70,12 +71,20 @@ pub struct SessionGridTopology { shuffled_indices: Vec, /// The canonical shuffling of validators for the session. canonical_shuffling: Vec, + /// The list of peer-ids in an efficient way to search. + peer_ids: HashSet, } impl SessionGridTopology { /// Create a new session grid topology. pub fn new(shuffled_indices: Vec, canonical_shuffling: Vec) -> Self { - SessionGridTopology { shuffled_indices, canonical_shuffling } + let mut peer_ids = HashSet::new(); + for peer_info in canonical_shuffling.iter() { + for peer_id in peer_info.peer_ids.iter() { + peer_ids.insert(*peer_id); + } + } + SessionGridTopology { shuffled_indices, canonical_shuffling, peer_ids } } /// Produces the outgoing routing logic for a particular peer. @@ -108,6 +117,11 @@ impl SessionGridTopology { Some(grid_subset) } + + /// Tells if a given peer id is validator in a session + pub fn is_validator(&self, peer: &PeerId) -> bool { + self.peer_ids.contains(peer) + } } struct MatrixNeighbors { @@ -268,6 +282,11 @@ impl SessionGridTopologyEntry { pub fn get(&self) -> &SessionGridTopology { &self.topology } + + /// Tells if a given peer id is validator in a session + pub fn is_validator(&self, peer: &PeerId) -> bool { + self.topology.is_validator(peer) + } } /// A set of topologies indexed by session @@ -342,6 +361,7 @@ impl Default for SessionBoundGridTopologyStorage { topology: SessionGridTopology { shuffled_indices: Vec::new(), canonical_shuffling: Vec::new(), + peer_ids: Default::default(), }, local_neighbors: GridNeighbors::empty(), }, From 1dbcb90291cda44d57958ce207530ee0879b3c63 Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe Date: Thu, 17 Aug 2023 16:36:10 +0300 Subject: [PATCH 25/54] Count all observe no-shows No-shows are currently logged in the metric only at the moment of the approval candidate, but that doesn't account for votes that arrive late after the no-show period which is an early indicator that something might be off. Signed-off-by: Alexandru Gheorghe --- .../approval-voting/src/approval_checking.rs | 65 ++++++++++++------- node/core/approval-voting/src/lib.rs | 37 +++++++++-- 2 files changed, 73 insertions(+), 29 deletions(-) diff --git a/node/core/approval-voting/src/approval_checking.rs b/node/core/approval-voting/src/approval_checking.rs index 3e8fe651eae6..518429f59025 100644 --- a/node/core/approval-voting/src/approval_checking.rs +++ b/node/core/approval-voting/src/approval_checking.rs @@ -372,19 +372,22 @@ pub fn tranches_to_approve( block_tick: Tick, no_show_duration: Tick, needed_approvals: usize, -) -> RequiredTranches { +) -> (RequiredTranches, usize) { let tick_now = tranche_now as Tick + block_tick; let n_validators = approval_entry.n_validators(); - let initial_state = State { - assignments: 0, - depth: 0, - covered: 0, - covering: needed_approvals, - uncovered: 0, - next_no_show: None, - last_assignment_tick: None, - }; + let initial_state = ( + State { + assignments: 0, + depth: 0, + covered: 0, + covering: needed_approvals, + uncovered: 0, + next_no_show: None, + last_assignment_tick: None, + }, + 0usize, + ); // The `ApprovalEntry` doesn't have any data for empty tranches. We still want to iterate over // these empty tranches, so we create an iterator to fill the gaps. @@ -395,7 +398,7 @@ pub fn tranches_to_approve( tranches_with_gaps_filled .scan(Some(initial_state), |state, (tranche, assignments)| { // The `Option` here is used for early exit. - let s = state.take()?; + let (s, prev_no_shows) = state.take()?; let clock_drift = s.clock_drift(no_show_duration); let drifted_tick_now = tick_now.saturating_sub(clock_drift); @@ -444,11 +447,11 @@ pub fn tranches_to_approve( RequiredTranches::Pending { .. } => { // Pending results are only interesting when they are the last result of the iterator // i.e. we never achieve a satisfactory level of assignment. - Some(s) + Some((s, no_shows + prev_no_shows)) } }; - Some(output) + Some((output, no_shows + prev_no_shows)) }) .last() .expect("the underlying iterator is infinite, starts at 0, and never exits early before tranche 1; qed") @@ -675,7 +678,8 @@ mod tests { block_tick, no_show_duration, needed_approvals, - ), + ) + .0, RequiredTranches::Exact { needed: 1, tolerated_missing: 0, @@ -715,7 +719,8 @@ mod tests { block_tick, no_show_duration, needed_approvals, - ), + ) + .0, RequiredTranches::Pending { considered: 2, next_no_show: Some(block_tick + no_show_duration), @@ -759,7 +764,8 @@ mod tests { block_tick, no_show_duration, needed_approvals, - ), + ) + .0, RequiredTranches::Pending { considered: 11, next_no_show: None, @@ -807,7 +813,8 @@ mod tests { block_tick, no_show_duration, needed_approvals, - ), + ) + .0, RequiredTranches::Pending { considered: 1, next_no_show: None, @@ -826,7 +833,8 @@ mod tests { block_tick, no_show_duration, needed_approvals, - ), + ) + .0, RequiredTranches::Pending { considered: 1, next_no_show: None, @@ -879,7 +887,8 @@ mod tests { block_tick, no_show_duration, needed_approvals, - ), + ) + .0, RequiredTranches::Exact { needed: 1, tolerated_missing: 0, @@ -898,7 +907,8 @@ mod tests { block_tick, no_show_duration, needed_approvals, - ), + ) + .0, RequiredTranches::Exact { needed: 2, tolerated_missing: 1, @@ -917,7 +927,8 @@ mod tests { block_tick, no_show_duration, needed_approvals, - ), + ) + .0, RequiredTranches::Pending { considered: 2, next_no_show: None, @@ -970,7 +981,8 @@ mod tests { block_tick, no_show_duration, needed_approvals, - ), + ) + .0, RequiredTranches::Exact { needed: 2, tolerated_missing: 1, @@ -992,7 +1004,8 @@ mod tests { block_tick, no_show_duration, needed_approvals, - ), + ) + .0, RequiredTranches::Pending { considered: 2, next_no_show: None, @@ -1013,7 +1026,8 @@ mod tests { block_tick, no_show_duration, needed_approvals, - ), + ) + .0, RequiredTranches::Exact { needed: 3, tolerated_missing: 2, @@ -1068,7 +1082,8 @@ mod tests { block_tick, no_show_duration, needed_approvals, - ), + ) + .0, RequiredTranches::Pending { considered: 10, next_no_show: None, diff --git a/node/core/approval-voting/src/lib.rs b/node/core/approval-voting/src/lib.rs index fbe1c6c783e1..47c8128508fb 100644 --- a/node/core/approval-voting/src/lib.rs +++ b/node/core/approval-voting/src/lib.rs @@ -166,6 +166,10 @@ struct MetricsInner { assignments_produced: prometheus::Histogram, approvals_produced_total: prometheus::CounterVec, no_shows_total: prometheus::Counter, + // The difference is that this counts all observed no-shows. + // approvals might arrive late and `no_shows_total` wouldn't catch + // that number. + observed_no_shows: prometheus::Counter, approved_by_one_third: prometheus::Counter, wakeups_triggered_total: prometheus::Counter, coalesced_approvals_buckets: prometheus::Histogram, @@ -238,6 +242,12 @@ impl Metrics { } } + fn on_observed_no_shows(&self, n: usize) { + if let Some(metrics) = &self.0 { + metrics.observed_no_shows.inc_by(n as u64); + } + } + fn on_approved_by_one_third(&self) { if let Some(metrics) = &self.0 { metrics.approved_by_one_third.inc(); @@ -315,6 +325,13 @@ impl metrics::Metrics for Metrics { )?, registry, )?, + observed_no_shows: prometheus::register( + prometheus::Counter::new( + "polkadot_parachain_approvals_observed_no_shows_total", + "Number of observed no shows", + )?, + registry, + )?, wakeups_triggered_total: prometheus::register( prometheus::Counter::new( "polkadot_parachain_approvals_wakeups_total", @@ -586,6 +603,7 @@ struct ApprovalStatus { required_tranches: RequiredTranches, tranche_now: DelayTranche, block_tick: Tick, + last_no_shows: usize, } #[derive(Copy, Clone)] @@ -745,7 +763,7 @@ impl State { ); if let Some(approval_entry) = candidate_entry.approval_entry(&block_hash) { - let required_tranches = approval_checking::tranches_to_approve( + let (required_tranches, last_no_shows) = approval_checking::tranches_to_approve( approval_entry, candidate_entry.approvals(), tranche_now, @@ -754,7 +772,8 @@ impl State { session_info.needed_approvals as _, ); - let status = ApprovalStatus { required_tranches, block_tick, tranche_now }; + let status = + ApprovalStatus { required_tranches, block_tick, tranche_now, last_no_shows }; Some((approval_entry, status)) } else { @@ -2521,7 +2540,17 @@ where // assignment tick of `now - APPROVAL_DELAY` - that is, that // all counted assignments are at least `APPROVAL_DELAY` ticks old. let is_approved = check.is_approved(tick_now.saturating_sub(APPROVAL_DELAY)); - + if status.last_no_shows != 0 { + metrics.on_observed_no_shows(status.last_no_shows); + gum::debug!( + target: LOG_TARGET, + ?candidate_hash, + ?block_hash, + last_no_shows = ?status.last_no_shows, + no_shows_zzz = ?check.known_no_shows(), + "Observed no_shows.", + ); + } if is_approved { gum::trace!( target: LOG_TARGET, @@ -2707,7 +2736,7 @@ async fn process_wakeup( None => return Ok(Vec::new()), }; - let tranches_to_approve = approval_checking::tranches_to_approve( + let (tranches_to_approve, _last_no_shows) = approval_checking::tranches_to_approve( &approval_entry, candidate_entry.approvals(), tranche_now, From 3ac044e862a3de5fa7f70a9909eaddf75ae1f6ab Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe Date: Thu, 17 Aug 2023 16:52:11 +0300 Subject: [PATCH 26/54] Fix warning Signed-off-by: Alexandru Gheorghe --- node/network/protocol/src/grid_topology.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/node/network/protocol/src/grid_topology.rs b/node/network/protocol/src/grid_topology.rs index d4983990fedc..fbcb73db53e2 100644 --- a/node/network/protocol/src/grid_topology.rs +++ b/node/network/protocol/src/grid_topology.rs @@ -32,7 +32,6 @@ use crate::PeerId; use polkadot_primitives::{AuthorityDiscoveryId, SessionIndex, ValidatorIndex}; use rand::{CryptoRng, Rng}; -use sc_network::network_state::Peer; use std::{ collections::{hash_map, HashMap, HashSet}, fmt::Debug, From 2cb67566539a9a67049f618a0db28b89f760c489 Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe Date: Thu, 17 Aug 2023 17:17:19 +0300 Subject: [PATCH 27/54] Minor fixes for review Signed-off-by: Alexandru Gheorghe --- node/core/approval-voting/src/lib.rs | 17 ++++++++--------- node/core/dispute-coordinator/src/import.rs | 4 ---- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/node/core/approval-voting/src/lib.rs b/node/core/approval-voting/src/lib.rs index 47c8128508fb..ece1033524a2 100644 --- a/node/core/approval-voting/src/lib.rs +++ b/node/core/approval-voting/src/lib.rs @@ -2547,8 +2547,7 @@ where ?candidate_hash, ?block_hash, last_no_shows = ?status.last_no_shows, - no_shows_zzz = ?check.known_no_shows(), - "Observed no_shows.", + "Observed no_shows", ); } if is_approved { @@ -3177,7 +3176,7 @@ async fn issue_approval( ?candidate_hash, ?block_hash, validator_index = validator_index.0, - "Issuing approval vote", + "Ready to issue approval vote", ); let actions = advance_approval_state( @@ -3330,7 +3329,7 @@ async fn maybe_create_signature( .values() .map(|unsigned_approval| db.load_candidate_entry(&unsigned_approval.candidate_hash)) .collect::>>>()?; - let candidate_indexes: Vec = + let candidate_indices: Vec = block_entry.candidates_pending_signature.keys().map(|val| *val).collect(); for candidate_entry in candidate_entries { let mut candidate_entry = candidate_entry @@ -3341,7 +3340,7 @@ async fn maybe_create_signature( approval_entry.import_approval_sig(OurApproval { signature: signature.clone(), signed_candidates_indices: Some( - candidate_indexes + candidate_indices .clone() .try_into() .expect("Fails only of array empty, it can't be, qed"), @@ -3350,14 +3349,14 @@ async fn maybe_create_signature( db.write_candidate_entry(candidate_entry); } - metrics.on_approval_coalesce(candidate_indexes.len() as u32); + metrics.on_approval_coalesce(candidate_indices.len() as u32); metrics.on_approval_produced(); ctx.send_unbounded_message(ApprovalDistributionMessage::DistributeApproval( IndirectSignedApprovalVoteV2 { block_hash: block_entry.block_hash(), - candidate_indices: candidate_indexes + candidate_indices: candidate_indices .try_into() .expect("Fails only of array empty, it can't be, qed"), validator: validator_index, @@ -3368,8 +3367,8 @@ async fn maybe_create_signature( gum::debug!( target: LOG_TARGET, ?block_hash, - "Approval entry for num_candidates was sent {:}", - block_entry.candidates_pending_signature.len() + signed_candidates = ?block_entry.candidates_pending_signature.len(), + "Issue approval votes", ); block_entry.candidates_pending_signature.clear(); db.write_block_entry(block_entry.into()); diff --git a/node/core/dispute-coordinator/src/import.rs b/node/core/dispute-coordinator/src/import.rs index c5ece106e42c..305ad52b43c1 100644 --- a/node/core/dispute-coordinator/src/import.rs +++ b/node/core/dispute-coordinator/src/import.rs @@ -520,10 +520,6 @@ impl ImportResult { let (mut votes, _) = new_state.into_old_state(); for (index, (candidate_hashes, sig)) in approval_votes.into_iter() { - gum::debug!( - target: LOG_TARGET, - "Imported votes for {:?}", candidate_hashes - ); debug_assert!( { let pub_key = &env.session_info().validators.get(index).expect("indices are validated by approval-voting subsystem; qed"); From 042954948c575648b146533169a16d0564c7e44f Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe Date: Fri, 18 Aug 2023 11:36:23 +0300 Subject: [PATCH 28/54] Fix approval-distribution tests ... to take into consideration that we are not gossiping random assignments to non-validators nodes. Signed-off-by: Alexandru Gheorghe --- .../approval-distribution/src/tests.rs | 132 +++++++++--------- 1 file changed, 69 insertions(+), 63 deletions(-) diff --git a/node/network/approval-distribution/src/tests.rs b/node/network/approval-distribution/src/tests.rs index bc2709d61db1..408a14cba86f 100644 --- a/node/network/approval-distribution/src/tests.rs +++ b/node/network/approval-distribution/src/tests.rs @@ -133,14 +133,13 @@ fn make_gossip_topology( all_peers: &[(PeerId, AuthorityDiscoveryId)], neighbors_x: &[usize], neighbors_y: &[usize], + local_index: usize, ) -> network_bridge_event::NewGossipTopology { // This builds a grid topology which is a square matrix. // The local validator occupies the top left-hand corner. // The X peers occupy the same row and the Y peers occupy // the same column. - let local_index = 1; - assert_eq!( neighbors_x.len(), neighbors_y.len(), @@ -366,10 +365,11 @@ fn state_with_reputation_delay() -> State { /// the new peer sends us the same assignment #[test] fn try_import_the_same_assignment() { - let peer_a = PeerId::random(); - let peer_b = PeerId::random(); - let peer_c = PeerId::random(); - let peer_d = PeerId::random(); + let peers = make_peers_and_authority_ids(15); + let peer_a = peers.get(0).unwrap().0; + let peer_b = peers.get(1).unwrap().0; + let peer_c = peers.get(2).unwrap().0; + let peer_d = peers.get(4).unwrap().0; let parent_hash = Hash::repeat_byte(0xFF); let hash = Hash::repeat_byte(0xAA); @@ -380,6 +380,9 @@ fn try_import_the_same_assignment() { setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V1).await; setup_peer_with_view(overseer, &peer_c, view![hash], ValidationVersion::V1).await; + // Set up a gossip topology, where a, b, c and d are topology neighboors to the node under testing. + setup_gossip_topology(overseer, make_gossip_topology(1, &peers, &[0, 1], &[2, 4], 3)).await; + // new block `hash_a` with 1 candidates let meta = BlockApprovalMeta { hash, @@ -450,10 +453,11 @@ fn try_import_the_same_assignment() { /// cores. #[test] fn try_import_the_same_assignment_v2() { - let peer_a = PeerId::random(); - let peer_b = PeerId::random(); - let peer_c = PeerId::random(); - let peer_d = PeerId::random(); + let peers = make_peers_and_authority_ids(15); + let peer_a = peers.get(0).unwrap().0; + let peer_b = peers.get(1).unwrap().0; + let peer_c = peers.get(2).unwrap().0; + let peer_d = peers.get(4).unwrap().0; let parent_hash = Hash::repeat_byte(0xFF); let hash = Hash::repeat_byte(0xAA); @@ -464,6 +468,9 @@ fn try_import_the_same_assignment_v2() { setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::VStaging).await; setup_peer_with_view(overseer, &peer_c, view![hash], ValidationVersion::VStaging).await; + // Set up a gossip topology, where a, b, c and d are topology neighboors to the node under testing. + setup_gossip_topology(overseer, make_gossip_topology(1, &peers, &[0, 1], &[2, 4], 3)).await; + // new block `hash_a` with 1 candidates let meta = BlockApprovalMeta { hash, @@ -691,14 +698,19 @@ fn spam_attack_results_in_negative_reputation_change() { #[test] fn peer_sending_us_the_same_we_just_sent_them_is_ok() { let parent_hash = Hash::repeat_byte(0xFF); - let peer_a = PeerId::random(); let hash = Hash::repeat_byte(0xAA); + let peers = make_peers_and_authority_ids(8); + let peer_a = peers.first().unwrap().0; + let _ = test_harness(state_without_reputation_delay(), |mut virtual_overseer| async move { let overseer = &mut virtual_overseer; let peer = &peer_a; setup_peer_with_view(overseer, peer, view![], ValidationVersion::V1).await; + // Setup a topology where peer_a is neigboor to current node. + setup_gossip_topology(overseer, make_gossip_topology(1, &peers, &[0], &[2], 1)).await; + // new block `hash` with 1 candidates let meta = BlockApprovalMeta { hash, @@ -767,9 +779,11 @@ fn peer_sending_us_the_same_we_just_sent_them_is_ok() { #[test] fn import_approval_happy_path() { - let peer_a = PeerId::random(); - let peer_b = PeerId::random(); - let peer_c = PeerId::random(); + let peers = make_peers_and_authority_ids(15); + + let peer_a = peers.get(0).unwrap().0; + let peer_b = peers.get(1).unwrap().0; + let peer_c = peers.get(2).unwrap().0; let parent_hash = Hash::repeat_byte(0xFF); let hash = Hash::repeat_byte(0xAA); @@ -792,6 +806,9 @@ fn import_approval_happy_path() { let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); overseer_send(overseer, msg).await; + // Set up a gossip topology, where a, b, and c are topology neighboors to the node. + setup_gossip_topology(overseer, make_gossip_topology(1, &peers, &[0, 1], &[2, 4], 3)).await; + // import an assignment related to `hash` locally let validator_index = ValidatorIndex(0); let candidate_index = 0u32; @@ -1034,7 +1051,8 @@ fn update_peer_view() { let hash_b = Hash::repeat_byte(0xBB); let hash_c = Hash::repeat_byte(0xCC); let hash_d = Hash::repeat_byte(0xDD); - let peer_a = PeerId::random(); + let peers = make_peers_and_authority_ids(8); + let peer_a = peers.first().unwrap().0; let peer = &peer_a; let state = test_harness(State::default(), |mut virtual_overseer| async move { @@ -1068,6 +1086,9 @@ fn update_peer_view() { let msg = ApprovalDistributionMessage::NewBlocks(vec![meta_a, meta_b, meta_c]); overseer_send(overseer, msg).await; + // Setup a topology where peer_a is neigboor to current node. + setup_gossip_topology(overseer, make_gossip_topology(1, &peers, &[0], &[2], 1)).await; + let cert_a = fake_assignment_cert(hash_a, ValidatorIndex(0)); let cert_b = fake_assignment_cert(hash_b, ValidatorIndex(0)); @@ -1281,7 +1302,8 @@ fn import_remotely_then_locally() { #[test] fn sends_assignments_even_when_state_is_approved() { - let peer_a = PeerId::random(); + let peers = make_peers_and_authority_ids(8); + let peer_a = peers.first().unwrap().0; let parent_hash = Hash::repeat_byte(0xFF); let hash = Hash::repeat_byte(0xAA); let peer = &peer_a; @@ -1301,6 +1323,9 @@ fn sends_assignments_even_when_state_is_approved() { let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); overseer_send(overseer, msg).await; + // Setup a topology where peer_a is neigboor to current node. + setup_gossip_topology(overseer, make_gossip_topology(1, &peers, &[0], &[2], 1)).await; + let validator_index = ValidatorIndex(0); let candidate_index = 0u32; @@ -1368,7 +1393,8 @@ fn sends_assignments_even_when_state_is_approved() { /// Same as `sends_assignments_even_when_state_is_approved_v2` but with `VRFModuloCompact` assignemnts. #[test] fn sends_assignments_even_when_state_is_approved_v2() { - let peer_a = PeerId::random(); + let peers = make_peers_and_authority_ids(8); + let peer_a = peers.first().unwrap().0; let parent_hash = Hash::repeat_byte(0xFF); let hash = Hash::repeat_byte(0xAA); let peer = &peer_a; @@ -1388,6 +1414,9 @@ fn sends_assignments_even_when_state_is_approved_v2() { let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); overseer_send(overseer, msg).await; + // Setup a topology where peer_a is neigboor to current node. + setup_gossip_topology(overseer, make_gossip_topology(1, &peers, &[0], &[2], 1)).await; + let validator_index = ValidatorIndex(0); let cores = vec![0, 1, 2, 3]; let candidate_bitfield: CandidateBitfield = cores.clone().try_into().unwrap(); @@ -1568,13 +1597,19 @@ fn propagates_locally_generated_assignment_to_both_dimensions() { // Set up a gossip topology. setup_gossip_topology( overseer, - make_gossip_topology(1, &peers, &[0, 10, 20, 30], &[50, 51, 52, 53]), + make_gossip_topology( + 1, + &peers, + &[0, 10, 20, 30, 40, 60, 70, 80], + &[50, 51, 52, 53, 54, 55, 56, 57], + 1, + ), ) .await; let expected_indices = [ // Both dimensions in the gossip topology - 0, 10, 20, 30, 50, 51, 52, 53, + 0, 10, 20, 30, 40, 60, 70, 80, 50, 51, 52, 53, 54, 55, 56, 57, ]; // new block `hash_a` with 1 candidates @@ -1679,7 +1714,7 @@ fn propagates_assignments_along_unshared_dimension() { // Set up a gossip topology. setup_gossip_topology( overseer, - make_gossip_topology(1, &peers, &[0, 10, 20, 30], &[50, 51, 52, 53]), + make_gossip_topology(1, &peers, &[0, 10, 20, 30], &[50, 51, 52, 53], 1), ) .await; @@ -1822,13 +1857,19 @@ fn propagates_to_required_after_connect() { // Set up a gossip topology. setup_gossip_topology( overseer, - make_gossip_topology(1, &peers, &[0, 10, 20, 30], &[50, 51, 52, 53]), + make_gossip_topology( + 1, + &peers, + &[0, 10, 20, 30, 40, 60, 70, 80], + &[50, 51, 52, 53, 54, 55, 56, 57], + 1, + ), ) .await; let expected_indices = [ // Both dimensions in the gossip topology, minus omitted. - 20, 30, 52, 53, + 20, 30, 40, 60, 70, 80, 52, 53, 54, 55, 56, 57, ]; // new block `hash_a` with 1 candidates @@ -2005,47 +2046,12 @@ fn sends_to_more_peers_after_getting_topology() { let assignments = vec![(cert.clone(), candidate_index)]; let approvals = vec![approval.clone()]; - let mut expected_indices = vec![0, 10, 20, 30, 50, 51, 52, 53]; - let assignment_sent_peers = assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - sent_peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) - )) - )) => { - // Only sends to random peers. - assert_eq!(sent_peers.len(), 4); - for peer in &sent_peers { - let i = peers.iter().position(|p| peer == &p.0).unwrap(); - // Random gossip before topology can send to topology-targeted peers. - // Remove them from the expected indices so we don't expect - // them to get the messages again after the assignment. - expected_indices.retain(|&i2| i2 != i); - } - assert_eq!(sent_assignments, assignments); - sent_peers - } - ); - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - sent_peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Approvals(sent_approvals) - )) - )) => { - // Random sampling is reused from the assignment. - assert_eq!(sent_peers, assignment_sent_peers); - assert_eq!(sent_approvals, approvals); - } - ); + let expected_indices = vec![0, 10, 20, 30, 50, 51, 52, 53]; // Set up a gossip topology. setup_gossip_topology( overseer, - make_gossip_topology(1, &peers, &[0, 10, 20, 30], &[50, 51, 52, 53]), + make_gossip_topology(1, &peers, &[0, 10, 20, 30], &[50, 51, 52, 53], 1), ) .await; @@ -2148,7 +2154,7 @@ fn originator_aggression_l1() { // Set up a gossip topology. setup_gossip_topology( overseer, - make_gossip_topology(1, &peers, &[0, 10, 20, 30], &[50, 51, 52, 53]), + make_gossip_topology(1, &peers, &[0, 10, 20, 30], &[50, 51, 52, 53], 1), ) .await; @@ -2309,7 +2315,7 @@ fn non_originator_aggression_l1() { // Set up a gossip topology. setup_gossip_topology( overseer, - make_gossip_topology(1, &peers, &[0, 10, 20, 30], &[50, 51, 52, 53]), + make_gossip_topology(1, &peers, &[0, 10, 20, 30], &[50, 51, 52, 53], 1), ) .await; @@ -2414,7 +2420,7 @@ fn non_originator_aggression_l2() { // Set up a gossip topology. setup_gossip_topology( overseer, - make_gossip_topology(1, &peers, &[0, 10, 20, 30], &[50, 51, 52, 53]), + make_gossip_topology(1, &peers, &[0, 10, 20, 30], &[50, 51, 52, 53], 1), ) .await; @@ -2561,7 +2567,7 @@ fn resends_messages_periodically() { // Set up a gossip topology. setup_gossip_topology( overseer, - make_gossip_topology(1, &peers, &[0, 10, 20, 30], &[50, 51, 52, 53]), + make_gossip_topology(1, &peers, &[0, 10, 20, 30], &[50, 51, 52, 53], 1), ) .await; From 76823a495403fc895c57a0f1488c3e6878d6d52b Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe Date: Fri, 18 Aug 2023 12:57:55 +0300 Subject: [PATCH 29/54] Address initial review feedback Signed-off-by: Alexandru Gheorghe --- .../approval-voting/src/approval_db/v2/mod.rs | 7 +- node/core/approval-voting/src/import.rs | 1 + node/core/approval-voting/src/lib.rs | 201 +++++++++++------- .../approval-voting/src/persisted_entries.rs | 52 ++++- node/core/approval-voting/src/tests.rs | 11 +- node/core/approval-voting/src/time.rs | 6 +- node/network/approval-distribution/src/lib.rs | 4 +- primitives/src/vstaging/mod.rs | 3 - runtime/parachains/src/configuration.rs | 5 +- .../src/configuration/migration/v7.rs | 1 - runtime/parachains/src/configuration/tests.rs | 5 +- .../functional/0002-parachains-disputes.toml | 1 - .../0006-approval-voting-coalescing.toml | 1 - 13 files changed, 189 insertions(+), 109 deletions(-) diff --git a/node/core/approval-voting/src/approval_db/v2/mod.rs b/node/core/approval-voting/src/approval_db/v2/mod.rs index 8fb2e12f57c2..88650a539130 100644 --- a/node/core/approval-voting/src/approval_db/v2/mod.rs +++ b/node/core/approval-voting/src/approval_db/v2/mod.rs @@ -203,9 +203,10 @@ pub struct TrancheEntry { /// Metadata about our approval signature #[derive(Encode, Decode, Debug, Clone, PartialEq)] pub struct OurApproval { - /// The + /// The signature for the candidates hashes pointed by indices. pub signature: ValidatorSignature, - // The hashes of the candidates signed in this approval + /// The indices of the candidates signed in this approval, an empty value means only + /// the candidate referred by this approval entry was signed. pub signed_candidates_indices: Option, } @@ -266,7 +267,9 @@ pub struct BlockEntry { /// Context needed for creating an approval signature for a given candidate. pub struct CandidateSigningContext { + /// The candidate hash, to be included in the signature. pub candidate_hash: CandidateHash, + /// The latest tick we have to create and send the approval. pub send_no_later_than_tick: Tick, } diff --git a/node/core/approval-voting/src/import.rs b/node/core/approval-voting/src/import.rs index 51cb70bebcd8..8478fcc35a5b 100644 --- a/node/core/approval-voting/src/import.rs +++ b/node/core/approval-voting/src/import.rs @@ -638,6 +638,7 @@ pub(crate) mod tests { clock: Box::new(MockClock::default()), assignment_criteria: Box::new(MockAssignmentCriteria), spans: HashMap::new(), + approval_voting_params_cache: None, } } diff --git a/node/core/approval-voting/src/lib.rs b/node/core/approval-voting/src/lib.rs index ece1033524a2..b3b71fa9d4b3 100644 --- a/node/core/approval-voting/src/lib.rs +++ b/node/core/approval-voting/src/lib.rs @@ -23,6 +23,7 @@ use itertools::Itertools; use jaeger::{hash_to_trace_identifier, PerLeafSpan}; +use lru::LruCache; use polkadot_node_jaeger as jaeger; use polkadot_node_primitives::{ approval::{ @@ -102,7 +103,7 @@ use crate::{ approval_db::v2::{Config as DatabaseConfig, DbBackend}, backend::{Backend, OverlayedBackend}, criteria::InvalidAssignmentReason, - persisted_entries::{CandidateSigningContext, OurApproval}, + persisted_entries::OurApproval, }; #[cfg(test)] @@ -123,6 +124,16 @@ const TICK_TOO_FAR_IN_FUTURE: Tick = 20; // 10 seconds. const APPROVAL_DELAY: Tick = 2; pub(crate) const LOG_TARGET: &str = "parachain::approval-voting"; +// The max number of ticks we delay sending the approval after we are ready to issue the approval +const MAX_APPROVAL_COALESCE_WAIT_TICKS: Tick = 2; + +// The maximum approval params we cache locally in the subsytem, so that we don't have +// to do the back and forth to the runtime subsystem api. +const APPROVAL_PARAMS_CACHE_SIZE: NonZeroUsize = match NonZeroUsize::new(128) { + Some(cap) => cap, + None => panic!("Approval params cache size must be non-zero."), +}; + /// Configuration for the approval voting subsystem #[derive(Debug, Clone)] pub struct Config { @@ -158,6 +169,9 @@ pub struct ApprovalVotingSubsystem { db: Arc, mode: Mode, metrics: Metrics, + // Store approval-voting params, so that we don't to always go to RuntimeAPI + // to ask this information which requires a task context switch. + approval_voting_params_cache: Option>, } #[derive(Clone)] @@ -166,9 +180,9 @@ struct MetricsInner { assignments_produced: prometheus::Histogram, approvals_produced_total: prometheus::CounterVec, no_shows_total: prometheus::Counter, - // The difference is that this counts all observed no-shows. - // approvals might arrive late and `no_shows_total` wouldn't catch - // that number. + // The difference from `no_shows_total` is that this counts all observed no-shows at any + // moment in time. While `no_shows_total` catches that the no-shows at the moment the candidate + // is approved, approvals might arrive late and `no_shows_total` wouldn't catch that number. observed_no_shows: prometheus::Counter, approved_by_one_third: prometheus::Counter, wakeups_triggered_total: prometheus::Counter, @@ -199,7 +213,8 @@ impl Metrics { fn on_approval_coalesce(&self, num_coalesced: u32) { if let Some(metrics) = &self.0 { - // So we count how many candidates we covered with this approvals. + // Count how many candidates we covered with this coalesced approvals, + // so that the heat-map really gives a good understanding of the scales. for _ in 0..num_coalesced { metrics.coalesced_approvals_buckets.observe(num_coalesced as f64) } @@ -328,7 +343,7 @@ impl metrics::Metrics for Metrics { observed_no_shows: prometheus::register( prometheus::Counter::new( "polkadot_parachain_approvals_observed_no_shows_total", - "Number of observed no shows", + "Number of observed no shows at any moment in time", )?, registry, )?, @@ -412,6 +427,24 @@ impl ApprovalVotingSubsystem { keystore: Arc, sync_oracle: Box, metrics: Metrics, + ) -> Self { + Self::with_config_and_cache( + config, + db, + keystore, + sync_oracle, + metrics, + Some(LruCache::new(APPROVAL_PARAMS_CACHE_SIZE)), + ) + } + + pub fn with_config_and_cache( + config: Config, + db: Arc, + keystore: Arc, + sync_oracle: Box, + metrics: Metrics, + approval_voting_params_cache: Option>, ) -> Self { ApprovalVotingSubsystem { keystore, @@ -420,6 +453,7 @@ impl ApprovalVotingSubsystem { db_config: DatabaseConfig { col_approval_data: config.col_approval_data }, mode: Mode::Syncing(sync_oracle), metrics, + approval_voting_params_cache, } } @@ -725,6 +759,9 @@ struct State { clock: Box, assignment_criteria: Box, spans: HashMap, + // Store approval-voting params, so that we don't to always go to RuntimeAPI + // to ask this information which requires a task context switch. + approval_voting_params_cache: Option>, } #[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] @@ -780,6 +817,47 @@ impl State { None } } + + // Returns the approval voting from the RuntimeApi. + // To avoid crossing the subsystem boundary every-time we are caching locally the values. + #[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] + async fn get_approval_voting_params_or_default( + &mut self, + ctx: &mut Context, + block_hash: Hash, + ) -> ApprovalVotingParams { + if let Some(params) = self + .approval_voting_params_cache + .as_mut() + .and_then(|cache| cache.get(&block_hash)) + { + *params + } else { + let (s_tx, s_rx) = oneshot::channel(); + + ctx.send_message(RuntimeApiMessage::Request( + block_hash, + RuntimeApiRequest::ApprovalVotingParams(s_tx), + )) + .await; + + match s_rx.await { + Ok(Ok(params)) => { + self.approval_voting_params_cache + .as_mut() + .map(|cache| cache.put(block_hash, params)); + params + }, + _ => { + gum::error!( + target: LOG_TARGET, + "Could not request approval voting params from runtime using defaults" + ); + ApprovalVotingParams { max_approval_coalesce_count: 1 } + }, + } + } + } } #[derive(Debug, Clone)] @@ -828,6 +906,7 @@ where clock, assignment_criteria, spans: HashMap::new(), + approval_voting_params_cache: subsystem.approval_voting_params_cache.take(), }; // `None` on start-up. Gets initialized/updated on leaf update @@ -923,7 +1002,7 @@ where "Sign approval for multiple candidates", ); - let approval_params = get_approval_voting_params_or_default(&mut ctx, block_hash).await; + let approval_params = state.get_approval_voting_params_or_default(&mut ctx, block_hash).await; match maybe_create_signature( &mut overlayed_db, @@ -952,7 +1031,7 @@ where if handle_actions( &mut ctx, - &state, + &mut state, &mut overlayed_db, &mut session_info_provider, &subsystem.metrics, @@ -1000,7 +1079,7 @@ where #[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] async fn handle_actions( ctx: &mut Context, - state: &State, + state: &mut State, overlayed_db: &mut OverlayedBackend<'_, impl Backend>, session_info_provider: &mut RuntimeInfo, metrics: &Metrics, @@ -1267,7 +1346,7 @@ fn distribution_messages_for_activation( &candidate_hash, &block_entry, ) { - if !block_entry.candidates_pending_signature.is_empty() { + if block_entry.has_candidates_pending_signature() { delayed_approvals_timers.maybe_arm_timer( state.clock.tick_now(), state.clock.as_ref(), @@ -2568,9 +2647,9 @@ where metrics.on_no_shows(no_shows); } if check == Check::ApprovedOneThird { - // No-shows are not counted when more than one third of validators, - // so count candidates where more than one third of validators had - // to approve it, this is indicative of something breaking. + // No-shows are not counted when more than one third of validators approve a candidate, + // so count candidates where more than one third of validators had to approve it, + // this is indicative of something breaking. metrics.on_approved_by_one_third() } @@ -3069,7 +3148,7 @@ async fn launch_approval( #[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] async fn issue_approval( ctx: &mut Context, - state: &State, + state: &mut State, db: &mut OverlayedBackend<'_, impl Backend>, session_info_provider: &mut RuntimeInfo, metrics: &Metrics, @@ -3156,20 +3235,22 @@ async fn issue_approval( None => return Ok(Vec::new()), }; - let approval_params = get_approval_voting_params_or_default(ctx, block_hash).await; - - block_entry.candidates_pending_signature.insert( - candidate_index as _, - CandidateSigningContext { + if block_entry + .defer_candidate_signature( + candidate_index as _, candidate_hash, - send_no_later_than_tick: compute_delayed_approval_sending_tick( - state, - &block_entry, - session_info, - &approval_params, - ), - }, - ); + compute_delayed_approval_sending_tick(state, &block_entry, session_info), + ) + .is_some() + { + gum::error!( + target: LOG_TARGET, + ?candidate_hash, + ?block_hash, + validator_index = validator_index.0, + "Possible bug, we shouldn't have to defer a candidate more than once", + ); + } gum::info!( target: LOG_TARGET, @@ -3179,6 +3260,8 @@ async fn issue_approval( "Ready to issue approval vote", ); + let approval_params = state.get_approval_voting_params_or_default(ctx, block_hash).await; + let actions = advance_approval_state( ctx.sender(), state, @@ -3241,14 +3324,10 @@ async fn maybe_create_signature( gum::debug!( target: LOG_TARGET, - "Candidates pending signatures {:}", block_entry.candidates_pending_signature.len() + "Candidates pending signatures {:}", block_entry.num_candidates_pending_signature() ); - let oldest_candidate_to_sign = match block_entry - .candidates_pending_signature - .values() - .min_by(|a, b| a.send_no_later_than_tick.cmp(&b.send_no_later_than_tick)) - { + let oldest_candidate_to_sign = match block_entry.longest_waiting_candidate_signature() { Some(candidate) => candidate, // No cached candidates, nothing to do here, this just means the timer fired, // but the signatures were already sent because we gathered more than max_approval_coalesce_count. @@ -3258,7 +3337,7 @@ async fn maybe_create_signature( let tick_now = state.clock.tick_now(); if oldest_candidate_to_sign.send_no_later_than_tick > tick_now && - (block_entry.candidates_pending_signature.len() as u32) < + (block_entry.num_candidates_pending_signature() as u32) < approval_params.max_approval_coalesce_count { return Ok(Some(oldest_candidate_to_sign.send_no_later_than_tick)) @@ -3298,11 +3377,7 @@ async fn maybe_create_signature( }, }; - let candidate_hashes: Vec = block_entry - .candidates_pending_signature - .values() - .map(|unsigned_approval| unsigned_approval.candidate_hash) - .collect(); + let candidate_hashes = block_entry.candidate_hashes_pending_signature(); let signature = match sign_approval( &state.keystore, @@ -3324,13 +3399,13 @@ async fn maybe_create_signature( }, }; - let candidate_entries = block_entry - .candidates_pending_signature - .values() - .map(|unsigned_approval| db.load_candidate_entry(&unsigned_approval.candidate_hash)) + let candidate_entries = candidate_hashes + .iter() + .map(|candidate_hash| db.load_candidate_entry(candidate_hash)) .collect::>>>()?; - let candidate_indices: Vec = - block_entry.candidates_pending_signature.keys().map(|val| *val).collect(); + + let candidate_indices = block_entry.candidate_indices_pending_signature(); + for candidate_entry in candidate_entries { let mut candidate_entry = candidate_entry .expect("Candidate was scheduled to be signed entry in db should exist; qed"); @@ -3367,10 +3442,10 @@ async fn maybe_create_signature( gum::debug!( target: LOG_TARGET, ?block_hash, - signed_candidates = ?block_entry.candidates_pending_signature.len(), + signed_candidates = ?block_entry.num_candidates_pending_signature(), "Issue approval votes", ); - block_entry.candidates_pending_signature.clear(); + block_entry.issued_approval(); db.write_block_entry(block_entry.into()); Ok(None) } @@ -3415,39 +3490,11 @@ fn issue_local_invalid_statement( )); } -#[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] -async fn get_approval_voting_params_or_default( - ctx: &mut Context, - block_hash: Hash, -) -> ApprovalVotingParams { - let (s_tx, s_rx) = oneshot::channel(); - - ctx.send_message(RuntimeApiMessage::Request( - block_hash, - RuntimeApiRequest::ApprovalVotingParams(s_tx), - )) - .await; - - match s_rx.await { - Ok(Ok(s)) => s, - _ => { - gum::error!( - target: LOG_TARGET, - "Could not request approval voting params from runtime using defaults" - ); - ApprovalVotingParams { - max_approval_coalesce_count: 1, - max_approval_coalesce_wait_ticks: 2, - } - }, - } -} - +// Computes what is the latest tick we can send an approval fn compute_delayed_approval_sending_tick( state: &State, block_entry: &BlockEntry, session_info: &SessionInfo, - approval_params: &ApprovalVotingParams, ) -> Tick { let current_block_tick = slot_number_to_tick(state.slot_duration_millis, block_entry.slot()); @@ -3458,7 +3505,7 @@ fn compute_delayed_approval_sending_tick( let tick_now = state.clock.tick_now(); min( - tick_now + approval_params.max_approval_coalesce_wait_ticks as Tick, + tick_now + MAX_APPROVAL_COALESCE_WAIT_TICKS as Tick, // We don't want to accidentally cause, no-shows so if we are past // the 2 thirds of the no show time, force the sending of the // approval immediately. diff --git a/node/core/approval-voting/src/persisted_entries.rs b/node/core/approval-voting/src/persisted_entries.rs index d599c2eac133..e441c23be122 100644 --- a/node/core/approval-voting/src/persisted_entries.rs +++ b/node/core/approval-voting/src/persisted_entries.rs @@ -102,7 +102,7 @@ impl From for OurApproval { /// Metadata about our approval signature #[derive(Debug, Clone, PartialEq)] pub struct OurApproval { - /// The + /// The signature for the candidates hashes pointed by indices. pub signature: ValidatorSignature, /// The indices of the candidates signed in this approval, an empty value means only /// the candidate referred by this approval entry was signed. @@ -388,7 +388,7 @@ pub struct BlockEntry { pub children: Vec, // A list of candidates that has been approved, but we didn't not sign and // advertise the vote yet. - pub candidates_pending_signature: BTreeMap, + candidates_pending_signature: BTreeMap, // A list of assignments for which wea already distributed the assignment. // We use this to ensure we don't distribute multiple core assignments twice as we track // individual wakeups for each core. @@ -489,6 +489,54 @@ impl BlockEntry { distributed } + + /// Defer signing and issuing an approval for a candidate no later than the specified tick + pub fn defer_candidate_signature( + &mut self, + candidate_index: CandidateIndex, + candidate_hash: CandidateHash, + send_no_later_than_tick: Tick, + ) -> Option { + self.candidates_pending_signature.insert( + candidate_index, + CandidateSigningContext { candidate_hash, send_no_later_than_tick }, + ) + } + + /// Returns the number of candidates waiting for an approval to be issued. + pub fn num_candidates_pending_signature(&self) -> usize { + self.candidates_pending_signature.len() + } + + /// Return if we have candidates waiting for signature to be issued + pub fn has_candidates_pending_signature(&self) -> bool { + !self.candidates_pending_signature.is_empty() + } + + /// Candidate hashes for candidates pending signatures + pub fn candidate_hashes_pending_signature(&self) -> Vec { + self.candidates_pending_signature + .values() + .map(|unsigned_approval| unsigned_approval.candidate_hash) + .collect() + } + + /// Candidate indices for candidates pending signature + pub fn candidate_indices_pending_signature(&self) -> Vec { + self.candidates_pending_signature.keys().map(|val| *val).collect() + } + + /// Returns the candidate that has been longest in the queue. + pub fn longest_waiting_candidate_signature(&self) -> Option<&CandidateSigningContext> { + self.candidates_pending_signature + .values() + .min_by(|a, b| a.send_no_later_than_tick.cmp(&b.send_no_later_than_tick)) + } + + /// Signals the approval was issued for the candidates pending signature + pub fn issued_approval(&mut self) { + self.candidates_pending_signature.clear(); + } } impl From for BlockEntry { diff --git a/node/core/approval-voting/src/tests.rs b/node/core/approval-voting/src/tests.rs index d3f5a3fe9581..3ced911f7109 100644 --- a/node/core/approval-voting/src/tests.rs +++ b/node/core/approval-voting/src/tests.rs @@ -539,7 +539,7 @@ fn test_harness>( let subsystem = run( context, - ApprovalVotingSubsystem::with_config( + ApprovalVotingSubsystem::with_config_and_cache( Config { col_approval_data: test_constants::TEST_CONFIG.col_approval_data, slot_duration_millis: SLOT_DURATION_MILLIS, @@ -548,6 +548,7 @@ fn test_harness>( Arc::new(keystore), sync_oracle, Metrics::default(), + None, ), clock.clone(), assignment_criteria, @@ -2805,7 +2806,6 @@ async fn handle_double_assignment_import( AllMessages::RuntimeApi(RuntimeApiMessage::Request(_, RuntimeApiRequest::ApprovalVotingParams(sender))) => { let _ = sender.send(Ok(ApprovalVotingParams { max_approval_coalesce_count: 1, - max_approval_coalesce_wait_ticks: 0, })); } ); @@ -2820,7 +2820,6 @@ async fn handle_double_assignment_import( AllMessages::RuntimeApi(RuntimeApiMessage::Request(_, RuntimeApiRequest::ApprovalVotingParams(sender))) => { let _ = sender.send(Ok(ApprovalVotingParams { max_approval_coalesce_count: 1, - max_approval_coalesce_wait_ticks: 0, })); } ); @@ -3744,7 +3743,6 @@ async fn handle_approval_on_max_coalesce_count( AllMessages::RuntimeApi(RuntimeApiMessage::Request(_, RuntimeApiRequest::ApprovalVotingParams(sender))) => { let _ = sender.send(Ok(ApprovalVotingParams { max_approval_coalesce_count: 2, - max_approval_coalesce_wait_ticks: 10000, })); } ); @@ -3754,7 +3752,6 @@ async fn handle_approval_on_max_coalesce_count( AllMessages::RuntimeApi(RuntimeApiMessage::Request(_, RuntimeApiRequest::ApprovalVotingParams(sender))) => { let _ = sender.send(Ok(ApprovalVotingParams { max_approval_coalesce_count: 2, - max_approval_coalesce_wait_ticks: 10000, })); } ); @@ -3777,7 +3774,6 @@ async fn handle_approval_on_max_wait_time( ) { const TICK_NOW_BEGIN: u64 = 1; const MAX_COALESCE_COUNT: u32 = 3; - const MAX_APPROVAL_COALESCE_WAIT_TICKS: u32 = 4; clock.inner.lock().set_tick(TICK_NOW_BEGIN); @@ -3812,7 +3808,6 @@ async fn handle_approval_on_max_wait_time( AllMessages::RuntimeApi(RuntimeApiMessage::Request(_, RuntimeApiRequest::ApprovalVotingParams(sender))) => { let _ = sender.send(Ok(ApprovalVotingParams { max_approval_coalesce_count: MAX_COALESCE_COUNT, - max_approval_coalesce_wait_ticks: MAX_APPROVAL_COALESCE_WAIT_TICKS, })); } ); @@ -3823,7 +3818,6 @@ async fn handle_approval_on_max_wait_time( AllMessages::RuntimeApi(RuntimeApiMessage::Request(_, RuntimeApiRequest::ApprovalVotingParams(sender))) => { let _ = sender.send(Ok(ApprovalVotingParams { max_approval_coalesce_count: MAX_COALESCE_COUNT, - max_approval_coalesce_wait_ticks: MAX_APPROVAL_COALESCE_WAIT_TICKS, })); } ); @@ -3850,7 +3844,6 @@ async fn handle_approval_on_max_wait_time( AllMessages::RuntimeApi(RuntimeApiMessage::Request(_, RuntimeApiRequest::ApprovalVotingParams(sender))) => { let _ = sender.send(Ok(ApprovalVotingParams { max_approval_coalesce_count: 3, - max_approval_coalesce_wait_ticks: 4, })); } ); diff --git a/node/core/approval-voting/src/time.rs b/node/core/approval-voting/src/time.rs index ca6ba288d161..61091f3c34cd 100644 --- a/node/core/approval-voting/src/time.rs +++ b/node/core/approval-voting/src/time.rs @@ -98,9 +98,9 @@ pub(crate) fn slot_number_to_tick(slot_duration_millis: u64, slot: Slot) -> Tick u64::from(slot) * ticks_per_slot } -// A list of delayed futures that gets triggered when the waiting time has expired and it is -// time to sign the candidate. -// We have a timer per relay-chain block. +/// A list of delayed futures that gets triggered when the waiting time has expired and it is +/// time to sign the candidate. +/// We have a timer per relay-chain block. #[derive(Default)] pub struct DelayedApprovalTimer { timers: FuturesUnordered>, diff --git a/node/network/approval-distribution/src/lib.rs b/node/network/approval-distribution/src/lib.rs index cb759021b504..213785d62403 100644 --- a/node/network/approval-distribution/src/lib.rs +++ b/node/network/approval-distribution/src/lib.rs @@ -1907,7 +1907,7 @@ impl State { // Filter approval votes. for approval_message in approval_messages { - let (should_forward_approval, covered_approvals) = + let (should_forward_approval, candidates_covered_by_approvals) = approval_message.candidate_indices.iter_ones().fold( (true, Vec::new()), |(should_forward_approval, mut new_covered_approvals), @@ -1931,7 +1931,7 @@ impl State { ); if should_forward_approval { approvals_to_send.push(approval_message); - covered_approvals.into_iter().for_each( + candidates_covered_by_approvals.into_iter().for_each( |(approval_knowledge, message_kind)| { peer_knowledge.sent.insert(approval_knowledge, message_kind); }, diff --git a/primitives/src/vstaging/mod.rs b/primitives/src/vstaging/mod.rs index aa652f433f66..bc1b80296f5d 100644 --- a/primitives/src/vstaging/mod.rs +++ b/primitives/src/vstaging/mod.rs @@ -69,7 +69,4 @@ pub struct ApprovalVotingParams { /// /// Setting it to 1, means we send the approval as soon as we have it available. pub max_approval_coalesce_count: u32, - /// The maximum ticks we await for a candidate approval to be coalesced with - /// the ones for other candidate before we sign it and distribute to our peers - pub max_approval_coalesce_wait_ticks: u32, } diff --git a/runtime/parachains/src/configuration.rs b/runtime/parachains/src/configuration.rs index 0f73e85c3ea9..6f8c9dbf7c7a 100644 --- a/runtime/parachains/src/configuration.rs +++ b/runtime/parachains/src/configuration.rs @@ -291,10 +291,7 @@ impl> Default for HostConfiguration Date: Fri, 18 Aug 2023 16:57:19 +0300 Subject: [PATCH 30/54] Add zombient test 0006-approval-voting-coalescing to pipeline Signed-off-by: Alexandru Gheorghe --- scripts/ci/gitlab/pipeline/zombienet.yml | 30 +++++++++++++++++++ .../0006-approval-voting-coalescing.toml | 4 +-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/scripts/ci/gitlab/pipeline/zombienet.yml b/scripts/ci/gitlab/pipeline/zombienet.yml index 6e43eb4964de..b329db3d3501 100644 --- a/scripts/ci/gitlab/pipeline/zombienet.yml +++ b/scripts/ci/gitlab/pipeline/zombienet.yml @@ -62,6 +62,36 @@ zombienet-tests-parachains-pvf: tags: - zombienet-polkadot-integration-test +zombienet-tests-parachains-approval-coalescing: + stage: zombienet + image: "${ZOMBIENET_IMAGE}" + extends: + - .kubernetes-env + - .zombienet-refs + needs: + - job: publish-polkadot-debug-image + - job: publish-test-collators-image + variables: + GH_DIR: "https://github.com/paritytech/polkadot/tree/${CI_COMMIT_SHORT_SHA}/zombienet_tests/functional" + before_script: + - echo "Zombie-net Tests Config" + - echo "${ZOMBIENET_IMAGE}" + - echo "${PARACHAINS_IMAGE_NAME} ${PARACHAINS_IMAGE_TAG}" + - echo "COL_IMAGE=${COLLATOR_IMAGE_NAME}:${COLLATOR_IMAGE_TAG}" + - echo "${GH_DIR}" + - export DEBUG=zombie,zombie::network-node + - export ZOMBIENET_INTEGRATION_TEST_IMAGE=${PARACHAINS_IMAGE_NAME}:${PARACHAINS_IMAGE_TAG} + - export MALUS_IMAGE=${MALUS_IMAGE_NAME}:${MALUS_IMAGE_TAG} + - export COL_IMAGE=${COLLATOR_IMAGE_NAME}:${COLLATOR_IMAGE_TAG} + script: + - /home/nonroot/zombie-net/scripts/ci/run-test-env-manager.sh + --github-remote-dir="${GH_DIR}" + --test="0006-approval-voting-coalescing.zndsl" + allow_failure: false + retry: 2 + tags: + - zombienet-polkadot-integration-test + zombienet-tests-parachains-disputes: stage: zombienet image: "${ZOMBIENET_IMAGE}" diff --git a/zombienet_tests/functional/0006-approval-voting-coalescing.toml b/zombienet_tests/functional/0006-approval-voting-coalescing.toml index ce523743511f..444f56e70b34 100644 --- a/zombienet_tests/functional/0006-approval-voting-coalescing.toml +++ b/zombienet_tests/functional/0006-approval-voting-coalescing.toml @@ -7,11 +7,11 @@ chain = "rococo-local" chain_spec_command = "polkadot build-spec --chain rococo-local --disable-default-bootnode" [relaychain.genesis.runtime.runtime_genesis_config.configuration.config] - needed_approvals = 8 + needed_approvals = 5 relay_vrf_modulo_samples = 3 [relaychain.genesis.runtime.runtime_genesis_config.configuration.config.approval_voting_params] - max_approval_coalesce_count = {{MAX_APPROVAL_COALESCE_COUNT}} + max_approval_coalesce_count = 6 [relaychain.default_resources] limits = { memory = "4G", cpu = "2" } From 62a57b5040faab44103bbb43260d55bc75d51ec9 Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe Date: Fri, 18 Aug 2023 18:17:44 +0300 Subject: [PATCH 31/54] Update implementers guide Signed-off-by: Alexandru Gheorghe --- .../src/node/approval/approval-voting.md | 35 +++++++++++++++---- .../src/protocol-approval.md | 6 ++++ 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/roadmap/implementers-guide/src/node/approval/approval-voting.md b/roadmap/implementers-guide/src/node/approval/approval-voting.md index 8ccd76a4b983..ca811cf6b7f4 100644 --- a/roadmap/implementers-guide/src/node/approval/approval-voting.md +++ b/roadmap/implementers-guide/src/node/approval/approval-voting.md @@ -2,7 +2,7 @@ Reading the [section on the approval protocol](../../protocol-approval.md) will likely be necessary to understand the aims of this subsystem. -Approval votes are split into two parts: Assignments and Approvals. Validators first broadcast their assignment to indicate intent to check a candidate. Upon successfully checking, they broadcast an approval vote. If a validator doesn't broadcast their approval vote shortly after issuing an assignment, this is an indication that they are being prevented from recovering or validating the block data and that more validators should self-select to check the candidate. This is known as a "no-show". +Approval votes are split into two parts: Assignments and Approvals. Validators first broadcast their assignment to indicate intent to check a candidate. Upon successfully checking, they don't immediately send the vote instead they queue the check for a short period of time `MAX_APPROVALS_COALESCE_TICKS` to give the opportunity of the validator to vote for more than one candidate. Once MAX_APPROVALS_COALESCE_TICKS have passed or at least `MAX_APPROVAL_COALESCE_COUNT` are ready they broadcast an approval vote for all candidates. If a validator doesn't broadcast their approval vote shortly after issuing an assignment, this is an indication that they are being prevented from recovering or validating the block data and that more validators should self-select to check the candidate. This is known as a "no-show". The core of this subsystem is a Tick-based timer loop, where Ticks are 500ms. We also reason about time in terms of `DelayTranche`s, which measure the number of ticks elapsed since a block was produced. We track metadata for all un-finalized but included candidates. We compute our local assignments to check each candidate, as well as which `DelayTranche` those assignments may be minimally triggered at. As the same candidate may appear in more than one block, we must produce our potential assignments for each (Block, Candidate) pair. The timing loop is based on waiting for assignments to become no-shows or waiting to broadcast and begin our own assignment to check. @@ -95,6 +95,13 @@ struct BlockEntry { // this block. The block can be considered approved has all bits set to 1 approved_bitfield: Bitfield, children: Vec, + // A list of candidates that has been approved, but we didn't not sign and + // advertise the vote yet. + candidates_pending_signature: BTreeMap, + // Assignments we already distributed. A 1 bit means the candidate index for which + // we already have sent out an assignment. We need this to avoid distributing + // multiple core assignments more than once. + distributed_assignments: Bitfield, } // slot_duration * 2 + DelayTranche gives the number of delay tranches since the @@ -225,10 +232,10 @@ On receiving a `ApprovalVotingMessage::CheckAndImportAssignment` message, we che On receiving a `CheckAndImportApproval(indirect_approval_vote, response_channel)` message: * Fetch the `BlockEntry` from the indirect approval vote's `block_hash`. If none, return `ApprovalCheckResult::Bad`. - * Fetch the `CandidateEntry` from the indirect approval vote's `candidate_index`. If the block did not trigger inclusion of enough candidates, return `ApprovalCheckResult::Bad`. - * Construct a `SignedApprovalVote` using the candidate hash and check against the validator's approval key, based on the session info of the block. If invalid or no such validator, return `ApprovalCheckResult::Bad`. + * Fetch all `CandidateEntry` from the indirect approval vote's `candidate_indices`. If the block did not trigger inclusion of enough candidates, return `ApprovalCheckResult::Bad`. + * Construct a `SignedApprovalVote` using the candidates hashes and check against the validator's approval key, based on the session info of the block. If invalid or no such validator, return `ApprovalCheckResult::Bad`. * Send `ApprovalCheckResult::Accepted` - * [Import the checked approval vote](#import-checked-approval) + * [Import the checked approval vote](#import-checked-approval) for all candidates #### `ApprovalVotingMessage::ApprovedAncestor` @@ -296,10 +303,24 @@ On receiving an `ApprovedAncestor(Hash, BlockNumber, response_channel)`: #### Issue Approval Vote * Fetch the block entry and candidate entry. Ignore if `None` - we've probably just lost a race with finality. - * Construct a `SignedApprovalVote` with the validator index for the session. * [Import the checked approval vote](#import-checked-approval). It is "checked" as we've just issued the signature. - * Construct a `IndirectSignedApprovalVote` using the information about the vote. - * Dispatch `ApprovalDistributionMessage::DistributeApproval`. + * IF `MAX_APPROVAL_COALESCE_COUNT` candidates are in the waiting queue + * Construct a `SignedApprovalVote` with the validator index for the session and all candidate hashes in the waiting queue. + * Construct a `IndirectSignedApprovalVote` using the information about the vote. + * Dispatch `ApprovalDistributionMessage::DistributeApproval`. + * ELSE + * Queue the candidate in the `BlockEntry::candidates_pending_signature` + * Arm a per BlockEntry timer with latest tick we can send the vote. + +### Delayed vote distribution + * [Issue Approval Vote](#issue-approval-vote) arms once a per block timer if there are no requirements to send the vote immediately. + * When the timer wakes up it will either: + * IF there is a candidate in the queue past its sending tick: + * Construct a `SignedApprovalVote` with the validator index for the session and all candidate hashes in the waiting queue. + * Construct a `IndirectSignedApprovalVote` using the information about the vote. + * Dispatch `ApprovalDistributionMessage::DistributeApproval`. + * ELSE + * Re-arm the timer with latest tick we have the send a the vote. ### Determining Approval of Candidate diff --git a/roadmap/implementers-guide/src/protocol-approval.md b/roadmap/implementers-guide/src/protocol-approval.md index aa513c16292d..63345032cbb0 100644 --- a/roadmap/implementers-guide/src/protocol-approval.md +++ b/roadmap/implementers-guide/src/protocol-approval.md @@ -152,6 +152,12 @@ We strongly prefer if postponements come from tranches higher aka less important TODO: When? Is this optimal for the network? etc. +## Approval coalescing +To reduce the necessary network bandwidth and cpu time when a validator has more than one candidate to approve we are doing our best effort to send a single message that approves all available candidates with a single signature. The implemented heuristic, is that each time we are ready to create a signature and send a vote for a candidate we delay the sending of it untill one of three things happen: +- We gathered a maximum of `MAX_APPROVAL_COALESCE_COUNT` candidates that we are ready to vote for. +- `MAX_APPROVALS_COALESCE_TICKS` have passed since the we were ready to approve the candidate. +- We are already in the last third of the now-show period in order to avoid creating accidental no shows, which in turn my trigger other assignments. + ## On-chain verification We should verify approval on-chain to reward approval checkers. We therefore require the "no show" timeout to be longer than a relay chain slot so that we can witness "no shows" on-chain, which helps with this goal. The major challenge with an on-chain record of the off-chain process is adversarial block producers who may either censor votes or publish votes to the chain which cause other votes to be ignored and unrewarded (reward stealing). From f38697e4696a69c0a94724ceb6a131731e6245ec Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe Date: Sat, 19 Aug 2023 10:32:33 +0300 Subject: [PATCH 32/54] Fix importing of approval out-of-view Signed-off-by: Alexandru Gheorghe --- node/network/approval-distribution/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/node/network/approval-distribution/src/lib.rs b/node/network/approval-distribution/src/lib.rs index 213785d62403..7d10235bceff 100644 --- a/node/network/approval-distribution/src/lib.rs +++ b/node/network/approval-distribution/src/lib.rs @@ -1441,7 +1441,6 @@ impl State { modify_reputation(reputation, ctx.sender(), peer_id, COST_UNEXPECTED_MESSAGE) .await; metrics.on_approval_out_of_view(); - return true }, } } From 903cef2ae1be9994c9517086b7141be3e4036644 Mon Sep 17 00:00:00 2001 From: Marcin S Date: Mon, 21 Aug 2023 15:18:31 +0200 Subject: [PATCH 33/54] PVF worker: Prevent access to env vars (#7330) --- node/core/pvf/common/src/worker/mod.rs | 10 ++++++++++ .../src/node/utility/pvf-host-and-workers.md | 7 +++++++ 2 files changed, 17 insertions(+) diff --git a/node/core/pvf/common/src/worker/mod.rs b/node/core/pvf/common/src/worker/mod.rs index d9a0dff71b24..d249007ec36e 100644 --- a/node/core/pvf/common/src/worker/mod.rs +++ b/node/core/pvf/common/src/worker/mod.rs @@ -128,6 +128,16 @@ pub fn worker_event_loop( } } + // Delete all env vars to prevent malicious code from accessing them. + for (key, _) in std::env::vars() { + // TODO: *theoretically* the value (or mere presence) of `RUST_LOG` can be a source of + // randomness for malicious code. In the future we can remove it also and log in the host; + // see . + if key != "RUST_LOG" { + std::env::remove_var(key); + } + } + // Run the main worker loop. let rt = Runtime::new().expect("Creates tokio runtime. If this panics the worker will die and the host will detect that and deal with it."); let err = rt diff --git a/roadmap/implementers-guide/src/node/utility/pvf-host-and-workers.md b/roadmap/implementers-guide/src/node/utility/pvf-host-and-workers.md index 017b7fc025cc..bcf01b61f217 100644 --- a/roadmap/implementers-guide/src/node/utility/pvf-host-and-workers.md +++ b/roadmap/implementers-guide/src/node/utility/pvf-host-and-workers.md @@ -125,3 +125,10 @@ A basic security mechanism is to make sure that any thread directly interfacing with untrusted code does not have access to the file-system. This provides some protection against attackers accessing sensitive data or modifying data on the host machine. + +### Clearing env vars + +We clear environment variables before handling untrusted code, because why give +attackers potentially sensitive data unnecessarily? And even if everything else +is locked down, env vars can potentially provide a source of randomness (see +point 1, "Consensus faults" above). From ea027a8e3722346b5e29587c2b5370f455c34b90 Mon Sep 17 00:00:00 2001 From: Sebastian Kunert Date: Mon, 21 Aug 2023 17:02:13 +0200 Subject: [PATCH 34/54] Companion for substrate#14776 (#7648) * Bump dalek * update lockfile for {"substrate"} --------- Co-authored-by: parity-processbot <> --- Cargo.lock | 457 ++++++++++++++++++++++++++++------------------------- 1 file changed, 239 insertions(+), 218 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c963a3f5eef1..0e72e6aabc57 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -778,7 +778,7 @@ dependencies = [ [[package]] name = "binary-merkle-tree" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "hash-db", "log", @@ -1847,18 +1847,32 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "4.0.0-rc.1" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d4ba9852b42210c7538b75484f9daa0655e9a3ac04f693747bb0f02cf3cfe16" +checksum = "f711ade317dd348950a9910f81c5947e3d8907ebd2b83f76203ff1807e6a2bc2" dependencies = [ "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest 0.10.7", "fiat-crypto", - "packed_simd_2", "platforms", + "rustc_version", "subtle", "zeroize", ] +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.28", +] + [[package]] name = "cxx" version = "1.0.102" @@ -2339,6 +2353,16 @@ dependencies = [ "signature 1.6.4", ] +[[package]] +name = "ed25519" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60f6d271ca33075c88028be6f04d502853d63a5ece419d269c15315d4fc1cf1d" +dependencies = [ + "pkcs8 0.10.2", + "signature 2.1.0", +] + [[package]] name = "ed25519-dalek" version = "1.0.1" @@ -2346,13 +2370,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" dependencies = [ "curve25519-dalek 3.2.0", - "ed25519", + "ed25519 1.5.3", "rand 0.7.3", "serde", "sha2 0.9.9", "zeroize", ] +[[package]] +name = "ed25519-dalek" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7277392b266383ef8396db7fdeb1e77b6c52fed775f5df15bb24f35b72156980" +dependencies = [ + "curve25519-dalek 4.0.0", + "ed25519 2.2.2", + "serde", + "sha2 0.10.7", + "zeroize", +] + [[package]] name = "ed25519-zebra" version = "3.1.0" @@ -2831,7 +2868,7 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "fork-tree" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "parity-scale-codec", ] @@ -2854,7 +2891,7 @@ checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" [[package]] name = "frame-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-support", "frame-support-procedural", @@ -2879,7 +2916,7 @@ dependencies = [ [[package]] name = "frame-benchmarking-cli" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "Inflector", "array-bytes", @@ -2927,7 +2964,7 @@ dependencies = [ [[package]] name = "frame-election-provider-solution-type" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -2938,7 +2975,7 @@ dependencies = [ [[package]] name = "frame-election-provider-support" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-election-provider-solution-type", "frame-support", @@ -2955,7 +2992,7 @@ dependencies = [ [[package]] name = "frame-executive" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-support", "frame-system", @@ -2984,7 +3021,7 @@ dependencies = [ [[package]] name = "frame-remote-externalities" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "async-recursion", "futures", @@ -3006,7 +3043,7 @@ dependencies = [ [[package]] name = "frame-support" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "aquamarine", "bitflags 1.3.2", @@ -3044,7 +3081,7 @@ dependencies = [ [[package]] name = "frame-support-procedural" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "Inflector", "cfg-expr", @@ -3062,7 +3099,7 @@ dependencies = [ [[package]] name = "frame-support-procedural-tools" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-support-procedural-tools-derive", "proc-macro-crate", @@ -3074,7 +3111,7 @@ dependencies = [ [[package]] name = "frame-support-procedural-tools-derive" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "proc-macro2", "quote", @@ -3084,7 +3121,7 @@ dependencies = [ [[package]] name = "frame-support-test" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-benchmarking", "frame-executive", @@ -3111,7 +3148,7 @@ dependencies = [ [[package]] name = "frame-support-test-pallet" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-support", "frame-system", @@ -3124,7 +3161,7 @@ dependencies = [ [[package]] name = "frame-system" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "cfg-if", "frame-support", @@ -3143,7 +3180,7 @@ dependencies = [ [[package]] name = "frame-system-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-benchmarking", "frame-support", @@ -3158,7 +3195,7 @@ dependencies = [ [[package]] name = "frame-system-rpc-runtime-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "parity-scale-codec", "sp-api", @@ -3167,7 +3204,7 @@ dependencies = [ [[package]] name = "frame-try-runtime" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-support", "parity-scale-codec", @@ -3342,7 +3379,7 @@ dependencies = [ [[package]] name = "generate-bags" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "chrono", "frame-election-provider-support", @@ -4472,12 +4509,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "libm" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a" - [[package]] name = "libp2p" version = "0.51.3" @@ -4607,7 +4638,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e2d584751cecb2aabaa56106be6be91338a60a0f4e420cf2af639204f596fc1" dependencies = [ "bs58", - "ed25519-dalek", + "ed25519-dalek 1.0.1", "log", "multiaddr", "multihash", @@ -5346,7 +5377,7 @@ dependencies = [ [[package]] name = "mmr-gadget" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "futures", "log", @@ -5365,7 +5396,7 @@ dependencies = [ [[package]] name = "mmr-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "anyhow", "jsonrpsee", @@ -5889,20 +5920,10 @@ dependencies = [ "sha2 0.10.7", ] -[[package]] -name = "packed_simd_2" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1914cd452d8fccd6f9db48147b29fd4ae05bea9dc5d9ad578509f72415de282" -dependencies = [ - "cfg-if", - "libm", -] - [[package]] name = "pallet-assets" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-benchmarking", "frame-support", @@ -5917,7 +5938,7 @@ dependencies = [ [[package]] name = "pallet-authority-discovery" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-support", "frame-system", @@ -5933,7 +5954,7 @@ dependencies = [ [[package]] name = "pallet-authorship" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-support", "frame-system", @@ -5947,7 +5968,7 @@ dependencies = [ [[package]] name = "pallet-babe" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-benchmarking", "frame-support", @@ -5971,7 +5992,7 @@ dependencies = [ [[package]] name = "pallet-bags-list" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "aquamarine", "docify", @@ -5993,7 +6014,7 @@ dependencies = [ [[package]] name = "pallet-bags-list-remote-tests" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-election-provider-support", "frame-remote-externalities", @@ -6012,7 +6033,7 @@ dependencies = [ [[package]] name = "pallet-balances" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-benchmarking", "frame-support", @@ -6027,7 +6048,7 @@ dependencies = [ [[package]] name = "pallet-beefy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-support", "frame-system", @@ -6046,7 +6067,7 @@ dependencies = [ [[package]] name = "pallet-beefy-mmr" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "array-bytes", "binary-merkle-tree", @@ -6070,7 +6091,7 @@ dependencies = [ [[package]] name = "pallet-bounties" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-benchmarking", "frame-support", @@ -6088,7 +6109,7 @@ dependencies = [ [[package]] name = "pallet-child-bounties" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-benchmarking", "frame-support", @@ -6107,7 +6128,7 @@ dependencies = [ [[package]] name = "pallet-collective" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-benchmarking", "frame-support", @@ -6124,7 +6145,7 @@ dependencies = [ [[package]] name = "pallet-conviction-voting" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "assert_matches", "frame-benchmarking", @@ -6141,7 +6162,7 @@ dependencies = [ [[package]] name = "pallet-democracy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-benchmarking", "frame-support", @@ -6159,7 +6180,7 @@ dependencies = [ [[package]] name = "pallet-election-provider-multi-phase" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -6182,7 +6203,7 @@ dependencies = [ [[package]] name = "pallet-election-provider-support-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -6195,7 +6216,7 @@ dependencies = [ [[package]] name = "pallet-elections-phragmen" version = "5.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-benchmarking", "frame-support", @@ -6214,7 +6235,7 @@ dependencies = [ [[package]] name = "pallet-fast-unstake" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "docify", "frame-benchmarking", @@ -6233,7 +6254,7 @@ dependencies = [ [[package]] name = "pallet-grandpa" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-benchmarking", "frame-support", @@ -6256,7 +6277,7 @@ dependencies = [ [[package]] name = "pallet-identity" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "enumflags2", "frame-benchmarking", @@ -6272,7 +6293,7 @@ dependencies = [ [[package]] name = "pallet-im-online" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-benchmarking", "frame-support", @@ -6292,7 +6313,7 @@ dependencies = [ [[package]] name = "pallet-indices" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-benchmarking", "frame-support", @@ -6309,7 +6330,7 @@ dependencies = [ [[package]] name = "pallet-membership" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-benchmarking", "frame-support", @@ -6326,7 +6347,7 @@ dependencies = [ [[package]] name = "pallet-message-queue" version = "7.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-benchmarking", "frame-support", @@ -6345,7 +6366,7 @@ dependencies = [ [[package]] name = "pallet-mmr" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-benchmarking", "frame-support", @@ -6362,7 +6383,7 @@ dependencies = [ [[package]] name = "pallet-multisig" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-benchmarking", "frame-support", @@ -6378,7 +6399,7 @@ dependencies = [ [[package]] name = "pallet-nis" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-benchmarking", "frame-support", @@ -6394,7 +6415,7 @@ dependencies = [ [[package]] name = "pallet-nomination-pools" version = "1.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-support", "frame-system", @@ -6413,7 +6434,7 @@ dependencies = [ [[package]] name = "pallet-nomination-pools-benchmarking" version = "1.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -6433,7 +6454,7 @@ dependencies = [ [[package]] name = "pallet-nomination-pools-runtime-api" version = "1.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "pallet-nomination-pools", "parity-scale-codec", @@ -6444,7 +6465,7 @@ dependencies = [ [[package]] name = "pallet-offences" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-support", "frame-system", @@ -6461,7 +6482,7 @@ dependencies = [ [[package]] name = "pallet-offences-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -6485,7 +6506,7 @@ dependencies = [ [[package]] name = "pallet-preimage" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-benchmarking", "frame-support", @@ -6502,7 +6523,7 @@ dependencies = [ [[package]] name = "pallet-proxy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-benchmarking", "frame-support", @@ -6517,7 +6538,7 @@ dependencies = [ [[package]] name = "pallet-ranked-collective" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-benchmarking", "frame-support", @@ -6535,7 +6556,7 @@ dependencies = [ [[package]] name = "pallet-recovery" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-benchmarking", "frame-support", @@ -6550,7 +6571,7 @@ dependencies = [ [[package]] name = "pallet-referenda" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "assert_matches", "frame-benchmarking", @@ -6569,7 +6590,7 @@ dependencies = [ [[package]] name = "pallet-salary" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-benchmarking", "frame-support", @@ -6587,7 +6608,7 @@ dependencies = [ [[package]] name = "pallet-scheduler" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "docify", "frame-benchmarking", @@ -6605,7 +6626,7 @@ dependencies = [ [[package]] name = "pallet-session" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-support", "frame-system", @@ -6626,7 +6647,7 @@ dependencies = [ [[package]] name = "pallet-session-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-benchmarking", "frame-support", @@ -6642,7 +6663,7 @@ dependencies = [ [[package]] name = "pallet-society" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-benchmarking", "frame-support", @@ -6660,7 +6681,7 @@ dependencies = [ [[package]] name = "pallet-staking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -6683,7 +6704,7 @@ dependencies = [ [[package]] name = "pallet-staking-reward-curve" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -6694,7 +6715,7 @@ dependencies = [ [[package]] name = "pallet-staking-reward-fn" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "log", "sp-arithmetic", @@ -6703,7 +6724,7 @@ dependencies = [ [[package]] name = "pallet-staking-runtime-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "parity-scale-codec", "sp-api", @@ -6712,7 +6733,7 @@ dependencies = [ [[package]] name = "pallet-state-trie-migration" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-benchmarking", "frame-support", @@ -6729,7 +6750,7 @@ dependencies = [ [[package]] name = "pallet-sudo" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-benchmarking", "frame-support", @@ -6744,7 +6765,7 @@ dependencies = [ [[package]] name = "pallet-timestamp" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-benchmarking", "frame-support", @@ -6762,7 +6783,7 @@ dependencies = [ [[package]] name = "pallet-tips" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-benchmarking", "frame-support", @@ -6781,7 +6802,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-support", "frame-system", @@ -6797,7 +6818,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "jsonrpsee", "pallet-transaction-payment-rpc-runtime-api", @@ -6813,7 +6834,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment-rpc-runtime-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "pallet-transaction-payment", "parity-scale-codec", @@ -6825,7 +6846,7 @@ dependencies = [ [[package]] name = "pallet-treasury" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-benchmarking", "frame-support", @@ -6842,7 +6863,7 @@ dependencies = [ [[package]] name = "pallet-uniques" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-benchmarking", "frame-support", @@ -6857,7 +6878,7 @@ dependencies = [ [[package]] name = "pallet-utility" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-benchmarking", "frame-support", @@ -6873,7 +6894,7 @@ dependencies = [ [[package]] name = "pallet-vesting" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-benchmarking", "frame-support", @@ -6888,7 +6909,7 @@ dependencies = [ [[package]] name = "pallet-whitelist" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-benchmarking", "frame-support", @@ -10084,7 +10105,7 @@ dependencies = [ [[package]] name = "sc-allocator" version = "4.1.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "log", "sp-core", @@ -10095,7 +10116,7 @@ dependencies = [ [[package]] name = "sc-authority-discovery" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "async-trait", "futures", @@ -10123,7 +10144,7 @@ dependencies = [ [[package]] name = "sc-basic-authorship" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "futures", "futures-timer", @@ -10146,7 +10167,7 @@ dependencies = [ [[package]] name = "sc-block-builder" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "parity-scale-codec", "sc-client-api", @@ -10161,7 +10182,7 @@ dependencies = [ [[package]] name = "sc-chain-spec" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "memmap2", "sc-chain-spec-derive", @@ -10180,7 +10201,7 @@ dependencies = [ [[package]] name = "sc-chain-spec-derive" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -10191,7 +10212,7 @@ dependencies = [ [[package]] name = "sc-cli" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "array-bytes", "chrono", @@ -10230,7 +10251,7 @@ dependencies = [ [[package]] name = "sc-client-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "fnv", "futures", @@ -10256,7 +10277,7 @@ dependencies = [ [[package]] name = "sc-client-db" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "hash-db", "kvdb", @@ -10282,7 +10303,7 @@ dependencies = [ [[package]] name = "sc-consensus" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "async-trait", "futures", @@ -10307,7 +10328,7 @@ dependencies = [ [[package]] name = "sc-consensus-babe" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "async-trait", "fork-tree", @@ -10343,7 +10364,7 @@ dependencies = [ [[package]] name = "sc-consensus-babe-rpc" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "futures", "jsonrpsee", @@ -10365,7 +10386,7 @@ dependencies = [ [[package]] name = "sc-consensus-beefy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "array-bytes", "async-channel", @@ -10399,7 +10420,7 @@ dependencies = [ [[package]] name = "sc-consensus-beefy-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "futures", "jsonrpsee", @@ -10418,7 +10439,7 @@ dependencies = [ [[package]] name = "sc-consensus-epochs" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "fork-tree", "parity-scale-codec", @@ -10431,7 +10452,7 @@ dependencies = [ [[package]] name = "sc-consensus-grandpa" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "ahash 0.8.3", "array-bytes", @@ -10472,7 +10493,7 @@ dependencies = [ [[package]] name = "sc-consensus-grandpa-rpc" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "finality-grandpa", "futures", @@ -10492,7 +10513,7 @@ dependencies = [ [[package]] name = "sc-consensus-slots" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "async-trait", "futures", @@ -10515,7 +10536,7 @@ dependencies = [ [[package]] name = "sc-executor" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "parity-scale-codec", "parking_lot 0.12.1", @@ -10537,7 +10558,7 @@ dependencies = [ [[package]] name = "sc-executor-common" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "sc-allocator", "sp-maybe-compressed-blob", @@ -10549,7 +10570,7 @@ dependencies = [ [[package]] name = "sc-executor-wasmtime" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "anyhow", "cfg-if", @@ -10566,7 +10587,7 @@ dependencies = [ [[package]] name = "sc-informant" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "ansi_term", "futures", @@ -10582,7 +10603,7 @@ dependencies = [ [[package]] name = "sc-keystore" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "array-bytes", "parking_lot 0.12.1", @@ -10596,7 +10617,7 @@ dependencies = [ [[package]] name = "sc-network" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "array-bytes", "async-channel", @@ -10637,7 +10658,7 @@ dependencies = [ [[package]] name = "sc-network-bitswap" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "async-channel", "cid", @@ -10657,7 +10678,7 @@ dependencies = [ [[package]] name = "sc-network-common" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "async-trait", "bitflags 1.3.2", @@ -10674,7 +10695,7 @@ dependencies = [ [[package]] name = "sc-network-gossip" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "ahash 0.8.3", "futures", @@ -10692,7 +10713,7 @@ dependencies = [ [[package]] name = "sc-network-light" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "array-bytes", "async-channel", @@ -10713,7 +10734,7 @@ dependencies = [ [[package]] name = "sc-network-sync" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "array-bytes", "async-channel", @@ -10747,7 +10768,7 @@ dependencies = [ [[package]] name = "sc-network-transactions" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "array-bytes", "futures", @@ -10765,7 +10786,7 @@ dependencies = [ [[package]] name = "sc-offchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "array-bytes", "bytes", @@ -10799,7 +10820,7 @@ dependencies = [ [[package]] name = "sc-proposer-metrics" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "log", "substrate-prometheus-endpoint", @@ -10808,7 +10829,7 @@ dependencies = [ [[package]] name = "sc-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "futures", "jsonrpsee", @@ -10839,7 +10860,7 @@ dependencies = [ [[package]] name = "sc-rpc-api" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "jsonrpsee", "parity-scale-codec", @@ -10858,7 +10879,7 @@ dependencies = [ [[package]] name = "sc-rpc-server" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "http", "jsonrpsee", @@ -10873,7 +10894,7 @@ dependencies = [ [[package]] name = "sc-rpc-spec-v2" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "array-bytes", "futures", @@ -10901,7 +10922,7 @@ dependencies = [ [[package]] name = "sc-service" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "async-trait", "directories", @@ -10965,7 +10986,7 @@ dependencies = [ [[package]] name = "sc-state-db" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "log", "parity-scale-codec", @@ -10976,7 +10997,7 @@ dependencies = [ [[package]] name = "sc-storage-monitor" version = "0.1.0" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "clap 4.3.19", "fs4", @@ -10990,7 +11011,7 @@ dependencies = [ [[package]] name = "sc-sync-state-rpc" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "jsonrpsee", "parity-scale-codec", @@ -11009,7 +11030,7 @@ dependencies = [ [[package]] name = "sc-sysinfo" version = "6.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "futures", "libc", @@ -11028,7 +11049,7 @@ dependencies = [ [[package]] name = "sc-telemetry" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "chrono", "futures", @@ -11047,7 +11068,7 @@ dependencies = [ [[package]] name = "sc-tracing" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "ansi_term", "atty", @@ -11076,7 +11097,7 @@ dependencies = [ [[package]] name = "sc-tracing-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -11087,7 +11108,7 @@ dependencies = [ [[package]] name = "sc-transaction-pool" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "async-trait", "futures", @@ -11113,7 +11134,7 @@ dependencies = [ [[package]] name = "sc-transaction-pool-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "async-trait", "futures", @@ -11129,7 +11150,7 @@ dependencies = [ [[package]] name = "sc-utils" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "async-channel", "futures", @@ -11658,14 +11679,14 @@ checksum = "5e9f0ab6ef7eb7353d9119c170a436d1bf248eea575ac42d19d12f4e34130831" [[package]] name = "snow" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ccba027ba85743e09d15c03296797cad56395089b832b48b5a5217880f57733" +checksum = "0c9d1425eb528a21de2755c75af4c9b5d57f50a0d4c3b7f1828a4cd03f8ba155" dependencies = [ "aes-gcm 0.9.4", "blake2", "chacha20poly1305", - "curve25519-dalek 4.0.0-rc.1", + "curve25519-dalek 4.0.0", "rand_core 0.6.4", "ring 0.16.20", "rustc_version", @@ -11713,7 +11734,7 @@ dependencies = [ [[package]] name = "sp-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "hash-db", "log", @@ -11734,7 +11755,7 @@ dependencies = [ [[package]] name = "sp-api-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "Inflector", "blake2", @@ -11748,7 +11769,7 @@ dependencies = [ [[package]] name = "sp-application-crypto" version = "23.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "parity-scale-codec", "scale-info", @@ -11761,7 +11782,7 @@ dependencies = [ [[package]] name = "sp-arithmetic" version = "16.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "integer-sqrt", "num-traits", @@ -11775,7 +11796,7 @@ dependencies = [ [[package]] name = "sp-authority-discovery" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "parity-scale-codec", "scale-info", @@ -11788,7 +11809,7 @@ dependencies = [ [[package]] name = "sp-block-builder" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "sp-api", "sp-inherents", @@ -11799,7 +11820,7 @@ dependencies = [ [[package]] name = "sp-blockchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "futures", "log", @@ -11817,7 +11838,7 @@ dependencies = [ [[package]] name = "sp-consensus" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "async-trait", "futures", @@ -11832,7 +11853,7 @@ dependencies = [ [[package]] name = "sp-consensus-aura" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "async-trait", "parity-scale-codec", @@ -11849,7 +11870,7 @@ dependencies = [ [[package]] name = "sp-consensus-babe" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "async-trait", "parity-scale-codec", @@ -11868,7 +11889,7 @@ dependencies = [ [[package]] name = "sp-consensus-beefy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "lazy_static", "parity-scale-codec", @@ -11887,7 +11908,7 @@ dependencies = [ [[package]] name = "sp-consensus-grandpa" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "finality-grandpa", "log", @@ -11905,7 +11926,7 @@ dependencies = [ [[package]] name = "sp-consensus-slots" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "parity-scale-codec", "scale-info", @@ -11917,7 +11938,7 @@ dependencies = [ [[package]] name = "sp-core" version = "21.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "array-bytes", "arrayvec 0.7.4", @@ -11964,7 +11985,7 @@ dependencies = [ [[package]] name = "sp-core-hashing" version = "9.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "blake2b_simd", "byteorder", @@ -11977,7 +11998,7 @@ dependencies = [ [[package]] name = "sp-core-hashing-proc-macro" version = "9.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "quote", "sp-core-hashing", @@ -11987,7 +12008,7 @@ dependencies = [ [[package]] name = "sp-database" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "kvdb", "parking_lot 0.12.1", @@ -11996,7 +12017,7 @@ dependencies = [ [[package]] name = "sp-debug-derive" version = "8.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "proc-macro2", "quote", @@ -12006,7 +12027,7 @@ dependencies = [ [[package]] name = "sp-externalities" version = "0.19.0" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "environmental", "parity-scale-codec", @@ -12017,7 +12038,7 @@ dependencies = [ [[package]] name = "sp-genesis-builder" version = "0.1.0" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "serde_json", "sp-api", @@ -12028,7 +12049,7 @@ dependencies = [ [[package]] name = "sp-inherents" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "async-trait", "impl-trait-for-tuples", @@ -12042,11 +12063,10 @@ dependencies = [ [[package]] name = "sp-io" version = "23.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "bytes", - "ed25519", - "ed25519-dalek", + "ed25519-dalek 2.0.0", "libsecp256k1", "log", "parity-scale-codec", @@ -12067,7 +12087,7 @@ dependencies = [ [[package]] name = "sp-keyring" version = "24.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "lazy_static", "sp-core", @@ -12078,7 +12098,7 @@ dependencies = [ [[package]] name = "sp-keystore" version = "0.27.0" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "parity-scale-codec", "parking_lot 0.12.1", @@ -12090,7 +12110,7 @@ dependencies = [ [[package]] name = "sp-maybe-compressed-blob" version = "4.1.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "thiserror", "zstd 0.12.4", @@ -12099,7 +12119,7 @@ dependencies = [ [[package]] name = "sp-metadata-ir" version = "0.1.0" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-metadata", "parity-scale-codec", @@ -12110,7 +12130,7 @@ dependencies = [ [[package]] name = "sp-mmr-primitives" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "ckb-merkle-mountain-range", "log", @@ -12128,7 +12148,7 @@ dependencies = [ [[package]] name = "sp-npos-elections" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "parity-scale-codec", "scale-info", @@ -12142,7 +12162,7 @@ dependencies = [ [[package]] name = "sp-offchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "sp-api", "sp-core", @@ -12152,7 +12172,7 @@ dependencies = [ [[package]] name = "sp-panic-handler" version = "8.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "backtrace", "lazy_static", @@ -12162,7 +12182,7 @@ dependencies = [ [[package]] name = "sp-rpc" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "rustc-hash", "serde", @@ -12172,7 +12192,7 @@ dependencies = [ [[package]] name = "sp-runtime" version = "24.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "either", "hash256-std-hasher", @@ -12194,7 +12214,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface" version = "17.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "bytes", "impl-trait-for-tuples", @@ -12212,7 +12232,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface-proc-macro" version = "11.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "Inflector", "proc-macro-crate", @@ -12224,7 +12244,7 @@ dependencies = [ [[package]] name = "sp-session" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "parity-scale-codec", "scale-info", @@ -12239,7 +12259,7 @@ dependencies = [ [[package]] name = "sp-staking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "impl-trait-for-tuples", "parity-scale-codec", @@ -12253,7 +12273,7 @@ dependencies = [ [[package]] name = "sp-state-machine" version = "0.28.0" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "hash-db", "log", @@ -12274,11 +12294,11 @@ dependencies = [ [[package]] name = "sp-statement-store" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "aes-gcm 0.10.2", - "curve25519-dalek 3.2.0", - "ed25519-dalek", + "curve25519-dalek 4.0.0", + "ed25519-dalek 2.0.0", "hkdf", "parity-scale-codec", "rand 0.8.5", @@ -12292,18 +12312,18 @@ dependencies = [ "sp-runtime-interface", "sp-std", "thiserror", - "x25519-dalek 2.0.0-pre.1", + "x25519-dalek 2.0.0", ] [[package]] name = "sp-std" version = "8.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" [[package]] name = "sp-storage" version = "13.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "impl-serde", "parity-scale-codec", @@ -12316,7 +12336,7 @@ dependencies = [ [[package]] name = "sp-timestamp" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "async-trait", "parity-scale-codec", @@ -12329,7 +12349,7 @@ dependencies = [ [[package]] name = "sp-tracing" version = "10.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "parity-scale-codec", "sp-std", @@ -12341,7 +12361,7 @@ dependencies = [ [[package]] name = "sp-transaction-pool" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "sp-api", "sp-runtime", @@ -12350,7 +12370,7 @@ dependencies = [ [[package]] name = "sp-transaction-storage-proof" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "async-trait", "parity-scale-codec", @@ -12365,7 +12385,7 @@ dependencies = [ [[package]] name = "sp-trie" version = "22.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "ahash 0.8.3", "hash-db", @@ -12388,7 +12408,7 @@ dependencies = [ [[package]] name = "sp-version" version = "22.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "impl-serde", "parity-scale-codec", @@ -12405,7 +12425,7 @@ dependencies = [ [[package]] name = "sp-version-proc-macro" version = "8.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "parity-scale-codec", "proc-macro2", @@ -12416,7 +12436,7 @@ dependencies = [ [[package]] name = "sp-wasm-interface" version = "14.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "anyhow", "impl-trait-for-tuples", @@ -12429,7 +12449,7 @@ dependencies = [ [[package]] name = "sp-weights" version = "20.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "parity-scale-codec", "scale-info", @@ -12670,12 +12690,12 @@ dependencies = [ [[package]] name = "substrate-build-script-utils" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" [[package]] name = "substrate-frame-rpc-system" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "frame-system-rpc-runtime-api", "futures", @@ -12694,7 +12714,7 @@ dependencies = [ [[package]] name = "substrate-prometheus-endpoint" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "hyper", "log", @@ -12706,7 +12726,7 @@ dependencies = [ [[package]] name = "substrate-rpc-client" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "async-trait", "jsonrpsee", @@ -12719,7 +12739,7 @@ dependencies = [ [[package]] name = "substrate-state-trie-migration-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "jsonrpsee", "parity-scale-codec", @@ -12736,7 +12756,7 @@ dependencies = [ [[package]] name = "substrate-test-client" version = "2.0.1" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "array-bytes", "async-trait", @@ -12762,7 +12782,7 @@ dependencies = [ [[package]] name = "substrate-test-utils" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "futures", "substrate-test-utils-derive", @@ -12772,7 +12792,7 @@ dependencies = [ [[package]] name = "substrate-test-utils-derive" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -12783,7 +12803,7 @@ dependencies = [ [[package]] name = "substrate-wasm-builder" version = "5.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "ansi_term", "build-helper", @@ -13691,7 +13711,7 @@ checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "try-runtime-cli" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#94be94be6d26becd2395b58ae09ca31f596afe7d" +source = "git+https://github.com/paritytech/substrate?branch=master#51695bb7009ea2e0996eb94ab4dfdc643a076702" dependencies = [ "async-trait", "clap 4.3.19", @@ -14513,7 +14533,7 @@ dependencies = [ "tokio", "webpki 0.21.4", "webrtc-util", - "x25519-dalek 2.0.0-pre.1", + "x25519-dalek 2.0.0", "x509-parser 0.13.2", ] @@ -15032,12 +15052,13 @@ dependencies = [ [[package]] name = "x25519-dalek" -version = "2.0.0-pre.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5da623d8af10a62342bcbbb230e33e58a63255a58012f8653c578e54bab48df" +checksum = "fb66477291e7e8d2b0ff1bcb900bf29489a9692816d79874bea351e7a8b6de96" dependencies = [ - "curve25519-dalek 3.2.0", + "curve25519-dalek 4.0.0", "rand_core 0.6.4", + "serde", "zeroize", ] From 0bbe0a7621500788dd841be7e4d869cfba5f0676 Mon Sep 17 00:00:00 2001 From: Marcin S Date: Mon, 21 Aug 2023 19:31:02 +0200 Subject: [PATCH 35/54] PVF worker: random fixes (#7649) * PVF worker: random fixes - Fixes possible panic due to non-UTF-8 env vars (https://github.com/paritytech/polkadot/pull/7330#discussion_r1300101716) - Very small refactor of some duplicated code * Don't need `to_str()` for comparison between OsString and str * Check edge cases that can cause env::remove_var to panic In case of a key or value that would cause env::remove_var to panic, we first log a warning and then proceed to attempt to remove the env var. * Make warning message clearer for end users * Backslash was unescaped, but can just remove it from error messages --- node/core/pvf/common/src/worker/mod.rs | 64 ++++++++++++++++++++------ 1 file changed, 51 insertions(+), 13 deletions(-) diff --git a/node/core/pvf/common/src/worker/mod.rs b/node/core/pvf/common/src/worker/mod.rs index d249007ec36e..8b41cb82f73b 100644 --- a/node/core/pvf/common/src/worker/mod.rs +++ b/node/core/pvf/common/src/worker/mod.rs @@ -121,22 +121,13 @@ pub fn worker_event_loop( "Node and worker version mismatch, node needs restarting, forcing shutdown", ); kill_parent_node_in_emergency(); - let err: io::Result = - Err(io::Error::new(io::ErrorKind::Unsupported, "Version mismatch")); - gum::debug!(target: LOG_TARGET, %worker_pid, "quitting pvf worker({}): {:?}", debug_id, err); + let err = io::Error::new(io::ErrorKind::Unsupported, "Version mismatch"); + worker_shutdown_message(debug_id, worker_pid, err); return } } - // Delete all env vars to prevent malicious code from accessing them. - for (key, _) in std::env::vars() { - // TODO: *theoretically* the value (or mere presence) of `RUST_LOG` can be a source of - // randomness for malicious code. In the future we can remove it also and log in the host; - // see . - if key != "RUST_LOG" { - std::env::remove_var(key); - } - } + remove_env_vars(debug_id); // Run the main worker loop. let rt = Runtime::new().expect("Creates tokio runtime. If this panics the worker will die and the host will detect that and deal with it."); @@ -152,7 +143,7 @@ pub fn worker_event_loop( // It's never `Ok` because it's `Ok(Never)`. .unwrap_err(); - gum::debug!(target: LOG_TARGET, %worker_pid, "quitting pvf worker ({}): {:?}", debug_id, err); + worker_shutdown_message(debug_id, worker_pid, err); // We don't want tokio to wait for the tasks to finish. We want to bring down the worker as fast // as possible and not wait for stalled validation to finish. This isn't strictly necessary now, @@ -160,6 +151,53 @@ pub fn worker_event_loop( rt.shutdown_background(); } +/// Delete all env vars to prevent malicious code from accessing them. +fn remove_env_vars(debug_id: &'static str) { + for (key, value) in std::env::vars_os() { + // TODO: *theoretically* the value (or mere presence) of `RUST_LOG` can be a source of + // randomness for malicious code. In the future we can remove it also and log in the host; + // see . + if key == "RUST_LOG" { + continue + } + + // In case of a key or value that would cause [`env::remove_var` to + // panic](https://doc.rust-lang.org/std/env/fn.remove_var.html#panics), we first log a + // warning and then proceed to attempt to remove the env var. + let mut err_reasons = vec![]; + let (key_str, value_str) = (key.to_str(), value.to_str()); + if key.is_empty() { + err_reasons.push("key is empty"); + } + if key_str.is_some_and(|s| s.contains('=')) { + err_reasons.push("key contains '='"); + } + if key_str.is_some_and(|s| s.contains('\0')) { + err_reasons.push("key contains null character"); + } + if value_str.is_some_and(|s| s.contains('\0')) { + err_reasons.push("value contains null character"); + } + if !err_reasons.is_empty() { + gum::warn!( + target: LOG_TARGET, + %debug_id, + ?key, + ?value, + "Attempting to remove badly-formatted env var, this may cause the PVF worker to crash. Please remove it yourself. Reasons: {:?}", + err_reasons + ); + } + + std::env::remove_var(key); + } +} + +/// Provide a consistent message on worker shutdown. +fn worker_shutdown_message(debug_id: &'static str, worker_pid: u32, err: io::Error) { + gum::debug!(target: LOG_TARGET, %worker_pid, "quitting pvf worker ({}): {:?}", debug_id, err); +} + /// Loop that runs in the CPU time monitor thread on prepare and execute jobs. Continuously wakes up /// and then either blocks for the remaining CPU time, or returns if we exceed the CPU timeout. /// From e39c00381e8d5e8aa6fe752fc5cf2fcba14c8599 Mon Sep 17 00:00:00 2001 From: Adrian Catangiu Date: Tue, 22 Aug 2023 12:08:54 +0300 Subject: [PATCH 36/54] Add BEEFY capabilities to Westend and Kusama (#7591) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * runtime: add BEEFY and MMR to Westend Signed-off-by: Adrian Catangiu * runtime: add BEEFY and MMR to Kusama Signed-off-by: Adrian Catangiu * node/service: enable BEEFY for Westend and Kusama Signed-off-by: Adrian Catangiu * node/service: regenerate genesis keys for westend-native and kusama-native Since these keys are only used for development/local chains, also publish the secret seeds used to generate the public keys, so that developers can recover/generate the private key pairs if needed. Signed-off-by: Adrian Catangiu * runtime: add session keys migration to add BEEFY to Westend and Kusama * runtime: fix migration * fix try-runtime build * cargo fmt * fix parachains slashing benchmark * address review comments * Apply suggestions from code review Co-authored-by: Bastian Köcher * runtime: fix session keys migration --------- Signed-off-by: Adrian Catangiu Co-authored-by: parity-processbot <> Co-authored-by: Bastian Köcher --- Cargo.lock | 10 + cli/src/command.rs | 5 +- node/service/src/chain_spec.rs | 339 ++++++++++-------- node/service/src/lib.rs | 18 +- runtime/kusama/Cargo.toml | 12 + runtime/kusama/src/lib.rs | 221 ++++++++++-- runtime/kusama/src/tests.rs | 2 +- .../src/disputes/slashing/benchmarking.rs | 11 +- runtime/polkadot/src/lib.rs | 7 +- runtime/rococo/src/lib.rs | 17 +- runtime/westend/Cargo.toml | 12 + runtime/westend/src/lib.rs | 222 ++++++++++-- runtime/westend/src/tests.rs | 7 +- scripts/prepare-test-net.sh | 2 +- 14 files changed, 638 insertions(+), 247 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0e72e6aabc57..5b19bc139384 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4292,6 +4292,7 @@ dependencies = [ name = "kusama-runtime" version = "0.9.43" dependencies = [ + "binary-merkle-tree", "bitvec", "frame-benchmarking", "frame-election-provider-support", @@ -4310,6 +4311,8 @@ dependencies = [ "pallet-babe", "pallet-bags-list", "pallet-balances", + "pallet-beefy", + "pallet-beefy-mmr", "pallet-bounties", "pallet-child-bounties", "pallet-collective", @@ -4325,6 +4328,7 @@ dependencies = [ "pallet-indices", "pallet-membership", "pallet-message-queue", + "pallet-mmr", "pallet-multisig", "pallet-nis", "pallet-nomination-pools", @@ -4366,6 +4370,7 @@ dependencies = [ "serde_json", "smallvec", "sp-api", + "sp-application-crypto", "sp-arithmetic", "sp-authority-discovery", "sp-block-builder", @@ -14653,6 +14658,7 @@ dependencies = [ name = "westend-runtime" version = "0.9.43" dependencies = [ + "binary-merkle-tree", "bitvec", "frame-benchmarking", "frame-election-provider-support", @@ -14670,6 +14676,8 @@ dependencies = [ "pallet-babe", "pallet-bags-list", "pallet-balances", + "pallet-beefy", + "pallet-beefy-mmr", "pallet-collective", "pallet-democracy", "pallet-election-provider-multi-phase", @@ -14682,6 +14690,7 @@ dependencies = [ "pallet-indices", "pallet-membership", "pallet-message-queue", + "pallet-mmr", "pallet-multisig", "pallet-nomination-pools", "pallet-nomination-pools-benchmarking", @@ -14720,6 +14729,7 @@ dependencies = [ "serde_json", "smallvec", "sp-api", + "sp-application-crypto", "sp-authority-discovery", "sp-block-builder", "sp-consensus-babe", diff --git a/cli/src/command.rs b/cli/src/command.rs index 0fbeafb99a07..6b36e6895b6e 100644 --- a/cli/src/command.rs +++ b/cli/src/command.rs @@ -240,9 +240,8 @@ where .map_err(Error::from)?; let chain_spec = &runner.config().chain_spec; - // By default, enable BEEFY on test networks. - let enable_beefy = (chain_spec.is_rococo() || chain_spec.is_wococo() || chain_spec.is_versi()) && - !cli.run.no_beefy; + // By default, enable BEEFY on all networks except Polkadot (for now). + let enable_beefy = !chain_spec.is_polkadot() && !cli.run.no_beefy; set_default_ss58_version(chain_spec); diff --git a/node/service/src/chain_spec.rs b/node/service/src/chain_spec.rs index 4d0d11ba436e..1e5aaa807b81 100644 --- a/node/service/src/chain_spec.rs +++ b/node/service/src/chain_spec.rs @@ -274,6 +274,7 @@ fn kusama_session_keys( para_validator: ValidatorId, para_assignment: AssignmentId, authority_discovery: AuthorityDiscoveryId, + beefy: BeefyId, ) -> kusama::SessionKeys { kusama::SessionKeys { babe, @@ -282,6 +283,7 @@ fn kusama_session_keys( para_validator, para_assignment, authority_discovery, + beefy, } } @@ -293,6 +295,7 @@ fn westend_session_keys( para_validator: ValidatorId, para_assignment: AssignmentId, authority_discovery: AuthorityDiscoveryId, + beefy: BeefyId, ) -> westend::SessionKeys { westend::SessionKeys { babe, @@ -301,6 +304,7 @@ fn westend_session_keys( para_validator, para_assignment, authority_discovery, + beefy, } } @@ -330,12 +334,16 @@ fn westend_staging_testnet_config_genesis(wasm_binary: &[u8]) -> westend::Runtim use hex_literal::hex; use sp_core::crypto::UncheckedInto; - // subkey inspect "$SECRET" + // Following keys are used in genesis config for development chains. + // DO NOT use them in production chains as the secret seed is public. + // + // SECRET_SEED="slow awkward present example safe bundle science ocean cradle word tennis earn" + // subkey inspect -n polkadot "$SECRET_SEED" let endowed_accounts = vec![ - // 5DaVh5WRfazkGaKhx1jUu6hjz7EmRe4dtW6PKeVLim84KLe8 - hex!["42f4a4b3e0a89c835ee696205caa90dd85c8ea1d7364b646328ee919a6b2fc1e"].into(), + // 15S75FkhCWEowEGfxWwVfrW3LQuy8w8PNhVmrzfsVhCMjUh1 + hex!["c416837e232d9603e83162ef4bda08e61580eeefe60fe92fc044aa508559ae42"].into(), ]; - // SECRET='...' ./scripts/prepare-test-net.sh 4 + // SECRET=$SECRET_SEED ./scripts/prepare-test-net.sh 4 let initial_authorities: Vec<( AccountId, AccountId, @@ -345,101 +353,114 @@ fn westend_staging_testnet_config_genesis(wasm_binary: &[u8]) -> westend::Runtim ValidatorId, AssignmentId, AuthorityDiscoveryId, + BeefyId, )> = vec![ ( - //5ERCqy118nnXDai8g4t3MjdX7ZC5PrQzQpe9vwex5cELWqbt - hex!["681af4f93073484e1acd6b27395d0d258f1a6b158c808846c8fd05ee2435056e"].into(), - //5GTS114cfQNBgpQULhMaNCPXGds6NokegCnikxDe1vqANhtn - hex!["c2463372598ebabd21ee5bc33e1d7e77f391d2df29ce2fbe6bed0d13be629a45"].into(), - //5FhGbceKeH7fuGogcBwd28ZCkAwDGYBADCTeHiYrvx2ztyRd - hex!["a097bfc6a33499ed843b711f52f523f8a7174f798a9f98620e52f4170dbe2948"] + //5EvydUTtHvt39Khac3mMxNPgzcfu49uPDzUs3TL7KEzyrwbw + hex!["7ecfd50629cdd246649959d88d490b31508db511487e111a52a392e6e458f518"].into(), + //5HQyX5gyy77m9QLXguAhiwjTArHYjYspeY98dYDu1JDetfZg + hex!["eca2cca09bdc66a7e6d8c3d9499a0be2ad4690061be8a9834972e17d13d2fe7e"].into(), + //5G13qYRudTyttwTJvHvnwp8StFtcfigyPnwfD4v7LNopsnX4 + hex!["ae27367cb77850fb195fe1f9c60b73210409e68c5ad953088070f7d8513d464c"] + .unchecked_into(), + //5Eb7wM65PNgtY6e33FEAzYtU5cRTXt6WQvZTnzaKQwkVcABk + hex!["6faae44b21c6f2681a7f60df708e9f79d340f7d441d28bd987fab8d05c6487e8"] .unchecked_into(), - //5Es7nDkJt2by5qVCCD7PZJdp76KJw1LdRCiNst5S5f4eecnz - hex!["7bde49dda82c2c9f082b807ef3ceebff96437d67b3e630c584db7a220ecafacf"] + //5CdS2wGo4qdTQceVfEnbZH8vULeBrnGYCxSCxDna4tQSMV6y + hex!["18f5d55f138bfa8e0ea26ed6fa56817b247de3c2e2030a908c63fb37c146473f"] .unchecked_into(), - //5D4e8zRjaYzFamqChGPPtu26PcKbKgUrhb7WqcNbKa2RDFUR - hex!["2c2fb730a7d9138e6d62fcf516f9ecc2d712af3f2f03ca330c9564b8c0c1bb33"] + //5FqMLAgygdX9UqzukDp15Uid9PAKdFAR621U7xtp5ut2NfrW + hex!["a6c1a5b501985a83cb1c37630c5b41e6b0a15b3675b2fd94694758e6cfa6794d"] .unchecked_into(), - //5DD3JY5ENkjcgVFbVSgUbZv7WmrnyJ8bxxu56ee6hZFiRdnh - hex!["3297a8622988cc23dd9c131e3fb8746d49e007f6e58a81d43420cd539e250e4c"] + //5DhXAV75BKvF9o447ikWqLttyL2wHtLMFSX7GrsKF9Ny61Ta + hex!["485051748ab9c15732f19f3fbcf1fd00a6d9709635f084505107fbb059c33d2f"] .unchecked_into(), - //5Gpodowhud8FG9xENXR5YwTFbUAWyoEtw7sYFytFsG4z7SU6 - hex!["d2932edf775088bd088dc5a112ad867c24cc95858f77f8a1ab014de8d4f96a3f"] + //5GNHfmrtWLTawnGCmc39rjAEiW97vKvE7DGePYe4am5JtE4i + hex!["be59ed75a72f7b47221ce081ba4262cf2e1ea7867e30e0b3781822f942b97677"] .unchecked_into(), - //5GUMj8tnjL3PJZgXoiWtgLCaMVNHBNeSeTqDsvcxmaVAjKn9 - hex!["c2fb0f74591a00555a292bc4882d3158bafc4c632124cb60681f164ef81bcf72"] + //5DA6Z8RUF626stn94aTRBCeobDCYcFbU7Pdk4Tz1R9vA8B8F + hex!["0207e43990799e1d02b0507451e342a1240ff836ea769c57297589a5fd072ad8f4"] .unchecked_into(), ), ( - //5HgDCznTkHKUjzPkQoTZGWbvbyqB7sqHDBPDKdF1FyVYM7Er - hex!["f8418f189f84814fd40cc1b2e90873e72ea789487f3b98ed42811ba76d10fc37"].into(), - //5GQTryeFwuvgmZ2tH5ZeAKZHRM9ch5WGVGo6ND9P8f9uMsNY - hex!["c002bb4af4a1bd2f33d104aef8a41878fe1ac94ba007029c4dfdefa8b698d043"].into(), - //5C7YkWSVH1zrpsE5KwW1ua1qatyphzYxiZrL24mjkxz7mUbn - hex!["022b14fbcf65a93b81f453105b9892c3fc4aa74c22c53b4abab019e1d58fbd41"] + //5DFpvDUdCgw54E3E357GR1PyJe3Ft9s7Qyp7wbELAoJH9RQa + hex!["34b7b3efd35fcc3c1926ca065381682b1af29b57dabbcd091042c6de1d541b7d"].into(), + //5DZSSsND5wCjngvyXv27qvF3yPzt3MCU8rWnqNy4imqZmjT8 + hex!["4226796fa792ac78875e023ff2e30e3c2cf79f0b7b3431254cd0f14a3007bc0e"].into(), + //5CPrgfRNDQvQSnLRdeCphP3ibj5PJW9ESbqj2fw29vBMNQNn + hex!["0e9b60f04be3bffe362eb2212ea99d2b909b052f4bff7c714e13c2416a797f5d"] + .unchecked_into(), + //5FXFsPReTUEYPRNKhbTdUathcWBsxTNsLbk2mTpYdKCJewjA + hex!["98f4d81cb383898c2c3d54dab28698c0f717c81b509cb32dc6905af3cc697b18"] .unchecked_into(), - //5GwFC6Tmg4fhj4PxSqHycgJxi3PDfnC9RGDsNHoRwAvXvpnZ - hex!["d77cafd3b32c8b52b0e2780a586a6e527c94f1bdec117c4e4acb0a491461ffa3"] + //5CDYSCJK91r8y2r1V4Ddrit4PFMEkwZXJe8mNBqGXJ4xWCWq + hex!["06bd7dd4ab4c808c7d09d9cb6bd27fbcd99ad8400e99212b335056c475c24031"] .unchecked_into(), - //5DSVrGURuDuh8Luzo8FYq7o2NWiUSLSN6QAVNrj9BtswWH6R - hex!["3cdb36a5a14715999faffd06c5b9e5dcdc24d4b46bc3e4df1aaad266112a7b49"] + //5CZjurB78XbSHf6SLkLhCdkqw52Zm7aBYUDdfkLqEDWJ9Zhj + hex!["162508accd470e379b04cb0c7c60b35a7d5357e84407a89ed2dd48db4b726960"] .unchecked_into(), - //5DLEG2AupawCXGwhJtrzBRc3zAhuP8V662dDrUTzAsCiB9Ec - hex!["38134245c9919ecb20bf2eedbe943b69ba92ceb9eb5477b92b0afd3cb6ce2858"] + //5DkAqCtSjUMVoJFauuGoAbSEgn2aFCRGziKJiLGpPwYgE1pS + hex!["4a559c028b69a7f784ce553393e547bec0aa530352157603396d515f9c83463b"] .unchecked_into(), - //5D83o9fDgnHxaKPkSx59hk8zYzqcgzN2mrf7cp8fiVEi7V4E - hex!["2ec917690dc1d676002e3504c530b2595490aa5a4603d9cc579b9485b8d0d854"] + //5GsBt9MhGwkg8Jfb1F9LAy2kcr88WNyNy4L5ezwbCr8NWKQU + hex!["d464908266c878acbf181bf8fda398b3aa3fd2d05508013e414aaece4cf0d702"] .unchecked_into(), - //5DwBJquZgncRWXFxj2ydbF8LBUPPUbiq86sXWXgm8Z38m8L2 - hex!["52bae9b8dedb8058dda93ec6f57d7e5a517c4c9f002a4636fada70fed0acf376"] + //5DtJVkz8AHevEnpszy3X4dUcPvACW6x1qBMQZtFxjexLr5bq + hex!["02fdf30222d2cb88f2376d558d3de9cb83f9fde3aa4b2dd40c93e3104e3488bcd2"] .unchecked_into(), ), ( - //5DMHpkRpQV7NWJFfn2zQxCLiAKv7R12PWFRPHKKk5X3JkYfP - hex!["38e280b35d08db46019a210a944e4b7177665232ab679df12d6a8bbb317a2276"].into(), - //5FbJpSHmFDe5FN3DVGe1R345ZePL9nhcC9V2Cczxo7q8q6rN - hex!["9c0bc0e2469924d718ae683737f818a47c46b0612376ecca06a2ac059fe1f870"].into(), - //5E5Pm3Udzxy26KGkLE5pc8JPfQrvkYHiaXWtuEfmQsBSgep9 - hex!["58fecadc2df8182a27e999e7e1fd7c99f8ec18f2a81f9a0db38b3653613f3f4d"] + //5E2cob2jrXsBkTih56pizwSqENjE4siaVdXhaD6akLdDyVq7 + hex!["56e0f73c563d49ee4a3971c393e17c44eaa313dabad7fcf297dc3271d803f303"].into(), + //5D4rNYgP9uFNi5GMyDEXTfiaFLjXyDEEX2VvuqBVi3f1qgCh + hex!["2c58e5e1d5aef77774480cead4f6876b1a1a6261170166995184d7f86140572b"].into(), + //5Ea2D65KXqe625sz4uV1jjhSfuigVnkezC8VgEj9LXN7ERAk + hex!["6ed45cb7af613be5d88a2622921e18d147225165f24538af03b93f2a03ce6e13"] .unchecked_into(), - //5FxcystSLHtaWoy2HEgRNerj9PrUs452B6AvHVnQZm5ZQmqE - hex!["ac4d0c5e8f8486de05135c10a707f58aa29126d5eb28fdaaba00f9a505f5249d"] + //5G4kCbgqUhEyrRHCyFwFEkgBZXoYA8sbgsRxT9rY8Tp5Jj5F + hex!["b0f8d2b9e4e1eafd4dab6358e0b9d5380d78af27c094e69ae9d6d30ca300fd86"] .unchecked_into(), - //5E7KqVXaVGuAqiqMigpuH8oXHLVh4tmijmpJABLYANpjMkem - hex!["5a781385a0235fe8594dd101ec55ef9ba01883f8563a0cdd37b89e0303f6a578"] + //5HVhFBLFTKSZK9fX6RktckWDTgYNoSd33fgonsEC8zfr4ddm + hex!["f03c3e184b2883eec9beaeb97f54321587e7476b228831ea0b5fc6da847ea975"] .unchecked_into(), - //5H9AybjkpyZ79yN5nHuBqs6RKuZPgM7aAVVvTQsDFovgXb2A - hex!["e09570f62a062450d4406b4eb43e7f775ff954e37606646cd590d1818189501f"] + //5CS7thd2n54WfqeKU3cjvZzK4z5p7zku1Zw97mSzXgPioAAs + hex!["1055100a283968271a0781450b389b9093231be809be1e48a305ebad2a90497e"] .unchecked_into(), - //5Ccgs7VwJKBawMbwMENDmj2eFAxhFdGksVHdk8aTAf4w7xox - hex!["1864832dae34df30846d5cc65973f58a2d01b337d094b1284ec3466ecc90251d"] + //5DSaL4ZmSYarZSazhL5NQh7LT6pWhNRDcefk2QS9RxEXfsJe + hex!["3cea4ab74bab4adf176cf05a6e18c1599a7bc217d4c6c217275bfbe3b037a527"] .unchecked_into(), - //5EsSaZZ7niJs7hmAtp4QeK19AcAuTp7WXB7N7gRipVooerq4 - hex!["7c1d92535e6d94e21cffea6633a855a7e3c9684cd2f209e5ddbdeaf5111e395b"] + //5CaNLkYEbFYXZodXhd3UjV6RNLjFGNLiYafc8X5NooMkZiAq + hex!["169faa81aebfe74533518bda28567f2e2664014c8905aa07ea003336afda5a58"] + .unchecked_into(), + //5ERwhKiePayukzZStMuzGzRJGxGRFpwxYUXVarQpMSMrXzDS + hex!["03429d0d20f6ac5ca8b349f04d014f7b5b864acf382a744104d5d9a51108156c0f"] .unchecked_into(), ), ( - //5Ea11qhmGRntQ7pyEkEydbwxvfrYwGMKW6rPERU4UiSBB6rd - hex!["6ed057d2c833c45629de2f14b9f6ce6df1edbf9421b7a638e1fb4828c2bd2651"].into(), - //5CZomCZwPB78BZMZsCiy7WSpkpHhdrN8QTSyjcK3FFEZHBor - hex!["1631ff446b3534d031adfc37b7f7aed26d2a6b3938d10496aab3345c54707429"].into(), - //5CSM6vppouFHzAVPkVFWN76DPRUG7B9qwJe892ccfSfJ8M5f - hex!["108188c43a7521e1abe737b343341c2179a3a89626c7b017c09a5b10df6f1c42"] + //5H6j9ovzYk9opckVjvM9SvVfaK37ASTtPTzWeRfqk1tgLJUN + hex!["deb804ed2ed2bb696a3dd4ed7de4cd5c496528a2b204051c6ace385bacd66a3a"].into(), + //5DJ51tMW916mGwjMpfS1o9skcNt6Sb28YnZQXaKVg4h89agE + hex!["366da6a748afedb31f07902f2de36ab265beccee37762d3ae1f237de234d9c36"].into(), + //5CSPYDYoCDGSoSLgSp4EHkJ52YasZLHG2woqhPZkdbtNQpke + hex!["1089bc0cd60237d061872925e81d36c9d9205d250d5d8b542c8e08a8ecf1b911"] + .unchecked_into(), + //5ChfdrAqmLjCeDJvynbMjcxYLHYzPe8UWXd3HnX9JDThUMbn + hex!["1c309a70b4e274314b84c9a0a1f973c9c4fc084df5479ef686c54b1ae4950424"] .unchecked_into(), - //5GwkG4std9KcjYi3ThSC7QWfhqokmYVvWEqTU9h7iswjhLnr - hex!["d7de8a43f7ee49fa3b3aaf32fb12617ec9ff7b246a46ab14e9c9d259261117fa"] + //5DnsMm24575xK2b2aGfmafiDxwCet6Mr4iiZQeDdWvi8CzuF + hex!["4c64868ba6d8ace235d3efb4c10d745a67cf3bdfeae23b264d7ea2f3439dec42"] .unchecked_into(), - //5CoUk3wrCGJAWbiJEcsVjYhnd2JAHvR59jBRbSw77YrBtRL1 - hex!["209f680bc501f9b59358efe3636c51fd61238a8659bac146db909aea2595284b"] + //5D8C3HHEp5E8fJsXRD56494F413CdRSR9QKGXe7v5ZEfymdj + hex!["2ee4d78f328db178c54f205ac809da12e291a33bcbd4f29f081ce7e74bdc5044"] .unchecked_into(), - //5EcSu96wprFM7G2HfJTjYu8kMParnYGznSUNTsoEKXywEsgG - hex!["70adf80395b3f59e4cab5d9da66d5a286a0b6e138652a06f72542e46912df922"] + //5GxeTYCGmp1C3ZRLDkRWqJc6gB2GYmuqnygweuH3vsivMQq6 + hex!["d88e40e3c2c7a7c5abf96ffdd8f7b7bec8798cc277bc97e255881871ab73b529"] .unchecked_into(), - //5Ge3sjpD43Cuy7rNoJQmE9WctgCn6Faw89Pe7xPs3i55eHwJ - hex!["ca5f6b970b373b303f64801a0c2cadc4fc05272c6047a2560a27d0c65589ca1d"] + //5DoGpsgSLcJsHa9B8V4PKjxegWAqDZttWfxicAd68prUX654 + hex!["4cb3863271b70daa38612acd5dae4f5afcb7c165fa277629e5150d2214df322a"] .unchecked_into(), - //5EFcjHLvB2z5vd5g63n4gABmhzP5iPsKvTwd8sjfvTehNNrk - hex!["60cae7fa5a079d9fc8061d715fbcc35ef57c3b00005694c2badce22dcc5a9f1b"] + //5G1KLjqFyMsPAodnjSRkwRFJztTTEzmZWxow2Q3ZSRCPdthM + hex!["03be5ec86d10a94db89c9b7a396d3c7742e3bec5f85159d4cf308cef505966ddf5"] .unchecked_into(), ), ]; @@ -456,6 +477,7 @@ fn westend_staging_testnet_config_genesis(wasm_binary: &[u8]) -> westend::Runtim .chain(initial_authorities.iter().map(|x| (x.0.clone(), STASH))) .collect(), }, + beefy: Default::default(), indices: westend::IndicesConfig { indices: vec![] }, session: westend::SessionConfig { keys: initial_authorities @@ -471,6 +493,7 @@ fn westend_staging_testnet_config_genesis(wasm_binary: &[u8]) -> westend::Runtim x.5.clone(), x.6.clone(), x.7.clone(), + x.8.clone(), ), ) }) @@ -521,18 +544,17 @@ fn kusama_staging_testnet_config_genesis(wasm_binary: &[u8]) -> kusama::RuntimeG use hex_literal::hex; use sp_core::crypto::UncheckedInto; - // subkey inspect "$SECRET" + // Following keys are used in genesis config for development chains. + // DO NOT use them in production chains as the secret seed is public. + // + // SECRET_SEED="explain impose opinion genius bar parrot erupt panther surround best expire + // album" subkey inspect -n kusama "$SECRET_SEED" let endowed_accounts = vec![ - // 5CVFESwfkk7NmhQ6FwHCM9roBvr9BGa4vJHFYU8DnGQxrXvz - hex!["12b782529c22032ed4694e0f6e7d486be7daa6d12088f6bc74d593b3900b8438"].into(), + // FLN5cfhF7VCGJYefjPQJR2V6WwbfRmb9ozTwLAzBNeQQG6y + hex!["7a0fe424217ed176da7abf12e08198db0d0949298e1372c80a1930cb6dc21d3e"].into(), ]; - // for i in 1 2 3 4; do for j in stash controller; do subkey inspect "$SECRET//$i//$j"; done; - // done for i in 1 2 3 4; do for j in babe; do subkey --sr25519 inspect "$SECRET//$i//$j"; done; - // done for i in 1 2 3 4; do for j in grandpa; do subkey --ed25519 inspect "$SECRET//$i//$j"; - // done; done for i in 1 2 3 4; do for j in im_online; do subkey --sr25519 inspect - // "$SECRET//$i//$j"; done; done for i in 1 2 3 4; do for j in para_validator para_assignment; - // do subkey --sr25519 inspect "$SECRET//$i//$j"; done; done + // SECRET=$SECRET_SEED ./scripts/prepare-test-net.sh 4 let initial_authorities: Vec<( AccountId, AccountId, @@ -542,101 +564,114 @@ fn kusama_staging_testnet_config_genesis(wasm_binary: &[u8]) -> kusama::RuntimeG ValidatorId, AssignmentId, AuthorityDiscoveryId, + BeefyId, )> = vec![ ( - // 5DD7Q4VEfPTLEdn11CnThoHT5f9xKCrnofWJL5SsvpTghaAT - hex!["32a5718e87d16071756d4b1370c411bbbb947eb62f0e6e0b937d5cbfc0ea633b"].into(), - // 5GNzaEqhrZAtUQhbMe2gn9jBuNWfamWFZHULryFwBUXyd1cG - hex!["bee39fe862c85c91aaf343e130d30b643c6ea0b4406a980206f1df8331f7093b"].into(), - // 5FpewyS2VY8Cj3tKgSckq8ECkjd1HKHvBRnWhiHqRQsWfFC1 - hex!["a639b507ee1585e0b6498ff141d6153960794523226866d1b44eba3f25f36356"] + //5D5EsvSJf3KR3WHeZNG8rETdW6homig1cGHezspFt1P4o7sL + hex!["2ca4a9582244a3356a0d96e59d71f7e4d12aa88bca6d46f360ef11f6487cab1f"].into(), + //5Ev6RixvmK62UQE2PW19MPdLsYT4Nomwj85HKPdbnRECbDYh + hex!["7e237806f642b7f45f70ec45fbc41034516c8e5561bae2a62cd287129e1d0712"].into(), + //5GbjzK1uYVo6v1SaYhTeK3dbYy2GN9X4K5iwRkHEQ9eLS3We + hex!["c89cb7afc47ec0b5aac5824e5338a62959c92978167d3f841491836746e70b3d"] + .unchecked_into(), + //5GFz3YFW8QzEUsWhRjJzvDP7e5X5tPf5U12vUw32R8oJVgqb + hex!["b98b200021a608148f9817aeb553596b6968a5aa61b6d320c522f520ecc9cf9c"] .unchecked_into(), - // 5EjvdwATjyFFikdZibVvx1q5uBHhphS2Mnsq5c7yfaYK25vm - hex!["76620f7c98bce8619979c2b58cf2b0aff71824126d2b039358729dad993223db"] + //5GzaFD8YsqnP5FYe5ijA9M4LQvzU9TPJmnBGdpuoqEvR1gQC + hex!["da0690438c0dd7a9aa26e03c9f1deaa58ba2b88d0bec0954b06478632164a401"] .unchecked_into(), - // 5FpewyS2VY8Cj3tKgSckq8ECkjd1HKHvBRnWhiHqRQsWfFC1 - hex!["a639b507ee1585e0b6498ff141d6153960794523226866d1b44eba3f25f36356"] + //5CkZPtNy61PtbJpLqnjNFmbi1qukGkFdqFr5GKduSEthJ1cd + hex!["1e6554d35f6f17a37176c71801426204d6df400a1869114e4f00564b35d31150"] .unchecked_into(), - // 5FpewyS2VY8Cj3tKgSckq8ECkjd1HKHvBRnWhiHqRQsWfFC1 - hex!["a639b507ee1585e0b6498ff141d6153960794523226866d1b44eba3f25f36356"] + //5CodnwweaYA1zB4QhdP4YVYFWnuZHY6W7zkN1NCRqJ9wZhap + hex!["20bddf09b1d0a2d93bafeb87fe19eb5bd59950c174f23a141a6d99736a5e700d"] .unchecked_into(), - // 5FpewyS2VY8Cj3tKgSckq8ECkjd1HKHvBRnWhiHqRQsWfFC1 - hex!["a639b507ee1585e0b6498ff141d6153960794523226866d1b44eba3f25f36356"] + //5E7TSvNAP6QeJNeckdvYvADpHsx7v6aHXtGoQv5R2N1V3hEB + hex!["5a91b2546f1aac1c388eb0739c83e42d9972884d74360200ce32b7595bc65a04"] .unchecked_into(), - // 5FpewyS2VY8Cj3tKgSckq8ECkjd1HKHvBRnWhiHqRQsWfFC1 - hex!["a639b507ee1585e0b6498ff141d6153960794523226866d1b44eba3f25f36356"] + //5GsoKeoM2HmjXPsdCua4oPu3Ms1Jgu4HbSnB81Lisa2tBFZp + hex!["02fd1e7e8455ab888ad054bbec7bc19409e6b1a5bb0300feefc6b58e60efae7e85"] .unchecked_into(), ), ( - // 5G9VGb8ESBeS8Ca4or43RfhShzk9y7T5iTmxHk5RJsjZwsRx - hex!["b496c98a405ceab59b9e970e59ef61acd7765a19b704e02ab06c1cdfe171e40f"].into(), - // 5F7V9Y5FcxKXe1aroqvPeRiUmmeQwTFcL3u9rrPXcMuMiCNx - hex!["86d3a7571dd60139d297e55d8238d0c977b2e208c5af088f7f0136b565b0c103"].into(), - // 5GvuM53k1Z4nAB5zXJFgkRSHv4Bqo4BsvgbQWNWkiWZTMwWY - hex!["765e46067adac4d1fe6c783aa2070dfa64a19f84376659e12705d1734b3eae01"] + //5HMtKQuL2GQ7YvLBTh3vqFJEpkZW19sQh2X2mcUzAwBAe885 + hex!["ea478deab0ebfbeab7342febc236a9f1af5129ca0083fa25e6b0cf6a998d8354"].into(), + //5EFD5pLC3w5NFEcmQ6rGw9dUZ6fTSjWJemsvJZuaj7Qmq2WT + hex!["607b4e88129804eca8cd6fa26cbe2dd36667130e2a061050b08d9015871f4263"].into(), + //5DFztsnvC9hN85j5AP116atcnzFhAxnbzPodEp1AsYq1LYXu + hex!["34d949c39fae5801ba328ac6d0ddc76e469b7d5a4372a4a0d94f6aad6f9c1600"] .unchecked_into(), - // 5HBDAaybNqjmY7ww8ZcZZY1L5LHxvpnyfqJwoB7HhR6raTmG - hex!["e2234d661bee4a04c38392c75d1566200aa9e6ae44dd98ee8765e4cc9af63cb7"] + //5EZJNJ4j1eEEwCWusg7nYsZxTYBwoTH2drszxRqgMBTgNxMW + hex!["6e47830dcfc1f2b53a1b5db3f76702fc2760c1cc119119aceb00a57ec6658465"] .unchecked_into(), - // 5GvuM53k1Z4nAB5zXJFgkRSHv4Bqo4BsvgbQWNWkiWZTMwWY - hex!["765e46067adac4d1fe6c783aa2070dfa64a19f84376659e12705d1734b3eae01"] + //5Dts3SrgDQMY9XCzKeQrxYSTh5MphPek994qkDCDk5c4neeF + hex!["50f6ef6326cd61ac500f167493e435f1204ce1d66ad18024bc5810d09673785e"] .unchecked_into(), - // 5GvuM53k1Z4nAB5zXJFgkRSHv4Bqo4BsvgbQWNWkiWZTMwWY - hex!["765e46067adac4d1fe6c783aa2070dfa64a19f84376659e12705d1734b3eae01"] + //5DMKT99825TvA8F1yCQvE1ZcKTqg8T8Ad1KEjN6EuVpz4E6w + hex!["38e7fb2f6a1dcec73d93b07a0dc7cff1f9a9cc32cde8eb1e6ea1782f5316b431"] .unchecked_into(), - // 5GvuM53k1Z4nAB5zXJFgkRSHv4Bqo4BsvgbQWNWkiWZTMwWY - hex!["765e46067adac4d1fe6c783aa2070dfa64a19f84376659e12705d1734b3eae01"] + //5EestuSehdMsWsBZ1hXCVo5YQiYiTPJwtV281x5fjUVtaqtP + hex!["72889a7b6ada28c3bd05a5a7298437f01d6d3270559768d16275efaf11864c0a"] .unchecked_into(), - // 5GvuM53k1Z4nAB5zXJFgkRSHv4Bqo4BsvgbQWNWkiWZTMwWY - hex!["765e46067adac4d1fe6c783aa2070dfa64a19f84376659e12705d1734b3eae01"] + //5FNd5EabUbcReXEPwY9aASJMwSqyiic9w1Qt23YxNXj3dzbi + hex!["925f03f6211c68377987b0f78cd02aa882ad1fa9cc00c01fe6ce68e14c23340d"] + .unchecked_into(), + //5DxhuqfovpooTn8yH7WJGFjYw3pQxSEN9y9kvYUiGguHAj9D + hex!["030e77039e470ccdec7fe23dbc41c66f1c187ec8345e8919d3dc1250d975c3ce82"] .unchecked_into(), ), ( - // 5FzwpgGvk2kk9agow6KsywLYcPzjYc8suKej2bne5G5b9YU3 - hex!["ae12f70078a22882bf5135d134468f77301927aa67c376e8c55b7ff127ace115"].into(), - // 5EqoZhVC2BcsM4WjvZNidu2muKAbu5THQTBKe3EjvxXkdP7A - hex!["7addb914ec8486bbc60643d2647685dcc06373401fa80e09813b630c5831d54b"].into(), - // 5CXNq1mSKJT4Sc2CbyBBdANeSkbUvdWvE4czJjKXfBHi9sX5 - hex!["664eae1ca4713dd6abf8c15e6c041820cda3c60df97dc476c2cbf7cb82cb2d2e"] + //5DAiYTKQ5KxwLncfNoTAH58dXBk2oDcQxtAXyDwMdKGLpGeY + hex!["30d203d942c1d056245b51e466a50b684f172a37c1cdde678f5346a0b3dbcd52"].into(), + //5Dq778qqNiAsjdF4qLVdkSBR8SftJKU35nyeBnkztRgniVhV + hex!["4e194bbafeec45647b2679e6b615b2a879d2e74fe706921930509ab3c9dbb22d"].into(), + //5E6iENoE1tXJUd7PkopQ8uqejg6xhPpqAnsVjS3hAQHWK1tm + hex!["5a0037b6bfc5e879ba5ef480ac29c59a12873854159686899082f41950ffd472"] + .unchecked_into(), + //5F8Dtgoc5dCaLAGYtaDqQUDg91fPQUynd497Fvhor8SYMdXp + hex!["87638aef8ab75db093150a6677c0919292ff66fc17f9f006a71fd0618415e164"] .unchecked_into(), - // 5E8ULLQrDAtWhfnVfZmX41Yux86zNAwVJYguWJZVWrJvdhBe - hex!["5b57ed1443c8967f461db1f6eb2ada24794d163a668f1cf9d9ce3235dfad8799"] + //5EKsYx6Wj1Qg7LLc12U2YRjRUFmHa4Q3rNSoGZaP1ofS54km + hex!["6409c85a1125fa456b9dc6e85408a6d931aa8e04f48511c87fc147d1c103e902"] .unchecked_into(), - // 5CXNq1mSKJT4Sc2CbyBBdANeSkbUvdWvE4czJjKXfBHi9sX5 - hex!["664eae1ca4713dd6abf8c15e6c041820cda3c60df97dc476c2cbf7cb82cb2d2e"] + //5H3UQy1NhCUUq3getmSEG8R1capY7Uy8JtKJz68UABmD9UxS + hex!["dc3cab0f94fa974cba826984f23dd4dc77ade20f25d935af5f07b85518da8044"] .unchecked_into(), - // 5CXNq1mSKJT4Sc2CbyBBdANeSkbUvdWvE4czJjKXfBHi9sX5 - hex!["664eae1ca4713dd6abf8c15e6c041820cda3c60df97dc476c2cbf7cb82cb2d2e"] + //5DstCjokShCt9NppNnAcjg2nS4M5PKY3etn2BoFkZzMhQJ3w + hex!["50379866eb62e5c8aac31133efc4a1723e964a8e30c93c3ce2e7758bd03eb776"] .unchecked_into(), - // 5CXNq1mSKJT4Sc2CbyBBdANeSkbUvdWvE4czJjKXfBHi9sX5 - hex!["664eae1ca4713dd6abf8c15e6c041820cda3c60df97dc476c2cbf7cb82cb2d2e"] + //5E4SCbSqUWKC4NVRCkMkJEnXCaVRiNQbSHL4upRB1ffd1Mk1 + hex!["5843c339c39d2c308bfb1841cd10beecfa157580492db05b66db8553e8d6512c"] .unchecked_into(), - // 5CXNq1mSKJT4Sc2CbyBBdANeSkbUvdWvE4czJjKXfBHi9sX5 - hex!["664eae1ca4713dd6abf8c15e6c041820cda3c60df97dc476c2cbf7cb82cb2d2e"] + //5HNoMQ1PL3m7eBhp24FZxZUBtz4eh3AiwWq8i8jXLCRpJHsu + hex!["03c81d4e72cbdb96a7e6aad76830ae783b0b4650dc19703dde96866d8894dc921f"] .unchecked_into(), ), ( - // 5CFj6Kg9rmVn1vrqpyjau2ztyBzKeVdRKwNPiA3tqhB5HPqq - hex!["0867dbb49721126df589db100dda728dc3b475cbf414dad8f72a1d5e84897252"].into(), - // 5CwQXP6nvWzigFqNhh2jvCaW9zWVzkdveCJY3tz2MhXMjTon - hex!["26ab2b4b2eba2263b1e55ceb48f687bb0018130a88df0712fbdaf6a347d50e2a"].into(), - // 5FCd9Y7RLNyxz5wnCAErfsLbXGG34L2BaZRHzhiJcMUMd5zd - hex!["2adb17a5cafbddc7c3e00ec45b6951a8b12ce2264235b4def342513a767e5d3d"] + //5FNnjg8hXcPVLKASA69bPbooatacxcWNqkQAyXZfFiXi7T8r + hex!["927f8b12a0fa7185077353d9f6b4fe6bc6cd9682bd498642fa3801280909711a"].into(), + //5GipjBdL3rbex9qyxMinZpJYQbobbwk1ctbZp6B2mh3H25c6 + hex!["ce03638cd1e8496793b0540ba23370034511ea5d08837deb17f6c4d905b8d017"].into(), + //5GByn4uRpwmPe4i4MA4PjTQ8HXuycdue8HMWDhZ7vbU4WR9R + hex!["b67d3ed42ab1fcf3fcd7dee99bd6963bc22058ee22bcfddddb776492e85bd76e"] + .unchecked_into(), + //5GnZZ1rs7RE1jwPiyw1kts4JqaxnML5SdsWMuHV9TqCcuPWj + hex!["d0dd492b1a33d2f06a9aa7213e1aaa41d8820a6b56e95cd2462129b446574014"] .unchecked_into(), - // 5HGLmrZsiTFTPp3QoS1W8w9NxByt8PVq79reqvdxNcQkByqK - hex!["e60d23f49e93c1c1f2d7c115957df5bbd7faf5ebf138d1e9d02e8b39a1f63df0"] + //5GKEKSAa3gbitHhvu5gm4f7q942azCVGDNhrw3hnsGPEMzyg + hex!["bc04e9764e23330b9f4e6922aa6437f87f3dd17b8590825e824724ae89d4ac51"] .unchecked_into(), - // 5FCd9Y7RLNyxz5wnCAErfsLbXGG34L2BaZRHzhiJcMUMd5zd - hex!["2adb17a5cafbddc7c3e00ec45b6951a8b12ce2264235b4def342513a767e5d3d"] + //5H6QLnsfU7sAQ5ZACs9bPivsn9CXrqqwxhq4KKyoquZb5mVW + hex!["de78b26966c08357d66f7f56e7dcac7e4beb16aa0b74939290a42b3f5949bc36"] .unchecked_into(), - // 5FCd9Y7RLNyxz5wnCAErfsLbXGG34L2BaZRHzhiJcMUMd5zd - hex!["2adb17a5cafbddc7c3e00ec45b6951a8b12ce2264235b4def342513a767e5d3d"] + //5FUUeYiAvFfXfB5yZLNkis2ZDy9T3CBLBPC6SwXFriGEjH5f + hex!["96d61fe92a50a79944ea93e3afc0a95a328773878e774cf8c8fbe8eba81cd95c"] .unchecked_into(), - // 5FCd9Y7RLNyxz5wnCAErfsLbXGG34L2BaZRHzhiJcMUMd5zd - hex!["2adb17a5cafbddc7c3e00ec45b6951a8b12ce2264235b4def342513a767e5d3d"] + //5DLkWtgJahWG99cMcQxtftW9W14oduySyQi6hdhav7w3BiKq + hex!["38791c68ee472b94105c66cf150387979c49175062a687d1a1509119cfdc9e0c"] .unchecked_into(), - // 5FCd9Y7RLNyxz5wnCAErfsLbXGG34L2BaZRHzhiJcMUMd5zd - hex!["2adb17a5cafbddc7c3e00ec45b6951a8b12ce2264235b4def342513a767e5d3d"] + //5Cjm1c3Jwt5jp6AaN2XfnncgZcswAmyfJn1buHEUaPauXAKK + hex!["025185a88886008267d27797fc74e34241e3aa8da767fafc9dd3ae5a59546802bb"] .unchecked_into(), ), ]; @@ -653,6 +688,7 @@ fn kusama_staging_testnet_config_genesis(wasm_binary: &[u8]) -> kusama::RuntimeG .chain(initial_authorities.iter().map(|x| (x.0.clone(), STASH))) .collect(), }, + beefy: Default::default(), indices: kusama::IndicesConfig { indices: vec![] }, session: kusama::SessionConfig { keys: initial_authorities @@ -668,6 +704,7 @@ fn kusama_staging_testnet_config_genesis(wasm_binary: &[u8]) -> kusama::RuntimeG x.5.clone(), x.6.clone(), x.7.clone(), + x.8.clone(), ), ) }) @@ -1321,6 +1358,7 @@ pub fn kusama_testnet_genesis( ValidatorId, AssignmentId, AuthorityDiscoveryId, + BeefyId, )>, _root_key: AccountId, endowed_accounts: Option>, @@ -1336,6 +1374,7 @@ pub fn kusama_testnet_genesis( balances: kusama::BalancesConfig { balances: endowed_accounts.iter().map(|k| (k.clone(), ENDOWMENT)).collect(), }, + beefy: Default::default(), session: kusama::SessionConfig { keys: initial_authorities .iter() @@ -1350,6 +1389,7 @@ pub fn kusama_testnet_genesis( x.5.clone(), x.6.clone(), x.7.clone(), + x.8.clone(), ), ) }) @@ -1405,6 +1445,7 @@ pub fn westend_testnet_genesis( ValidatorId, AssignmentId, AuthorityDiscoveryId, + BeefyId, )>, root_key: AccountId, endowed_accounts: Option>, @@ -1420,6 +1461,7 @@ pub fn westend_testnet_genesis( balances: westend::BalancesConfig { balances: endowed_accounts.iter().map(|k| (k.clone(), ENDOWMENT)).collect(), }, + beefy: Default::default(), session: westend::SessionConfig { keys: initial_authorities .iter() @@ -1434,6 +1476,7 @@ pub fn westend_testnet_genesis( x.5.clone(), x.6.clone(), x.7.clone(), + x.8.clone(), ), ) }) @@ -1583,7 +1626,7 @@ fn polkadot_development_config_genesis(wasm_binary: &[u8]) -> polkadot::RuntimeG fn kusama_development_config_genesis(wasm_binary: &[u8]) -> kusama::RuntimeGenesisConfig { kusama_testnet_genesis( wasm_binary, - vec![get_authority_keys_from_seed_no_beefy("Alice")], + vec![get_authority_keys_from_seed("Alice")], get_account_id_from_seed::("Alice"), None, ) @@ -1593,7 +1636,7 @@ fn kusama_development_config_genesis(wasm_binary: &[u8]) -> kusama::RuntimeGenes fn westend_development_config_genesis(wasm_binary: &[u8]) -> westend::RuntimeGenesisConfig { westend_testnet_genesis( wasm_binary, - vec![get_authority_keys_from_seed_no_beefy("Alice")], + vec![get_authority_keys_from_seed("Alice")], get_account_id_from_seed::("Alice"), None, ) @@ -1772,10 +1815,7 @@ pub fn polkadot_local_testnet_config() -> Result { fn kusama_local_testnet_genesis(wasm_binary: &[u8]) -> kusama::RuntimeGenesisConfig { kusama_testnet_genesis( wasm_binary, - vec![ - get_authority_keys_from_seed_no_beefy("Alice"), - get_authority_keys_from_seed_no_beefy("Bob"), - ], + vec![get_authority_keys_from_seed("Alice"), get_authority_keys_from_seed("Bob")], get_account_id_from_seed::("Alice"), None, ) @@ -1804,10 +1844,7 @@ pub fn kusama_local_testnet_config() -> Result { fn westend_local_testnet_genesis(wasm_binary: &[u8]) -> westend::RuntimeGenesisConfig { westend_testnet_genesis( wasm_binary, - vec![ - get_authority_keys_from_seed_no_beefy("Alice"), - get_authority_keys_from_seed_no_beefy("Bob"), - ], + vec![get_authority_keys_from_seed("Alice"), get_authority_keys_from_seed("Bob")], get_account_id_from_seed::("Alice"), None, ) diff --git a/node/service/src/lib.rs b/node/service/src/lib.rs index 3d9486ccc87b..95c887947c98 100644 --- a/node/service/src/lib.rs +++ b/node/service/src/lib.rs @@ -753,13 +753,9 @@ pub fn new_full( Some(backoff) }; - // If not on a known test network, warn the user that BEEFY is still experimental. - if enable_beefy && - !config.chain_spec.is_rococo() && - !config.chain_spec.is_wococo() && - !config.chain_spec.is_versi() - { - gum::warn!("BEEFY is still experimental, usage on a production network is discouraged."); + // Warn the user that BEEFY is still experimental for Polkadot. + if enable_beefy && config.chain_spec.is_polkadot() { + gum::warn!("BEEFY is still experimental, usage on Polkadot network is discouraged."); } let disable_grandpa = config.disable_grandpa; @@ -1204,14 +1200,14 @@ pub fn new_full( let gadget = beefy::start_beefy_gadget::<_, _, _, _, _, _, _>(beefy_params); - // BEEFY currently only runs on testnets, if it fails we'll - // bring the node down with it to make sure it is noticed. + // BEEFY is part of consensus, if it fails we'll bring the node down with it to make sure it + // is noticed. task_manager .spawn_essential_handle() .spawn_blocking("beefy-gadget", None, gadget); - + // When offchain indexing is enabled, MMR gadget should also run. if is_offchain_indexing_enabled { - task_manager.spawn_handle().spawn_blocking( + task_manager.spawn_essential_handle().spawn_blocking( "mmr-gadget", None, MmrGadget::start( diff --git a/runtime/kusama/Cargo.toml b/runtime/kusama/Cargo.toml index 28598bde9443..a88654d26121 100644 --- a/runtime/kusama/Cargo.toml +++ b/runtime/kusama/Cargo.toml @@ -20,11 +20,13 @@ smallvec = "1.8.0" authority-discovery-primitives = { package = "sp-authority-discovery", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } babe-primitives = { package = "sp-consensus-babe", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } beefy-primitives = { package = "sp-consensus-beefy", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +binary-merkle-tree = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } kusama-runtime-constants = { package = "kusama-runtime-constants", path = "./constants", default-features = false } sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } inherents = { package = "sp-inherents", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } offchain-primitives = { package = "sp-offchain", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-std = { package = "sp-std", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-application-crypto = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-arithmetic = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-io = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-mmr-primitives = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -42,6 +44,8 @@ pallet-authorship = { git = "https://github.com/paritytech/substrate", branch = pallet-babe = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-bags-list = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-beefy = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-beefy-mmr = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-bounties = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-child-bounties = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -61,6 +65,7 @@ pallet-im-online = { git = "https://github.com/paritytech/substrate", branch = " pallet-indices = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-membership = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-message-queue = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-mmr = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-multisig = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-nomination-pools = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-offences = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -144,6 +149,8 @@ std = [ "pallet-authorship/std", "pallet-bags-list/std", "pallet-balances/std", + "pallet-beefy/std", + "pallet-beefy-mmr/std", "pallet-bounties/std", "pallet-child-bounties/std", "pallet-transaction-payment/std", @@ -161,6 +168,7 @@ std = [ "pallet-indices/std", "pallet-membership/std", "pallet-message-queue/std", + "pallet-mmr/std", "pallet-multisig/std", "pallet-nomination-pools/std", "pallet-nomination-pools-runtime-api/std", @@ -184,6 +192,7 @@ std = [ "pallet-whitelist/std", "pallet-babe/std", "pallet-xcm/std", + "sp-application-crypto/std", "sp-mmr-primitives/std", "sp-runtime/std", "sp-staking/std", @@ -265,6 +274,8 @@ try-runtime = [ "pallet-authorship/try-runtime", "pallet-bags-list/try-runtime", "pallet-balances/try-runtime", + "pallet-beefy/try-runtime", + "pallet-beefy-mmr/try-runtime", "pallet-bounties/try-runtime", "pallet-child-bounties/try-runtime", "pallet-transaction-payment/try-runtime", @@ -281,6 +292,7 @@ try-runtime = [ "pallet-indices/try-runtime", "pallet-membership/try-runtime", "pallet-message-queue/try-runtime", + "pallet-mmr/try-runtime", "pallet-multisig/try-runtime", "pallet-nomination-pools/try-runtime", "pallet-offences/try-runtime", diff --git a/runtime/kusama/src/lib.rs b/runtime/kusama/src/lib.rs index c7077e38a653..3d6a56cfecbd 100644 --- a/runtime/kusama/src/lib.rs +++ b/runtime/kusama/src/lib.rs @@ -52,7 +52,10 @@ use runtime_parachains::{ }; use authority_discovery_primitives::AuthorityId as AuthorityDiscoveryId; -use beefy_primitives::ecdsa_crypto::{AuthorityId as BeefyId, Signature as BeefySignature}; +use beefy_primitives::{ + ecdsa_crypto::{AuthorityId as BeefyId, Signature as BeefySignature}, + mmr::{BeefyDataProvider, MmrLeafVersion}, +}; use frame_election_provider_support::{ bounds::ElectionBoundsBuilder, generate_solution_type, onchain, NposSolution, SequentialPhragmen, @@ -71,13 +74,12 @@ use pallet_grandpa::{fg_primitives, AuthorityId as GrandpaId}; use pallet_im_online::sr25519::AuthorityId as ImOnlineId; use pallet_session::historical as session_historical; use pallet_transaction_payment::{CurrencyAdapter, FeeDetails, RuntimeDispatchInfo}; -use sp_core::{ConstU128, OpaqueMetadata}; -use sp_mmr_primitives as mmr; +use sp_core::{ConstU128, OpaqueMetadata, H256}; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, traits::{ AccountIdLookup, BlakeTwo256, Block as BlockT, ConvertInto, Extrinsic as ExtrinsicT, - OpaqueKeys, SaturatedConversion, Verify, + Keccak256, OpaqueKeys, SaturatedConversion, Verify, }, transaction_validity::{TransactionPriority, TransactionSource, TransactionValidity}, ApplyExtrinsicResult, FixedU128, KeyTypeId, Perbill, Percent, Permill, @@ -316,6 +318,81 @@ impl pallet_balances::Config for Runtime { type MaxHolds = ConstU32<1>; } +parameter_types! { + pub BeefySetIdSessionEntries: u32 = BondingDuration::get() * SessionsPerEra::get(); +} + +impl pallet_beefy::Config for Runtime { + type BeefyId = BeefyId; + type MaxAuthorities = MaxAuthorities; + type MaxNominators = MaxNominatorRewardedPerValidator; + type MaxSetIdSessionEntries = BeefySetIdSessionEntries; + type OnNewValidatorSet = BeefyMmrLeaf; + type WeightInfo = (); + type KeyOwnerProof = >::Proof; + type EquivocationReportSystem = + pallet_beefy::EquivocationReportSystem; +} + +impl pallet_mmr::Config for Runtime { + const INDEXING_PREFIX: &'static [u8] = mmr::INDEXING_PREFIX; + type Hashing = Keccak256; + type OnNewRoot = pallet_beefy_mmr::DepositBeefyDigest; + type WeightInfo = (); + type LeafData = pallet_beefy_mmr::Pallet; +} + +/// MMR helper types. +mod mmr { + use super::Runtime; + pub use pallet_mmr::primitives::*; + + pub type Leaf = <::LeafData as LeafDataProvider>::LeafData; + pub type Hashing = ::Hashing; + pub type Hash = ::Output; +} + +parameter_types! { + /// Version of the produced MMR leaf. + /// + /// The version consists of two parts; + /// - `major` (3 bits) + /// - `minor` (5 bits) + /// + /// `major` should be updated only if decoding the previous MMR Leaf format from the payload + /// is not possible (i.e. backward incompatible change). + /// `minor` should be updated if fields are added to the previous MMR Leaf, which given SCALE + /// encoding does not prevent old leafs from being decoded. + /// + /// Hence we expect `major` to be changed really rarely (think never). + /// See [`MmrLeafVersion`] type documentation for more details. + pub LeafVersion: MmrLeafVersion = MmrLeafVersion::new(0, 0); +} + +/// A BEEFY data provider that merkelizes all the parachain heads at the current block +/// (sorted by their parachain id). +pub struct ParaHeadsRootProvider; +impl BeefyDataProvider for ParaHeadsRootProvider { + fn extra_data() -> H256 { + let mut para_heads: Vec<(u32, Vec)> = Paras::parachains() + .into_iter() + .filter_map(|id| Paras::para_head(&id).map(|head| (id.into(), head.0))) + .collect(); + para_heads.sort_by_key(|k| k.0); + binary_merkle_tree::merkle_root::( + para_heads.into_iter().map(|pair| pair.encode()), + ) + .into() + } +} + +impl pallet_beefy_mmr::Config for Runtime { + type LeafVersion = LeafVersion; + type BeefyAuthorityToMerkleLeaf = pallet_beefy_mmr::BeefyEcdsaToEthereum; + type LeafExtra = H256; + type BeefyDataProvider = ParaHeadsRootProvider; +} + parameter_types! { pub const TransactionByteFee: Balance = 10 * MILLICENTS; /// This value increases the priority of `Operational` transactions by adding @@ -347,6 +424,17 @@ impl pallet_authorship::Config for Runtime { type EventHandler = (Staking, ImOnline); } +impl_opaque_keys! { + pub struct OldSessionKeys { + pub grandpa: Grandpa, + pub babe: Babe, + pub im_online: ImOnline, + pub para_validator: Initializer, + pub para_assignment: ParaSessionInfo, + pub authority_discovery: AuthorityDiscovery, + } +} + impl_opaque_keys! { pub struct SessionKeys { pub grandpa: Grandpa, @@ -355,6 +443,33 @@ impl_opaque_keys! { pub para_validator: Initializer, pub para_assignment: ParaSessionInfo, pub authority_discovery: AuthorityDiscovery, + pub beefy: Beefy, + } +} + +// remove this when removing `OldSessionKeys` +fn transform_session_keys(v: AccountId, old: OldSessionKeys) -> SessionKeys { + SessionKeys { + grandpa: old.grandpa, + babe: old.babe, + im_online: old.im_online, + para_validator: old.para_validator, + para_assignment: old.para_assignment, + authority_discovery: old.authority_discovery, + beefy: { + // From Session::upgrade_keys(): + // + // Care should be taken that the raw versions of the + // added keys are unique for every `ValidatorId, KeyTypeId` combination. + // This is an invariant that the session pallet typically maintains internally. + // + // So, produce a dummy value that's unique for the `ValidatorId, KeyTypeId` combination. + let mut id: BeefyId = sp_application_crypto::ecdsa::Public::from_raw([0u8; 33]).into(); + let id_raw: &mut [u8] = id.as_mut(); + id_raw[1..33].copy_from_slice(v.as_ref()); + id_raw[0..4].copy_from_slice(b"beef"); + id + }, } } @@ -1390,6 +1505,14 @@ construct_runtime! { Staking: pallet_staking::{Pallet, Call, Storage, Config, Event} = 6, Offences: pallet_offences::{Pallet, Storage, Event} = 7, Historical: session_historical::{Pallet} = 34, + + // BEEFY Bridges support. + Beefy: pallet_beefy::{Pallet, Call, Storage, Config, ValidateUnsigned} = 200, + // MMR leaf construction must be before session in order to have leaf contents + // refer to block consistently. see substrate issue #11797 for details. + Mmr: pallet_mmr::{Pallet, Storage} = 201, + BeefyMmrLeaf: pallet_beefy_mmr::{Pallet, Storage} = 202, + Session: pallet_session::{Pallet, Call, Storage, Event, Config} = 8, Grandpa: pallet_grandpa::{Pallet, Call, Storage, Config, Event, ValidateUnsigned} = 10, ImOnline: pallet_im_online::{Pallet, Call, Storage, Event, ValidateUnsigned, Config} = 11, @@ -1527,7 +1650,7 @@ impl Get for NominationPoolsMigrationV4OldPallet { /// /// This contains the combined migrations of the last 10 releases. It allows to skip runtime /// upgrades in case governance decides to do so. THE ORDER IS IMPORTANT. -pub type Migrations = (migrations::Unreleased,); +pub type Migrations = migrations::Unreleased; /// The runtime migrations per release. #[allow(deprecated, missing_docs)] @@ -1579,6 +1702,16 @@ pub mod migrations { type PalletName = TipsPalletName; } + /// Upgrade Session keys to include BEEFY key. + /// When this is removed, should also remove `OldSessionKeys`. + pub struct UpgradeSessionKeys; + impl frame_support::traits::OnRuntimeUpgrade for UpgradeSessionKeys { + fn on_runtime_upgrade() -> Weight { + Session::upgrade_keys::(transform_session_keys); + Perbill::from_percent(50) * BlockWeights::get().max_block + } + } + /// Unreleased migrations. Add new ones here: pub type Unreleased = ( init_state_migration::InitMigrate, @@ -1606,6 +1739,8 @@ pub mod migrations { frame_support::migrations::RemovePallet::DbWeight>, frame_support::migrations::RemovePallet::DbWeight>, + // Upgrade SessionKeys to include BEEFY key + UpgradeSessionKeys, ); } @@ -1886,62 +2021,94 @@ sp_api::impl_runtime_apis! { impl beefy_primitives::BeefyApi for Runtime { fn beefy_genesis() -> Option { - // dummy implementation due to lack of BEEFY pallet. - None + Beefy::genesis_block() } fn validator_set() -> Option> { - // dummy implementation due to lack of BEEFY pallet. - None + Beefy::validator_set() } fn submit_report_equivocation_unsigned_extrinsic( - _equivocation_proof: beefy_primitives::EquivocationProof< + equivocation_proof: beefy_primitives::EquivocationProof< BlockNumber, BeefyId, BeefySignature, >, - _key_owner_proof: beefy_primitives::OpaqueKeyOwnershipProof, + key_owner_proof: beefy_primitives::OpaqueKeyOwnershipProof, ) -> Option<()> { - None + let key_owner_proof = key_owner_proof.decode()?; + + Beefy::submit_unsigned_equivocation_report( + equivocation_proof, + key_owner_proof, + ) } fn generate_key_ownership_proof( _set_id: beefy_primitives::ValidatorSetId, - _authority_id: BeefyId, + authority_id: BeefyId, ) -> Option { - None + use parity_scale_codec::Encode; + + Historical::prove((beefy_primitives::KEY_TYPE, authority_id)) + .map(|p| p.encode()) + .map(beefy_primitives::OpaqueKeyOwnershipProof::new) } } impl mmr::MmrApi for Runtime { - fn mmr_root() -> Result { - Err(mmr::Error::PalletNotIncluded) + fn mmr_root() -> Result { + Ok(Mmr::mmr_root()) } fn mmr_leaf_count() -> Result { - Err(mmr::Error::PalletNotIncluded) + Ok(Mmr::mmr_leaves()) } fn generate_proof( - _block_numbers: Vec, - _best_known_block_number: Option, - ) -> Result<(Vec, mmr::Proof), mmr::Error> { - Err(mmr::Error::PalletNotIncluded) + block_numbers: Vec, + best_known_block_number: Option, + ) -> Result<(Vec, mmr::Proof), mmr::Error> { + Mmr::generate_proof(block_numbers, best_known_block_number).map( + |(leaves, proof)| { + ( + leaves + .into_iter() + .map(|leaf| mmr::EncodableOpaqueLeaf::from_leaf(&leaf)) + .collect(), + proof, + ) + }, + ) } - fn verify_proof(_leaves: Vec, _proof: mmr::Proof) + fn verify_proof(leaves: Vec, proof: mmr::Proof) -> Result<(), mmr::Error> { - Err(mmr::Error::PalletNotIncluded) + let leaves = leaves.into_iter().map(|leaf| + leaf.into_opaque_leaf() + .try_decode() + .ok_or(mmr::Error::Verify)).collect::, mmr::Error>>()?; + Mmr::verify_leaves(leaves, proof) } fn verify_proof_stateless( - _root: Hash, - _leaves: Vec, - _proof: mmr::Proof + root: mmr::Hash, + leaves: Vec, + proof: mmr::Proof ) -> Result<(), mmr::Error> { - Err(mmr::Error::PalletNotIncluded) + let nodes = leaves.into_iter().map(|leaf|mmr::DataOrHash::Data(leaf.into_opaque_leaf())).collect(); + pallet_mmr::verify_leaves_proof::(root, nodes, proof) + } + } + + impl pallet_beefy_mmr::BeefyMmrApi for RuntimeApi { + fn authority_set_proof() -> beefy_primitives::mmr::BeefyAuthoritySet { + BeefyMmrLeaf::authority_set_proof() + } + + fn next_authority_set_proof() -> beefy_primitives::mmr::BeefyNextAuthoritySet { + BeefyMmrLeaf::next_authority_set_proof() } } diff --git a/runtime/kusama/src/tests.rs b/runtime/kusama/src/tests.rs index 5383e2824687..053c3054ab46 100644 --- a/runtime/kusama/src/tests.rs +++ b/runtime/kusama/src/tests.rs @@ -148,7 +148,7 @@ fn nominator_limit() { #[test] fn call_size() { - RuntimeCall::assert_size_under(230); + RuntimeCall::assert_size_under(256); } #[test] diff --git a/runtime/parachains/src/disputes/slashing/benchmarking.rs b/runtime/parachains/src/disputes/slashing/benchmarking.rs index 271da6d58437..3ede1c908802 100644 --- a/runtime/parachains/src/disputes/slashing/benchmarking.rs +++ b/runtime/parachains/src/disputes/slashing/benchmarking.rs @@ -23,7 +23,7 @@ use frame_system::{pallet_prelude::BlockNumberFor, RawOrigin}; use pallet_staking::testing_utils::create_validators; use parity_scale_codec::Decode; use primitives::{Hash, PARACHAIN_KEY_TYPE_ID}; -use sp_runtime::traits::{One, StaticLookup}; +use sp_runtime::traits::{One, OpaqueKeys, StaticLookup}; use sp_session::MembershipProof; // Candidate hash of the disputed candidate. @@ -54,9 +54,14 @@ where let controller = pallet_staking::Pallet::::bonded(validator).unwrap(); let keys = { - const NUM_SESSION_KEYS: usize = 6; const SESSION_KEY_LEN: usize = 32; - let mut keys = [0u8; NUM_SESSION_KEYS * SESSION_KEY_LEN]; + let key_ids = T::Keys::key_ids(); + let mut keys_len = key_ids.len() * SESSION_KEY_LEN; + if key_ids.contains(&sp_core::crypto::key_types::BEEFY) { + // BEEFY key is 33 bytes long, not 32. + keys_len += 1; + } + let mut keys = vec![0u8; keys_len]; let mut rng = rand_chacha::ChaCha12Rng::seed_from_u64(n as u64); rng.fill_bytes(&mut keys); keys diff --git a/runtime/polkadot/src/lib.rs b/runtime/polkadot/src/lib.rs index ad10de445ab3..dca0d0d986da 100644 --- a/runtime/polkadot/src/lib.rs +++ b/runtime/polkadot/src/lib.rs @@ -2346,12 +2346,7 @@ mod test { #[test] fn call_size() { - assert!( - core::mem::size_of::() <= 230, - "size of RuntimeCall is more than 230 bytes: some calls have too big arguments, use Box to \ - reduce the size of RuntimeCall. - If the limit is too strong, maybe consider increase the limit", - ); + RuntimeCall::assert_size_under(230); } #[test] diff --git a/runtime/rococo/src/lib.rs b/runtime/rococo/src/lib.rs index fb2a56c8100c..145b25d8aa9a 100644 --- a/runtime/rococo/src/lib.rs +++ b/runtime/rococo/src/lib.rs @@ -1331,8 +1331,8 @@ parameter_types! { pub LeafVersion: MmrLeafVersion = MmrLeafVersion::new(0, 0); } -pub struct ParasProvider; -impl BeefyDataProvider for ParasProvider { +pub struct ParaHeadsRootProvider; +impl BeefyDataProvider for ParaHeadsRootProvider { fn extra_data() -> H256 { let mut para_heads: Vec<(u32, Vec)> = Paras::parachains() .into_iter() @@ -1350,7 +1350,7 @@ impl pallet_beefy_mmr::Config for Runtime { type LeafVersion = LeafVersion; type BeefyAuthorityToMerkleLeaf = pallet_beefy_mmr::BeefyEcdsaToEthereum; type LeafExtra = H256; - type BeefyDataProvider = ParasProvider; + type BeefyDataProvider = ParaHeadsRootProvider; } impl paras_sudo_wrapper::Config for Runtime {} @@ -1402,9 +1402,14 @@ construct_runtime! { Authorship: pallet_authorship::{Pallet, Storage} = 5, Offences: pallet_offences::{Pallet, Storage, Event} = 7, Historical: session_historical::{Pallet} = 34, + + // BEEFY Bridges support. + Beefy: pallet_beefy::{Pallet, Call, Storage, Config, ValidateUnsigned} = 240, // MMR leaf construction must be before session in order to have leaf contents // refer to block consistently. see substrate issue #11797 for details. Mmr: pallet_mmr::{Pallet, Storage} = 241, + MmrLeaf: pallet_beefy_mmr::{Pallet, Storage} = 242, + Session: pallet_session::{Pallet, Call, Storage, Event, Config} = 8, Grandpa: pallet_grandpa::{Pallet, Call, Storage, Config, Event, ValidateUnsigned} = 10, ImOnline: pallet_im_online::{Pallet, Call, Storage, Event, ValidateUnsigned, Config} = 11, @@ -1488,12 +1493,6 @@ construct_runtime! { // Pallet for sending XCM. XcmPallet: pallet_xcm::{Pallet, Call, Storage, Event, Origin, Config} = 99, - // Rococo specific pallets (not included in Kusama). Start indices at 240 - // - // BEEFY Bridges support. - Beefy: pallet_beefy::{Pallet, Call, Storage, Config, ValidateUnsigned} = 240, - MmrLeaf: pallet_beefy_mmr::{Pallet, Storage} = 242, - ParasSudoWrapper: paras_sudo_wrapper::{Pallet, Call} = 250, AssignedSlots: assigned_slots::{Pallet, Call, Storage, Event, Config} = 251, diff --git a/runtime/westend/Cargo.toml b/runtime/westend/Cargo.toml index 79583fc2fb1e..a0b13145578d 100644 --- a/runtime/westend/Cargo.toml +++ b/runtime/westend/Cargo.toml @@ -19,9 +19,11 @@ smallvec = "1.8.0" authority-discovery-primitives = { package = "sp-authority-discovery", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } babe-primitives = { package = "sp-consensus-babe", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } beefy-primitives = { package = "sp-consensus-beefy", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +binary-merkle-tree = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } inherents = { package = "sp-inherents", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } offchain-primitives = { package = "sp-offchain", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-application-crypto = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-std = { package = "sp-std", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-io = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-mmr-primitives = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -45,6 +47,8 @@ pallet-authorship = { git = "https://github.com/paritytech/substrate", branch = pallet-babe = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-bags-list = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-beefy = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-beefy-mmr = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-collective = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-democracy = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-elections-phragmen = { package = "pallet-elections-phragmen", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -56,6 +60,7 @@ pallet-im-online = { git = "https://github.com/paritytech/substrate", branch = " pallet-indices = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-membership = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-message-queue = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-mmr = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-multisig = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-nomination-pools = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-offences = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -134,6 +139,8 @@ std = [ "pallet-authority-discovery/std", "pallet-authorship/std", "pallet-balances/std", + "pallet-beefy/std", + "pallet-beefy-mmr/std", "pallet-transaction-payment/std", "pallet-transaction-payment-rpc-runtime-api/std", "pallet-collective/std", @@ -147,6 +154,7 @@ std = [ "pallet-indices/std", "pallet-membership/std", "pallet-message-queue/std", + "pallet-mmr/std", "beefy-primitives/std", "pallet-multisig/std", "pallet-nomination-pools/std", @@ -170,6 +178,7 @@ std = [ "pallet-babe/std", "pallet-bags-list/std", "frame-executive/std", + "sp-application-crypto/std", "sp-mmr-primitives/std", "sp-runtime/std", "sp-staking/std", @@ -243,6 +252,8 @@ try-runtime = [ "pallet-authority-discovery/try-runtime", "pallet-authorship/try-runtime", "pallet-balances/try-runtime", + "pallet-beefy/try-runtime", + "pallet-beefy-mmr/try-runtime", "pallet-transaction-payment/try-runtime", "pallet-collective/try-runtime", "pallet-elections-phragmen/try-runtime", @@ -255,6 +266,7 @@ try-runtime = [ "pallet-indices/try-runtime", "pallet-membership/try-runtime", "pallet-message-queue/try-runtime", + "pallet-mmr/try-runtime", "pallet-multisig/try-runtime", "pallet-nomination-pools/try-runtime", "pallet-offences/try-runtime", diff --git a/runtime/westend/src/lib.rs b/runtime/westend/src/lib.rs index 3ade28c51fba..62770e0992fb 100644 --- a/runtime/westend/src/lib.rs +++ b/runtime/westend/src/lib.rs @@ -17,11 +17,14 @@ //! The Westend runtime. This can be compiled with `#[no_std]`, ready for Wasm. #![cfg_attr(not(feature = "std"), no_std)] -// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256. -#![recursion_limit = "256"] +// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 512. +#![recursion_limit = "512"] use authority_discovery_primitives::AuthorityId as AuthorityDiscoveryId; -use beefy_primitives::ecdsa_crypto::{AuthorityId as BeefyId, Signature as BeefySignature}; +use beefy_primitives::{ + ecdsa_crypto::{AuthorityId as BeefyId, Signature as BeefySignature}, + mmr::{BeefyDataProvider, MmrLeafVersion}, +}; use frame_election_provider_support::{bounds::ElectionBoundsBuilder, onchain, SequentialPhragmen}; use frame_support::{ construct_runtime, parameter_types, @@ -64,15 +67,14 @@ use runtime_parachains::{ shared as parachains_shared, }; use scale_info::TypeInfo; -use sp_core::{OpaqueMetadata, RuntimeDebug}; -use sp_mmr_primitives as mmr; +use sp_core::{OpaqueMetadata, RuntimeDebug, H256}; use sp_runtime::{ create_runtime_str, curve::PiecewiseLinear, generic, impl_opaque_keys, traits::{ AccountIdLookup, BlakeTwo256, Block as BlockT, ConvertInto, Extrinsic as ExtrinsicT, - OpaqueKeys, SaturatedConversion, Verify, + Keccak256, OpaqueKeys, SaturatedConversion, Verify, }, transaction_validity::{TransactionPriority, TransactionSource, TransactionValidity}, ApplyExtrinsicResult, FixedU128, KeyTypeId, Perbill, @@ -271,6 +273,81 @@ impl pallet_balances::Config for Runtime { type MaxFreezes = ConstU32<0>; } +parameter_types! { + pub const BeefySetIdSessionEntries: u32 = BondingDuration::get() * SessionsPerEra::get(); +} + +impl pallet_beefy::Config for Runtime { + type BeefyId = BeefyId; + type MaxAuthorities = MaxAuthorities; + type MaxNominators = MaxNominatorRewardedPerValidator; + type MaxSetIdSessionEntries = BeefySetIdSessionEntries; + type OnNewValidatorSet = BeefyMmrLeaf; + type WeightInfo = (); + type KeyOwnerProof = >::Proof; + type EquivocationReportSystem = + pallet_beefy::EquivocationReportSystem; +} + +impl pallet_mmr::Config for Runtime { + const INDEXING_PREFIX: &'static [u8] = mmr::INDEXING_PREFIX; + type Hashing = Keccak256; + type OnNewRoot = pallet_beefy_mmr::DepositBeefyDigest; + type WeightInfo = (); + type LeafData = pallet_beefy_mmr::Pallet; +} + +/// MMR helper types. +mod mmr { + use super::Runtime; + pub use pallet_mmr::primitives::*; + + pub type Leaf = <::LeafData as LeafDataProvider>::LeafData; + pub type Hashing = ::Hashing; + pub type Hash = ::Output; +} + +parameter_types! { + /// Version of the produced MMR leaf. + /// + /// The version consists of two parts; + /// - `major` (3 bits) + /// - `minor` (5 bits) + /// + /// `major` should be updated only if decoding the previous MMR Leaf format from the payload + /// is not possible (i.e. backward incompatible change). + /// `minor` should be updated if fields are added to the previous MMR Leaf, which given SCALE + /// encoding does not prevent old leafs from being decoded. + /// + /// Hence we expect `major` to be changed really rarely (think never). + /// See [`MmrLeafVersion`] type documentation for more details. + pub LeafVersion: MmrLeafVersion = MmrLeafVersion::new(0, 0); +} + +/// A BEEFY data provider that merkelizes all the parachain heads at the current block +/// (sorted by their parachain id). +pub struct ParaHeadsRootProvider; +impl BeefyDataProvider for ParaHeadsRootProvider { + fn extra_data() -> H256 { + let mut para_heads: Vec<(u32, Vec)> = Paras::parachains() + .into_iter() + .filter_map(|id| Paras::para_head(&id).map(|head| (id.into(), head.0))) + .collect(); + para_heads.sort_by_key(|k| k.0); + binary_merkle_tree::merkle_root::( + para_heads.into_iter().map(|pair| pair.encode()), + ) + .into() + } +} + +impl pallet_beefy_mmr::Config for Runtime { + type LeafVersion = LeafVersion; + type BeefyAuthorityToMerkleLeaf = pallet_beefy_mmr::BeefyEcdsaToEthereum; + type LeafExtra = H256; + type BeefyDataProvider = ParaHeadsRootProvider; +} + parameter_types! { pub const TransactionByteFee: Balance = 10 * MILLICENTS; /// This value increases the priority of `Operational` transactions by adding @@ -307,6 +384,17 @@ parameter_types! { pub const Offset: BlockNumber = 0; } +impl_opaque_keys! { + pub struct OldSessionKeys { + pub grandpa: Grandpa, + pub babe: Babe, + pub im_online: ImOnline, + pub para_validator: Initializer, + pub para_assignment: ParaSessionInfo, + pub authority_discovery: AuthorityDiscovery, + } +} + impl_opaque_keys! { pub struct SessionKeys { pub grandpa: Grandpa, @@ -315,6 +403,33 @@ impl_opaque_keys! { pub para_validator: Initializer, pub para_assignment: ParaSessionInfo, pub authority_discovery: AuthorityDiscovery, + pub beefy: Beefy, + } +} + +// remove this when removing `OldSessionKeys` +fn transform_session_keys(v: AccountId, old: OldSessionKeys) -> SessionKeys { + SessionKeys { + grandpa: old.grandpa, + babe: old.babe, + im_online: old.im_online, + para_validator: old.para_validator, + para_assignment: old.para_assignment, + authority_discovery: old.authority_discovery, + beefy: { + // From Session::upgrade_keys(): + // + // Care should be taken that the raw versions of the + // added keys are unique for every `ValidatorId, KeyTypeId` combination. + // This is an invariant that the session pallet typically maintains internally. + // + // So, produce a dummy value that's unique for the `ValidatorId, KeyTypeId` combination. + let mut id: BeefyId = sp_application_crypto::ecdsa::Public::from_raw([0u8; 33]).into(); + let id_raw: &mut [u8] = id.as_mut(); + id_raw[1..33].copy_from_slice(v.as_ref()); + id_raw[0..4].copy_from_slice(b"beef"); + id + }, } } @@ -1167,6 +1282,14 @@ construct_runtime! { Staking: pallet_staking::{Pallet, Call, Storage, Config, Event} = 6, Offences: pallet_offences::{Pallet, Storage, Event} = 7, Historical: session_historical::{Pallet} = 27, + + // BEEFY Bridges support. + Beefy: pallet_beefy::{Pallet, Call, Storage, Config, ValidateUnsigned} = 200, + // MMR leaf construction must be before session in order to have leaf contents + // refer to block consistently. see substrate issue #11797 for details. + Mmr: pallet_mmr::{Pallet, Storage} = 201, + BeefyMmrLeaf: pallet_beefy_mmr::{Pallet, Storage} = 202, + Session: pallet_session::{Pallet, Call, Storage, Event, Config} = 8, Grandpa: pallet_grandpa::{Pallet, Call, Storage, Config, Event, ValidateUnsigned} = 10, ImOnline: pallet_im_online::{Pallet, Call, Storage, Event, ValidateUnsigned, Config} = 11, @@ -1284,6 +1407,16 @@ pub type Migrations = migrations::Unreleased; pub mod migrations { use super::*; + /// Upgrade Session keys to include BEEFY key. + /// When this is removed, should also remove `OldSessionKeys`. + pub struct UpgradeSessionKeys; + impl frame_support::traits::OnRuntimeUpgrade for UpgradeSessionKeys { + fn on_runtime_upgrade() -> Weight { + Session::upgrade_keys::(transform_session_keys); + Perbill::from_percent(50) * BlockWeights::get().max_block + } + } + /// Unreleased migrations. Add new ones here: pub type Unreleased = ( pallet_im_online::migration::v1::Migration, @@ -1291,6 +1424,7 @@ pub mod migrations { assigned_slots::migration::v1::VersionCheckedMigrateToV1, parachains_scheduler::migration::v1::MigrateToV1, parachains_configuration::migration::v8::MigrateToV8, + UpgradeSessionKeys, ); } @@ -1561,64 +1695,94 @@ sp_api::impl_runtime_apis! { impl beefy_primitives::BeefyApi for Runtime { fn beefy_genesis() -> Option { - // dummy implementation due to lack of BEEFY pallet. - None + Beefy::genesis_block() } fn validator_set() -> Option> { - // dummy implementation due to lack of BEEFY pallet. - None + Beefy::validator_set() } fn submit_report_equivocation_unsigned_extrinsic( - _equivocation_proof: beefy_primitives::EquivocationProof< + equivocation_proof: beefy_primitives::EquivocationProof< BlockNumber, BeefyId, BeefySignature, >, - _key_owner_proof: beefy_primitives::OpaqueKeyOwnershipProof, + key_owner_proof: beefy_primitives::OpaqueKeyOwnershipProof, ) -> Option<()> { - None + let key_owner_proof = key_owner_proof.decode()?; + + Beefy::submit_unsigned_equivocation_report( + equivocation_proof, + key_owner_proof, + ) } fn generate_key_ownership_proof( _set_id: beefy_primitives::ValidatorSetId, - _authority_id: BeefyId, + authority_id: BeefyId, ) -> Option { - None + use parity_scale_codec::Encode; + + Historical::prove((beefy_primitives::KEY_TYPE, authority_id)) + .map(|p| p.encode()) + .map(beefy_primitives::OpaqueKeyOwnershipProof::new) } } impl mmr::MmrApi for Runtime { - fn mmr_root() -> Result { - Err(mmr::Error::PalletNotIncluded) + fn mmr_root() -> Result { + Ok(Mmr::mmr_root()) } fn mmr_leaf_count() -> Result { - Err(mmr::Error::PalletNotIncluded) + Ok(Mmr::mmr_leaves()) } fn generate_proof( - _block_numbers: Vec, - _best_known_block_number: Option, - ) -> Result<(Vec, mmr::Proof), mmr::Error> { - Err(mmr::Error::PalletNotIncluded) + block_numbers: Vec, + best_known_block_number: Option, + ) -> Result<(Vec, mmr::Proof), mmr::Error> { + Mmr::generate_proof(block_numbers, best_known_block_number).map( + |(leaves, proof)| { + ( + leaves + .into_iter() + .map(|leaf| mmr::EncodableOpaqueLeaf::from_leaf(&leaf)) + .collect(), + proof, + ) + }, + ) } - fn verify_proof(_leaves: Vec, _proof: mmr::Proof) + fn verify_proof(leaves: Vec, proof: mmr::Proof) -> Result<(), mmr::Error> { - - Err(mmr::Error::PalletNotIncluded) + let leaves = leaves.into_iter().map(|leaf| + leaf.into_opaque_leaf() + .try_decode() + .ok_or(mmr::Error::Verify)).collect::, mmr::Error>>()?; + Mmr::verify_leaves(leaves, proof) } fn verify_proof_stateless( - _root: Hash, - _leaves: Vec, - _proof: mmr::Proof + root: mmr::Hash, + leaves: Vec, + proof: mmr::Proof ) -> Result<(), mmr::Error> { + let nodes = leaves.into_iter().map(|leaf|mmr::DataOrHash::Data(leaf.into_opaque_leaf())).collect(); + pallet_mmr::verify_leaves_proof::(root, nodes, proof) + } + } + + impl pallet_beefy_mmr::BeefyMmrApi for RuntimeApi { + fn authority_set_proof() -> beefy_primitives::mmr::BeefyAuthoritySet { + BeefyMmrLeaf::authority_set_proof() + } - Err(mmr::Error::PalletNotIncluded) + fn next_authority_set_proof() -> beefy_primitives::mmr::BeefyNextAuthoritySet { + BeefyMmrLeaf::next_authority_set_proof() } } diff --git a/runtime/westend/src/tests.rs b/runtime/westend/src/tests.rs index 9e8acb67c36a..78062662fee0 100644 --- a/runtime/westend/src/tests.rs +++ b/runtime/westend/src/tests.rs @@ -46,12 +46,7 @@ fn sample_size_is_sensible() { #[test] fn call_size() { - assert!( - core::mem::size_of::() <= 230, - "size of RuntimeCall is more than 230 bytes: some calls have too big arguments, use Box to reduce \ - the size of RuntimeCall. - If the limit is too strong, maybe consider increase the limit to 300.", - ); + RuntimeCall::assert_size_under(256); } #[test] diff --git a/scripts/prepare-test-net.sh b/scripts/prepare-test-net.sh index 14bbd680d6ef..c908aba308a3 100755 --- a/scripts/prepare-test-net.sh +++ b/scripts/prepare-test-net.sh @@ -15,7 +15,7 @@ generate_address() { } generate_public_key() { - subkey inspect ${3:-} ${4:-} "$SECRET//$1//$2" | grep "Public" | awk '{ print $4 }' + subkey inspect ${3:-} ${4:-} "$SECRET//$1//$2" | grep "Public key (hex)" | awk '{ print $4 }' } generate_address_and_public_key() { From 8ce17160bf6e281ec4d54557c937fc9db510d987 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Aug 2023 10:28:19 +0000 Subject: [PATCH 37/54] Bump actions/setup-node from 3.8.0 to 3.8.1 (#7639) Bumps [actions/setup-node](https://github.com/actions/setup-node) from 3.8.0 to 3.8.1. - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](https://github.com/actions/setup-node/compare/v3.8.0...v3.8.1) --- updated-dependencies: - dependency-name: actions/setup-node dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/check-licenses.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check-licenses.yml b/.github/workflows/check-licenses.yml index b61005649eec..1e654f7b3070 100644 --- a/.github/workflows/check-licenses.yml +++ b/.github/workflows/check-licenses.yml @@ -9,7 +9,7 @@ jobs: steps: - name: Checkout sources uses: actions/checkout@v3 - - uses: actions/setup-node@v3.8.0 + - uses: actions/setup-node@v3.8.1 with: node-version: '18.x' registry-url: 'https://npm.pkg.github.com' From 9f1e9ea531687397ec68b74cb7abb1dc647c0f6a Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Tue, 22 Aug 2023 13:19:05 +0200 Subject: [PATCH 38/54] Bound number of assets which can be withdrawn to pay for execution. (#7641) * Bound number of assets which can be withdrawn to pay for execution. * ".git/.scripts/commands/fmt/fmt.sh" * Include ClaimAsset in limiting the assets * Change max assets to constant --------- Co-authored-by: command-bot <> Co-authored-by: Francisco Aguirre --- xcm/xcm-builder/src/barriers.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/xcm/xcm-builder/src/barriers.rs b/xcm/xcm-builder/src/barriers.rs index 6996c7145528..353e111b813b 100644 --- a/xcm/xcm-builder/src/barriers.rs +++ b/xcm/xcm-builder/src/barriers.rs @@ -52,6 +52,8 @@ impl ShouldExecute for TakeWeightCredit { } } +const MAX_ASSETS_FOR_BUY_EXECUTION: usize = 1; + /// Allows execution from `origin` if it is contained in `T` (i.e. `T::Contains(origin)`) taking /// payments into account. /// @@ -79,10 +81,10 @@ impl> ShouldExecute for AllowTopLevelPaidExecutionFro instructions[..end] .matcher() .match_next_inst(|inst| match inst { - ReceiveTeleportedAsset(..) | - WithdrawAsset(..) | - ReserveAssetDeposited(..) | - ClaimAsset { .. } => Ok(()), + ReceiveTeleportedAsset(..) | ReserveAssetDeposited(..) => Ok(()), + WithdrawAsset(ref assets) if assets.len() <= MAX_ASSETS_FOR_BUY_EXECUTION => Ok(()), + ClaimAsset { ref assets, .. } if assets.len() <= MAX_ASSETS_FOR_BUY_EXECUTION => + Ok(()), _ => Err(ProcessMessageError::BadFormat), })? .skip_inst_while(|inst| matches!(inst, ClearOrigin))? From d5fc80c14332ae3ffed99e3eb77208187cd7d75e Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe Date: Tue, 22 Aug 2023 14:21:07 +0300 Subject: [PATCH 39/54] Enable approval-coalescing by default Signed-off-by: Alexandru Gheorghe --- node/core/approval-voting/src/lib.rs | 58 ++++++++++++++-------------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/node/core/approval-voting/src/lib.rs b/node/core/approval-voting/src/lib.rs index 39e0fd58c2cd..86905a5b8ca7 100644 --- a/node/core/approval-voting/src/lib.rs +++ b/node/core/approval-voting/src/lib.rs @@ -837,7 +837,7 @@ impl State { #[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] async fn get_approval_voting_params_or_default( &mut self, - ctx: &mut Context, + _ctx: &mut Context, block_hash: Hash, ) -> ApprovalVotingParams { if let Some(params) = self @@ -847,29 +847,30 @@ impl State { { *params } else { - let (s_tx, s_rx) = oneshot::channel(); - - ctx.send_message(RuntimeApiMessage::Request( - block_hash, - RuntimeApiRequest::ApprovalVotingParams(s_tx), - )) - .await; - - match s_rx.await { - Ok(Ok(params)) => { - self.approval_voting_params_cache - .as_mut() - .map(|cache| cache.put(block_hash, params)); - params - }, - _ => { - gum::error!( - target: LOG_TARGET, - "Could not request approval voting params from runtime using defaults" - ); - ApprovalVotingParams { max_approval_coalesce_count: 1 } - }, - } + // let (s_tx, s_rx) = oneshot::channel(); + + // ctx.send_message(RuntimeApiMessage::Request( + // block_hash, + // RuntimeApiRequest::ApprovalVotingParams(s_tx), + // )) + // .await; + + // match s_rx.await { + // Ok(Ok(params)) => { + // self.approval_voting_params_cache + // .as_mut() + // .map(|cache| cache.put(block_hash, params)); + // params + // }, + // _ => { + // gum::error!( + // target: LOG_TARGET, + // "Could not request approval voting params from runtime using defaults" + // ); + // TODO: Uncomment this after versi test + ApprovalVotingParams { max_approval_coalesce_count: 6 } + // }, + // } } } } @@ -2681,9 +2682,9 @@ where metrics.on_no_shows(no_shows); } if check == Check::ApprovedOneThird { - // No-shows are not counted when more than one third of validators approve a candidate, - // so count candidates where more than one third of validators had to approve it, - // this is indicative of something breaking. + // No-shows are not counted when more than one third of validators approve a + // candidate, so count candidates where more than one third of validators had to + // approve it, this is indicative of something breaking. metrics.on_approved_by_one_third() } @@ -3366,7 +3367,8 @@ async fn maybe_create_signature( let oldest_candidate_to_sign = match block_entry.longest_waiting_candidate_signature() { Some(candidate) => candidate, // No cached candidates, nothing to do here, this just means the timer fired, - // but the signatures were already sent because we gathered more than max_approval_coalesce_count. + // but the signatures were already sent because we gathered more than + // max_approval_coalesce_count. None => return Ok(None), }; From 301dab1ed425c78def8f78d9a62b858c23411c53 Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe Date: Tue, 22 Aug 2023 14:22:03 +0300 Subject: [PATCH 40/54] Increase num-workers Signed-off-by: Alexandru Gheorghe --- node/core/pvf/src/host.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/node/core/pvf/src/host.rs b/node/core/pvf/src/host.rs index 9f3b7e23fd89..aadc14775025 100644 --- a/node/core/pvf/src/host.rs +++ b/node/core/pvf/src/host.rs @@ -186,7 +186,8 @@ impl Config { prepare_workers_hard_max_num: 1, execute_worker_program_path, execute_worker_spawn_timeout: Duration::from_secs(3), - execute_workers_max_num: 2, + // TODO: cleanup increased for versi experimenting. + execute_workers_max_num: 4, } } } From d861d814dc245e3590bf4c906153d65824c348ad Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe Date: Tue, 22 Aug 2023 14:23:25 +0300 Subject: [PATCH 41/54] Increase SMALL_POV_SIZE Signed-off-by: Alexandru Gheorghe --- node/network/availability-recovery/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/node/network/availability-recovery/src/lib.rs b/node/network/availability-recovery/src/lib.rs index fb0cdb720571..c8a628645f34 100644 --- a/node/network/availability-recovery/src/lib.rs +++ b/node/network/availability-recovery/src/lib.rs @@ -104,7 +104,8 @@ const TIMEOUT_START_NEW_REQUESTS: Duration = CHUNK_REQUEST_TIMEOUT; const TIMEOUT_START_NEW_REQUESTS: Duration = Duration::from_millis(100); /// PoV size limit in bytes for which prefer fetching from backers. -const SMALL_POV_LIMIT: usize = 128 * 1024; +//TODO: Cleanup increased for versi testing +const SMALL_POV_LIMIT: usize = 3 * 1024 * 1024; #[derive(Clone, PartialEq)] /// The strategy we use to recover the PoV. From a216036f701e4a6429bccd7eb7f095ba9d82ac63 Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe Date: Tue, 22 Aug 2023 16:54:00 +0300 Subject: [PATCH 42/54] Put logs on trace Signed-off-by: Alexandru Gheorghe --- node/core/approval-voting/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/node/core/approval-voting/src/lib.rs b/node/core/approval-voting/src/lib.rs index 86905a5b8ca7..4d097946d5d2 100644 --- a/node/core/approval-voting/src/lib.rs +++ b/node/core/approval-voting/src/lib.rs @@ -2470,7 +2470,7 @@ where )), }; - gum::debug!( + gum::trace!( target: LOG_TARGET, "Received approval for num_candidates {:}", approval.candidate_indices.count_ones() @@ -3359,7 +3359,7 @@ async fn maybe_create_signature( }, }; - gum::debug!( + gum::trace!( target: LOG_TARGET, "Candidates pending signatures {:}", block_entry.num_candidates_pending_signature() ); @@ -3477,7 +3477,7 @@ async fn maybe_create_signature( }, )); - gum::debug!( + gum::trace!( target: LOG_TARGET, ?block_hash, signed_candidates = ?block_entry.num_candidates_pending_signature(), From 759fe213acd6071b4a2bfebcef08be8a5f2ecc15 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Tue, 22 Aug 2023 17:12:13 +0200 Subject: [PATCH 43/54] Fix xcm-builder mock (#7652) * Fix xcm-builder mock (preparation for monorepo) The CI fails here when the runtime-benchmarks feature is enabled in the workspace. Signed-off-by: Oliver Tale-Yazdi * Update xcm/xcm-builder/Cargo.toml --------- Signed-off-by: Oliver Tale-Yazdi --- xcm/xcm-builder/Cargo.toml | 1 + xcm/xcm-builder/src/tests/pay/mock.rs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/xcm/xcm-builder/Cargo.toml b/xcm/xcm-builder/Cargo.toml index 702d5bd7fa06..6e4db1016864 100644 --- a/xcm/xcm-builder/Cargo.toml +++ b/xcm/xcm-builder/Cargo.toml @@ -42,6 +42,7 @@ runtime-benchmarks = [ "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", "xcm-executor/runtime-benchmarks", + "pallet-assets/runtime-benchmarks", ] std = [ "log/std", diff --git a/xcm/xcm-builder/src/tests/pay/mock.rs b/xcm/xcm-builder/src/tests/pay/mock.rs index 3231611d3dd8..c663b0a4d76f 100644 --- a/xcm/xcm-builder/src/tests/pay/mock.rs +++ b/xcm/xcm-builder/src/tests/pay/mock.rs @@ -119,6 +119,8 @@ impl pallet_assets::Config for Test { type RemoveItemsLimit = RemoveItemsLimit; type AssetIdParameter = AssetIdForAssets; type CallbackHandle = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); } parameter_types! { From e8ac355f0bc3578aa536ac135410d9432978b76a Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe Date: Wed, 23 Aug 2023 11:08:47 +0300 Subject: [PATCH 44/54] Do not cleanup peer-knowledge on peer-connected/disconnected Signed-off-by: Alexandru Gheorghe --- node/network/approval-distribution/src/lib.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/node/network/approval-distribution/src/lib.rs b/node/network/approval-distribution/src/lib.rs index c457b33a0547..7c393891e0e8 100644 --- a/node/network/approval-distribution/src/lib.rs +++ b/node/network/approval-distribution/src/lib.rs @@ -599,9 +599,10 @@ impl State { NetworkBridgeEvent::PeerDisconnected(peer_id) => { gum::trace!(target: LOG_TARGET, ?peer_id, "Peer disconnected"); self.peer_views.remove(&peer_id); - self.blocks.iter_mut().for_each(|(_hash, entry)| { - entry.known_by.remove(&peer_id); - }) + // TODO: Test some hypot + // self.blocks.iter_mut().for_each(|(_hash, entry)| { + // entry.known_by.remove(&peer_id); + // }) }, NetworkBridgeEvent::NewGossipTopology(topology) => { self.handle_new_session_topology( From 7f0eb5f06e4a403b10be1f7893859dde79f4d813 Mon Sep 17 00:00:00 2001 From: Juan Date: Wed, 23 Aug 2023 13:39:43 +0200 Subject: [PATCH 45/54] Companion: restructure macro related exports (#7626) * move RuntimeDebug out of frame_support * move RuntimeDebug out of frame_support * fix xcm export * ".git/.scripts/commands/fmt/fmt.sh" * fix xcm intefration tests * fix cargo lock for xcm intefration tests * wip * restructure benchmarking macro related exports * update cargo lock --------- Co-authored-by: parity-processbot <> --- Cargo.lock | 5 +++++ runtime/kusama/Cargo.toml | 1 + runtime/kusama/src/lib.rs | 7 ++++--- runtime/polkadot/Cargo.toml | 1 + runtime/polkadot/src/lib.rs | 7 ++++--- runtime/rococo/Cargo.toml | 1 + runtime/rococo/src/lib.rs | 7 ++++--- runtime/westend/Cargo.toml | 1 + runtime/westend/src/lib.rs | 3 ++- xcm/xcm-executor/integration-tests/Cargo.toml | 1 + xcm/xcm-executor/integration-tests/src/lib.rs | 3 ++- xcm/xcm-simulator/example/src/lib.rs | 2 +- 12 files changed, 27 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5b19bc139384..61f838f8c35e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4387,6 +4387,7 @@ dependencies = [ "sp-session", "sp-staking", "sp-std", + "sp-storage", "sp-tracing", "sp-transaction-pool", "sp-trie", @@ -8405,6 +8406,7 @@ dependencies = [ "sp-session", "sp-staking", "sp-std", + "sp-storage", "sp-tracing", "sp-transaction-pool", "sp-trie", @@ -9834,6 +9836,7 @@ dependencies = [ "sp-session", "sp-staking", "sp-std", + "sp-storage", "sp-tracing", "sp-transaction-pool", "sp-trie", @@ -14745,6 +14748,7 @@ dependencies = [ "sp-session", "sp-staking", "sp-std", + "sp-storage", "sp-tracing", "sp-transaction-pool", "sp-version", @@ -15184,6 +15188,7 @@ dependencies = [ "frame-system", "futures", "pallet-xcm", + "parity-scale-codec", "polkadot-test-client", "polkadot-test-runtime", "polkadot-test-service", diff --git a/runtime/kusama/Cargo.toml b/runtime/kusama/Cargo.toml index a88654d26121..3a253f47dde6 100644 --- a/runtime/kusama/Cargo.toml +++ b/runtime/kusama/Cargo.toml @@ -34,6 +34,7 @@ sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master sp-staking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-session = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-storage = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-version = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } tx-pool-api = { package = "sp-transaction-pool", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } block-builder-api = { package = "sp-block-builder", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } diff --git a/runtime/kusama/src/lib.rs b/runtime/kusama/src/lib.rs index 3d6a56cfecbd..e9e3fb2d2026 100644 --- a/runtime/kusama/src/lib.rs +++ b/runtime/kusama/src/lib.rs @@ -67,7 +67,7 @@ use frame_support::{ PrivilegeCmp, ProcessMessage, ProcessMessageError, StorageMapShim, WithdrawReasons, }, weights::{ConstantMultiplier, WeightMeter}, - PalletId, RuntimeDebug, + PalletId, }; use frame_system::EnsureRoot; use pallet_grandpa::{fg_primitives, AuthorityId as GrandpaId}; @@ -82,7 +82,7 @@ use sp_runtime::{ Keccak256, OpaqueKeys, SaturatedConversion, Verify, }, transaction_validity::{TransactionPriority, TransactionSource, TransactionValidity}, - ApplyExtrinsicResult, FixedU128, KeyTypeId, Perbill, Percent, Permill, + ApplyExtrinsicResult, FixedU128, KeyTypeId, Perbill, Percent, Permill, RuntimeDebug, }; use sp_staking::SessionIndex; #[cfg(any(feature = "std", test))] @@ -2330,7 +2330,8 @@ sp_api::impl_runtime_apis! { sp_runtime::RuntimeString, > { use frame_support::traits::WhitelistedStorageKeys; - use frame_benchmarking::{Benchmarking, BenchmarkBatch, TrackedStorageKey, BenchmarkError}; + use frame_benchmarking::{Benchmarking, BenchmarkBatch, BenchmarkError}; + use sp_storage::TrackedStorageKey; // Trying to add benchmarks directly to some pallets caused cyclic dependency issues. // To get around that, we separated the benchmarks into its own crate. use pallet_session_benchmarking::Pallet as SessionBench; diff --git a/runtime/polkadot/Cargo.toml b/runtime/polkadot/Cargo.toml index 5d343811fb14..76a01051a122 100644 --- a/runtime/polkadot/Cargo.toml +++ b/runtime/polkadot/Cargo.toml @@ -33,6 +33,7 @@ sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master sp-staking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-session = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-storage = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-version = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-npos-elections = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } diff --git a/runtime/polkadot/src/lib.rs b/runtime/polkadot/src/lib.rs index dca0d0d986da..c4458076cb3d 100644 --- a/runtime/polkadot/src/lib.rs +++ b/runtime/polkadot/src/lib.rs @@ -51,7 +51,7 @@ use frame_support::{ ProcessMessage, ProcessMessageError, WithdrawReasons, }, weights::{ConstantMultiplier, WeightMeter}, - PalletId, RuntimeDebug, + PalletId, }; use frame_system::EnsureRoot; use pallet_grandpa::{fg_primitives, AuthorityId as GrandpaId}; @@ -78,7 +78,7 @@ use sp_runtime::{ OpaqueKeys, SaturatedConversion, Verify, }, transaction_validity::{TransactionPriority, TransactionSource, TransactionValidity}, - ApplyExtrinsicResult, FixedU128, KeyTypeId, Perbill, Percent, Permill, + ApplyExtrinsicResult, FixedU128, KeyTypeId, Perbill, Percent, Permill, RuntimeDebug, }; use sp_staking::SessionIndex; use sp_std::{cmp::Ordering, collections::btree_map::BTreeMap, prelude::*}; @@ -2077,7 +2077,8 @@ sp_api::impl_runtime_apis! { sp_runtime::RuntimeString, > { use frame_support::traits::WhitelistedStorageKeys; - use frame_benchmarking::{Benchmarking, BenchmarkBatch, TrackedStorageKey, BenchmarkError}; + use frame_benchmarking::{Benchmarking, BenchmarkBatch, BenchmarkError}; + use sp_storage::TrackedStorageKey; // Trying to add benchmarks directly to some pallets caused cyclic dependency issues. // To get around that, we separated the benchmarks into its own crate. use pallet_session_benchmarking::Pallet as SessionBench; diff --git a/runtime/rococo/Cargo.toml b/runtime/rococo/Cargo.toml index 95791edda710..527bc1af9ccc 100644 --- a/runtime/rococo/Cargo.toml +++ b/runtime/rococo/Cargo.toml @@ -30,6 +30,7 @@ sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master sp-staking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-session = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-storage = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-version = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } tx-pool-api = { package = "sp-transaction-pool", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } block-builder-api = { package = "sp-block-builder", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } diff --git a/runtime/rococo/src/lib.rs b/runtime/rococo/src/lib.rs index 145b25d8aa9a..6894bd7bbf44 100644 --- a/runtime/rococo/src/lib.rs +++ b/runtime/rococo/src/lib.rs @@ -64,7 +64,7 @@ use frame_support::{ PrivilegeCmp, ProcessMessage, ProcessMessageError, StorageMapShim, WithdrawReasons, }, weights::{ConstantMultiplier, WeightMeter}, - PalletId, RuntimeDebug, + PalletId, }; use frame_system::EnsureRoot; use pallet_grandpa::{fg_primitives, AuthorityId as GrandpaId}; @@ -79,7 +79,7 @@ use sp_runtime::{ Extrinsic as ExtrinsicT, Keccak256, OpaqueKeys, SaturatedConversion, Verify, }, transaction_validity::{TransactionPriority, TransactionSource, TransactionValidity}, - ApplyExtrinsicResult, FixedU128, KeyTypeId, Perbill, Percent, Permill, + ApplyExtrinsicResult, FixedU128, KeyTypeId, Perbill, Percent, Permill, RuntimeDebug, }; use sp_staking::SessionIndex; #[cfg(any(feature = "std", test))] @@ -2114,9 +2114,10 @@ sp_api::impl_runtime_apis! { sp_runtime::RuntimeString, > { use frame_support::traits::WhitelistedStorageKeys; - use frame_benchmarking::{Benchmarking, BenchmarkBatch, TrackedStorageKey, BenchmarkError}; + use frame_benchmarking::{Benchmarking, BenchmarkBatch, BenchmarkError}; use frame_system_benchmarking::Pallet as SystemBench; use frame_benchmarking::baseline::Pallet as Baseline; + use sp_storage::TrackedStorageKey; use xcm::latest::prelude::*; use xcm_config::{ LocalCheckAccount, LocationConverter, Rockmine, TokenLocation, XcmConfig, diff --git a/runtime/westend/Cargo.toml b/runtime/westend/Cargo.toml index a0b13145578d..0231198ba820 100644 --- a/runtime/westend/Cargo.toml +++ b/runtime/westend/Cargo.toml @@ -31,6 +31,7 @@ sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master sp-staking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-session = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-storage = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-version = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } tx-pool-api = { package = "sp-transaction-pool", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } block-builder-api = { package = "sp-block-builder", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } diff --git a/runtime/westend/src/lib.rs b/runtime/westend/src/lib.rs index 62770e0992fb..9ae30c376010 100644 --- a/runtime/westend/src/lib.rs +++ b/runtime/westend/src/lib.rs @@ -2006,7 +2006,8 @@ sp_api::impl_runtime_apis! { sp_runtime::RuntimeString, > { use frame_support::traits::WhitelistedStorageKeys; - use frame_benchmarking::{Benchmarking, BenchmarkBatch, TrackedStorageKey, BenchmarkError}; + use frame_benchmarking::{Benchmarking, BenchmarkBatch, BenchmarkError}; + use sp_storage::TrackedStorageKey; // Trying to add benchmarks directly to some pallets caused cyclic dependency issues. // To get around that, we separated the benchmarks into its own crate. use pallet_session_benchmarking::Pallet as SessionBench; diff --git a/xcm/xcm-executor/integration-tests/Cargo.toml b/xcm/xcm-executor/integration-tests/Cargo.toml index 18a729e082d2..cec9754baa31 100644 --- a/xcm/xcm-executor/integration-tests/Cargo.toml +++ b/xcm/xcm-executor/integration-tests/Cargo.toml @@ -8,6 +8,7 @@ version.workspace = true publish = false [dependencies] +codec = { package = "parity-scale-codec", version = "3.6.1" } frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } frame-system = { git = "https://github.com/paritytech/substrate", branch = "master" } futures = "0.3.21" diff --git a/xcm/xcm-executor/integration-tests/src/lib.rs b/xcm/xcm-executor/integration-tests/src/lib.rs index 111833e67cfb..d8c77f8317e1 100644 --- a/xcm/xcm-executor/integration-tests/src/lib.rs +++ b/xcm/xcm-executor/integration-tests/src/lib.rs @@ -17,7 +17,8 @@ #![cfg_attr(not(feature = "std"), no_std)] #![cfg(test)] -use frame_support::{codec::Encode, dispatch::GetDispatchInfo, weights::Weight}; +use codec::Encode; +use frame_support::{dispatch::GetDispatchInfo, weights::Weight}; use polkadot_test_client::{ BlockBuilderExt, ClientBlockImportExt, DefaultTestClientBuilderExt, InitPolkadotBlockBuilder, TestClientBuilder, TestClientBuilderExt, diff --git a/xcm/xcm-simulator/example/src/lib.rs b/xcm/xcm-simulator/example/src/lib.rs index 1f6956485a84..85b8ad1c5cb7 100644 --- a/xcm/xcm-simulator/example/src/lib.rs +++ b/xcm/xcm-simulator/example/src/lib.rs @@ -17,8 +17,8 @@ mod parachain; mod relay_chain; -use frame_support::sp_tracing; use sp_runtime::BuildStorage; +use sp_tracing; use xcm::prelude::*; use xcm_executor::traits::ConvertLocation; use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain, TestExt}; From 7972d31404ce1edcdd8a81f4a4dfd97bb4a55102 Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe Date: Wed, 23 Aug 2023 15:39:22 +0300 Subject: [PATCH 46/54] Revert some hacks Signed-off-by: Alexandru Gheorghe --- node/core/approval-voting/src/lib.rs | 2 +- node/network/approval-distribution/src/lib.rs | 7 +++---- node/network/protocol/src/peer_set.rs | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/node/core/approval-voting/src/lib.rs b/node/core/approval-voting/src/lib.rs index 4d097946d5d2..9f0a0a2ad42c 100644 --- a/node/core/approval-voting/src/lib.rs +++ b/node/core/approval-voting/src/lib.rs @@ -3351,7 +3351,7 @@ async fn maybe_create_signature( None => { // not a cause for alarm - just lost a race with pruning, most likely. metrics.on_approval_stale(); - gum::info!( + gum::debug!( target: LOG_TARGET, "Could not find block that needs signature {:}", block_hash ); diff --git a/node/network/approval-distribution/src/lib.rs b/node/network/approval-distribution/src/lib.rs index 7c393891e0e8..c457b33a0547 100644 --- a/node/network/approval-distribution/src/lib.rs +++ b/node/network/approval-distribution/src/lib.rs @@ -599,10 +599,9 @@ impl State { NetworkBridgeEvent::PeerDisconnected(peer_id) => { gum::trace!(target: LOG_TARGET, ?peer_id, "Peer disconnected"); self.peer_views.remove(&peer_id); - // TODO: Test some hypot - // self.blocks.iter_mut().for_each(|(_hash, entry)| { - // entry.known_by.remove(&peer_id); - // }) + self.blocks.iter_mut().for_each(|(_hash, entry)| { + entry.known_by.remove(&peer_id); + }) }, NetworkBridgeEvent::NewGossipTopology(topology) => { self.handle_new_session_topology( diff --git a/node/network/protocol/src/peer_set.rs b/node/network/protocol/src/peer_set.rs index fa7fd508e48c..b6f8c9dec231 100644 --- a/node/network/protocol/src/peer_set.rs +++ b/node/network/protocol/src/peer_set.rs @@ -120,7 +120,7 @@ impl PeerSet { pub fn get_main_version(self) -> ProtocolVersion { #[cfg(not(feature = "network-protocol-staging"))] match self { - PeerSet::Validation => ValidationVersion::VStaging.into(), + PeerSet::Validation => ValidationVersion::V1.into(), PeerSet::Collation => CollationVersion::V1.into(), } From feefbbb3c3e946350f37f12a78113664cfeb3e7e Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe Date: Wed, 23 Aug 2023 15:57:17 +0300 Subject: [PATCH 47/54] Build image with network vstaging Signed-off-by: Alexandru Gheorghe --- scripts/ci/gitlab/pipeline/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/ci/gitlab/pipeline/build.yml b/scripts/ci/gitlab/pipeline/build.yml index 845ac7970108..70d1b1fc5456 100644 --- a/scripts/ci/gitlab/pipeline/build.yml +++ b/scripts/ci/gitlab/pipeline/build.yml @@ -21,7 +21,7 @@ build-linux-stable: # Ensure we run the UI tests. RUN_UI_TESTS: 1 script: - - time cargo build --locked --profile testnet --features pyroscope,fast-runtime --verbose --bins + - time cargo build --locked --profile testnet --features pyroscope,fast-runtime,network-protocol-staging --verbose --bins # pack artifacts - mkdir -p ./artifacts - VERSION="${CI_COMMIT_REF_NAME}" # will be tag or branch name From 71c08a40c00201846fdfc983fa58d1d5b2efbaac Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe Date: Wed, 23 Aug 2023 20:38:17 +0300 Subject: [PATCH 48/54] Fixup sending approvals more than once Signed-off-by: Alexandru Gheorghe --- node/network/approval-distribution/src/lib.rs | 48 ++++++++++++++----- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/node/network/approval-distribution/src/lib.rs b/node/network/approval-distribution/src/lib.rs index bcdae2570681..919ec7ffc961 100644 --- a/node/network/approval-distribution/src/lib.rs +++ b/node/network/approval-distribution/src/lib.rs @@ -1518,6 +1518,7 @@ impl State { ) .await; metrics.on_approval_invalid_block(); + } else { metrics.on_approval_recent_outdated(); } @@ -1836,7 +1837,7 @@ impl State { let _timer = metrics.time_unify_with_peer(); let mut assignments_to_send = Vec::new(); - let mut approvals_to_send = Vec::new(); + let mut approvals_to_send = HashMap::new(); let view_finalized_number = view.finalized_number; for head in view.into_iter() { @@ -1930,8 +1931,21 @@ impl State { (should_forward_approval, new_covered_approvals) }, ); + if should_forward_approval { - approvals_to_send.push(approval_message); + if !approvals_to_send.contains_key(&( + approval_message.validator, + approval_message.candidate_indices.clone(), + )) { + approvals_to_send.insert( + ( + approval_message.validator, + approval_message.candidate_indices.clone(), + ), + approval_message, + ); + } + candidates_covered_by_approvals.into_iter().for_each( |(approval_knowledge, message_kind)| { peer_knowledge.sent.insert(approval_knowledge, message_kind); @@ -1971,8 +1985,12 @@ impl State { "Sending approvals to unified peer", ); - send_approvals_batched(sender, approvals_to_send, &vec![(peer_id, protocol_version)]) - .await; + send_approvals_batched( + sender, + approvals_to_send.into_values().collect_vec(), + &vec![(peer_id, protocol_version)], + ) + .await; } } @@ -2242,19 +2260,25 @@ async fn adjust_required_routing_and_propagate Date: Thu, 24 Aug 2023 00:04:45 +0300 Subject: [PATCH 49/54] Fixup unify_with_peer Signed-off-by: Alexandru Gheorghe --- node/network/approval-distribution/src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/node/network/approval-distribution/src/lib.rs b/node/network/approval-distribution/src/lib.rs index 919ec7ffc961..3b8624e9166d 100644 --- a/node/network/approval-distribution/src/lib.rs +++ b/node/network/approval-distribution/src/lib.rs @@ -1934,11 +1934,13 @@ impl State { if should_forward_approval { if !approvals_to_send.contains_key(&( + approval_message.block_hash, approval_message.validator, approval_message.candidate_indices.clone(), )) { approvals_to_send.insert( ( + approval_message.block_hash, approval_message.validator, approval_message.candidate_indices.clone(), ), @@ -2269,11 +2271,13 @@ async fn adjust_required_routing_and_propagate Date: Thu, 24 Aug 2023 10:19:27 +0000 Subject: [PATCH 50/54] Bump chevdor/srtool-actions from 0.7.0 to 0.8.0 (#7660) Bumps [chevdor/srtool-actions](https://github.com/chevdor/srtool-actions) from 0.7.0 to 0.8.0. - [Release notes](https://github.com/chevdor/srtool-actions/releases) - [Commits](https://github.com/chevdor/srtool-actions/compare/v0.7.0...v0.8.0) --- updated-dependencies: - dependency-name: chevdor/srtool-actions dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release-30_publish-draft-release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-30_publish-draft-release.yml b/.github/workflows/release-30_publish-draft-release.yml index ce6f31fa5483..206b1871d80a 100644 --- a/.github/workflows/release-30_publish-draft-release.yml +++ b/.github/workflows/release-30_publish-draft-release.yml @@ -40,7 +40,7 @@ jobs: - name: Build ${{ matrix.runtime }} runtime id: srtool_build - uses: chevdor/srtool-actions@v0.7.0 + uses: chevdor/srtool-actions@v0.8.0 with: image: paritytech/srtool chain: ${{ matrix.runtime }} From ebb610fc320c533dee7383b0aa687a29a61dceb0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 24 Aug 2023 13:08:44 +0200 Subject: [PATCH 51/54] Bump rustls-webpki from 0.101.2 to 0.101.4 (#7653) Bumps [rustls-webpki](https://github.com/rustls/webpki) from 0.101.2 to 0.101.4. - [Release notes](https://github.com/rustls/webpki/releases) - [Commits](https://github.com/rustls/webpki/compare/v/0.101.2...v/0.101.4) --- updated-dependencies: - dependency-name: rustls-webpki dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 61f838f8c35e..3271f6671401 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10061,9 +10061,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.101.2" +version = "0.101.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "513722fd73ad80a71f72b61009ea1b584bcfa1483ca93949c8f290298837fa59" +checksum = "7d93931baf2d282fff8d3a532bbfd7653f734643161b87e3e01e59a04439bf0d" dependencies = [ "ring 0.16.20", "untrusted", From f3da93d3e28e990fc52c5e9d5fb0fac7154d97e4 Mon Sep 17 00:00:00 2001 From: Chris Sosnin <48099298+slumber@users.noreply.github.com> Date: Thu, 24 Aug 2023 15:00:10 +0300 Subject: [PATCH 52/54] extend abridged host config (#7659) added asynchronous backing params --- primitives/src/v5/mod.rs | 2 ++ runtime/parachains/src/configuration.rs | 4 ++-- runtime/parachains/src/configuration/tests.rs | 5 ++++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/primitives/src/v5/mod.rs b/primitives/src/v5/mod.rs index 4e107c881d4e..59fb6c927b2d 100644 --- a/primitives/src/v5/mod.rs +++ b/primitives/src/v5/mod.rs @@ -1165,6 +1165,8 @@ pub struct AbridgedHostConfiguration { pub validation_upgrade_cooldown: BlockNumber, /// The delay, in blocks, before a validation upgrade is applied. pub validation_upgrade_delay: BlockNumber, + /// Asynchronous backing parameters. + pub async_backing_params: super::vstaging::AsyncBackingParams, } /// Abridged version of `HrmpChannel` (from the `Hrmp` parachains host runtime module) meant to be diff --git a/runtime/parachains/src/configuration.rs b/runtime/parachains/src/configuration.rs index 03d1ae420495..accc01a2b180 100644 --- a/runtime/parachains/src/configuration.rs +++ b/runtime/parachains/src/configuration.rs @@ -124,13 +124,13 @@ pub struct HostConfiguration { /// /// [#4601]: https://github.com/paritytech/polkadot/issues/4601 pub validation_upgrade_delay: BlockNumber, + /// Asynchronous backing parameters. + pub async_backing_params: AsyncBackingParams, /** * The parameters that are not essential, but still may be of interest for parachains. */ - /// Asynchronous backing parameters. - pub async_backing_params: AsyncBackingParams, /// The maximum POV block size, in bytes. pub max_pov_size: u32, /// The maximum size of a message that can be put in a downward message queue. diff --git a/runtime/parachains/src/configuration/tests.rs b/runtime/parachains/src/configuration/tests.rs index 83de7db932b4..43c03067a9a7 100644 --- a/runtime/parachains/src/configuration/tests.rs +++ b/runtime/parachains/src/configuration/tests.rs @@ -487,7 +487,9 @@ fn verify_externally_accessible() { use primitives::{well_known_keys, AbridgedHostConfiguration}; new_test_ext(Default::default()).execute_with(|| { - let ground_truth = HostConfiguration::default(); + let mut ground_truth = HostConfiguration::default(); + ground_truth.async_backing_params = + AsyncBackingParams { allowed_ancestry_len: 111, max_candidate_depth: 222 }; // Make sure that the configuration is stored in the storage. ActiveConfig::::put(ground_truth.clone()); @@ -511,6 +513,7 @@ fn verify_externally_accessible() { hrmp_max_message_num_per_candidate: ground_truth.hrmp_max_message_num_per_candidate, validation_upgrade_cooldown: ground_truth.validation_upgrade_cooldown, validation_upgrade_delay: ground_truth.validation_upgrade_delay, + async_backing_params: ground_truth.async_backing_params, }, ); }); From 598f4c6b550fed897f050b74481718783425e53c Mon Sep 17 00:00:00 2001 From: Adrian Catangiu Date: Fri, 25 Aug 2023 06:16:22 +0300 Subject: [PATCH 53/54] cli: disallow BEEFY and warp sync together (#7661) Signed-off-by: Adrian Catangiu --- cli/src/command.rs | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/cli/src/command.rs b/cli/src/command.rs index 6b36e6895b6e..a2a00d0ebd3f 100644 --- a/cli/src/command.rs +++ b/cli/src/command.rs @@ -17,7 +17,7 @@ use crate::cli::{Cli, Subcommand, NODE_VERSION}; use frame_benchmarking_cli::{BenchmarkCmd, ExtrinsicFactory, SUBSTRATE_REFERENCE_HARDWARE}; use futures::future::TryFutureExt; -use log::info; +use log::{info, warn}; use sc_cli::SubstrateCli; use service::{ self, @@ -240,8 +240,24 @@ where .map_err(Error::from)?; let chain_spec = &runner.config().chain_spec; - // By default, enable BEEFY on all networks except Polkadot (for now). - let enable_beefy = !chain_spec.is_polkadot() && !cli.run.no_beefy; + // By default, enable BEEFY on all networks except Polkadot (for now), unless + // explicitly disabled through CLI. + let mut enable_beefy = !chain_spec.is_polkadot() && !cli.run.no_beefy; + // BEEFY doesn't (yet) support warp sync: + // Until we implement https://github.com/paritytech/substrate/issues/14756 + // - disallow warp sync for validators, + // - disable BEEFY when warp sync for non-validators. + if enable_beefy && runner.config().network.sync_mode.is_warp() { + if runner.config().role.is_authority() { + return Err(Error::Other( + "Warp sync not supported for validator nodes running BEEFY.".into(), + )) + } else { + // disable BEEFY for non-validator nodes that are warp syncing + warn!("🥩 BEEFY not supported when warp syncing. Disabling BEEFY."); + enable_beefy = false; + } + } set_default_ss58_version(chain_spec); From 52209dcfe546ff39cc031b92d64e787e7e8264d4 Mon Sep 17 00:00:00 2001 From: Joyce Siqueira <98593770+the-right-joyce@users.noreply.github.com> Date: Fri, 25 Aug 2023 10:04:43 +0200 Subject: [PATCH 54/54] update readme: archived repo (#7654) * Update README.md * Update README.md --- README.md | 265 ++---------------------------------------------------- 1 file changed, 7 insertions(+), 258 deletions(-) diff --git a/README.md b/README.md index dcebcf727754..59e0cff015d3 100644 --- a/README.md +++ b/README.md @@ -1,264 +1,13 @@ -# Polkadot +Dear contributors and users, -Implementation of a node in Rust based on the Substrate framework. +We would like to inform you that we have recently made significant changes to our repository structure. In order to streamline our development process and foster better contributions, we have merged three separate repositories Cumulus, Substrate and Polkadot into a single new repository: [the Polkadot SDK](https://github.com/paritytech/polkadot-sdk). Go ahead and make sure to support us by giving a star ⭐️ to the new repo. -> **NOTE:** In 2018, we split our implementation of "Polkadot" from its development framework -> "Substrate". See the [Substrate][substrate-repo] repo for git history prior to 2018. +By consolidating our codebase, we aim to enhance collaboration and provide a more efficient platform for future development. -[substrate-repo]: https://github.com/paritytech/substrate +If you currently have an open pull request in any of the merged repositories, we kindly request that you resubmit your PR in the new repository. This will ensure that your contributions are considered within the updated context and enable us to review and merge them more effectively. -This repo contains runtimes for the Polkadot, Kusama, and Westend networks. The README provides -information about installing the `polkadot` binary and developing on the codebase. For more -specific guides, like how to be a validator, see the -[Polkadot Wiki](https://wiki.polkadot.network/docs/getting-started). +We appreciate your understanding and ongoing support throughout this transition. Should you have any questions or require further assistance, please don't hesitate to [reach out to us](https://forum.polkadot.network/t/psa-parity-is-currently-working-on-merging-the-polkadot-stack-repositories-into-one-single-repository/2883). -## Installation +Best Regards, -If you just wish to run a Polkadot node without compiling it yourself, you may -either run the latest binary from our -[releases](https://github.com/paritytech/polkadot/releases) page, or install -Polkadot from one of our package repositories. - -Installation from the Debian repository will create a `systemd` -service that can be used to run a Polkadot node. This is disabled by default, -and can be started by running `systemctl start polkadot` on demand (use -`systemctl enable polkadot` to make it auto-start after reboot). By default, it -will run as the `polkadot` user. Command-line flags passed to the binary can -be customized by editing `/etc/default/polkadot`. This file will not be -overwritten on updating polkadot. You may also just run the node directly from -the command-line. - -### Debian-based (Debian, Ubuntu) - -Currently supports Debian 10 (Buster) and Ubuntu 20.04 (Focal), and -derivatives. Run the following commands as the `root` user. - -```bash -# Import the security@parity.io GPG key -gpg --recv-keys --keyserver hkps://keys.mailvelope.com 9D4B2B6EB8F97156D19669A9FF0812D491B96798 -gpg --export 9D4B2B6EB8F97156D19669A9FF0812D491B96798 > /usr/share/keyrings/parity.gpg -# Add the Parity repository and update the package index -echo 'deb [signed-by=/usr/share/keyrings/parity.gpg] https://releases.parity.io/deb release main' > /etc/apt/sources.list.d/parity.list -apt update -# Install the `parity-keyring` package - This will ensure the GPG key -# used by APT remains up-to-date -apt install parity-keyring -# Install polkadot -apt install polkadot - -``` - -## Building - -### Install via Cargo - -Make sure you have the support software installed from the **Build from Source** section -below this section. - -If you want to install Polkadot in your PATH, you can do so with: - -```bash -cargo install --git https://github.com/paritytech/polkadot --tag polkadot --locked -``` - -### Build from Source - -If you'd like to build from source, first install Rust. You may need to add Cargo's bin directory -to your PATH environment variable. Restarting your computer will do this for you automatically. - -```bash -curl https://sh.rustup.rs -sSf | sh -``` - -If you already have Rust installed, make sure you're using the latest version by running: - -```bash -rustup update -``` - -Once done, finish installing the support software: - -```bash -sudo apt install build-essential git clang libclang-dev pkg-config libssl-dev protobuf-compiler -``` - -Build the client by cloning this repository and running the following commands from the root -directory of the repo: - -```bash -git checkout -./scripts/init.sh -cargo build --release -``` - -**Note:** compilation is a memory intensive process. We recommend having 4 GiB of physical RAM or swap available (keep in mind that if a build hits swap it tends to be very slow). - -**Note:** if you want to move the built `polkadot` binary somewhere (e.g. into $PATH) you will also need to move `polkadot-execute-worker` and `polkadot-prepare-worker`. You can let cargo do all this for you by running: - -```sh -cargo install --path . --locked -``` - -#### Build from Source with Docker - -You can also build from source using -[Parity CI docker image](https://github.com/paritytech/scripts/tree/master/dockerfiles/ci-linux): - -```bash -git checkout -docker run --rm -it -w /shellhere/polkadot \ - -v $(pwd):/shellhere/polkadot \ - paritytech/ci-linux:production cargo build --release -sudo chown -R $(id -u):$(id -g) target/ -``` - -If you want to reproduce other steps of CI process you can use the following -[guide](https://github.com/paritytech/scripts#gitlab-ci-for-building-docker-images). - -## Networks - -This repo supports runtimes for Polkadot, Kusama, and Westend. - -### Connect to Polkadot Mainnet - -Connect to the global Polkadot Mainnet network by running: - -```bash -./target/release/polkadot --chain=polkadot -``` - -You can see your node on [telemetry] (set a custom name with `--name "my custom name"`). - -[telemetry]: https://telemetry.polkadot.io/#list/Polkadot - -### Connect to the "Kusama" Canary Network - -Connect to the global Kusama canary network by running: - -```bash -./target/release/polkadot --chain=kusama -``` - -You can see your node on [telemetry] (set a custom name with `--name "my custom name"`). - -[telemetry]: https://telemetry.polkadot.io/#list/Kusama - -### Connect to the Westend Testnet - -Connect to the global Westend testnet by running: - -```bash -./target/release/polkadot --chain=westend -``` - -You can see your node on [telemetry] (set a custom name with `--name "my custom name"`). - -[telemetry]: https://telemetry.polkadot.io/#list/Westend - -### Obtaining DOTs - -If you want to do anything on Polkadot, Kusama, or Westend, then you'll need to get an account and -some DOT, KSM, or WND tokens, respectively. See the -[claims instructions](https://claims.polkadot.network/) for Polkadot if you have DOTs to claim. For -Westend's WND tokens, see the faucet -[instructions](https://wiki.polkadot.network/docs/learn-DOT#getting-westies) on the Wiki. - -## Hacking on Polkadot - -If you'd actually like to hack on Polkadot, you can grab the source code and build it. Ensure you have -Rust and the support software installed. This script will install or update Rust and install the -required dependencies (this may take up to 30 minutes on Mac machines): - -```bash -curl https://getsubstrate.io -sSf | bash -s -- --fast -``` - -Then, grab the Polkadot source code: - -```bash -git clone https://github.com/paritytech/polkadot.git -cd polkadot -``` - -Then build the code. You will need to build in release mode (`--release`) to start a network. Only -use debug mode for development (faster compile times for development and testing). - -```bash -./scripts/init.sh # Install WebAssembly. Update Rust -cargo build # Builds all native code -``` - -You can run the tests if you like: - -```bash -cargo test --workspace --release -``` - -You can start a development chain with: - -```bash -cargo build -cargo run -- --dev -``` - -Detailed logs may be shown by running the node with the following environment variables set: - -```bash -RUST_LOG=debug RUST_BACKTRACE=1 cargo run -- --dev -``` - -### Development - -You can run a simple single-node development "network" on your machine by running: - -```bash -polkadot --dev -``` - -You can muck around by heading to and choose "Local Node" from the -Settings menu. - -### Local Two-node Testnet - -If you want to see the multi-node consensus algorithm in action locally, then you can create a -local testnet. You'll need two terminals open. In one, run: - -```bash -polkadot --chain=polkadot-local --alice -d /tmp/alice -``` - -And in the other, run: - -```bash -polkadot --chain=polkadot-local --bob -d /tmp/bob --port 30334 --bootnodes '/ip4/127.0.0.1/tcp/30333/p2p/ALICE_BOOTNODE_ID_HERE' -``` - -Ensure you replace `ALICE_BOOTNODE_ID_HERE` with the node ID from the output of the first terminal. - -### Monitoring - -[Setup Prometheus and Grafana](https://wiki.polkadot.network/docs/maintain-guides-how-to-monitor-your-node). - -Once you set this up you can take a look at the [Polkadot Grafana dashboards](grafana/README.md) that we currently maintain. - -### Using Docker - -[Using Docker](doc/docker.md) - -### Shell Completion - -[Shell Completion](doc/shell-completion.md) - -## Contributing - -### Contributing Guidelines - -[Contribution Guidelines](CONTRIBUTING.md) - -### Contributor Code of Conduct - -[Code of Conduct](CODE_OF_CONDUCT.md) - -## License - -Polkadot is [GPL 3.0 licensed](LICENSE). +Parity Technologies \ No newline at end of file