Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Count weight from equivocations #4

Closed
rphmeier opened this issue Aug 24, 2018 · 2 comments
Closed

Count weight from equivocations #4

rphmeier opened this issue Aug 24, 2018 · 2 comments

Comments

@rphmeier
Copy link
Contributor

Based on #2 (review)

But only in vote-nodes where no vote-weight from the equivocating node has already been counted.
As a matter of practicality, we can rewrite the Vote object used in the round counter as

enum Kind {
    Prevote,
    Precommit,
}

// better name than `Vote`
struct Weight<Id> {
    prevote: usize,
    precommit: usize,
    votes: HashMap<(Id, Kind), usize>,
}

and alter the Add operation to only add weight where the voter hasn't been counted yet.
This will probably impede performance somewhat because we will have to do several hash-map queries per addition.


cc @AlistairStewart

Another open question is whether this introduces a DoS vector, as we could be spammed with votes on all possible blocks by an equivocating authority.

We discussed a few simple rules for dropping/forgetting votes, but we may want to investigate more:

  • The vote is on the same block as another (in this case, the equivocation is in the signature)
  • The vote is not on a descendent of the last finalized block of the last round.
@rphmeier
Copy link
Contributor Author

A more optimized way of using the votes is to have weight defined as

struct Weight {
    prevote: usize,
    precommit: usize,
    vote_bitfield: Bitfield, // 2x n_validators
    weight_lookup: Box<Fn(u32) -> (usize, Kind)>,
}

with add as

fn add(&mut self, other: Self) {
    self.prevote += other.prevote;
    self.precommit += other.precommit;

    let bitfield_overlap = self.bitfield & other.bitfield;

    // returns immediately if empty.
    for idx in bitfield_overlap {
        match (self.weight_lookup)(idx) {
            (weight, Kind::Prevote) => self.prevote -= weight,
            (weight, Kind::Precommit) => self.precommit -= weight,
        }
    }
}

We will need to remove the Default bound on the V type in VoteGraph and replace it by a factory of some kind.

@rphmeier
Copy link
Contributor Author

A bit of further optimization that lets us keep the Default implementation.

The Bitfield is a wrapper around an Option<InnerBitfield> that is a heap-allocated bitfield s.t. None | None and None & None are both None. The default is also None.

When we import the first equivocation from a validator, we "re-import" the first vote (not the equivocation) by that validator with a Some(InnerBitfield) that has only that validator's bit set. Any subsequent votes, including our first equivocation, imported by that validator will have the same bit set.

This means that in the general case, with no equivocations, we drag around only an extra word or two for the Option, and don't pay much more expense per equivocation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant