From 6ce115ec95ba8f71b7e29c96ace4881357a6a9b8 Mon Sep 17 00:00:00 2001 From: carllin Date: Wed, 23 Oct 2019 12:35:47 -0700 Subject: [PATCH] Add commitment metrics implementation to book (#5903) * Add commitment metrics implementation to book --- book/src/implemented-proposals/commitment.md | 89 ++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 book/src/implemented-proposals/commitment.md diff --git a/book/src/implemented-proposals/commitment.md b/book/src/implemented-proposals/commitment.md new file mode 100644 index 000000000..1a07cd5ec --- /dev/null +++ b/book/src/implemented-proposals/commitment.md @@ -0,0 +1,89 @@ +# Commitment + +The commitment metric aims to give clients a measure of the network confirmation +and stake levels on a particular block. Clients can then use this information to +derive their own measures of confidence. + +# Calculation RPC + +Clients can request commitment metrics from a validator for a signature `s` +through `get_block_commitment(s: Signature) -> BlockCommitment` over RPC. The +`BlockCommitment` struct contains an array of u64 `[u64, MAX_CONFIRMATIONS]`. This +array represents the commitment metric for the particular block `N` that +contains the signature `s` as of the last block `M` that the validator voted on. + +An entry `s` at index `i` in the `BlockCommitment` array implies that the +validator observed `s` total stake in the cluster reaching `i` confirmations on +block `N` as observed in some block `M`. There will be `MAX_CONFIRMATIONS` elements in +this array, representing all the possible number of confirmations from 1 to +`MAX_CONFIRMATIONS`. + +# Computation of commitment metric + +Building this `BlockCommitment` struct leverages the computations already being +performed for building consensus. The `collect_vote_lockouts` function in +`consensus.rs` builds a HashMap, where each entry is of the form `(b, s)` +where `s` is a `StakeLockout` struct representing the amount of stake and +lockout on a bank `b`. + +This computation is performed on a votable candidate bank `b` as follows. + +``` + let output: HashMap = HashMap::new(); + for vote_account in b.vote_accounts { + for v in vote_account.vote_stack { + for a in ancestors(v) { + f(*output.get_mut(a), vote_account, v); + } + } + } +``` + +where `f` is some accumulation function that modifies the `StakeLockout` entry +for slot `a` with some data derivable from vote `v` and `vote_account` +(stake, lockout, etc.). Note here that the `ancestors` here only includes +slots that are present in the current status cache. Signatures for banks earlier +than those present in the status cache would not be queryable anyway, so those +banks are not included in the commitment calculations here. + +Now we can naturally augment the above computation to also build a +`BlockCommitment` array for every bank `b` by: +1) Adding a `ForkCommitmentCache` to collect the `BlockCommitment` structs +2) Replacing `f` with `f'` such that the above computation also builds this +`BlockCommitment` for every bank `b`. + +We will proceed with the details of 2) as 1) is trivial. + +Before continuing, it is noteworthy that for some validator's vote account `a`, +the number of local confirmations for that validator on slot `s` is +`v.num_confirmations`, where `v` is the smallest vote in the stack of votes +`a.votes` such that `v.slot >= s` (i.e. there is no need to look at any +votes > v as the number of confirmations will be lower). + +Now more specifically, we augment the above computation to: + +``` + let output: HashMap = HashMap::new(); + let fork_commitment_cache = ForkCommitmentCache::default(); + for vote_account in b.vote_accounts { + // vote stack is sorted from oldest vote to newest vote + for (v1, v2) in vote_account.vote_stack.windows(2) { + for a in ancestors(v1).difference(ancestors(v2)) { + f'(*output.get_mut(a), *fork_commitment_cache.get_mut(a), vote_account, v); + } + } + } +``` + +where `f'` is defined as: +``` + fn f`( + stake_lockout: &mut StakeLockout, + some_ancestor: &mut BlockCommitment, + vote_account: VoteAccount, + v: Vote, total_stake: u64 + ){ + f(stake_lockout, vote_account, v); + *some_ancestor.commitment[v.num_confirmations] += vote_account.stake; + } +```