diff --git a/roadmap/implementers-guide/src/SUMMARY.md b/roadmap/implementers-guide/src/SUMMARY.md index f90f149f2556..c6c4f39250f3 100644 --- a/roadmap/implementers-guide/src/SUMMARY.md +++ b/roadmap/implementers-guide/src/SUMMARY.md @@ -8,7 +8,6 @@ - [Architecture Overview](architecture.md) - [Messaging Overview](messaging.md) - [Runtime Architecture](runtime/README.md) - - [ApprovalsInherent Module](runtime/approvals_inherent.md) - [Initializer Module](runtime/initializer.md) - [Configuration Module](runtime/configuration.md) - [Disputes Module](runtime/disputes.md) @@ -48,7 +47,7 @@ - [Bitfield Signing](node/availability/bitfield-signing.md) - [Approval Subsystems](node/approval/README.md) - [Approval Voting](node/approval/approval-voting.md) - - [Approval Networking](node/approval/approval-networking.md) + - [Approval Distribution](node/approval/approval-distribution.md) - [Dispute Participation](node/approval/dispute-participation.md) - [Utility Subsystems](node/utility/README.md) - [Availability Store](node/utility/availability-store.md) diff --git a/roadmap/implementers-guide/src/node/approval/README.md b/roadmap/implementers-guide/src/node/approval/README.md index 41ba527f1b64..2d0815376728 100644 --- a/roadmap/implementers-guide/src/node/approval/README.md +++ b/roadmap/implementers-guide/src/node/approval/README.md @@ -2,6 +2,6 @@ The approval subsystems implement the node-side of the [Approval Protocol](../../protocol-approval.md). -We make a divide between the [assignment/voting logic](approval-voting.md) and the [networking](approval-networking.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. +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. \ No newline at end of file diff --git a/roadmap/implementers-guide/src/node/approval/approval-distribution.md b/roadmap/implementers-guide/src/node/approval/approval-distribution.md new file mode 100644 index 000000000000..6ded8b6db9ad --- /dev/null +++ b/roadmap/implementers-guide/src/node/approval/approval-distribution.md @@ -0,0 +1,196 @@ +# Approval Distribution + +A subsystem for the distribution of assignments and approvals for approval checks on candidates over the network. + +The [Approval Voting](approval-voting.md) subsystem is responsible for active participation in a protocol designed to select a sufficient number of validators to check each and every candidate which appears in the relay chain. Statements of participation in this checking process are divided into two kinds: + - **Assignments** indicate that validators have been selected to do checking + - **Approvals** indicate that validators have checked and found the candidate satisfactory. + +The [Approval Voting](approval-voting.md) subsystem handles all the issuing and tallying of this protocol, but this subsystem is responsible for the disbursal of statements among the validator-set. + +The inclusion pipeline of candidates concludes after availability, and only after inclusion do candidates actually get pushed into the approval checking pipeline. As such, this protocol deals with the candidates _made available by_ particular blocks, as opposed to the candidates which actually appear within those blocks, which are the candidates _backed by_ those blocks. Unless stated otherwise, whenever we reference a candidate partially by block hash, we are referring to the set of candidates _made available by_ those blocks. + +We implement this protocol as a gossip protocol, and like other parachain-related gossip protocols our primary concerns are about ensuring fast message propagation while maintaining an upper bound on the number of messages any given node must store at any time. + +Approval messages should always follow assignments, so we need to be able to discern two pieces of information based on our [View](../../types/network.md#universal-types): + 1. Is a particular assignment relevant under a given `View`? + 2. Is a particular approval relevant to any assignment in a set? + +It is acceptable for these two queries to yield false negatives with respect to our peers' views. For our own local view, they must not yield false negatives. When applied to our peers' views, it is acceptable for them to yield false negatives. The reason for that is that our peers' views may be beyond ours, and we are not capable of fully evaluating them. Once we have caught up, we can check again for false negatives to continue distributing. + +For assignments, what we need to be checking is whether we are aware of the (block, candidate) pair that the assignment references. For approvals, we need to be aware of an assignment by the same validator which references the candidate being approved. + +However, awareness on its own of a (block, candidate) pair would imply that even ancient candidates all the way back to the genesis are relevant. We are actually not interested in anything before finality. + + +## Protocol + +## Functionality + +```rust +type BlockScopedCandidate = (Hash, CandidateHash); + +/// The `State` struct is responsible for tracking the overall state of the subsystem. +/// +/// It tracks metadata about our view of the unfinalized chain, which assignments and approvals we have seen, and our peers' views. +struct State { + // These three fields are used in conjunction to construct a view over the unfinalized chain. + blocks_by_number: BTreeMap>, + blocks: HashMap, + finalized_number: BlockNumber, + + // Peer view data is partially stored here, and partially inline within the `BlockEntry`s + peer_views: HashMap, +} + +enum MessageFingerprint { + Assigment(Hash, u32, ValidatorIndex), + Approval(Hash, u32, ValidatorIndex), +} + +struct Knowledge { + known_messages: HashSet, +} + +/// Information about blocks in our current view as well as whether peers know of them. +struct BlockEntry { + // Peers who we know are aware of this block and thus, the candidates within it. This maps to their knowledge of messages. + known_by: HashMap, + // The number of the block. + number: BlockNumber, + // The parent hash of the block. + parent_hash: Hash, + // Our knowledge of messages. + knowledge: Knowledge, + // A votes entry for each candidate. + candidates: IndexMap, +} + +enum ApprovalState { + Assigned(AssignmentCert), + Approved(AssignmentCert, ApprovalSignature), +} + +/// Information about candidates in the context of a particular block they are included in. In other words, +/// multiple `CandidateEntry`s may exist for the same candidate, if it is included by multiple blocks - this is likely the case +/// when there are forks. +struct CandidateEntry { + approvals: HashMap, +} +``` + +### Network updates + +#### `NetworkBridgeEvent::PeerConnected` + +Add a blank view to the `peer_views` state. + +#### `NetworkBridgeEvent::PeerDisconnected` + +Remove the view under the associated `PeerId` from `State::peer_views`. + +Iterate over every `BlockEntry` and remove `PeerId` from it. + +#### `NetworkBridgeEvent::PeerViewChange` + +Invoke `unify_with_peer(peer, view)` to catch them up to messages we have. + +We also need to use the `view.finalized_number` to remove the `PeerId` from any blocks that it won't be wanting information about anymore. Note that we have to be on guard for peers doing crazy stuff like jumping their 'finalized_number` forward 10 trillion blocks to try and get us stuck in a loop for ages. + +One of the safeguards we can implement is to reject view updates from peers where the new `finalized_number` is less than the previous. + +We augment that by defining `constrain(x)` to output the x bounded by the first and last numbers in `state.blocks_by_number`. + +From there, we can loop backwards from `constrain(view.finalized_number)` until `constrain(last_view.finalized_number)` is reached, removing the `PeerId` from all `BlockEntry`s referenced at that height. We can break the loop early if we ever exit the bound supplied by the first block in `state.blocks_by_number`. + +#### `NetworkBridgeEvent::OurViewChange` + +Prune all lists from `blocks_by_number` with number less than or equal to `view.finalized_number`. Prune all the `BlockEntry`s referenced by those lists. + +#### `NetworkBridgeEvent::PeerMessage` + +If the message is of type `ApprovalDistributionV1Message::Assignment(assignment_cert, claimed_index)`, then call `import_and_circulate_assignment(MessageSource::Peer(sender), assignment_cert, claimed_index)` + +If the message is of type `ApprovalDistributionV1Message::Approval(approval_vote)`, then call `import_and_circulate_approval(MessageSource::Peer(sender), approval_vote)` + +### Subsystem Updates + +#### `ApprovalDistributionMessage::NewBlocks` + +Create `BlockEntry` and `CandidateEntries` for all blocks. + +For all peers: + * Compute `view_intersection` as the intersection of the peer's view blocks with the hashes of the new blocks. + * Invoke `unify_with_peer(peer, view_intersection)`. + +#### `ApprovalDistributionMessage::DistributeAsignment` + +Load the corresponding `BlockEntry`. Distribute to all peers in `known_by`. Add to the corresponding `CandidateEntry`. + +#### `ApprovalDistributionMessage::DistributeApproval` + +Load the corresponding `BlockEntry`. Distribute to all peers in `known_by`. Add to the corresponding `CandidateEntry`. + +### Utility + +```rust +enum MessageSource { + Peer(PeerId), + Local, +} +``` + +#### `import_and_circulate_assignment(source: MessageSource, assignment: IndirectAssignmentCert, claimed_candidate_index: u32)` + +Imports an assignment cert referenced by block hash and candidate index. As a postcondition, if the cert is valid, it will have distributed the cert to all peers who have the block in their view, with the exclusion of the peer referenced by the `MessageSource`. + + * Load the BlockEntry using `assignment.block_hash`. If it does not exist, report the source if it is `MessageSource::Peer` and return. + * Compute a fingerprint for the `assignment` using `claimed_candidate_index`. + * If the source is `MessageSource::Peer(sender)`: + * check if `peer` appears under `known_by` and whether the fingerprint is in the `known_messages` of the peer. If the peer does not know the block, report for providing data out-of-view and proceed. If the peer does know the block and the knowledge contains the fingerprint, report for providing replicate data and return. + * If the message fingerprint appears under the `BlockEntry`'s `Knowledge`, give the peer a small positive reputation boost and return. Note that we must do this after checking for out-of-view to avoid being spammed. If we did this check earlier, a peer could provide data out-of-view repeatedly and be rewarded for it. + * Dispatch `ApprovalVotingMessage::CheckAndImportAssignment(assignment)` and wait for the response. + * If the result is `AssignmentCheckResult::Accepted` or `AssignmentCheckResult::AcceptedDuplicate` + * If the vote was accepted but not duplicate, give the peer a positive reputation boost + * add the fingerprint to both our and the peer's knowledge in the `BlockEntry`. Note that we only doing this after making sure we have the right fingerprint. + * If the result is `AssignmentCheckResult::TooFarInFuture`, mildly punish the peer and return. + * If the result is `AssignmentCheckResult::Bad`, punish the peer and return. + * If the source is `MessageSource::Local(CandidateIndex)` + * check if the fingerprint appears under the `BlockEntry's` knowledge. If not, add it. + * Load the candidate entry for the given candidate index. It should exist unless there is a logic error in the approval voting subsystem. + * Set the approval state for the validator index to `ApprovalState::Assigned` unless the approval state is set already. This should not happen as long as the approval voting subsystem instructs us to ignore duplicate assignments. + * Dispatch a `ApprovalDistributionV1Message::Assignment(assignment, candidate_index)` to all peers in the `BlockEntry`'s `known_by` set, excluding the peer in the `source`, if `source` has kind `MessageSource::Peer`. Add the fingerprint of the assignment to the knowledge of each peer. + + +#### `import_and_circulate_approval(source: MessageSource, approval: IndirectSignedApprovalVote)` + +Imports an approval signature referenced by block hash and candidate index. + + * Load the BlockEntry using `approval.block_hash` and the candidate entry using `approval.candidate_entry`. If either does not exist, report the source if it is `MessageSource::Peer` and return. + * Compute a fingerprint for the approval. + * Compute a fingerprint for the corresponding assignment. If the `BlockEntry`'s knowledge does not contain that fingerprint, then report the source if it is `MessageSource::Peer` and return. All references to a fingerprint after this refer to the approval's, not the assignment's. + * If the source is `MessageSource::Peer(sender)`: + * check if `peer` appears under `known_by` and whether the fingerprint is in the `known_messages` of the peer. If the peer does not know the block, report for providing data out-of-view and proceed. If the peer does know the block and the knowledge contains the fingerprint, report for providing replicate data and return. + * If the message fingerprint appears under the `BlockEntry`'s `Knowledge`, give the peer a small positive reputation boost and return. Note that we must do this after checking for out-of-view to avoid being spammed. If we did this check earlier, a peer could provide data out-of-view repeatedly and be rewarded for it. + * Dispatch `ApprovalVotingMessage::CheckAndImportApproval(approval)` and wait for the response. + * If the result is `VoteCheckResult::Accepted(())`: + * Give the peer a positive reputation boost and add the fingerprint to both our and the peer's knowledge. + * If the result is `VoteCheckResult::Bad`: + * Report the peer and return. + * Load the candidate entry for the given candidate index. It should exist unless there is a logic error in the approval voting subsystem. + * Set the approval state for the validator index to `ApprovalState::Approved`. It should already be in the `Assigned` state as our `BlockEntry` knowledge contains a fingerprint for the assignment. + * Dispatch a `ApprovalDistributionV1Message::Approval(approval)` to all peers in the `BlockEntry`'s `known_by` set, excluding the peer in the `source`, if `source` has kind `MessageSource::Peer`. Add the fingerprint of the assignment to the knowledge of each peer. Note that this obeys the politeness conditions: + * We guarantee elsewhere that all peers within `known_by` are aware of all assignments relative to the block. + * We've checked that this specific approval has a corresponding assignment within the `BlockEntry`. + * Thus, all peers are aware of the assignment or have a message to them in-flight which will make them so. + + +#### `unify_with_peer(peer: PeerId, view)`: + +For each block in the view: + 1. Initialize a set `fresh_blocks = {}` + 2. Load the `BlockEntry` for the block. If the block is unknown, or the number is less than the view's finalized number, go to step 6. + 3. Inspect the `known_by` set of the `BlockEntry`. If the peer is already present, go to step 6. + 4. Add the peer to `known_by` with a cloned version of `block_entry.knowledge`. and add the hash of the block to `fresh_blocks`. + 5. Return to step 2 with the ancestor of the block. + 6. For each block in `fresh_blocks`, send all assignments and approvals for all candidates in those blocks to the peer. \ No newline at end of file diff --git a/roadmap/implementers-guide/src/node/approval/approval-networking.md b/roadmap/implementers-guide/src/node/approval/approval-networking.md deleted file mode 100644 index 558d4447c954..000000000000 --- a/roadmap/implementers-guide/src/node/approval/approval-networking.md +++ /dev/null @@ -1,7 +0,0 @@ -# Approval Networking - -> TODO: - -## Protocol - -## Functionality \ No newline at end of file diff --git a/roadmap/implementers-guide/src/node/approval/approval-voting.md b/roadmap/implementers-guide/src/node/approval/approval-voting.md index 5e9f1e360ce6..be87939f30c2 100644 --- a/roadmap/implementers-guide/src/node/approval/approval-voting.md +++ b/roadmap/implementers-guide/src/node/approval/approval-voting.md @@ -10,8 +10,8 @@ Input: - `ApprovalVotingMessage::ApprovedAncestor` Output: - - `ApprovalNetworkingMessage::DistributeAssignment` - - `ApprovalNetworkingMessage::DistributeApproval` + - `ApprovalDistributionMessage::DistributeAssignment` + - `ApprovalDistributionMessage::DistributeApproval` - `RuntimeApiMessage::Request` - `ChainApiMessage` - `AvailabilityRecoveryMessage::Recover` @@ -72,7 +72,6 @@ struct BlockEntry { block_hash: Hash, session: SessionIndex, slot: SlotNumber, - received_late_by: Duration, // 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. relay_vrf_story: [u8; 32], @@ -163,8 +162,9 @@ On receiving an `OverseerSignal::ActiveLeavesUpdate(update)`: * Ensure that the `CandidateEntry` contains a `block_assignments` entry for the block, with the correct backing group set. * If a validator in this session, compute and assign `our_assignment` for the `block_assignments` * Only if not a member of the backing group. - * Run `RelayVRFModulo` and `RelayVRFDelay` according to the [the approvals protocol section](../../protocol-approval.md#assignment-criteria) + * Run `RelayVRFModulo` and `RelayVRFDelay` according to the [the approvals protocol section](../../protocol-approval.md#assignment-criteria). Ensure that the assigned core derived from the output is covered by the auxiliary signature aggregated in the `VRFPRoof`. * invoke `process_wakeup(relay_block, candidate)` for each new candidate in each new block - this will automatically broadcast a 0-tranche assignment, kick off approval work, and schedule the next delay. + * Dispatch an `ApprovalDistributionMessage::NewBlocks` with the meta information filled out for each new block. #### `ApprovalVotingMessage::CheckAndImportAssignment` @@ -175,16 +175,18 @@ On receiving a `ApprovalVotingMessage::CheckAndImportAssignment` message, we che * Check the assignment cert * If the cert kind is `RelayVRFModulo`, then the certificate is valid as long as `sample < session_info.relay_vrf_samples` and the VRF is valid for the validator's key with the input `block_entry.relay_vrf_story ++ sample.encode()` as described with [the approvals protocol section](../../protocol-approval.md#assignment-criteria). We set `core_index = vrf.make_bytes().to_u32() % session_info.n_cores`. If the `BlockEntry` causes inclusion of a candidate at `core_index`, then this is a valid assignment for the candidate at `core_index` and has delay tranche 0. Otherwise, it can be ignored. * If the cert kind is `RelayVRFDelay`, then we check if the VRF is valid for the validator's key with the input `block_entry.relay_vrf_story ++ cert.core_index.encode()` as described in [the approvals protocol section](../../protocol-approval.md#assignment-criteria). The cert can be ignored if the block did not cause inclusion of a candidate on that core index. Otherwise, this is a valid assignment for the included candidate. The delay tranche for the assignment is determined by reducing `(vrf.make_bytes().to_u64() % (session_info.n_delay_tranches + session_info.zeroth_delay_tranche_width)).saturating_sub(session_info.zeroth_delay_tranche_width)`. + * We also check that the core index derived by the output is covered by the `VRFProof` by means of an auxiliary signature. + * If the delay tranche is too far in the future, return `VoteCheckResult::Ignore`. * `import_checked_assignment` * return the appropriate `VoteCheckResult` on the response channel. #### `ApprovalVotingMessage::CheckAndImportApproval` On receiving a `CheckAndImportApproval(indirect_approval_vote, response_channel)` message: - * Fetch the `BlockEntry` from the indirect approval vote's `block_hash`. If none, return `VoteCheckResult::Bad`. - * Fetch the `CandidateEntry` from the indirect approval vote's `candidate_index`. If the block did not trigger inclusion of enough candidates, return `VoteCheckResult::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 `VoteCheckResult::Bad`. - * Send `VoteCheckResult::Accepted`, + * 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`. + * Send `ApprovalCheckResult::Accepted` * `import_checked_approval(BlockEntry, CandidateEntry, ValidatorIndex)` #### `ApprovalVotingMessage::ApprovedAncestor` @@ -201,13 +203,14 @@ On receiving an `ApprovedAncestor(Hash, BlockNumber, response_channel)`: #### `import_checked_assignment` * Load the candidate in question and access the `approval_entry` for the block hash the cert references. + * Ignore if we already observe the validator as having been assigned. * Ensure the validator index is not part of the backing group for the candidate. * Ensure the validator index is not present in the approval entry already. * Create a tranche entry for the delay tranche in the approval entry and note the assignment within it. * Note the candidate index within the approval entry. #### `import_checked_approval(BlockEntry, CandidateEntry, ValidatorIndex)` - * Set the corresponding bit of the `approvals` bitfield in the `CandidateEntry` to `1`. + * Set the corresponding bit of the `approvals` bitfield in the `CandidateEntry` to `1`. If already `1`, return. * For each `ApprovalEntry` in the `CandidateEntry` (typically only 1), check whether the validator is assigned as a checker. * If so, set `n_tranches = tranches_to_approve(approval_entry)`. * If `check_approval(block_entry, approval_entry, n_tranches)` is true, set the corresponding bit in the `block_entry.approved_bitfield`. @@ -230,8 +233,8 @@ On receiving an `ApprovedAncestor(Hash, BlockNumber, response_channel)`: * Set `n_tranches = tranches_to_approve(approval_entry)` * If `OurAssignment` has tranche `<= n_tranches`, the tranche is live according to our local clock (based against block slot), and we have not triggered the assignment already * Import to `ApprovalEntry` - * Broadcast on network with an `ApprovalNetworkingMessage::DistributeAssignment`. - * Kick off approval work with `launch_approval` + * Broadcast on network with an `ApprovalDistributionMessage::DistributeAssignment`. + * Kick off approval work with `launch_approval`. Note that if the candidate appears in multiple current blocks, we will launch approval for each block it appears in. It may make sense to shortcut around this with caching either at this level or on the level of the other subsystems invoked by that function. * Schedule another wakeup based on `next_wakeup` #### `next_wakeup(approval_entry, candidate_entry)`: @@ -251,6 +254,6 @@ On receiving an `ApprovedAncestor(Hash, BlockNumber, response_channel)`: #### `issue_approval(request)`: * 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. - * Transform into an `IndirectSignedApprovalVote` using the `block_hash` and `candidate_index` from the request. * `import_checked_approval(block_entry, candidate_entry, validator_index)` - * Dispatch an `ApprovalNetworkingMessage::DistributeApproval` message. + * Construct a `IndirectSignedApprovalVote` using the informatio about the vote. + * Dispatch `ApprovalDistributionMessage::DistributeApproval`. diff --git a/roadmap/implementers-guide/src/node/utility/network-bridge.md b/roadmap/implementers-guide/src/node/utility/network-bridge.md index 5ace56b2a9c8..d89ec7e8fe42 100644 --- a/roadmap/implementers-guide/src/node/utility/network-bridge.md +++ b/roadmap/implementers-guide/src/node/utility/network-bridge.md @@ -61,6 +61,12 @@ The `activated` and `deactivated` lists determine the evolution of our local vie If we are connected to the same peer on both peer-sets, we will send the peer two view updates as a result. +### Overseer Signal: BlockFinalized + +We obtain the number of the block hash in the event by issuing a `ChainApiMessage::BlockNumber` request and then issue a `ProtocolMessage::ViewUpdate` to each connected peer on each peer-set. We also issue a `NetworkBridgeEvent::OurViewChange` to each event handler for each protocol. + +If we are connected to the same peer on both peer-sets, we will send the peer two view updates as a result. + ### Network Event: Peer Connected Issue a `NetworkBridgeEvent::PeerConnected` for each [Event Handler](#event-handlers) of the peer-set and negotiated protocol version of the peer. diff --git a/roadmap/implementers-guide/src/types/approval.md b/roadmap/implementers-guide/src/types/approval.md index 07266e718aa8..5603d03aa659 100644 --- a/roadmap/implementers-guide/src/types/approval.md +++ b/roadmap/implementers-guide/src/types/approval.md @@ -17,11 +17,9 @@ These certificates can be checked in the context of a specific block, candidate, ```rust enum AssignmentCertKind { RelayVRFModulo { - relay_vrf: (VRFInOut, VRFProof), sample: u32, }, RelayVRFDelay { - relay_vrf: (VRFInOut, VRFProof), core_index: CoreIndex, } } @@ -30,12 +28,25 @@ struct AssignmentCert { // The criterion which is claimed to be met by this cert. kind: AssignmentCertKind, // The VRF showing the criterion is met. - vrf: VRFInOut, + vrf: (VRFPreOut, VRFProof), } ``` > TODO: RelayEquivocation cert. Probably can only be broadcast to chains that have handled an equivocation report. +## IndirectAssignmentCert + +An assignment cert which refers to the candidate under which the assignment is relevant by block hash. + +```rust +struct IndirectAssignmentCert { + // A block hash where the candidate appears. + block_hash: Hash, + validator: ValidatorIndex, + cert: AssignmentCert, +} +``` + ## ApprovalVote A vote of approval on a candidate. @@ -58,7 +69,7 @@ struct SignedApprovalVote { A signed approval vote which references the candidate indirectly via the block. If there exists a look-up to the candidate hash from the block hash and candidate index, then this can be transformed into a `SignedApprovalVote`. -Although this vote references the candidate by a specific block hash and candidate index, the vote actually applies to +Although this vote references the candidate by a specific block hash and candidate index, the signature is computed on the actual `SignedApprovalVote` payload. ```rust struct IndirectSignedApprovalVote { diff --git a/roadmap/implementers-guide/src/types/network.md b/roadmap/implementers-guide/src/types/network.md index a5472a407153..5e78c30f2904 100644 --- a/roadmap/implementers-guide/src/types/network.md +++ b/roadmap/implementers-guide/src/types/network.md @@ -8,7 +8,12 @@ These types are those that are actually sent over the network to subsystems. type RequestId = u64; type ProtocolVersion = u32; struct PeerId(...); // opaque, unique identifier of a peer. -struct View(Vec); // Up to `N` (5?) chain heads. +struct View { + // Up to `N` (5?) chain heads. + heads: Vec, + // The number of the finalized block. + finalized_number: BlockNumber, +} enum ObservedRole { Full, @@ -18,6 +23,20 @@ enum ObservedRole { ## V1 Network Subsystem Message Types +### Approval Distribution V1 + +```rust +enum ApprovalDistributionV1Message { + /// Assignments for candidates in recent, unfinalized blocks. + /// + /// The u32 is the claimed index of the candidate this assignment corresponds to. Actually checking the assignment + /// may yield a different result. + Assignments(Vec<(IndirectAssignmentCert, u32)>), + /// Approvals for candidates in some recent, unfinalized block. + Approvals(Vec), +} +``` + ### Availability Distribution V1 ```rust @@ -82,6 +101,7 @@ These are the messages for the protocol on the validation peer-set. ```rust enum ValidationProtocolV1 { + ApprovalDistribution(ApprovalDistributionV1Message), AvailabilityDistribution(AvailabilityDistributionV1Message), BitfieldDistribution(BitfieldDistributionV1Message), PoVDistribution(PoVDistributionV1Message), diff --git a/roadmap/implementers-guide/src/types/overseer-protocol.md b/roadmap/implementers-guide/src/types/overseer-protocol.md index 3b4b0d5b02d4..daebbab34aac 100644 --- a/roadmap/implementers-guide/src/types/overseer-protocol.md +++ b/roadmap/implementers-guide/src/types/overseer-protocol.md @@ -40,24 +40,30 @@ struct ActiveLeavesUpdate { Messages received by the approval voting subsystem. ```rust -enum VoteCheckResult { +enum AssignmentCheckResult { + // The vote was accepted and should be propagated onwards. + Accepted, + // The vote was valid but duplicate and should not be propagated onwards. + AcceptedDuplicate, + // The vote was valid but too far in the future to accept right now. + TooFarInFuture, + // The vote was bad and should be ignored, reporting the peer who propagated it. + Bad, +} + +enum ApprovalCheckResult { // The vote was accepted and should be propagated onwards. Accepted, // The vote was bad and should be ignored, reporting the peer who propagated it. Bad, - // We do not have enough information to evaluate the vote. Ignore but don't report. - // This should occur primarily on startup. - Ignore, } enum ApprovalVotingMessage { /// Check if the assignment is valid and can be accepted by our view of the protocol. /// Should not be sent unless the block hash is known. CheckAndImportAssignment( - Hash, - AssignmentCert, - ValidatorIndex, - ResponseChannel, + IndirectAssignmentCert, + ResponseChannel, ), /// Check if the approval vote is valid and can be accepted by our view of the /// protocol. @@ -65,7 +71,7 @@ enum ApprovalVotingMessage { /// Should not be sent unless the block hash within the indirect vote is known. CheckAndImportApproval( IndirectSignedApprovalVote, - ResponseChannel, + ResponseChannel, ), /// Returns the highest possible ancestor hash of the provided block hash which is /// acceptable to vote on finality for. @@ -78,17 +84,37 @@ enum ApprovalVotingMessage { } ``` -## Approval Networking +## Approval Distribution -Messages received by the approval networking subsystem. +Messages received by the approval Distribution subsystem. ```rust -enum ApprovalNetworkingMessage { +/// Metadata about a block which is now live in the approval protocol. +struct BlockApprovalMeta { + /// The hash of the block. + hash: Hash, + /// The number of the block. + number: BlockNumber, + /// The candidates included by the block. Note that these are not the same as the candidates that appear within the + /// block body. + candidates: Vec, + /// The consensus slot number of the block. + slot_number: SlotNumber, +} + +enum ApprovalDistributionMessage { + /// Notify the `ApprovalDistribution` subsystem about new blocks and the candidates contained within + /// them. + NewBlocks(Vec), /// Distribute an assignment cert from the local validator. The cert is assumed - /// to be valid for the given relay-parent and validator index. - DistributeAssignment(Hash, AssignmentCert, ValidatorIndex), - /// Distribute an approval vote for the local validator. - DistributeApproval(IndirectApprovalVote), + /// to be valid, relevant, and for the given relay-parent and validator index. + /// + /// The `u32` param is the candidate index in the fully-included list. + DistributeAssignment(IndirectAssignmentCert, u32), + /// 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), } ```