* Refactor election solution trimming for efficiency
The previous version always trimmed the `CompactOf<T>` instance,
which was intrinsically inefficient: that's a packed data structure,
which is naturally expensive to edit. It's much easier to edit
the unpacked data structures: the `voters` and `assignments` lists.
* rework length-trim tests to work with the new interface
Test suite now compiles. Tests still don't pass because the macro
generating the compact structure still generates `unimplemented!()`
for the actual `compact_length_of` implementation.
* simplify
* add a fuzzer which can validate `Compact::encoded_size_for`
The `Compact` solution type is generated distinctly for each runtime,
and has both three type parameters and a built-in limit to the number
of candidates that each voter can vote for. Finally, they have an
optional `#[compact]` attribute which changes the encoding behavior.
The assignment truncation algorithm we're using depends on the ability
to efficiently and accurately determine how much space a `Compact`
solution will take once encoded.
Together, these two facts imply that simple unit tests are not
sufficient to validate the behavior of `Compact::encoded_size_for`.
This commit adds such a fuzzer. It is designed such that it is possible
to add a new fuzzer to the family by simply adjusting the
`generate_solution_type` macro invocation as desired, and making a
few minor documentation edits.
Of course, the fuzzer still fails for now: the generated implementation
for `encoded_size_for` is still `unimplemented!()`. However, once
the macro is updated appropriately, this fuzzer family should allow
us to gain confidence in the correctness of the generated code.
* Revert "add a fuzzer which can validate `Compact::encoded_size_for`"
This reverts commit 916038790887e64217c6a46e9a6d281386762bfb.
The design of `Compact::encoded_size_for` is flawed. When `#[compact]`
mode is enabled, every integer in the dataset is encoded using run-
length encoding. This means that it is impossible to compute the final
length faster than actually encoding the data structure, because the
encoded length of every field varies with the actual value stored.
Given that we won't be adding that method to the trait, we won't be
needing a fuzzer to validate its performance.
* revert changes to `trait CompactSolution`
If `CompactSolution::encoded_size_for` can't be implemented in the
way that we wanted, there's no point in adding it.
* WIP: restructure trim_assignments_length by actually encoding
This is not as efficient as what we'd hoped for, but it should still
be better than what it's replacing. Overall efficiency of
`fn trim_assignments_length` is now `O(edges * lg assignments.len())`.
* fix compiler errors
* don't sort voters, just assignments
Sorting the `voters` list causes lots of problems; an invariant that
we need to maintain is that an index into the voters list has a stable
meaning.
Luckily, it turns out that there is no need for the assignments list
to correspond to the voters list. That isn't an invariant, though previously
I'd thought that it was.
This simplifies things; we can just leave the voters list alone,
and sort the assignments list the way that is convenient.
* WIP: add `IndexAssignment` type to speed up repeatedly creating `Compact`
Next up: `impl<'a, T> From<&'a [IndexAssignmentOf<T>]> for Compact`,
in the proc-macro which makes `Compact`. Should be a pretty straightforward
adaptation of `from_assignment`.
* Add IndexAssignment and conversion method to CompactSolution
This involves a bit of duplication of types from
`election-provider-multi-phase`; we'll clean those up shortly.
I'm not entirely happy that we had to add a `from_index_assignments`
method to `CompactSolution`, but we couldn't define
`trait CompactSolution: TryFrom<&'a [Self::IndexAssignment]` because
that made trait lookup recursive, and I didn't want to propagate
`CompactSolutionOf<T> + TryFrom<&[IndexAssignmentOf<T>]>` everywhere
that compact solutions are specified.
* use `CompactSolution::from_index_assignment` and clean up dead code
* get rid of `from_index_assignments` in favor of `TryFrom`
* cause `pallet-election-provider-multi-phase` tests to compile successfully
Mostly that's just updating the various test functions to keep track of
refactorings elsewhere, though in a few places we needed to refactor some
test-only helpers as well.
* fix infinite binary search loop
Turns out that moving `low` and `high` into an averager function is a
bad idea, because the averager gets copies of those values, which
of course are never updated. Can't use mutable references, because
we want to read them elsewhere in the code. Just compute the average
directly; life is better that way.
* fix a test failure
* fix the rest of test failures
* remove unguarded subtraction
* fix npos-elections tests compilation
* ensure we use sp_std::vec::Vec in assignments
* add IndexAssignmentOf to sp_npos_elections
* move miner types to `unsigned`
* use stable sort
* rewrap some long comments
* use existing cache instead of building a dedicated stake map
* generalize the TryFrom bound on CompactSolution
* undo adding sp-core dependency
* consume assignments to produce index_assignments
* Add a test of Assignment -> IndexAssignment -> Compact
* fix `IndexAssignmentOf` doc
* move compact test from sp-npos-elections-compact to sp-npos-elections
This means that we can put the mocking parts of that into a proper
mock package, put the test into a test package among other tests.
Having the mocking parts in a mock package enables us to create a
benchmark (which is treated as a separate crate) import them.
* rename assignments -> sorted_assignments
* sort after reducing to avoid potential re-sort issues
* add runtime benchmark, fix critical binary search error
"Why don't you add a benchmark?", he said. "It'll be good practice,
and can help demonstrate that this isn't blowing up the runtime."
He was absolutely right.
The biggest discovery is that adding a parametric benchmark means that
you get a bunch of new test cases, for free. This is excellent, because
those test cases uncovered a binary search bug. Fixing that simplified
that part of the code nicely.
The other nice thing you get from a parametric benchmark is data about
what each parameter does. In this case, `f` is the size factor: what
percent of the votes (by size) should be removed. 0 means that we should
keep everything, 95 means that we should trim down to 5% of original size
or less.
```
Median Slopes Analysis
========
-- Extrinsic Time --
Model:
Time ~= 3846
+ v 0.015
+ t 0
+ a 0.192
+ d 0
+ f 0
µs
Min Squares Analysis
========
-- Extrinsic Time --
Data points distribution:
v t a d f mean µs sigma µs %
<snip>
6000 1600 3000 800 0 4385 75.87 1.7%
6000 1600 3000 800 9 4089 46.28 1.1%
6000 1600 3000 800 18 3793 36.45 0.9%
6000 1600 3000 800 27 3365 41.13 1.2%
6000 1600 3000 800 36 3096 7.498 0.2%
6000 1600 3000 800 45 2774 17.96 0.6%
6000 1600 3000 800 54 2057 37.94 1.8%
6000 1600 3000 800 63 1885 2.515 0.1%
6000 1600 3000 800 72 1591 3.203 0.2%
6000 1600 3000 800 81 1219 25.72 2.1%
6000 1600 3000 800 90 859 5.295 0.6%
6000 1600 3000 800 95 684.6 2.969 0.4%
Quality and confidence:
param error
v 0.008
t 0.029
a 0.008
d 0.044
f 0.185
Model:
Time ~= 3957
+ v 0.009
+ t 0
+ a 0.185
+ d 0
+ f 0
µs
```
What's nice about this is the clear negative correlation between
amount removed and total time. The more we remove, the less total
time things take.
* make types within `generate_solution_type` macro explicit
Closes#8444.
Just changes the parsing logic for that macro; does not change any
emitted code. The associated types associated with the macro now
require explicit, keyword-style declaration.
**Old**:
```rust
sp_npos_elections::generate_solution_type!(
#[compact]
pub struct TestCompact::<VoterIndex, TargetIndex, PerU16>(16)
);
```
**New**:
```rust
sp_npos_elections::generate_solution_type!(
#[compact]
pub struct TestCompact::<VoterIndex = VoterIndex, CandidateIndex = TargetIndex, Accuracy = PerU16>(16)
);
```
* un-ignore doc-tests
* use new form in bin/node/runtime/
* rename CandidateIndex -> TargetIndex
* add tests demonstrating some potential compile failures
* Improve complexity of CompactAssignments::unique_targets
Original implementation was O(n**2). Current impl is O(n log n).
Avoided the original proposed mitigation because it does not retain
the de-duplicating property present in the original implementation.
This implementation does a little more work, but retains that property.
* Explicitly choose sp_std Vec and BTreeSet
Ensures that the macro still works if someone uses it in a context
in which sp_std is not imported or is renamed.
* explicitly use sp_std vectors throughout compact macro
* Add PJR challenge functions
- Updates the PJR check to return a counterexample if one exists
- Adds functions to cheaply check counterexamples
This is in support of off-chain PJR challenges: if a miner discovers
that an accepted election solution does not satisfy PJR, it will be
eligible for substantial rewards. This helps ensure that validator
elections have an absolute quality floor, so even if someone manages
to censor well-behaved solutions to give themselves unfair representation,
we can catch them in the act and penalize them.
* counterexample -> counter_example
* reorganize: high -> low abstraction
* reorganize challenges high -> low abstraction
* add note justifying linear search
* Simplify max_pre_score validation
Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>
* add minor test of pjr challenge validation
Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>
* 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: Kian Paimani <5588131+kianenigma@users.noreply.github.com>
* 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: Kian Paimani <5588131+kianenigma@users.noreply.github.com>
* 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: Kian Paimani <5588131+kianenigma@users.noreply.github.com>
* Apply suggestions from code review
Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>
Co-authored-by: kianenigma <kian.peymani@gmail.com>
Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>
Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>
* Attempt to remove the where bounds.
* Fix further and further.
* Format better.
* Update primitives/npos-elections/src/lib.rs
* fix build
* remove unused
* Fix incorrect use of syn::exports
Instead of using `syn::exports` we should import the trait from the
quote crate directly.
* Use own macro for test cases to fix compilation with latest syn
* Fix test
* Revamp npos-elections and implement phragmms
* Update primitives/npos-elections/src/phragmms.rs
* Fix build
* Some review grumbles
* Add some stuff for remote testing
* fix some of the grumbles.
* Add remote testing stuff.
* Cleanup
* fix docs
* Update primitives/arithmetic/src/rational.rs
Co-authored-by: Dan Forbes <dan@danforbes.dev>
* Small config change
* Better handling of approval_stake == 0
* Final touhces.
* Clean fuzzer a bit
* Clean fuzzer a bit
* Update primitives/npos-elections/src/balancing.rs
Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>
* Fix fuzzer.
* Better api for normalize
* Add noramlize_up
* A large number of small fixes.
* make it merge ready
* Fix warns
* bump
* Fix fuzzers a bit.
* Fix warns as well.
* Fix more tests.
Co-authored-by: Dan Forbes <dan@danforbes.dev>
Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>
* Re-generate READMEs to fix doc links; set readme field in package manifests
* Re-generate READMEs to fix doc links; set readme field in package manifests
* Re-generate READMEs to fix doc links; set readme field in package manifests
* Re-generate READMEs to fix doc links; set readme field in package manifests
* Revert stuff that shouldn't have been committed
* Revert stuff that shouldn't have been committed
* Fix parent relative link generation
* Manually remove this incorrect link for now.
* implement events handling, implement parent_id for spans & events
* add events to sp_io::storage
* update test
* add tests
* adjust limit
* let tracing crate handle parent_ids
* re-enable current-id tracking
* add test for threads with CurrentSpan
* fix log level
* remove redundant check for non wasm traces
* remove duplicate definition in test
* Adding conditional events API
* prefer explicit parent_id over current,
enhance test
* limit changes to client::tracing event implementation
* remove From impl due to fallback required on parent_id
* make tracing codecable
* replace with global tracing
* new tracing interface
* impl TracingSubscriber in client
* implement access to global TracingSubscriber from primitives
* span for wasm
* increment towards Wasm Tracing Subscriber implementation
* increment, remove sp-tracing from runtime-interface
* increment, it compiles
* attained original functionality with new mechanism
* implement remaining TracingSubscriber functions
* remove spans from decl_module
* add handling for encoded values
* Revert "replace with global tracing"
This reverts commit 8824a60deea54d9b437407a21c8ceaf6a1902ee5.
* Wasm Side Tracing
* tracing on wasm
* enable tracing wasm on node-runtime
* export all the macros in std
* tracing subscriber on wasm-side only
* pass spans and events over and record them
* reactivate previous code and cleanup
* further cleaning up
* extend the span macros, activate through executive
* tracking the actual extrinsic, too
* style
* fixing tests
* spaces -> tabs
* attempting to reactivate params
* activate our tests in CI
* some passing
* tests passing
* with core lazy
* global tracer for wasm side with pass over
* fixing metadata referencing
* remove const_fn feature requirement
* reenable dispatch traces
* reset client tracing
* further cleaning up
* fixing runtime-test
* move tracing-build setup into runtime-test
* Merge DebugWriter from tracing and frame-support, move to sp-std
* remove dangling fixme
* Docs for tracing primitives
* cleaning up a bit more
* Wasm interface docs
* optimise docs.rs setup
* adding tracing flags to uncomment
* remove brace
* fixing imports
* fixing broken syntax
* add required modules
* nicer formatting
* better target management
* adding low level storage tracing events into frame
* add custom Debug impl for WasmMetadata
* cloning profiler
* adding info about cloning profiler
* using in-scope for within calls
* proper time tracing, cleaning up println
* allow to disable tracing on runtime_interface-macro
* disable tracing for wasm-tracing-interface
* simplify wasm-tracing-api
* update client to new interface
* fixing docs and tests for sp-tracing
* update integration tests
* re-activating enter_span
* dropping FIXME, it's documented
* fix formatting
* fix formatting
* fix imports
* more debug info
* inform wasm about it being disabled by returning 1
* only one tracer, but enabled multi-all support
* make trait pub again for tests
* Apply suggestions from code review
Co-authored-by: Niklas Adolfsson <niklasadolfsson1@gmail.com>
* fixing wasm doc tests for proper usage
* remove unnecessary import
* fixing formatting
* minor style fixes
* downgrading wabt
* update error message for UI
* Fix interface test
* next attempt to fix macros
* geee
* revert tracing on hashed for future PR
* remove local macros, use originals
* we are able to convert to static items
* implement more WasmValue types
* adding support to convert str, debug and encoded values
* more minor fixes
* revert unsafe 'static making
* fix indentation
* remove commented lines
* bump all them tracing versions
* cleaning up docs and info
* document new flag
* the new layered system handles span cloning better
* Apply suggestions from code review
Co-authored-by: David <dvdplm@gmail.com>
Co-authored-by: Matt Rutherford <mattrutherford@users.noreply.github.com>
Co-authored-by: Niklas Adolfsson <niklasadolfsson1@gmail.com>
Co-authored-by: David <dvdplm@gmail.com>
* Fix the benchmarks
* Migrate staking to weightInfo
* Fix global benchmarks
* re-calculate the submit solution weight.
* Fix some refund.
* Get rid of all the extra parameters.
* Fix staking tests.
* new values from the bench machine.
* Fix some grumbles
* better macro
* Some better doc
* Move to interpreted wasm
* Make it work temporarily
* Final fix of default ones.
* Fix payout benchmarks
* Fix payout stuff
* One last fix
* use benchmarking machine for numbers
* update weight docs
Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>
* Add fuzzer for the compact custom codec implementation introduced in PR #6720.
This commit adds a fuzzing harness for the custom compact encoding/decoding
introduced in PR #6720.
* Update primitives/npos-elections/fuzzer/src/compact.rs
Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>
* Update Cargo.lock: Add changes in elections-fuzzer
* Change indentation from spaces to tabs
Co-authored-by: Vincent Ulitzsch <vincent@srlabs.de>
Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>
This edge count is used for weighing, and it is somewhat trivial to review and verify that the current implementation was ignoring `votes16` field of the struct. As reminder, the struct is like this:
```rust
struct Compact {
votes1: ... ,
votes2: ...,
...,
votes16: ...,
}
```
I already will fix this in https://github.com/paritytech/substrate/pull/7007, but since it might take a while, this one can go in asap and make it to the very next runtime.
* Bump version
* update test-utils crates to be ready for publishing
* adding changelog
* Adding automaticly generated READMEs
* fixing versions
* another version mishap
* add normalize
* better api for normalize
* Some grumbles
* Update primitives/arithmetic/src/lib.rs
Co-authored-by: Guillaume Thiolliere <gui.thiolliere@gmail.com>
* More great review grumbles
* Way better doc for everything.
* Some improvement
* Update primitives/arithmetic/src/lib.rs
Co-authored-by: Bernhard Schuster <bernhard@ahoi.io>
Co-authored-by: Guillaume Thiolliere <gui.thiolliere@gmail.com>
Co-authored-by: Bernhard Schuster <bernhard@ahoi.io>
This removes the `dev-dependency` onto `sp-npos-elections` from itself.
A crate should not depend on itself directly, especially not to make any
macros work.