• Peter Goodspeed-Niklaus's avatar
    Implement PJR checker (#8160) · 781f9087
    Peter Goodspeed-Niklaus authored
    
    
    * Apply.
    
    * get rid of glob import
    
    * use meaningful generic type name
    
    * pjr_check operates on `Supports` struct used elsewhere
    
    * improve algorithmic complexity of `prepare_pjr_input`
    
    * fix rustdoc warnings
    
    * improve module docs
    
    * typo
    
    * simplify debug assertion
    
    * add test finding the phase-change threshold value for a constructed scenario
    
    * add more threshold scenarios to disambiguate plausible interpretations
    
    * add link to npos paper reference
    
    * docs: staked_assignment -> supports
    
    Co-authored-by: default avatarKian Paimani <[email protected]>
    
    * add utility method for generating npos inputs
    
    * add a fuzzer which asserts that all unbalanced seq_phragmen are PJR
    
    Note that this currently fails. I hope that this can be rectified
    by calculating the threshold instead of choosing some arbitrary number.
    
    * assert in all cases, not just debug
    
    * leverage a native solution to choose candidates
    
    * use existing helper methods
    
    * add pjr-check and incorporate into the fuzzer
    
    We should probably have one of the W3F people look at this to ensure
    we're not misconstruing any definitions, but this seems like a
    fairly straightforward implementation.
    
    * fix compilation errors
    
    * Enable manually setting iteration parameters in single run.
    
    This gives us the ability to reproducably extract cases where
    honggfuzz has discovered a panic. For example:
    
    $ cargo run --release --bin phragmen_pjr -- --candidates 569 --voters 100
    Tue 23 Feb 2021 11:23:39 AM CET
       Compiling bitflags v1.2.1
       Compiling unicode-width v0.1.8
       Compiling unicode-segmentation v1.7.1
       Compiling ansi_term v0.11.0
       Compiling strsim v0.8.0
       Compiling vec_map v0.8.2
       Compiling proc-macro-error-attr v1.0.4
       Compiling proc-macro-error v1.0.4
       Compiling textwrap v0.11.0
       Compiling atty v0.2.14
       Compiling heck v0.3.2
       Compiling clap v2.33.3
       Compiling structopt-derive v0.4.14
       Compiling structopt v0.3.21
       Compiling sp-npos-elections-fuzzer v2.0.0-alpha.5 (/home/coriolinus/Documents/Projects/paritytech/substrate/primitives/npos-elections/fuzzer)
        Finished release [optimized] target(s) in 6.15s
         Running `/home/coriolinus/Documents/Projects/paritytech/substrate/target/release/phragmen_pjr -c 569 -v 100`
    thread 'main' panicked at 'unbalanced sequential phragmen must satisfy PJR', primitives/npos-elections/fuzzer/src/phragmen_pjr.rs:133:5
    note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
    
    This is still not adequate proof that seq_phragmen is broken; it could
    very well be that our PJR checker is doing the wrong thing, or we've
    somehow missed a parameter of interest. Still, it's concerning.
    
    * update comment verbiage for accuracy
    
    * it is valid in PJR for an elected candidate to have 0 support
    
    * Fix phragmen_pjr fuzzer
    
    It turns out that the fundamental problem causing previous implementations
    of the fuzzer to fail wasn't in `seq_phragmen` _or_ in `pjr_check`: it was
    in the rounding errors introduced in the various conversions between the
    internal data representation and the external one.
    
    Fixing the fuzzer is then simply an issue of using the internal representation
    and staying in that representation. However, that leaves the issue that
    `seq_phragmen` occasionally produces an output which is technically not
    PJR due to rounding errors. In the future we will need to add some kind of
    "close-enough" threshold. However, that is explicitly out of scope of
    this PR.
    
    * restart ci; it appears to be stalled
    
    * use necessary import for no-std
    
    * use a more realistic distribution of voters and candidates
    
    This isn't ideal; more realistic numbers would be about twice these.
    However, either case generation or voting has nonlinear execution
    time, and doubling these values brings iteration time from ~20s to
    ~180s. Fuzzing 6x as fast should make up for fuzzing cases half the size.
    
    * identify specifically which PJR check may fail
    
    * move candidate collection comment into correct place
    
    * standard_threshold: use a calculation method which cannot overflow
    
    * Apply suggestions from code review (update comments)
    
    Co-authored-by: default avatarKian Paimani <[email protected]>
    
    * clarify the effectiveness bounds for t-pjr check
    
    * how to spell "committee"
    
    * reorganize: high -> low abstraction
    
    * ensure standard threshold calc cannot panic
    
    Co-authored-by: default avatarKian Paimani <[email protected]>
    
    * Apply suggestions from code review
    
    Co-authored-by: default avatarShawn Tabrizi <[email protected]>
    
    Co-authored-by: default avatarkianenigma <[email protected]>
    Co-authored-by: default avatarKian Paimani <[email protected]>
    Co-authored-by: default avatarShawn Tabrizi <[email protected]>
    781f9087