mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 17:01:09 +00:00
Node-side subsystems for Disputes (#2566)
* dispute subsystem files * rename * fix linkcheck * flesh out section README * coordinator schema * DisputeCoordinatorMessage * stub & coordinator protocol * dispute coordinator * add some more message fields * move links to bottom * dispute participation * Cleen It Up ! * runtime: store candidate receipts in dispute state yeah, this is a little heavier. why are you reading this? * Revert "runtime: store candidate receipts in dispute state" This reverts commit 51c10bfd4d866e287e6bd88f317ed57ed987eaee. * add dispute availability statement type and prepare for availability * add 'spam slots' to disputes runtmie * return Spam Slots info from runtime * rework `ImportStatement` to `ImportStatements` * some more methods for dispute coordinator * candidates-included runtime API * algo for providing disputes to runtime. * handle signing with coordinator * dispute coordinator chain ops * remove dead file * remove keystore from dispute participation * adjust ApprovedAncestor to return the necssary data * discuss how approved ancestor and determine undisputed chain are used together * add TODO * initiate disputes from approval voting * route statements from candidate backing and approval voting * fix guide build
This commit is contained in:
committed by
GitHub
parent
caebf642dd
commit
ccfabaa0c6
@@ -4,4 +4,4 @@ The approval subsystems implement the node-side of the [Approval Protocol](../..
|
||||
|
||||
We make a divide between the [assignment/voting logic](approval-voting.md) and the [distribution logic](approval-distribution.md) that distributes assignment certifications and approval votes. The logic in the assignment and voting also informs the GRANDPA voting rule on how to vote.
|
||||
|
||||
This category of subsystems also contains a module for [participating in live disputes](dispute-participation.md) and tracks all observed votes (backing or approval) by all validators on all candidates.
|
||||
These subsystems are intended to flag issues and begin [participating in live disputes](../disputes/dispute-participation.md). Dispute subsystems also track all observed votes (backing, approval, and dispute-specific) by all validators on all candidates.
|
||||
|
||||
@@ -134,7 +134,7 @@ struct State {
|
||||
earliest_session: SessionIndex,
|
||||
session_info: Vec<SessionInfo>,
|
||||
babe_epoch: Option<BabeEpoch>, // information about a cached BABE epoch.
|
||||
keystore: KeyStorePtr,
|
||||
keystore: KeyStore,
|
||||
|
||||
// A scheduler which keeps at most one wakeup per hash, candidate hash pair and
|
||||
// maps such pairs to `Tick`s.
|
||||
@@ -215,16 +215,18 @@ On receiving a `CheckAndImportApproval(indirect_approval_vote, response_channel)
|
||||
* 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`.
|
||||
* Send `ApprovalCheckResult::Accepted`
|
||||
* Dispatch a [`DisputeCoordinatorMessage::ImportStatement`](../../types/overseer-protocol.md#dispute-coordinator-message) with the approval statement.
|
||||
* [Import the checked approval vote](#import-checked-approval)
|
||||
|
||||
#### `ApprovalVotingMessage::ApprovedAncestor`
|
||||
|
||||
On receiving an `ApprovedAncestor(Hash, BlockNumber, response_channel)`:
|
||||
* Iterate over the ancestry of the hash all the way back to block number given, starting from the provided block hash.
|
||||
* Keep track of an `all_approved_max: Option<(Hash, BlockNumber)>`.
|
||||
* Iterate over the ancestry of the hash all the way back to block number given, starting from the provided block hash. Load the `CandidateHash`es from each block entry.
|
||||
* Keep track of an `all_approved_max: Option<(Hash, BlockNumber, Vec<(Hash, Vec<CandidateHash>))>`.
|
||||
* For each block hash encountered, load the `BlockEntry` associated. If any are not found, return `None` on the response channel and conclude.
|
||||
* If the block entry's `approval_bitfield` has all bits set to 1 and `all_approved_max == None`, set `all_approved_max = Some((current_hash, current_number))`.
|
||||
* If the block entry's `approval_bitfield` has any 0 bits, set `all_approved_max = None`.
|
||||
* If `all_approved_max` is `Some`, push the current block hash and candidate hashes onto the list of blocks and candidates `all_approved_max`.
|
||||
* After iterating all ancestry, return `all_approved_max`.
|
||||
|
||||
### Updates and Auxiliary Logic
|
||||
@@ -278,7 +280,9 @@ On receiving an `ApprovedAncestor(Hash, BlockNumber, response_channel)`:
|
||||
* Wait for the available data
|
||||
* Issue a `CandidateValidationMessage::ValidateFromExhaustive` message
|
||||
* Wait for the result of validation
|
||||
* Check that the result of validation, if valid, matches the commitments in the receipt.
|
||||
* If valid, issue a message on `background_tx` detailing the request.
|
||||
* If any of the data, the candidate, or the commitments are invalid, issue on `background_tx` a [`DisputeCoordinatorMessage::IssueLocalStatement`](../../types/overseer-protocol.md#dispute-coordinator-message) with `valid = false` to initiate a dispute.
|
||||
|
||||
#### Issue Approval Vote
|
||||
* Fetch the block entry and candidate entry. Ignore if `None` - we've probably just lost a race with finality.
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
# Dispute Participation
|
||||
|
||||
## Protocol
|
||||
|
||||
## Functionality
|
||||
@@ -94,6 +94,8 @@ match msg {
|
||||
Add `Seconded` statements and `Valid` statements to a quorum. If quorum reaches validator-group majority, send a [`ProvisionerMessage`][PM]`::ProvisionableData(ProvisionableData::BackedCandidate(CandidateReceipt))` message.
|
||||
`Invalid` statements that conflict with already witnessed `Seconded` and `Valid` statements for the given candidate, statements that are double-votes, self-contradictions and so on, should result in issuing a [`ProvisionerMessage`][PM]`::MisbehaviorReport` message for each newly detected case of this kind.
|
||||
|
||||
On each incoming statement, [`DisputeCoordinatorMessage::ImportStatement`][DCM] should be issued.
|
||||
|
||||
### Validating Candidates.
|
||||
|
||||
```rust
|
||||
@@ -137,6 +139,7 @@ Dispatch a [`StatementDistributionMessage`][PDM]`::Share(relay_parent, SignedFul
|
||||
[CBM]: ../../types/overseer-protocol.md#candidate-backing-message
|
||||
[ADM]: ../../types/overseer-protocol.md#availability-distribution-message
|
||||
[SDM]: ../../types/overseer-protocol.md#statement-distribution-message
|
||||
[DCM]: ../../types/overseer-protocol.md#dispute-coordinator-message
|
||||
|
||||
[CS]: candidate-selection.md
|
||||
[CV]: ../utility/candidate-validation.md
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
# Disputes Subsystems
|
||||
|
||||
This section is for the node-side subsystems that lead to participation in disputes. There are five major roles that validator nodes must participate in when it comes to disputes
|
||||
* Detection. Detect bad parablocks, either during candidate backing or approval checking, and initiate a dispute.
|
||||
* Participation. Participate in active disputes. When a node becomes aware of a dispute, it should recover the data for the disputed block, check the validity of the parablock, and issue a statement regarding the validity of the parablock.
|
||||
* Distribution. Validators should notify each other of active disputes and relevant statements over the network.
|
||||
* Submission. When authoring a block, a validator should inspect the state of the parent block and provide any information about disputes that the chain needs as part of the ParaInherent. This should initialize new disputes on-chain as necessary.
|
||||
* Fork-choice and Finality. When observing a block issuing a DisputeRollback digest in the header, that branch of the relay chain should be abandoned all the way back to the indicated block. When voting on chains in GRANDPA, no chains that contain blocks that are or have been disputed should be voted on.
|
||||
|
||||
## Components
|
||||
|
||||
### Dispute Coordinator
|
||||
|
||||
This component is responsible for coordinating other subsystems around disputes.
|
||||
|
||||
This component should track all statements received from all validators over some window of sessions. This includes backing statements, approval votes, and statements made specifically for disputes. This will be used to identify disagreements or instances where validators conflict with themselves.
|
||||
|
||||
This is responsible for tracking and initiating disputes. Disputes will be initiated either externally by another subsystem which has identified an issue with a parablock or internally upon observing two statements which conflict with each other on the validity of a parablock.
|
||||
|
||||
No more than one statement by each validator on each side of the dispute needs to be stored. That is, validators are allowed to participate on both sides of the dispute, although we won't write code to do so. Such behavior has negative EV in the runtime.
|
||||
|
||||
This will notify the dispute participation subsystem of a new dispute if the local validator has not issued any statements on the disputed candidate already.
|
||||
|
||||
Locally authored statements related to disputes will be forwarded to the dispute distribution subsystem.
|
||||
|
||||
This subsystem also provides two further behaviors for the interactions between disputes and fork-choice
|
||||
- Enhancing the finality voting rule. Given description of a chain and candidates included at different heights in that chain, it returns the BlockHash corresponding to the highest BlockNumber that there are no disputes before. I expect that we will slightly change ApprovalVoting::ApprovedAncestor to return this set and then the target block to vote on will be further constrained by this function.
|
||||
- Chain roll-backs. Whenever importing new blocks, the header should be scanned for a roll-back digest. If there is one, the chain should be rolled back according to the digest. I expect this would be implemented with a ChainApi function and possibly an ApprovalVoting function to clean up the approval voting DB.
|
||||
|
||||
### Dispute Participation
|
||||
|
||||
This subsystem ties together the dispute tracker, availability recovery, candidate validation, and dispute distribution subsystems. When notified of a new dispute by the Dispute Tracker, the data should be recovered, the validation code loaded from the relay chain, and the candidate is executed.
|
||||
|
||||
A statement depending on the outcome of the execution is produced, signed, and imported to the dispute tracker.
|
||||
|
||||
### Dispute Distribution
|
||||
|
||||
This is a networking component by which validators notify each other of live disputes and statements on those disputes.
|
||||
|
||||
Validators will in the future distribute votes to each other via the network, but at the moment learn of disputes just from watching the chain.
|
||||
@@ -0,0 +1,131 @@
|
||||
# Dispute Coordinator
|
||||
|
||||
This is the central subsystem of the node-side components which participate in disputes. This subsystem wraps a database which tracks all statements observed by all validators over some window of sessions. Votes older than this session window are pruned.
|
||||
|
||||
This subsystem will be the point which produce dispute votes, eiuther positive or negative, based on locally-observed validation results as well as a sink for votes received by other subsystems. When importing a dispute vote from another node, this will trigger the [dispute participation](dispute-participation.md) subsystem to recover and validate the block and call back to this subsystem.
|
||||
|
||||
## Database Schema
|
||||
|
||||
We use an underlying Key-Value database where we assume we have the following operations available:
|
||||
* `write(key, value)`
|
||||
* `read(key) -> Option<value>`
|
||||
* `iter_with_prefix(prefix) -> Iterator<(key, value)>` - gives all keys and values in lexicographical order where the key starts with `prefix`.
|
||||
|
||||
We use this database to encode the following schema:
|
||||
|
||||
```rust
|
||||
(SessionIndex, "candidate-votes", CandidateHash) -> Option<CandidateVotes>
|
||||
"active-disputes" -> ActiveDisputes
|
||||
"earliest-session" -> Option<SessionIndex>
|
||||
```
|
||||
|
||||
The meta information that we track per-candidate is defined as the `CandidateVotes` struct.
|
||||
This draws on the [dispute statement types][DisputeTypes]
|
||||
|
||||
```rust
|
||||
struct CandidateVotes {
|
||||
// The receipt of the candidate itself.
|
||||
candidate_receipt: CandidateReceipt,
|
||||
// Sorted by validator index.
|
||||
valid: Vec<(ValidDisputeStatementKind, ValidatorIndex, ValidatorSignature)>,
|
||||
// Sorted by validator index.
|
||||
invalid: Vec<(InvalidDisputeStatementKind, ValidatorIndex, ValidatorSignature)>,
|
||||
}
|
||||
|
||||
struct ActiveDisputes {
|
||||
// sorted by session index and then by candidate hash.
|
||||
disputed: Vec<(SessionIndex, CandidateHash)>,
|
||||
}
|
||||
```
|
||||
|
||||
## Protocol
|
||||
|
||||
Input: [`DisputeCoordinatorMessage`][DisputeCoordinatorMessage]
|
||||
|
||||
Output:
|
||||
- [`RuntimeApiMessage`][RuntimeApiMessage]
|
||||
- [`DisputeParticipationMessage`][DisputeParticipationMessage]
|
||||
|
||||
## Functionality
|
||||
|
||||
This assumes a constant `DISPUTE_WINDOW: SessionIndex`. This should correspond to at least 1 day.
|
||||
|
||||
Ephemeral in-memory state:
|
||||
|
||||
```rust
|
||||
struct State {
|
||||
keystore: KeyStore,
|
||||
// An in-memory overlay used as a write-cache.
|
||||
overlay: Map<(SessionIndex, CandidateReceipt), CandidateVotes>,
|
||||
highest_session: SessionIndex,
|
||||
}
|
||||
```
|
||||
|
||||
### On `OverseerSignal::ActiveLeavesUpdate`
|
||||
|
||||
For each leaf in the leaves update:
|
||||
* Fetch the session index for the child of the block with a [`RuntimeApiMessage::SessionIndexForChild`][RuntimeApiMessage].
|
||||
* If the session index is higher than `state.highest_session`:
|
||||
* update `state.highest_session`
|
||||
* remove everything with session index less than `state.highest_session - DISPUTE_WINDOW` from the overlay and from the `"active-disputes"` in the DB.
|
||||
* Use `iter_with_prefix` to remove everything from `"earliest-session"` up to `state.highest_session - DISPUTE_WINDOW` from the DB under `"candidate-votes"`.
|
||||
* Update `"earliest-session"` to be equal to `state.highest_session - DISPUTE_WINDOW`.
|
||||
* For each new block, explicitly or implicitly, under the new leaf, scan for a dispute digest which indicates a rollback. If a rollback is detected, use the ChainApi subsystem to blacklist the chain.
|
||||
|
||||
### On `OverseerSignal::Conclude`
|
||||
|
||||
Flush the overlay to DB and conclude.
|
||||
|
||||
### On `OverseerSignal::BlockFinalized`
|
||||
|
||||
Do nothing.
|
||||
|
||||
### On `DisputeCoordinatorMessage::ImportStatement`
|
||||
|
||||
* Deconstruct into parts `{ candidate_hash, candidate_receipt, session, statements }`.
|
||||
* If the session is earlier than `state.highest_session - DISPUTE_WINDOW`, return.
|
||||
* If there is an entry in the `state.overlay`, load that. Otherwise, load from underlying DB by querying `(session, "candidate-votes", candidate_hash). If that does not exist, create fresh with the given candidate receipt.
|
||||
* If candidate votes is empty and the statements only contain dispute-specific votes, return.
|
||||
* Otherwise, if there is already an entry from the validator in the respective `valid` or `invalid` field of the `CandidateVotes`, return.
|
||||
* Add an entry to the respective `valid` or `invalid` list of the `CandidateVotes` for each statement in `statements`.
|
||||
* Write the `CandidateVotes` to the `state.overlay`.
|
||||
* If the both `valid` and `invalid` lists now have non-zero length where previously one or both had zero length, the candidate is now freshly disputed.
|
||||
* If freshly disputed, load `"active-disputes"` and add the candidate hash and session index. Also issue a [`DisputeParticipationMessage::Participate`][DisputeParticipationMessage].
|
||||
* If the dispute now has supermajority votes in the "valid" direction, according to the `SessionInfo` of the dispute candidate's session, remove from `"active-disputes"`.
|
||||
* If the dispute now has supermajority votes in the "invalid" direction, there is no need to do anything explicitly. The actual rollback will be handled during the active leaves update by observing digests from the runtime.
|
||||
* Write `"active-disputes"`
|
||||
|
||||
### On `DisputeCoordinatorMessage::ActiveDisputes`
|
||||
|
||||
* Load `"active-disputes"` and return the data contained within.
|
||||
|
||||
### On `DisputeCoordinatorMessage::QueryCandidateVotes`
|
||||
|
||||
* Load from the `state.overlay`, and return the data if `Some`.
|
||||
* Otherwise, load `"candidate-votes"` and return the data within or `None` if missing.
|
||||
|
||||
### On `DisputeCoordinatorMessage::IssueLocalStatement`
|
||||
|
||||
* Deconstruct into parts `{ session_index, candidate_hash, candidate_receipt, is_valid }`.
|
||||
* Construct a [`DisputeStatement`][DisputeStatement] based on `Valid` or `Invalid`, depending on the parameterization of this routine.
|
||||
* Sign the statement with each key in the `SessionInfo`'s list of parachain validation keys which is present in the keystore, except those whose indices appear in `voted_indices`. This will typically just be one key, but this does provide some future-proofing for situations where the same node may run on behalf multiple validators. At the time of writing, this is not a use-case we support as other subsystems do not invariably provide this guarantee.
|
||||
|
||||
### On `DisputeCoordinatorMessage::DetermineUndisputedChain`
|
||||
|
||||
* Load `"active-disputes"`.
|
||||
* Deconstruct into parts `{ base_number, block_descriptions, rx }`
|
||||
* Starting from the beginning of `block_descriptions`:
|
||||
1. Check the `ActiveDisputes` for a dispute of each candidate in the block description.
|
||||
1. If there is a dispute, exit the loop.
|
||||
* For the highest index `i` reached in the `block_descriptions`, send `(base_number + i + 1, block_hash)` on the channel, unless `i` is 0, in which case `None` should be sent. The `block_hash` is determined by inspecting `block_descriptions[i]`.
|
||||
|
||||
### Periodically
|
||||
|
||||
* Flush the `state.overlay` to the DB, writing all entries within
|
||||
* Clear `state.overlay`.
|
||||
|
||||
[DisputeTypes]: ../../types/disputes.md
|
||||
[DisputeStatement]: ../../types/disputes.md#disputestatement
|
||||
[DisputeCoordinatorMessage]: ../../types/overseer-protocol.md#dispute-coordinator-message
|
||||
[RuntimeApiMessage]: ../../types/overseer-protocol.md#runtime-api-message
|
||||
[DisputeParticipationMessage]: ../../types/overseer-protocol.md#dispute-participation-message
|
||||
@@ -0,0 +1,3 @@
|
||||
# Dispute Distribution
|
||||
|
||||
TODO https://github.com/paritytech/polkadot/issues/2581
|
||||
@@ -0,0 +1,70 @@
|
||||
# Dispute Participation
|
||||
|
||||
This subsystem is responsible for actually participating in disputes: when notified of a dispute, we need to recover the candidate data, validate the candidate, and cast our vote in the dispute.
|
||||
|
||||
Fortunately, most of that work is handled by other subsystems; this subsystem is just a small glue component for tying other subsystems together and issuing statements based on their validity.
|
||||
|
||||
## Protocol
|
||||
|
||||
Input: [DisputeParticipationMessage][DisputeParticipationMessage]
|
||||
|
||||
Output:
|
||||
- [RuntimeApiMessage][RuntimeApiMessage]
|
||||
- [CandidateValidationMessage][CandidateValidationMessage]
|
||||
- [AvailabilityRecoveryMessage][AvailabilityRecoveryMessage]
|
||||
- [ChainApiMessage][ChainApiMessage]
|
||||
|
||||
## Functionality
|
||||
|
||||
In-memory state:
|
||||
|
||||
```rust
|
||||
struct State {
|
||||
recent_block_hash: Hash
|
||||
}
|
||||
```
|
||||
|
||||
### On `OverseerSignal::ActiveLeavesUpdate`
|
||||
|
||||
Do nothing.
|
||||
|
||||
### On `OverseerSignal::BlockFinalized`
|
||||
|
||||
Do nothing.
|
||||
|
||||
### On `OverseerSignal::Conclude`
|
||||
|
||||
Conclude.
|
||||
|
||||
### On `DisputeParticipationMessage::Participate`
|
||||
|
||||
> TODO: this validation code fetching procedure is not helpful for disputed blocks that are in chains we do not know. After https://github.com/paritytech/polkadot/issues/2457 we should use the `ValidationCodeByHash` runtime API using the code hash in the candidate receipt.
|
||||
|
||||
* Decompose into parts: `{ candidate_hash, candidate_receipt, session, voted_indices }`
|
||||
* Issue an [`AvailabilityRecoveryMessage::RecoverAvailableData`][AvailabilityRecoveryMessage]
|
||||
* If the result is `Unavailable`, return.
|
||||
* If the result is `Invalid`, [cast invalid votes](#cast-votes) and return.
|
||||
* Fetch the block number of `candidate_receipt.descriptor.relay_parent` using a [`ChainApiMessage::BlockNumber`][ChainApiMessage].
|
||||
* If the data is recovered, dispatch a [`RuntimeApiMessage::HistoricalValidationCode`][RuntimeApiMessage] with the parameters `(candidate_receipt.descriptor.para_id, relay_parent_number)`.
|
||||
* Dispatch a [`AvailabilityStoreMessage::StoreAvailableData`][AvailabilityStoreMessage] with the data.
|
||||
* If the code is not fetched from the chain, return. This should be impossible with correct relay chain configuration after the TODO above is addressed and is unlikely before then, at least if chain synchronization is working correctly.
|
||||
* Dispatch a [`CandidateValidationMessage::ValidateFromExhaustive`][CandidateValidationMessage] with the available data and the validation code.
|
||||
* If the validation result is `Invalid`, [cast invalid votes](#cast-votes) and return.
|
||||
* If the validation fails, [cast invalid votes](#cast-votes) and return.
|
||||
* If the validation succeeds, compute the `CandidateCommitments` based on the validation result and compare against the candidate receipt's `commitments_hash`. If they match, [cast valid votes](#cast-votes) and if not, [cast invalid votes](#cast-votes).
|
||||
|
||||
### Cast Votes
|
||||
|
||||
This requires the parameters `{ candidate_receipt, candidate_hash, session, voted_indices }` as well as a choice of either `Valid` or `Invalid`.
|
||||
|
||||
Invoke [`DisputeCoordinatorMessage::IssueLocalStatement`][DisputeCoordinatorMessage] with `is_valid` according to the parameterization.
|
||||
|
||||
Invoke [`DisputeCoordinatorMessage::ImportStatements`][DisputeCoordinatorMessage] with each signed statement.
|
||||
|
||||
[RuntimeApiMessage]: ../../types/overseer-protocol.md#runtime-api-message
|
||||
[DisputeParticipationMessage]: ../../types/overseer-protocol.md#dispute-participation-message
|
||||
[DisputeCoordinatorMessage]: ../../types/overseer-protocol.md#dispute-coordinator-message
|
||||
[CandidateValidationMessage]: ../../types/overseer-protocol.md#candidate-validation-message
|
||||
[AvailabilityRecoveryMessage]: ../../types/overseer-protocol.md#availability-recovery-message
|
||||
[ChainApiMessage]: ../../types/overseer-protocol.md#chain-api-message
|
||||
[AvailabilityStoreMessage]: ../../types/overseer-protocol.md#availability-store-message
|
||||
@@ -6,6 +6,6 @@ One broad goal of finality, which applies across many different blockchains, is
|
||||
|
||||
GRANDPA's regular voting rule is for each validator to select the longest chain they are aware of. GRANDPA proceeds in rounds, collecting information from all online validators and determines the blocks that a supermajority of validators all have in common with each other.
|
||||
|
||||
For parachains, we extend the security guarantee of finality to be such that no invalid parachain candidate may be included in a finalized block. Candidates may be included in some fork of the relay chain with only a few backing votes behind them. After that point, we run the [Approvals Protocol](../protocol-approval.md), which is implemented as the [Approval Voting](approval/approval-voting.md) subsystem. This system involves validators self-selecting to re-check candidates included in all observed forks of the relay chain as well as an algorithm for observing validators' statements about assignment and approval in order to determine which candidates, and thus blocks, are with high probability valid. The highest approved ancestor of a given block can be determined by querying the Approval Voting subsystem via the [`ApprovalVotingMessage::ApprovedAncestor`](../types/overseer-protocol.md#approval-voting) message.
|
||||
For parachains, we extend the security guarantee of finality to be such that no invalid parachain candidate may be included in a finalized block. Candidates may be included in some fork of the relay chain with only a few backing votes behind them. After that point, we run the [Approvals Protocol](../protocol-approval.md), which is implemented as the [Approval Voting](approval/approval-voting.md) subsystem. This system involves validators self-selecting to re-check candidates included in all observed forks of the relay chain as well as an algorithm for observing validators' statements about assignment and approval in order to determine which candidates, and thus blocks, are with high probability valid. The highest approved ancestor of a given block can be determined by querying the Approval Voting subsystem via the [`ApprovalVotingMessage::ApprovedAncestor`](../types/overseer-protocol.md#approval-voting) message. If the response of `ApprovedAncestor` is `Some`, we further constrain the voting rule to avoid unfinalized blocks. The list of block hashes and candidates should be reversed, and passed to the [`DisputeCoordinatorMessage::DetermineUndisputedChain`](../types/overseer-protocol.md#dispute-coordinator-message) for a final result.
|
||||
|
||||
Lastly, we refuse to finalize any block including a candidate for which we are aware of an ongoing dispute or of a dispute resolving against the candidate. The exact means of doing this has not been determined yet.
|
||||
|
||||
@@ -71,6 +71,30 @@ To determine availability:
|
||||
|
||||
The end result of this process is a vector of `BackedCandidate`s, sorted in order of their core index. Furthermore, this process should select at maximum one candidate which upgrades the runtime validation code.
|
||||
|
||||
### Dispute Statement Selection
|
||||
|
||||
This is the point at which the block author provides further votes to active disputes or initiates new disputes in the runtime state.
|
||||
|
||||
We must take care not to overwhelm the "spam slots" of the chain. That is, to avoid too many votes from the same validators being placed into the chain, which would trigger the anti-spam protection functionality of the [disputes module](../../runtime/disputes.md).
|
||||
|
||||
To select disputes:
|
||||
|
||||
- Make a `DisputesInfo` runtime API call and decompose into `{ spam_slots, disputes }`. Bind `disputes` to `onchain_disputes`.
|
||||
- Issue a `DisputeCoordinatorMessage::ActiveDisputes` message and wait for the response. Assign the value to `offchain_disputes`.
|
||||
- Make a `CandidatesIncluded` runtime API call for each dispute in `offchain_disputes` and tag each offchain dispute as local if the result for it is `true`.
|
||||
- Initialize `NewSpamSlots: Map<(SessionIndex, ValidatorIndex), u32>` as an empty map.
|
||||
- For each dispute in `offchain_disputes`:
|
||||
1. Make a `RuntimeApiRequest::SessionInfo` against the parent hash for the session of the dispute. If `None`, continue - this chain is in the past relative to the session the dispute belongs to and we can import it when it reaches that session.
|
||||
1. Load the spam slots from `spam_slots` for the given session. If it isn't present, treat as though all zeros.
|
||||
1. construct a `DisputeStatementSet` of all offchain votes we are aware of that the onchain doesn't already have a `valid` or `invalid` bit set for, respectively.
|
||||
1. If the `onchain_disputes` contains an entry for the dispute, load that. Otherwise, treat as empty.
|
||||
1. If the offchain dispute is local or the `DisputeStatementSet` and the onchain dispute together have at least `byzantine_threshold + 1` validators in it, continue to the next offchain dispute.
|
||||
1. Otherwise
|
||||
1. Filter out all votes from the `DisputeStatementSet` where the amount of spam slots occupied on-chain by the validator, plus the `NewSpamSlots` value, plus 1, is greater than `spam_slots.max_spam_slots`.
|
||||
1. After filtering, if either the `valid` or `invalid` lists in the combination of the `DisputeStatementSet` and the onchain dispute is empty, skip this dispute.
|
||||
1. Add 1 to the `NewSpamSlots` value for each validator in the `DisputeStatementSet`.
|
||||
- Construct a `MultiDisputeStatementSet` for each `DisputeStatement` and return that.
|
||||
|
||||
### Determining Bitfield Availability
|
||||
|
||||
An occupied core has a `CoreAvailability` bitfield. We also have a list of `SignedAvailabilityBitfield`s. We need to determine from these whether or not a core at a particular index has become available.
|
||||
|
||||
Reference in New Issue
Block a user