Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.

Approval Voting Database#2162

Merged
rphmeier merged 39 commits intomasterfrom
rh-approval-voting-db
Jan 18, 2021
Merged

Approval Voting Database#2162
rphmeier merged 39 commits intomasterfrom
rh-approval-voting-db

Conversation

@rphmeier
Copy link
Copy Markdown
Contributor

@rphmeier rphmeier commented Dec 23, 2020

This change-set became large on its own. This is implementing the described database and operations from the Approval Voting subsystem, against the AuxStore trait.

Main points to review:

  • The structs match the "Database Schema" section
  • The start-up logic is fulfilled by fn clear
  • The finalization logic is fulfilled by fn canonicalize
  • The ActiveLeavesUpdate logic is fulfilled by fn add_block_entry

Related to #1975

@rphmeier rphmeier added A3-in_progress Pull request is in progress. No review needed at this stage. B0-silent Changes should not be mentioned in any release notes C1-low PR touches the given topic and has a low impact on builders. labels Dec 23, 2020
@github-actions github-actions bot added A0-please_review Pull request needs code review. and removed A3-in_progress Pull request is in progress. No review needed at this stage. labels Dec 23, 2020
@rphmeier
Copy link
Copy Markdown
Contributor Author

The bot added pleasereview before I could :)

next_wakeup: Tick,
our_assignment: Option<OurAssignment>,
// `n_validators` bits.
assignments: BitVec<BitOrderLsb0, u8>,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could discuss this feature further, and even if we keep it caching the assignments remains fine, but in my design no-shows giving approval votes can unassign some no show replacements.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's still maintained in the logic PR that follows, because we always compute a mask over this bit-vec based on the tranches we take.

Copy link
Copy Markdown
Contributor Author

@rphmeier rphmeier Jan 2, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pub(crate) fn assignments_up_to(&self, tranche: DelayTranche) -> BitVec<BitOrderLsb0, u8> {
self.tranches.iter()
.take_while(|e| e.tranche <= tranche)
.fold(bitvec::bitvec![BitOrderLsb0, u8; 0; self.assignments.len()], |mut a, e| {
for &(v, _) in &e.assignments {
a.set(v as _, true);
}
a
})
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(from the next PR)

session: SessionIndex,
// Assignments are based on blocks, so we need to track assignments separately
// based on the block we are looking at.
block_assignments: BTreeMap<Hash, ApprovalEntry>,
Copy link
Copy Markdown
Contributor

@burdges burdges Dec 30, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose the Hash here is the hash of the relay chain block where inclusion happens? It's ambiguous form this comment.

Copy link
Copy Markdown
Contributor Author

@rphmeier rphmeier Jan 2, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's right. In our code-base Hash is block hash and CandidateHash is candidate hash.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be worthwhile to define a BlockHash newtype adjacent to CandidateHash? It won't change anything re: type safety, but it might make it even harder to confuse what each hash represents.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"worthwhile" is always a question of opportunity cost :) . I wouldn't object to such a change, but I don't think it's in the scope of this PR.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree, that would be its own thing. I'll extract an issue.

// A bitfield where the i'th bit corresponds to the i'th candidate in `candidates`.
// The i'th bit is `true` iff the candidate has been approved in the context of this
// block. The block can be considered approved if the bitfield has all bits set to `true`.
approved_bitfield: BitVec<BitOrderLsb0, u8>,
Copy link
Copy Markdown
Contributor

@burdges burdges Dec 30, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually either all bits for which self.candidates.binary_search_by(|probe| probe.0.cmp(&i)).is_ok() holds or else all bits but we set any missing cores initially.

I should double check when these bits get set later.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This bitfield is by candidates, not by cores. So if there were candidates included from cores [1, 3, 7], then the bit-field would have 3 bits corresponding to each of those, respectively. I think all bits is correct with that in mind, but I might misunderstand the purpose of the binary search snippet you posted if it's not for checking existence of the core in the included candidates set.

It does mean that every time we want to mark a candidate as approved, we have an extra search of the candidate set for each block entry a candidate appears in, but that's contiguous memory and would be dwarfed by the cost of writing that metadata to disk.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only meant that approved_bitfield could be initialized to zero for bits representing existent candidates and one for bits representing non-existent candidates. I just assumed candidates would always be indexed by core in this stage. Appears you're indexing by some other candidate index, which yes makes BitVec's length is equivalent to presetting non-existent candidates to one. I see..

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From the outside of this struct, we do lookup by core, but we have to do an internal look-up via the candidates vec first, which is cheap.

#### `OverseerSignal::BlockFinalized`

On receiving an `OverseerSignal::BlockFinalized(h)`, we fetch the block number `b` of that block from the ChainApi subsystem. We update our `StoredBlockRange` to begin at `b+1`. Additionally, we remove all block entries and candidates referenced by them up to and including `b`. Lastly, we prune out all descendents of `h` transitively: when we remove a `BlockEntry` with number `b` that is not equal to `h`, we recursively delete all the `BlockEntry`s referenced as children. We remove the `block_assignments` entry for the block hash and if `block_assignments` is now empty, remove the `CandidateEntry`.
On receiving an `OverseerSignal::BlockFinalized(h)`, we fetch the block number `b` of that block from the ChainApi subsystem. We update our `StoredBlockRange` to begin at `b+1`. Additionally, we remove all block entries and candidates referenced by them up to and including `b`. Lastly, we prune out all descendents of `h` transitively: when we remove a `BlockEntry` with number `b` that is not equal to `h`, we recursively delete all the `BlockEntry`s referenced as children. We remove the `block_assignments` entry for the block hash and if `block_assignments` is now empty, remove the `CandidateEntry`. We also update each of the `BlockNumber -> Vec<Hash>` keys in the database to reflect the blocks at that height, clearing if empty.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We keep an checks running however I guess. We should also keep the erasure coded pieces and the outgoing messages sent by blocks for another 24 hours or whatever our XCMP window is.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We keep around the data but don't do any checks after finality anymore.

Copy link
Copy Markdown
Contributor

@coriolinus coriolinus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some nits, but nothing essential. Looks like a reasonable implementation of a data store backed by AuxStore for the types described in the guide.

polkadot-overseer = { path = "../../overseer" }
polkadot-primitives = { path = "../../../primitives" }
polkadot-node-primitives = { path = "../../primitives" }
bitvec = "0.17.4"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: bitvec should go among the external dependencies, not among the polkadot dependencies.

sp-consensus-slots = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
sp-blockchain = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }

[dev-dependencies] No newline at end of file
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: text files should have trailing newlines.

rphmeier and others added 3 commits January 5, 2021 09:23
Co-authored-by: Peter Goodspeed-Niklaus <coriolinus@users.noreply.github.com>
Co-authored-by: Peter Goodspeed-Niklaus <coriolinus@users.noreply.github.com>
Co-authored-by: Peter Goodspeed-Niklaus <coriolinus@users.noreply.github.com>
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

A0-please_review Pull request needs code review. B0-silent Changes should not be mentioned in any release notes C1-low PR touches the given topic and has a low impact on builders.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants