feat: initialize Kurdistan SDK - independent fork of Polkadot SDK
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
# Availability Recovery
|
||||
|
||||
This subsystem is responsible for recovering the data made available via the
|
||||
[Availability Distribution](availability-distribution.md) subsystem, necessary for candidate validation during the
|
||||
approval/disputes processes. Additionally, it is also being used by collators to recover PoVs in adversarial scenarios
|
||||
where the other collators of the para are censoring blocks.
|
||||
|
||||
According to the Pezkuwi protocol, in order to recover any given `AvailableData`, we generally must recover at least
|
||||
`f + 1` pieces from validators of the session. Thus, we should connect to and query randomly chosen validators until we
|
||||
have received `f + 1` pieces.
|
||||
|
||||
In practice, there are various optimisations implemented in this subsystem which avoid querying all chunks from
|
||||
different validators and/or avoid doing the chunk reconstruction altogether.
|
||||
|
||||
## Protocol
|
||||
|
||||
This version of the availability recovery subsystem is based only on request-response network protocols.
|
||||
|
||||
Input:
|
||||
|
||||
* `AvailabilityRecoveryMessage::RecoverAvailableData(candidate, session, backing_group, core_index, response)`
|
||||
|
||||
Output:
|
||||
|
||||
* `NetworkBridgeMessage::SendRequests`
|
||||
* `AvailabilityStoreMessage::QueryAllChunks`
|
||||
* `AvailabilityStoreMessage::QueryAvailableData`
|
||||
* `AvailabilityStoreMessage::QueryChunkSize`
|
||||
|
||||
|
||||
## Functionality
|
||||
|
||||
We hold a state which tracks the currently ongoing recovery tasks. A `RecoveryTask` is a structure encapsulating all
|
||||
network tasks needed in order to recover the available data in respect to a candidate.
|
||||
|
||||
Each `RecoveryTask` has a collection of ordered recovery strategies to try.
|
||||
|
||||
```rust
|
||||
/// Subsystem state.
|
||||
struct State {
|
||||
/// Each recovery task is implemented as its own async task,
|
||||
/// and these handles are for communicating with them.
|
||||
ongoing_recoveries: FuturesUnordered<RecoveryHandle>,
|
||||
/// A recent block hash for which state should be available.
|
||||
live_block: (BlockNumber, Hash),
|
||||
/// An LRU cache of recently recovered data.
|
||||
availability_lru: LruMap<CandidateHash, CachedRecovery>,
|
||||
/// Cached runtime info.
|
||||
runtime_info: RuntimeInfo,
|
||||
}
|
||||
|
||||
struct RecoveryParams {
|
||||
/// Discovery ids of `validators`.
|
||||
pub validator_authority_keys: Vec<AuthorityDiscoveryId>,
|
||||
/// Number of validators.
|
||||
pub n_validators: usize,
|
||||
/// The number of regular chunks needed.
|
||||
pub threshold: usize,
|
||||
/// The number of systematic chunks needed.
|
||||
pub systematic_threshold: usize,
|
||||
/// A hash of the relevant candidate.
|
||||
pub candidate_hash: CandidateHash,
|
||||
/// The root of the erasure encoding of the candidate.
|
||||
pub erasure_root: Hash,
|
||||
/// Metrics to report.
|
||||
pub metrics: Metrics,
|
||||
/// Do not request data from availability-store. Useful for collators.
|
||||
pub bypass_availability_store: bool,
|
||||
/// The type of check to perform after available data was recovered.
|
||||
pub post_recovery_check: PostRecoveryCheck,
|
||||
/// The blake2-256 hash of the PoV.
|
||||
pub pov_hash: Hash,
|
||||
/// Protocol name for ChunkFetchingV1.
|
||||
pub req_v1_protocol_name: ProtocolName,
|
||||
/// Protocol name for ChunkFetchingV2.
|
||||
pub req_v2_protocol_name: ProtocolName,
|
||||
/// Whether or not chunk mapping is enabled.
|
||||
pub chunk_mapping_enabled: bool,
|
||||
/// Channel to the erasure task handler.
|
||||
pub erasure_task_tx: mpsc::Sender<ErasureTask>,
|
||||
}
|
||||
|
||||
pub struct RecoveryTask<Sender: overseer::AvailabilityRecoverySenderTrait> {
|
||||
sender: Sender,
|
||||
params: RecoveryParams,
|
||||
strategies: VecDeque<Box<dyn RecoveryStrategy<Sender>>>,
|
||||
state: task::State,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
/// Common trait for runnable recovery strategies.
|
||||
pub trait RecoveryStrategy<Sender: overseer::AvailabilityRecoverySenderTrait>: Send {
|
||||
/// Main entry point of the strategy.
|
||||
async fn run(
|
||||
mut self: Box<Self>,
|
||||
state: &mut task::State,
|
||||
sender: &mut Sender,
|
||||
common_params: &RecoveryParams,
|
||||
) -> Result<AvailableData, RecoveryError>;
|
||||
|
||||
/// Return the name of the strategy for logging purposes.
|
||||
fn display_name(&self) -> &'static str;
|
||||
|
||||
/// Return the strategy type for use as a metric label.
|
||||
fn strategy_type(&self) -> &'static str;
|
||||
}
|
||||
```
|
||||
|
||||
### Signal Handling
|
||||
|
||||
On `ActiveLeavesUpdate`, if `activated` is non-empty, set `state.live_block_hash` to the first block in `Activated`.
|
||||
|
||||
Ignore `BlockFinalized` signals.
|
||||
|
||||
On `Conclude`, shut down the subsystem.
|
||||
|
||||
#### `AvailabilityRecoveryMessage::RecoverAvailableData(...)`
|
||||
|
||||
1. Check the `availability_lru` for the candidate and return the data if present.
|
||||
1. Check if there is already a recovery handle for the request. If so, add the response handle to it.
|
||||
1. Otherwise, load the session info for the given session under the state of `live_block_hash`, and initiate a recovery
|
||||
task with `launch_recovery_task`. Add a recovery handle to the state and add the response channel to it.
|
||||
1. If the session info is not available, return `RecoveryError::Unavailable` on the response channel.
|
||||
|
||||
### Recovery logic
|
||||
|
||||
#### `handle_recover(...) -> Result<()>`
|
||||
|
||||
Instantiate the appropriate `RecoveryStrategy`es, based on the subsystem configuration, params and session info.
|
||||
Call `launch_recovery_task()`.
|
||||
|
||||
#### `launch_recovery_task(state, ctx, response_sender, recovery_strategies, params) -> Result<()>`
|
||||
|
||||
Create the `RecoveryTask` and launch it as a background task running `recovery_task.run()`.
|
||||
|
||||
#### `recovery_task.run(mut self) -> Result<AvailableData, RecoveryError>`
|
||||
|
||||
* Loop:
|
||||
* Pop a strategy from the queue. If none are left, return `RecoveryError::Unavailable`.
|
||||
* Run the strategy.
|
||||
* If the strategy returned successfully or returned `RecoveryError::Invalid`, break the loop.
|
||||
|
||||
### Recovery strategies
|
||||
|
||||
#### `FetchFull`
|
||||
|
||||
This strategy tries requesting the full available data from the validators in the backing group to
|
||||
which the node is already connected. They are tried one by one in a random order.
|
||||
It is very performant if there's enough network bandwidth and the backing group is not overloaded.
|
||||
The costly reed-solomon reconstruction is not needed.
|
||||
|
||||
#### `FetchSystematicChunks`
|
||||
|
||||
Very similar to `FetchChunks` below but requests from the validators that hold the systematic chunks, so that we avoid
|
||||
reed-solomon reconstruction. Only possible if `node_features::FeatureIndex::AvailabilityChunkMapping` is enabled and
|
||||
the `core_index` is supplied (currently only for recoveries triggered by approval voting).
|
||||
|
||||
More info in
|
||||
[RFC-47](https://github.com/polkadot-fellows/RFCs/blob/main/text/0047-assignment-of-availability-chunks.md).
|
||||
|
||||
#### `FetchChunks`
|
||||
|
||||
The least performant strategy but also the most comprehensive one. It's the only one that cannot fail under the
|
||||
byzantine threshold assumption, so it's always added as the last one in the `recovery_strategies` queue.
|
||||
|
||||
Performs parallel chunk requests to validators. When enough chunks were received, do the reconstruction.
|
||||
In the worst case, all validators will be tried.
|
||||
|
||||
### Default recovery strategy configuration
|
||||
|
||||
#### For validators
|
||||
|
||||
If the estimated available data size is smaller than a configured constant (currently 1Mib for Pezkuwi or 4Mib for
|
||||
other networks), try doing `FetchFull` first.
|
||||
Next, if the preconditions described in `FetchSystematicChunks` above are met, try systematic recovery.
|
||||
As a last resort, do `FetchChunks`.
|
||||
|
||||
#### For collators
|
||||
|
||||
Collators currently only use `FetchChunks`, as they only attempt recoveries in rare scenarios.
|
||||
|
||||
Moreover, the recovery task is specially configured to not attempt requesting data from the local availability-store
|
||||
(because it doesn't exist) and to not reencode the data after a successful recovery (because it's an expensive check
|
||||
that is not needed; checking the pov_hash is enough for collators).
|
||||
Reference in New Issue
Block a user