mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-14 22:41:06 +00:00
Asynchronous-backing compatible Aura, not plugged in (#2573)
* rough draft of potential parent search * get things compiling * fmt * add new function to all RelayChainInterface implementations * fix compilation * set slot and timestamp based on relay parent, prepare for find-parent * skeleton of new aura logic * fmt * introduce a collator module in the Aura crate * extract different implementations into own modules * make interface more convenient * docs and todos for lookahead * refactor basic collator to use new collator utility * some more refactoring * finish most of the control flow for new aura * introduce backend as parameter * fix compilation * fix a couple more TODOs * add an `announce_block` function to collator service * announce with barrier * rename block announcement validator to be more specific * fmt * clean up unused import errors * update references to BlockAnnounceValidator * rename unstable_reimpl * add AuraUnincludedSegmentApi * finish rename * integrate AuraUnincludedSegmentApi * add a new block announcement validator for backwards compatibility * add some naive equivocation defenses * rustfmt * clean up remaining TODO [now]s * fmt * try to fix inprocess-interface * actually fix compilation * ignored -> rejected rephrase * fix test compilation * fmt * clippy
This commit is contained in:
@@ -14,11 +14,22 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Cumulus. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use polkadot_primitives::{Hash as PHash, PersistedValidationData};
|
||||
use codec::Decode;
|
||||
use polkadot_primitives::{
|
||||
Block as PBlock, Hash as PHash, Header as PHeader, PersistedValidationData,
|
||||
};
|
||||
|
||||
use cumulus_primitives_core::{
|
||||
relay_chain::{BlockId as RBlockId, OccupiedCoreAssumption},
|
||||
ParaId,
|
||||
};
|
||||
use cumulus_relay_chain_interface::{RelayChainError, RelayChainInterface};
|
||||
|
||||
use sc_client_api::Backend;
|
||||
use sc_consensus::{shared_data::SharedData, BlockImport, ImportResult};
|
||||
use sp_consensus_slots::{Slot, SlotDuration};
|
||||
use sp_runtime::traits::{Block as BlockT, Header as HeaderT};
|
||||
use sp_timestamp::Timestamp;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -181,3 +192,193 @@ where
|
||||
pub trait ParachainBlockImportMarker {}
|
||||
|
||||
impl<B: BlockT, BI, BE> ParachainBlockImportMarker for ParachainBlockImport<B, BI, BE> {}
|
||||
|
||||
/// Parameters when searching for suitable parents to build on top of.
|
||||
pub struct ParentSearchParams {
|
||||
/// The relay-parent that is intended to be used.
|
||||
pub relay_parent: PHash,
|
||||
/// The ID of the parachain.
|
||||
pub para_id: ParaId,
|
||||
/// A limitation on the age of relay parents for parachain blocks that are being
|
||||
/// considered. This is relative to the `relay_parent` number.
|
||||
pub ancestry_lookback: usize,
|
||||
/// How "deep" parents can be relative to the included parachain block at the relay-parent.
|
||||
/// The included block has depth 0.
|
||||
pub max_depth: usize,
|
||||
/// Whether to only ignore "alternative" branches, i.e. branches of the chain
|
||||
/// which do not contain the block pending availability.
|
||||
pub ignore_alternative_branches: bool,
|
||||
}
|
||||
|
||||
/// A potential parent block returned from [`find_potential_parents`]
|
||||
pub struct PotentialParent<B: BlockT> {
|
||||
/// The hash of the block.
|
||||
pub hash: B::Hash,
|
||||
/// The header of the block.
|
||||
pub header: B::Header,
|
||||
/// The depth of the block.
|
||||
pub depth: usize,
|
||||
/// Whether the block is the included block, is itself pending on-chain, or descends
|
||||
/// from the block pending availability.
|
||||
pub aligned_with_pending: bool,
|
||||
}
|
||||
|
||||
/// Perform a recursive search through blocks to find potential
|
||||
/// parent blocks for a new block.
|
||||
///
|
||||
/// This accepts a relay-chain block to be used as an anchor and a maximum search depth,
|
||||
/// along with some arguments for filtering parachain blocks and performs a recursive search
|
||||
/// for parachain blocks. The search begins at the last included parachain block and returns
|
||||
/// a set of [`PotentialParent`]s which could be potential parents of a new block with this
|
||||
/// relay-parent according to the search parameters.
|
||||
///
|
||||
/// A parachain block is a potential parent if it is either the last included parachain block, the pending
|
||||
/// parachain block (when `max_depth` >= 1), or all of the following hold:
|
||||
/// * its parent is a potential parent
|
||||
/// * its relay-parent is within `ancestry_lookback` of the targeted relay-parent.
|
||||
/// * the block number is within `max_depth` blocks of the included block
|
||||
pub async fn find_potential_parents<B: BlockT>(
|
||||
params: ParentSearchParams,
|
||||
client: &impl sp_blockchain::Backend<B>,
|
||||
relay_client: &impl RelayChainInterface,
|
||||
) -> Result<Vec<PotentialParent<B>>, RelayChainError> {
|
||||
// 1. Build up the ancestry record of the relay chain to compare against.
|
||||
let rp_ancestry = {
|
||||
let mut ancestry = Vec::with_capacity(params.ancestry_lookback + 1);
|
||||
let mut current_rp = params.relay_parent;
|
||||
while ancestry.len() <= params.ancestry_lookback {
|
||||
let header = match relay_client.header(RBlockId::hash(current_rp)).await? {
|
||||
None => break,
|
||||
Some(h) => h,
|
||||
};
|
||||
|
||||
ancestry.push((current_rp, *header.state_root()));
|
||||
current_rp = *header.parent_hash();
|
||||
|
||||
// don't iterate back into the genesis block.
|
||||
if header.number == 1 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
ancestry
|
||||
};
|
||||
|
||||
let is_hash_in_ancestry = |hash| rp_ancestry.iter().any(|x| x.0 == hash);
|
||||
let is_root_in_ancestry = |root| rp_ancestry.iter().any(|x| x.1 == root);
|
||||
|
||||
// 2. Get the included and pending availability blocks.
|
||||
let included_header = relay_client
|
||||
.persisted_validation_data(
|
||||
params.relay_parent,
|
||||
params.para_id,
|
||||
OccupiedCoreAssumption::TimedOut,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let included_header = match included_header {
|
||||
Some(pvd) => pvd.parent_head,
|
||||
None => return Ok(Vec::new()), // this implies the para doesn't exist.
|
||||
};
|
||||
|
||||
let pending_header = relay_client
|
||||
.persisted_validation_data(
|
||||
params.relay_parent,
|
||||
params.para_id,
|
||||
OccupiedCoreAssumption::Included,
|
||||
)
|
||||
.await?
|
||||
.and_then(|x| if x.parent_head != included_header { Some(x.parent_head) } else { None });
|
||||
|
||||
let included_header = match B::Header::decode(&mut &included_header.0[..]).ok() {
|
||||
None => return Ok(Vec::new()),
|
||||
Some(x) => x,
|
||||
};
|
||||
// Silently swallow if pending block can't decode.
|
||||
let pending_header = pending_header.and_then(|p| B::Header::decode(&mut &p.0[..]).ok());
|
||||
let included_hash = included_header.hash();
|
||||
let pending_hash = pending_header.as_ref().map(|hdr| hdr.hash());
|
||||
|
||||
let mut frontier = vec![PotentialParent::<B> {
|
||||
hash: included_hash,
|
||||
header: included_header,
|
||||
depth: 0,
|
||||
aligned_with_pending: true,
|
||||
}];
|
||||
|
||||
// Recursive search through descendants of the included block which have acceptable
|
||||
// relay parents.
|
||||
let mut potential_parents = Vec::new();
|
||||
while let Some(entry) = frontier.pop() {
|
||||
let is_pending =
|
||||
entry.depth == 1 && pending_hash.as_ref().map_or(false, |h| &entry.hash == h);
|
||||
let is_included = entry.depth == 0;
|
||||
|
||||
// note: even if the pending block or included block have a relay parent
|
||||
// outside of the expected part of the relay chain, they are always allowed
|
||||
// because they have already been posted on chain.
|
||||
let is_potential = is_pending || is_included || {
|
||||
let digest = entry.header.digest();
|
||||
cumulus_primitives_core::extract_relay_parent(digest).map_or(false, is_hash_in_ancestry) ||
|
||||
cumulus_primitives_core::rpsr_digest::extract_relay_parent_storage_root(digest)
|
||||
.map(|(r, _n)| r)
|
||||
.map_or(false, is_root_in_ancestry)
|
||||
};
|
||||
|
||||
let parent_aligned_with_pending = entry.aligned_with_pending;
|
||||
let child_depth = entry.depth + 1;
|
||||
let hash = entry.hash;
|
||||
|
||||
if is_potential {
|
||||
potential_parents.push(entry);
|
||||
}
|
||||
|
||||
if !is_potential || child_depth > params.max_depth {
|
||||
continue
|
||||
}
|
||||
|
||||
// push children onto search frontier.
|
||||
for child in client.children(hash).ok().into_iter().flatten() {
|
||||
let aligned_with_pending = parent_aligned_with_pending &&
|
||||
if child_depth == 1 {
|
||||
pending_hash.as_ref().map_or(true, |h| &child == h)
|
||||
} else {
|
||||
true
|
||||
};
|
||||
|
||||
if params.ignore_alternative_branches && !aligned_with_pending {
|
||||
continue
|
||||
}
|
||||
|
||||
let header = match client.header(child) {
|
||||
Ok(Some(h)) => h,
|
||||
Ok(None) => continue,
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
frontier.push(PotentialParent {
|
||||
hash: child,
|
||||
header,
|
||||
depth: child_depth,
|
||||
aligned_with_pending,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(potential_parents)
|
||||
}
|
||||
|
||||
/// Get the relay-parent slot and timestamp from a header.
|
||||
pub fn relay_slot_and_timestamp(
|
||||
relay_parent_header: &PHeader,
|
||||
relay_chain_slot_duration: SlotDuration,
|
||||
) -> Option<(Slot, Timestamp)> {
|
||||
sc_consensus_babe::find_pre_digest::<PBlock>(relay_parent_header)
|
||||
.map(|babe_pre_digest| {
|
||||
let slot = babe_pre_digest.slot();
|
||||
let t = Timestamp::new(relay_chain_slot_duration.as_millis() * *slot);
|
||||
|
||||
(slot, t)
|
||||
})
|
||||
.ok()
|
||||
}
|
||||
|
||||
@@ -186,7 +186,7 @@ impl RelayChainInterface for Relaychain {
|
||||
}
|
||||
|
||||
async fn wait_for_block(&self, _: PHash) -> RelayChainResult<()> {
|
||||
unimplemented!("Not needed for test")
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn new_best_notification_stream(
|
||||
|
||||
Reference in New Issue
Block a user