Terminology in Polkadot
Terminology
PoV (Proof of Validity)
The PoV is the data package produced by parachain collators and sent to Relay Chain validators.
-
Purpose: It acts as the “argument” or evidence that a parachain has transitioned its state correctly
-
Contents: It typically includes the L2’s state proof and the witness data (extra data needed for verification)
-
Size: A PoV block is often around 5MB of untrusted data provided to the validator group
PVF (Parachain Validation Function)
The PVF is the specific logic or code stored on the Relay Chain that validators execute to check a parachain block
-
Purpose: It is the function that processes the PoV to ensure the parachain is proceeding correctly
-
Execution: While the code is stored on the Relay Chain, it is executed “in core” (off-chain) by validators assigned to that parachain’s core
-
Composition: In some contexts, the combination of the state proof and the parachain block is referred to collectively as the PVF
Candidate
Candidate Receipt
Compact representation of the result of a validation. This is what validators receive from collators, together with the PoV.
/// A candidate-receipt.
struct CandidateReceipt {
/// The descriptor of the candidate.
descriptor: CandidateDescriptor,
/// The hash of the encoded commitments made as a result of candidate execution.
commitments_hash: Hash,
}
Candidate Descriptor
This struct is pure description of the candidate, in a lightweight format.
/// A unique descriptor of the candidate receipt.
struct CandidateDescriptor {
/// The ID of the para this is a candidate for.
para_id: ParaId,
/// The hash of the relay-chain block this is executed in the context of.
relay_parent: Hash,
/// The collator's sr25519 public key.
collator: CollatorId,
/// The blake2-256 hash of the persisted validation data. These are extra parameters
/// derived from relay-chain state that influence the validity of the block which
/// must also be kept available for approval checkers.
persisted_validation_data_hash: Hash,
/// The blake2-256 hash of the `pov-block`.
pov_hash: Hash,
/// The root of a block's erasure encoding Merkle tree.
erasure_root: Hash,
/// Signature on blake2-256 of components of this receipt:
/// The parachain index, the relay parent, the validation data hash, and the `pov_hash`.
signature: CollatorSignature,
/// Hash of the para header that is being generated by this candidate.
para_head: Hash,
/// The blake2-256 hash of the validation code bytes.
validation_code_hash: ValidationCodeHash,
}
PersistedValidationData
The validation data provides information about how to create the inputs for validation of a candidate. This information is derived from the chain state and will vary from para to para, although some of the fields may be the same for every para.
struct PersistedValidationData {
/// The parent head-data.
parent_head: HeadData,
/// The relay-chain block number this is in the context of. This informs the collator.
relay_parent_number: BlockNumber,
/// The relay-chain block storage root this is in the context of.
relay_parent_storage_root: Hash,
/// The list of MQC heads for the inbound channels paired with the sender para ids. This
/// vector is sorted ascending by the para id and doesn't contain multiple entries with the same
/// sender.
///
/// The HRMP MQC heads will be used by the validation function to authorize the input messages passed
/// by the collator.
hrmp_mqc_heads: Vec<(ParaId, Hash)>,
/// The maximum legal size of a POV block, in bytes.
pub max_pov_size: u32,
}
Candidate Commitments
The execution and validation of parachain candidates produces a number of values which either must be committed to blocks on the relay chain or committed to the state of the relay chain.
/// Commitments made in a `CandidateReceipt`. Many of these are outputs of validation.
#[derive(PartialEq, Eq, Clone, Encode, Decode)]
#[cfg_attr(feature = "std", derive(Debug, Default))]
struct CandidateCommitments {
/// Messages directed to other paras routed via the relay chain.
horizontal_messages: Vec<OutboundHrmpMessage>,
/// Messages destined to be interpreted by the Relay chain itself.
upward_messages: Vec<UpwardMessage>,
/// New validation code.
new_validation_code: Option<ValidationCode>,
/// The head-data produced as a result of execution.
head_data: HeadData,
/// The number of messages processed from the DMQ.
processed_downward_messages: u32,
/// The mark which specifies the block number up to which all inbound HRMP messages are processed.
hrmp_watermark: BlockNumber,
}
Backing
Backed Candidate
An CommittedCandidateReceipt along with all data necessary to prove its backing. This is submitted to the relay-chain to process and move along the candidate to the pending-availability stage.
struct BackedCandidate {
candidate: CommittedCandidateReceipt,
validity_votes: Vec<ValidityAttestation>,
// the indices of validators who signed the candidate within the group. There is no need to include
// bit for any validators who are not in the group, so this is more compact.
// The number of bits is the number of validators in the group.
//
// the group should be apparent from context.
validator_indices: BitVec,
}
struct BackedCandidates(Vec<BackedCandidate>); // sorted by para-id.
Availability
Proof-of-Validity
Often referred to as PoV, this is a type-safe wrapper around bytes (Vec
struct PoV(Vec<u8>);
Available Data
This is the data we want to keep available for each candidate included in the relay chain. This is the PoV of the block, as well as the PersistedValidationData
struct AvailableData {
/// The Proof-of-Validation of the candidate.
pov: Arc<PoV>,
/// The persisted validation data used to check the candidate.
validation_data: PersistedValidationData,
}
Erasure Chunk
The AvailableData is split up into an erasure-coding as part of the availability process. Each validator gets a chunk. This describes one of those chunks, along with its proof against a merkle root hash, which should be apparent from context, and is the erasure_root field of a CandidateDescriptor.
struct ErasureChunk {
/// The erasure-encoded chunk of data belonging to the candidate block.
chunk: Vec<u8>,
/// The index of this erasure-encoded chunk of data.
index: u32,
/// Proof for this chunk's branch in the Merkle tree.
proof: Vec<Vec<u8>>,
}
Validity & Availability
-
Validity (Proof-of-Validity): Parachain collators produce blocks and send them to validators, who verify the state transitions are valid, rejecting invalid blocks.
-
Data Availability: Before inclusion in the Relay Chain, validators ensure the parachain block data is available (not hidden) by dividing it into erasure-coded chunks, requiring 2/3+ approval.
-
Approval Process: After a block is “backed” (tentatively approved), it goes through a secondary, more thorough check by a larger group of validators to confirm its final validity.
-
Finality: Once validity and availability are confirmed, the
Erasure Coding
The cryptographic method used in Polkadot to ensure Data Availability (DA) without requiring every validator to store every piece of data from every parachain.
-
Distribution: The total number of chunks created is equal to the total number of validators in the network. Each validator is sent exactly one unique chunk
-
Reconstruction: The original large message can be reconstructed by collecting only a fraction of these chunks. In Polkadot, this fraction is 1/3
Steps: parachain block → relay chain inclusion
The protocol separates backing (candidate accepted with enough validator votes on-chain) from inclusion / enactment (candidate commitments applied: para head, UMP/HRMP, etc., after availability).
1. Off-chain: collator produces a candidate
- The collator builds a parachain block and the PoV (proof-of-validity bundle validators execute).
- Backing validators assigned to that para/core (scheduler / claim queue) fetch the collation via the collator protocol, then run
candidate-validation(PVF / Wasm) to check the state transition. - Validators gossip statements (
Seconded, thenValid) via statement distribution. Enough valid votes ⇒ candidate is backable. - The backing subsystem forms a
BackedCandidate: receipt + validity attestations. The PoV is erasure-coded; validators store/gossip chunks (availability distribution / availability store).
Relay-chain parachain state is not updated yet.
2. Relay block author: parachains inherent
- The block author (BABE) fills the mandatory
paras_inherentdata:ParachainsInherentData— signed availability bitfields, newbacked_candidates, disputes, and parent header (process_inherent_datainparas_inherent).
3. On-chain: one enter call — bitfields/enact then new backing
Inside paras_inherent::enter → process_inherent_data, roughly:
- Scheduling context — allowed relay parents / claim-queue view (
shared::new_block, etc.). - Disputes — weight limits, import statements; freeze can halt parachain progress.
- Free disputed cores —
free_disputedfor invalidated candidates. - Bitfields — sanitize, then
inclusion::update_pending_availability_and_get_freed_cores: each bit means a validator has the erasure chunk for that core’s pending work. Enough votes ⇒ available. - For pending work that becomes available (respecting async backing order: predecessors enacted first),
enact_candidateruns — this is runtime “inclusion”: UMP/HRMP/DMP,Paras::note_new_head, etc. - Timeouts —
free_timedoutwhen applicable. - New
BackedCandidates —back_candidates→inclusion::process_candidates: checks relay parents, persisted validation data, backing signatures, message limits → stored inPendingAvailability(backed but not yet enacted).
Typical timeline
- Relay block R: candidate appears as
BackedCandidate→ pending availability. - Later relay blocks: bitfields until availability threshold is met.
- A later block:
enact_candidate— parachain head updates.
4. Finality and extra checks (after enactment)
- GRANDPA finalizes relay blocks; the parachain block is only final in the usual sense once the relay block that enacted it is finalized.
- Approval voting — extra validity checks for finality gating; separate from
enact_candidate.