mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-07-05 14:27:26 +00:00
5174b9d2d7
* inclusion emulator logic for asynchronous backing (#4790) * initial stab at candidate_context * fmt * docs & more TODOs * some cleanups * reframe as inclusion_emulator * documentations yes * update types * add constraint modifications * watermark * produce modifications * v2 primitives: re-export all v1 for consistency * vstaging primitives * emulator constraints: handle code upgrades * produce outbound HRMP modifications * stack. * method for applying modifications * method just for sanity-checking modifications * fragments produce modifications, not prospectives * make linear * add some TODOs * remove stacking; handle code upgrades * take `fragment` private * reintroduce stacking. * fragment constructor * add TODO * allow validating fragments against future constraints * docs * relay-parent number and min code size checks * check code upgrade restriction * check max hrmp per candidate * fmt * remove GoAhead logic because it wasn't helpful * docs on code upgrade failure * test stacking * test modifications against constraints * fmt * test fragments * descending or duplicate test * fmt * remove unused imports in vstaging * wrong primitives * spellcheck * Runtime changes for Asynchronous Backing (#4786) * inclusion: utility for allowed relay-parents * inclusion: use prev number instead of prev hash * track most recent context of paras * inclusion: accept previous relay-parents * update dmp advancement rule for async backing * fmt * add a comment about validation outputs * clean up a couple of TODOs * weights * fix weights * fmt * Resolve dmp todo * Restore inclusion tests * Restore paras_inherent tests * MostRecentContext test * Benchmark for new paras dispatchable * Prepare check_validation_outputs for upgrade * cargo run --quiet --profile=production --features=runtime-benchmarks -- benchmark --chain=kusama-dev --steps=50 --repeat=20 --pallet=runtime_parachains::paras --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --header=./file_header.txt --output=./runtime/kusama/src/weights/runtime_parachains_paras.rs * cargo run --quiet --profile=production --features=runtime-benchmarks -- benchmark --chain=westend-dev --steps=50 --repeat=20 --pallet=runtime_parachains::paras --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --header=./file_header.txt --output=./runtime/westend/src/weights/runtime_parachains_paras.rs * cargo run --quiet --profile=production --features=runtime-benchmarks -- benchmark --chain=polkadot-dev --steps=50 --repeat=20 --pallet=runtime_parachains::paras --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --header=./file_header.txt --output=./runtime/polkadot/src/weights/runtime_parachains_paras.rs * cargo run --quiet --profile=production --features=runtime-benchmarks -- benchmark --chain=rococo-dev --steps=50 --repeat=20 --pallet=runtime_parachains::paras --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --header=./file_header.txt --output=./runtime/rococo/src/weights/runtime_parachains_paras.rs * Implementers guide changes * More tests for allowed relay parents * Add a github issue link * Compute group index based on relay parent * Storage migration * Move allowed parents tracker to shared * Compile error * Get group assigned to core at the next block * Test group assignment * fmt * Error instead of panic * Update guide * Extend doc-comment * Update runtime/parachains/src/shared.rs Co-authored-by: Robert Habermeier <rphmeier@gmail.com> Co-authored-by: Chris Sosnin <chris125_@live.com> Co-authored-by: Parity Bot <admin@parity.io> Co-authored-by: Chris Sosnin <48099298+slumber@users.noreply.github.com> * Prospective Parachains Subsystem (#4913) * docs and skeleton * subsystem skeleton * main loop * fragment tree basics & fmt * begin fragment trees & view * flesh out more of view update logic * further flesh out update logic * some refcount functions for fragment trees * add fatal/non-fatal errors * use non-fatal results * clear up some TODOs * ideal format for scheduling info * add a bunch of TODOs * some more fluff * extract fragment graph to submodule * begin fragment graph API * trees, not graphs * improve docs * scope and constructor for trees * add some test TODOs * limit max ancestors and store constraints * constructor * constraints: fix bug in HRMP watermarks * fragment tree population logic * set::retain * extract population logic * implement add_and_populate * fmt * add some TODOs in tests * implement child-selection * strip out old stuff based on wrong assumptions * use fatality * implement pruning * remove unused ancestor constraints * fragment tree instantiation * remove outdated comment * add message/request types and skeleton for handling * fmt * implement handle_candidate_seconded * candidate storage: handle backed * implement handle_candidate_backed * implement answer_get_backable_candidate * remove async where not needed * implement fetch_ancestry * add logic for run_iteration * add some docs * remove global allow(unused), fix warnings * make spellcheck happy (despite English) * fmt * bump Cargo.lock * replace tracing with gum * introduce PopulateFrom trait * implement GetHypotheticalDepths * revise docs slightly * first fragment tree scope test * more scope tests * test add_candidate * fmt * test retain * refactor test code * test populate is recursive * test contiguity of depth 0 is maintained * add_and_populate tests * cycle tests * remove PopulateFrom trait * fmt * test hypothetical depths (non-recursive) * have CandidateSeconded return membership * tree membership requests * Add a ProspectiveParachainsSubsystem struct * add a staging API for base constraints * add a `From` impl * add runtime API for staging_validity_constraints * implement fetch_base_constraints * implement `fetch_upcoming_paras` * remove reconstruction of candidate receipt; no obvious usecase * fmt * export message to broader module * remove last TODO * correctly export * fix compilation and add GetMinimumRelayParent request * make provisioner into a real subsystem with proper mesage bounds * fmt * fix ChannelsOut in overseer test * fix overseer tests * fix again * fmt * Integrate prospective parachains subsystem into backing: Part 1 (#5557) * BEGIN ASYNC candidate-backing CHANGES * rename & document modes * answer prospective validation data requests * GetMinimumRelayParents request is now plural * implement an implicit view utility for backing subsystems * implicit-view: get allowed relay parents * refactorings and improvements to implicit view * add some TODOs for tests * split implicit view updates into 2 functions * backing: define State to prepare for functional refactor * add some docs * backing: implement bones of new leaf activation logic * backing: create per-relay-parent-states * use new handle_active_leaves_update * begin extracting logic from CandidateBackingJob * mostly extract statement import from job logic * handle statement imports outside of job logic * do some TODO planning for prospective parachains integration * finish rewriting backing subsystem in functional style * add prospective parachains mode to relay parent entries * fmt * add a RejectedByProspectiveParachains error * notify prospective parachains of seconded and backed candidates * always validate candidates exhaustively in backing. * return persisted_validation_data from validation * handle rejections by prospective parachains * implement seconding sanity check * invoke validate_and_second * Alter statement table to allow multiple seconded messages per validator * refactor backing to have statements carry PVD * clean up all warnings * Add tests for implicit view * Improve doc comments * Prospective parachains mode based on Runtime API version * Add a TODO * Rework seconding_sanity_check * Iterate over responses * Update backing tests * collator-protocol: load PVD from runtime * Fix validator side tests * Update statement-distribution to fetch PVD * Fix statement-distribution tests * Backing tests with prospective paras #1 * fix per_relay_parent pruning in backing * Test multiple leaves * Test seconding sanity check * Import statement order Before creating an entry in `PerCandidateState` map wait for the approval from the prospective parachains * Add a test for correct state updates * Second multiple candidates per relay parent test * Add backing tests with prospective paras * Second more than one test without prospective paras * Add a test for prospective para blocks * Update malus * typos Co-authored-by: Chris Sosnin <chris125_@live.com> * Track occupied depth in backing per parachain (#5778) * provisioner: async backing changes (#5711) * Provisioner changes for async backing * Select candidates based on prospective paras mode * Revert naming * Update tests * Update TODO comment * review * provisioner: async backing changes (#5711) * Provisioner changes for async backing * Select candidates based on prospective paras mode * Revert naming * Update tests * Update TODO comment * review * fmt * Network bridge changes for asynchronous backing + update subsystems to handle versioned packets (#5991) * BEGIN STATEMENT DISTRIBUTION WORK create a vstaging network protocol which is the same as v1 * mostly make network bridge amenable to vstaging * network-bridge: fully adapt to vstaging * add some TODOs for tests * fix fallout in bitfield-distribution * bitfield distribution tests + TODOs * fix fallout in gossip-support * collator-protocol: fix message fallout * collator-protocol: load PVD from runtime * add TODO for vstaging tests * make things compile * set used network protocol version using a feature * fmt * get approval-distribution building * fix approval-distribution tests * spellcheck * nits * approval distribution net protocol test * bitfield distribution net protocol test * Revert "collator-protocol: fix message fallout" This reverts commit 07cc887303e16c6b3843ecb25cdc7cc2080e2ed1. * Network bridge tests Co-authored-by: Chris Sosnin <chris125_@live.com> * remove max_pov_size requirement from prospective pvd request (#6014) * remove max_pov_size requirement from prospective pvd request * fmt * Extract legacy statement distribution to its own module (#6026) * add compatibility type to v2 statement distribution message * warning cleanup * handle compatibility layer for v2 * clean up an unimplemented!() block * circulate statements based on version * extract legacy v1 code into separate module * remove unimplemented * clean up naming of from_requester/responder * remove TODOs * have backing share seconded statements with PVD * fmt * fix warning * Quick fix unused warning for not yet implemented/used staging messages. * Fix network bridge test * Fix wrong merge. We now have 23 subsystems (network bridge split + prospective parachains) Co-authored-by: Robert Klotzner <robert.klotzner@gmx.at> * Version 3 is already live. * Fix tests (#6055) * Fix backing tests * Fix warnings. * fmt * collator-protocol: asynchronous backing changes (#5740) * Draft collator side changes * Start working on collations management * Handle peer's view change * Versioning on advertising * Versioned collation fetching request * Handle versioned messages * Improve docs for collation requests * Add spans * Add request receiver to overseer * Fix collator side tests * Extract relay parent mode to lib * Validator side draft * Add more checks for advertisement * Request pvd based on async backing mode * review * Validator side improvements * Make old tests green * More fixes * Collator side tests draft * Send collation test * fmt * Collator side network protocol versioning * cleanup * merge artifacts * Validator side net protocol versioning * Remove fragment tree membership request * Resolve todo * Collator side core state test * Improve net protocol compatibility * Validator side tests * more improvements * style fixes * downgrade log * Track implicit assignments * Limit the number of seconded candidates per para * Add a sanity check * Handle fetched candidate * fix tests * Retry fetch * Guard against dequeueing while already fetching * Reintegrate connection management * Timeout on advertisements * fmt * spellcheck * update tests after merge * validator assignment fixes for backing and collator protocol (#6158) * Rename depth->ancestry len in tests * Refactor group assignments * Remove implicit assignments * backing: consider occupied core assignments * Track a single para on validator side * Refactor prospective parachains mode request (#6179) * Extract prospective parachains mode into util * Skip activations depending on the mode * backing: don't send backed candidate to provisioner (#6185) * backing: introduce `CanSecond` request for advertisements filtering (#6225) * Drop BoundToRelayParent * draft changes * fix backing tests * Fix genesis ancestry * Fix validator side tests * more tests * cargo generate-lockfile * Implement `StagingValidityConstraints` Runtime API method (#6258) * Implement StagingValidityConstraints * spellcheck * fix ump params * Update hrmp comment * Introduce ump per candidate limit * hypothetical earliest block * refactor primitives usage * hypothetical earliest block number test * fix build * Prepare the Runtime for asynchronous backing upgrade (#6287) * Introduce async backing params to runtime config * fix cumulus config * use config * finish runtimes * Introduce new staging API * Update collator protocol * Update provisioner * Update prospective parachains * Update backing * Move async backing params lower in the config * make naming consistent * misc * Use real prospective parachains subsystem (#6407) * Backport `HypotheticalFrontier` into the feature branch (#6605) * implement more general HypotheticalFrontier * fmt * drop unneeded request Co-authored-by: Robert Habermeier <rphmeier@gmail.com> * Resolve todo about legacy leaf activation (#6447) * fix bug/warning in handling membership answers * Remove `HypotheticalDepthRequest` in favor of `HypotheticalFrontierRequest` (#6521) * Remove `HypotheticalDepthRequest` for `HypotheticalFrontierRequest` * Update tests * Fix (removed wrong docstring) * Fix can_second request * Patch some dead_code errors --------- Co-authored-by: Chris Sosnin <chris125_@live.com> * Async Backing: Send Statement Distribution "Backed" messages (#6634) * Backing: Send Statement Distribution "Backed" messages Closes #6590. **TODO:** - [ ] Adjust tests * Fix compile errors * (Mostly) fix tests * Fix comment * Fix test and compile error * Test that `StatementDistributionMessage::Backed` is sent * Fix compile error * Fix some clippy errors * Add prospective parachains subsystem tests (#6454) * Add prospective parachains subsystem test * Add `should_do_no_work_if_async_backing_disabled_for_leaf` test * Implement `activate_leaf` helper, up to getting ancestry * Finish implementing `activate_leaf` * Small refactor in `activate_leaf` * Get `CandidateSeconded` working * Finish `send_candidate_and_check_if_found` test * Refactor; send more leaves & candidates * Refactor test * Implement `check_candidate_parent_leaving_view` test * Start work on `check_candidate_on_multiple_forks` test * Don’t associate specific parachains with leaf * Finish `correctly_updates_leaves` test * Fix cycle due to reused head data * Fix `check_backable_query` test * Fix `check_candidate_on_multiple_forks` test * Add `check_depth_and_pvd_queries` test * Address review comments * Remove TODO * add a new index for output head data to candidate storage * Resolve test TODOs * Fix compile errors * test candidate storage pruning, make sure new index is cleaned up --------- Co-authored-by: Robert Habermeier <rphmeier@gmail.com> * Node-side metrics for asynchronous backing (#6549) * Add metrics for `prune_view_candidate_storage` * Add metrics for `request_unblocked_collations` * Fix docstring * Couple fixes from review comments * Fix `check_depth_query` test * inclusion-emulator: mirror advancement rule check (#6361) * inclusion-emulator: mirror advancement rule check * fix build * prospective-parachains: introduce `backed_in_path_only` flag for advertisements (#6649) * Introduce `backed_in_path_only` flag for depth request * fmt * update doc comment * fmt * Add async-backing zombienet tests (#6314) * Async backing: impl guide for statement distribution (#6738) Co-authored-by: Bradley Olson <34992650+BradleyOlson64@users.noreply.github.com> Co-authored-by: alexgparity <115470171+alexgparity@users.noreply.github.com> * Asynchronous backing statement distribution: Take III (#5999) * add notification types for v2 statement-distribution * improve protocol docs * add empty vstaging module * fmt * add backed candidate packet request types * start putting down structure of new logic * handle activated leaf * some sanity-checking on outbound statements * fmt * update vstaging share to use statements with PVD * tiny refactor, candidate_hash location * import local statements * refactor statement import * first stab at broadcast logic * fmt * fill out some TODOs * start on handling incoming * split off session info into separate map * start in on a knowledge tracker * address some grumbles * format * missed comment * some docs for direct * add note on slashing * amend * simplify 'direct' code * finish up the 'direct' logic * add a bunch of tests for the direct-in-group logic * rename 'direct' to 'cluster', begin a candidate_entry module * distill candidate_entry * start in on a statement-store module * some utilities for the statement store * rewrite 'send_statement_direct' using new tools * filter sending logic on peers which have the relay-parent in their view. * some more logic for handling incoming statements * req/res: BackedCandidatePacket -> AttestedCandidate + tweaks * add a `validated_in_group` bitfield to BackedCandidateInventory * BackedCandidateInventory -> Manifest * start in on requester module * add outgoing request for attested candidate * add a priority mechanism for requester * some request dispatch logic * add seconded mask to tagged-request * amend manifest to hold group index * handle errors and set up scaffold for response validation * validate attested candidate responses * requester -> requests * add some utilities for manipulating requests * begin integrating requester * start grid module * tiny * refactor grid topology to expose more info to subsystems * fix grid_topology test * fix overseer test * implement topology group-based view construction logic * fmt * flesh out grid slightly more * add indexed groups utility * integrate Groups into per-session info * refactor statement store to borrow Groups * implement manifest knowledge utility * add a test for topology setup * don't send to group members * test for conflicting manifests * manifest knowledge tests * fmt * rename field * garbage collection for grid tracker * routines for finding correct/incorrect advertisers * add manifest import logic * tweak naming * more tests for manifest import * add comment * rework candidates into a view-wide tracker * fmt * start writing boilerplate for grid sending * fmt * some more group boilerplate * refactor handling of topology and authority IDs * fmt * send statements directly to grid peers where possible * send to cluster only if statement belongs to cluster * improve handling of cluster statements * handle incoming statements along the grid * API for introduction of candidates into the tree * backing: use new prospective parachains API * fmt prospective parachains changes * fmt statement-dist * fix condition * get ready for tracking importable candidates * prospective parachains: add Cow logic * incomplete and complete hypothetical candidates * remove keep_if_unneeded * fmt * implement more general HypotheticalFrontier * fmt, cleanup * add a by_parent_hash index to candidate tracker * more framework for future code * utilities for getting all hypothetical candidates for frontier * track origin in statement store * fmt * requests should return peer * apply post-confirmation reckoning * flesh out import/announce/circulate logic on new statements * adjust * adjust TODO comment * fix backing tests * update statement-distribution to use new indexedvec * fmt * query hypothetical candidates * implement `note_importable_under` * extract common utility of fragment tree updates * add a helper function for getting statements unknown by backing * import fresh statements to backing * send announcements and acknowledgements over grid * provide freshly importable statements also avoid tracking backed candidates in statement distribution * do not issue requests on newly importable candidates * add TODO for later when confirming candidate * write a routine for handling backed candidate notifications * simplify grid substantially * add some test TODOs * handle confirmed candidates & grid announcements * finish implementing manifest handling, including follow up statements * send follow-up statements when acknowledging freshly backed * fmt * handle incoming acknowledgements * a little DRYing * wire up network messages to handlers * fmt * some skeleton code for peer view update handling * more peer view skeleton stuff * Fix async backing statement distribution tests (#6621) * Fix compile errors in tests * Cargo fmt * Resolve some todos in async backing statement-distribution branch (#6482) * Implement `remove_by_relay_parent` * Extract `minimum_votes` to shared primitives. * Add `can_send_statements_received_with_prejudice` test * Fix test * Update docstrings * Cargo fmt * Fix compile error * Fix compile errors in tests * Cargo fmt * Add module docs; write `test_priority_ordering` (first draft) * Fix `test_priority_ordering` * Move `insert_or_update_priority`: `Drop` -> `set_cluster_priority` * Address review comments * Remove `Entry::get_mut` * fix test compilation * add a TODO for a test * clean up a couple of TODOs * implement sending pending cluster statements * refactor utility function for sending acknowledgement and statements * mostly implement catching peers up via grid * Fix clippy error * alter grid to track all pending statements * fix more TODOs and format * tweak a TODO in requests * some logic for dispatching requests * fmt * skeleton for response receiving * Async backing statement distribution: cluster tests (#6678) * Add `pending_statements_set_when_receiving_fresh_statements` * Add `pending_statements_updated_when_sending_statements` test * fix up * fmt * update TODO * rework seconded mask in requests * change doc * change unhandledresponse not to borrow request manager * only accept responses sufficient to back * finish implementing response handling * extract statement filter to protocol crate * rework requests: use statement filter in network protocol * dispatch cluster requests correctly * rework cluster statement sending * implement request answering * fmt * only send confirmed candidate statement messages on unified relay-parent * Fix Tests In Statement Distribution Branch * Async Backing: Integrate `vstaging` of statement distribution into `lib.rs` (#6715) * Integrate `handle_active_leaves_update` * Integrate `share_local_statement`/`handle_backed_candidate_message` * Start hooking up request/response flow * Finish hooking up request/response flow * Limit number of parallel requests in responder * Fix test compilation errors * Fix missing check for prospective parachains mode * Fix some more compile errors * clean up some review comments * clean up warnings * Async backing statement distribution: grid tests (#6673) * Add `manifest_import_returns_ok_true` test * cargo fmt * Add pending_communication_receiving_manifest_on_confirmed_candidate * Add `senders_can_provide_manifests_in_acknowledgement` test * Add a couple of tests for pending statements * Add `pending_statements_cleared_when_sending` test * Add `pending_statements_respect_remote_knowledge` test * Refactor group creation in tests * Clarify docs * Address some review comments * Make some clarifications * Fix post-merge errors * Clarify test `senders_can_provide_manifests_in_acknowledgement` * Try writing `pending_statements_are_updated_after_manifest_exchange` * Document "seconding limit" and `reject_overflowing_manifests` test * Test that seconding counts are not updated for validators on error * Fix tests * Fix manifest exchange test * Add more tests in `requests.rs` (#6707) This resolves remaining TODOs in this file. * remove outdated inventory terminology * Async backing statement distribution: `Candidates` tests (#6658) * Async Backing: Fix clippy errors in statement distribution branch (#6720) * Integrate `handle_active_leaves_update` * Integrate `share_local_statement`/`handle_backed_candidate_message` * Start hooking up request/response flow * Finish hooking up request/response flow * Limit number of parallel requests in responder * Fix test compilation errors * Fix missing check for prospective parachains mode * Fix some more compile errors * Async Backing: Fix clippy errors in statement distribution branch * Fix some more clippy lints * add tests module * fix warnings in existing tests * create basic test harness * create a test state struct * fmt * create empty cluster & grid modules for tests * some TODOs for cluster test suite * describe test-suite for grid logic * describe request test suite * fix seconding-limit bug * Remove extraneous `pub` This somehow made it into my clippy PR. * Fix some test compile warnings * Remove some unneeded `allow`s * adapt some new test helpers from Marcin * add helper for activating a gossip topology * add utility for signing statements * helpers for connecting/disconnecting peers * round out network utilities * fmt * fix bug in initializing validator-meta * fix compilation * implement first cluster test * TODOs for incoming request tests * Remove unneeded `make_committed_candidate` helper * fmt * some more tests for cluster * add a TODO about grid senders * integrate inbound req/res into test harness * polish off initial cluster test suite * keep introduce candidate request * fix tests after introduce candidate request * fmt * Add grid protocol to module docs * Fix comments * Test `backed_in_path_only: true` * Update node/network/protocol/src/lib.rs Co-authored-by: Chris Sosnin <48099298+slumber@users.noreply.github.com> * Update node/network/protocol/src/request_response/mod.rs Co-authored-by: Chris Sosnin <48099298+slumber@users.noreply.github.com> * Mark receiver with `vstaging` * validate grid senders based on manifest kind * fix mask_seconded/valid * fix unwanted-mask check * fix build * resolve todo on leaf mode * Unify protocol naming to vstaging * fmt, fix grid test after topology change * typo Co-authored-by: Chris Sosnin <48099298+slumber@users.noreply.github.com> * address review * adjust comment, make easier to understand * Fix typo --------- Co-authored-by: Marcin S <marcin@bytedude.com> Co-authored-by: Marcin S <marcin@realemail.net> Co-authored-by: Chris Sosnin <48099298+slumber@users.noreply.github.com> Co-authored-by: Chris Sosnin <chris125_@live.com> * miscellaneous fixes to make asynchronous backing work (#6791) * propagate network-protocol-staging feature * add feature to adder-collator as well * allow collation-generation of occupied cores * prospective parachains: special treatment for pending availability candidates * runtime: fetch candidates pending availability * lazily construct PVD for pending candidates * fix fallout in prospective parachains hypothetical/select_child * runtime: enact candidates when creating paras-inherent * make tests compile * test pending availability in the scope * add prospective parachains test * fix validity constraints leftovers * drop prints * Fix typos --------- Co-authored-by: Chris Sosnin <chris125_@live.com> Co-authored-by: Marcin S <marcin@realemail.net> * Remove restart from test (#6840) * Async Backing: Statement Distribution Tests (#6755) * start on handling incoming * split off session info into separate map * start in on a knowledge tracker * address some grumbles * format * missed comment * some docs for direct * add note on slashing * amend * simplify 'direct' code * finish up the 'direct' logic * add a bunch of tests for the direct-in-group logic * rename 'direct' to 'cluster', begin a candidate_entry module * distill candidate_entry * start in on a statement-store module * some utilities for the statement store * rewrite 'send_statement_direct' using new tools * filter sending logic on peers which have the relay-parent in their view. * some more logic for handling incoming statements * req/res: BackedCandidatePacket -> AttestedCandidate + tweaks * add a `validated_in_group` bitfield to BackedCandidateInventory * BackedCandidateInventory -> Manifest * start in on requester module * add outgoing request for attested candidate * add a priority mechanism for requester * some request dispatch logic * add seconded mask to tagged-request * amend manifest to hold group index * handle errors and set up scaffold for response validation * validate attested candidate responses * requester -> requests * add some utilities for manipulating requests * begin integrating requester * start grid module * tiny * refactor grid topology to expose more info to subsystems * fix grid_topology test * fix overseer test * implement topology group-based view construction logic * fmt * flesh out grid slightly more * add indexed groups utility * integrate Groups into per-session info * refactor statement store to borrow Groups * implement manifest knowledge utility * add a test for topology setup * don't send to group members * test for conflicting manifests * manifest knowledge tests * fmt * rename field * garbage collection for grid tracker * routines for finding correct/incorrect advertisers * add manifest import logic * tweak naming * more tests for manifest import * add comment * rework candidates into a view-wide tracker * fmt * start writing boilerplate for grid sending * fmt * some more group boilerplate * refactor handling of topology and authority IDs * fmt * send statements directly to grid peers where possible * send to cluster only if statement belongs to cluster * improve handling of cluster statements * handle incoming statements along the grid * API for introduction of candidates into the tree * backing: use new prospective parachains API * fmt prospective parachains changes * fmt statement-dist * fix condition * get ready for tracking importable candidates * prospective parachains: add Cow logic * incomplete and complete hypothetical candidates * remove keep_if_unneeded * fmt * implement more general HypotheticalFrontier * fmt, cleanup * add a by_parent_hash index to candidate tracker * more framework for future code * utilities for getting all hypothetical candidates for frontier * track origin in statement store * fmt * requests should return peer * apply post-confirmation reckoning * flesh out import/announce/circulate logic on new statements * adjust * adjust TODO comment * fix backing tests * update statement-distribution to use new indexedvec * fmt * query hypothetical candidates * implement `note_importable_under` * extract common utility of fragment tree updates * add a helper function for getting statements unknown by backing * import fresh statements to backing * send announcements and acknowledgements over grid * provide freshly importable statements also avoid tracking backed candidates in statement distribution * do not issue requests on newly importable candidates * add TODO for later when confirming candidate * write a routine for handling backed candidate notifications * simplify grid substantially * add some test TODOs * handle confirmed candidates & grid announcements * finish implementing manifest handling, including follow up statements * send follow-up statements when acknowledging freshly backed * fmt * handle incoming acknowledgements * a little DRYing * wire up network messages to handlers * fmt * some skeleton code for peer view update handling * more peer view skeleton stuff * Fix async backing statement distribution tests (#6621) * Fix compile errors in tests * Cargo fmt * Resolve some todos in async backing statement-distribution branch (#6482) * Implement `remove_by_relay_parent` * Extract `minimum_votes` to shared primitives. * Add `can_send_statements_received_with_prejudice` test * Fix test * Update docstrings * Cargo fmt * Fix compile error * Fix compile errors in tests * Cargo fmt * Add module docs; write `test_priority_ordering` (first draft) * Fix `test_priority_ordering` * Move `insert_or_update_priority`: `Drop` -> `set_cluster_priority` * Address review comments * Remove `Entry::get_mut` * fix test compilation * add a TODO for a test * clean up a couple of TODOs * implement sending pending cluster statements * refactor utility function for sending acknowledgement and statements * mostly implement catching peers up via grid * Fix clippy error * alter grid to track all pending statements * fix more TODOs and format * tweak a TODO in requests * some logic for dispatching requests * fmt * skeleton for response receiving * Async backing statement distribution: cluster tests (#6678) * Add `pending_statements_set_when_receiving_fresh_statements` * Add `pending_statements_updated_when_sending_statements` test * fix up * fmt * update TODO * rework seconded mask in requests * change doc * change unhandledresponse not to borrow request manager * only accept responses sufficient to back * finish implementing response handling * extract statement filter to protocol crate * rework requests: use statement filter in network protocol * dispatch cluster requests correctly * rework cluster statement sending * implement request answering * fmt * only send confirmed candidate statement messages on unified relay-parent * Fix Tests In Statement Distribution Branch * Async Backing: Integrate `vstaging` of statement distribution into `lib.rs` (#6715) * Integrate `handle_active_leaves_update` * Integrate `share_local_statement`/`handle_backed_candidate_message` * Start hooking up request/response flow * Finish hooking up request/response flow * Limit number of parallel requests in responder * Fix test compilation errors * Fix missing check for prospective parachains mode * Fix some more compile errors * clean up some review comments * clean up warnings * Async backing statement distribution: grid tests (#6673) * Add `manifest_import_returns_ok_true` test * cargo fmt * Add pending_communication_receiving_manifest_on_confirmed_candidate * Add `senders_can_provide_manifests_in_acknowledgement` test * Add a couple of tests for pending statements * Add `pending_statements_cleared_when_sending` test * Add `pending_statements_respect_remote_knowledge` test * Refactor group creation in tests * Clarify docs * Address some review comments * Make some clarifications * Fix post-merge errors * Clarify test `senders_can_provide_manifests_in_acknowledgement` * Try writing `pending_statements_are_updated_after_manifest_exchange` * Document "seconding limit" and `reject_overflowing_manifests` test * Test that seconding counts are not updated for validators on error * Fix tests * Fix manifest exchange test * Add more tests in `requests.rs` (#6707) This resolves remaining TODOs in this file. * remove outdated inventory terminology * Async backing statement distribution: `Candidates` tests (#6658) * Async Backing: Fix clippy errors in statement distribution branch (#6720) * Integrate `handle_active_leaves_update` * Integrate `share_local_statement`/`handle_backed_candidate_message` * Start hooking up request/response flow * Finish hooking up request/response flow * Limit number of parallel requests in responder * Fix test compilation errors * Fix missing check for prospective parachains mode * Fix some more compile errors * Async Backing: Fix clippy errors in statement distribution branch * Fix some more clippy lints * add tests module * fix warnings in existing tests * create basic test harness * create a test state struct * fmt * create empty cluster & grid modules for tests * some TODOs for cluster test suite * describe test-suite for grid logic * describe request test suite * fix seconding-limit bug * Remove extraneous `pub` This somehow made it into my clippy PR. * Fix some test compile warnings * Remove some unneeded `allow`s * adapt some new test helpers from Marcin * add helper for activating a gossip topology * add utility for signing statements * helpers for connecting/disconnecting peers * round out network utilities * fmt * fix bug in initializing validator-meta * fix compilation * implement first cluster test * TODOs for incoming request tests * Remove unneeded `make_committed_candidate` helper * fmt * Hook up request sender * Add `valid_statement_without_prior_seconded_is_ignored` test * Fix `valid_statement_without_prior_seconded_is_ignored` test * some more tests for cluster * add a TODO about grid senders * integrate inbound req/res into test harness * polish off initial cluster test suite * keep introduce candidate request * fix tests after introduce candidate request * fmt * Add grid protocol to module docs * Remove obsolete test * Fix comments * Test `backed_in_path_only: true` * Update node/network/protocol/src/lib.rs Co-authored-by: Chris Sosnin <48099298+slumber@users.noreply.github.com> * Update node/network/protocol/src/request_response/mod.rs Co-authored-by: Chris Sosnin <48099298+slumber@users.noreply.github.com> * Mark receiver with `vstaging` * First draft of `ensure_seconding_limit_is_respected` test * validate grid senders based on manifest kind * fix mask_seconded/valid * fix unwanted-mask check * fix build * resolve todo on leaf mode * Unify protocol naming to vstaging * Fix `ensure_seconding_limit_is_respected` test * Start `backed_candidate_leads_to_advertisement` test * fmt, fix grid test after topology change * Send Backed notification * Finish `backed_candidate_leads_to_advertisement` test * Finish `peer_reported_for_duplicate_statements` test * Finish `received_advertisement_before_confirmation_leads_to_request` * Add `advertisements_rejected_from_incorrect_peers` test * Add `manifest_rejected_*` tests * Add `manifest_rejected_when_group_does_not_match_para` test * Add `local_node_sanity_checks_incoming_requests` test * Add `local_node_respects_statement_mask` test * Add tests where peer is reported for providing invalid signatures * Add `cluster_peer_allowed_to_send_incomplete_statements` test * Add `received_advertisement_after_backing_leads_to_acknowledgement` * Add `received_advertisement_after_confirmation_before_backing` test * peer_reported_for_advertisement_conflicting_with_confirmed_candidate * Add `peer_reported_for_not_enough_statements` test * Add `peer_reported_for_providing_statements_meant_to_be_masked_out` * Add `additional_statements_are_shared_after_manifest_exchange` * Add `grid_statements_imported_to_backing` test * Add `relay_parent_entering_peer_view_leads_to_advertisement` test * Add `advertisement_not_re_sent_when_peer_re_enters_view` test * Update node/network/statement-distribution/src/vstaging/tests/grid.rs Co-authored-by: asynchronous rob <rphmeier@gmail.com> * Resolve TODOs, update test * Address unused code * Add check after every test for unhandled requests * Refactor (`make_dummy_leaf` and `handle_sent_request`) * Refactor (`make_dummy_topology`) * Minor refactor --------- Co-authored-by: Robert Habermeier <rphmeier@gmail.com> Co-authored-by: Chris Sosnin <48099298+slumber@users.noreply.github.com> Co-authored-by: Chris Sosnin <chris125_@live.com> * Fix some clippy lints in tests * Async backing: minor fixes (#6920) * bitfield-distribution test * implicit view tests * Refactor parameters -> params * scheduler: update storage migration (#6963) * update scheduler migration * Adjust weight to account for storage read * Statement Distribution Guide Edits (#7025) * Statement distribution guide edits * Addressed Marcin's comments * Add attested candidate request retry timeouts (#6833) Co-authored-by: Chris Sosnin <48099298+slumber@users.noreply.github.com> Co-authored-by: asynchronous rob <rphmeier@gmail.com> Co-authored-by: Robert Habermeier <rphmeier@gmail.com> Co-authored-by: Chris Sosnin <chris125_@live.com> Fix async backing statement distribution tests (#6621) Resolve some todos in async backing statement-distribution branch (#6482) Fix clippy errors in statement distribution branch (#6720) * Async backing: add Prospective Parachains impl guide (#6933) Co-authored-by: Bradley Olson <34992650+BradleyOlson64@users.noreply.github.com> * Updates to Provisioner Guide for Async Backing (#7106) * Initial corrections and clarifications * Partial first draft * Finished first draft * Adding back wrongly removed test bit * fmt * Update roadmap/implementers-guide/src/node/utility/provisioner.md Co-authored-by: Marcin S. <marcin@realemail.net> * Addressing comments * Reorganization * fmt --------- Co-authored-by: Marcin S. <marcin@realemail.net> * fmt * Renaming Parathread Mentions (#7287) * Renaming parathreads * Renaming module to pallet * More updates * PVF: Refactor workers into separate crates, remove host dependency (#7253) * PVF: Refactor workers into separate crates, remove host dependency * Fix compile error * Remove some leftover code * Fix compile errors * Update Cargo.lock * Remove worker main.rs files I accidentally copied these from the other PR. This PR isn't intended to introduce standalone workers yet. * Address review comments * cargo fmt * Update a couple of comments * Update log targets * Update quote to 1.0.27 (#7280) Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> Co-authored-by: parity-processbot <> * pallets: implement `Default` for `GenesisConfig` in `no_std` (#7271) * pallets: implement Default for GenesisConfig in no_std This change is follow-up of: https://github.com/paritytech/substrate/pull/14108 It is a step towards: https://github.com/paritytech/substrate/issues/13334 * Cargo.lock updated * update lockfile for {"substrate"} --------- Co-authored-by: parity-processbot <> * cli: enable BEEFY by default on test networks (#7293) We consider BEEFY mature enough to run by default on all nodes for test networks (Rococo/Wococo/Versi). Right now, most nodes are not running it since it's opt-in using --beefy flag. Switch to an opt-out model for test networks. Replace --beefy flag from CLI with --no-beefy and have BEEFY client start by default on test networks. Signed-off-by: acatangiu <adrian@parity.io> * runtime: past session slashing runtime API (#6667) * runtime/vstaging: unapplied_slashes runtime API * runtime/vstaging: key_ownership_proof runtime API * runtime/ParachainHost: submit_report_dispute_lost * fix key_ownership_proof API * runtime: submit_report_dispute_lost runtime API * nits * Update node/subsystem-types/src/messages.rs Co-authored-by: Marcin S. <marcin@bytedude.com> * revert unrelated fmt changes * post merge fixes * fix compilation --------- Co-authored-by: Marcin S. <marcin@bytedude.com> * Correcting git mishap * Document usage of `gum` crate (#7294) * Document usage of gum crate * Small fix * Add some more basic info * Update node/gum/src/lib.rs Co-authored-by: Andrei Sandu <54316454+sandreim@users.noreply.github.com> * Update target docs --------- Co-authored-by: Andrei Sandu <54316454+sandreim@users.noreply.github.com> * XCM: Fix issue with RequestUnlock (#7278) * XCM: Fix issue with RequestUnlock * Leave API changes for v4 * Fix clippy errors * Fix tests --------- Co-authored-by: parity-processbot <> * Companion for Substrate#14228 (#7295) * Companion for Substrate#14228 https://github.com/paritytech/substrate/pull/14228 * update lockfile for {"substrate"} --------- Co-authored-by: parity-processbot <> * Companion for #14237: Use latest sp-crates (#7300) * To revert: Update substrate branch to "lexnv/bump_sp_crates" Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Revert "To revert: Update substrate branch to "lexnv/bump_sp_crates"" This reverts commit 5f1db84eac4a226c37b7f6ce6ee19b49dc7e2008. * Update cargo lock Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Update cargo.lock Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Update cargo.lock Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> --------- Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * bounded-collections bump to 0.1.7 (#7305) * bounded-collections bump to 0.1.7 Companion for: paritytech/substrate#14225 * update lockfile for {"substrate"} --------- Co-authored-by: parity-processbot <> * bump to quote 1.0.28 (#7306) * `RollingSessionWindow` cleanup (#7204) * Replace `RollingSessionWindow` with `RuntimeInfo` - initial commit * Fix tests in import * Fix the rest of the tests * Remove dead code * Fix todos * Simplify session caching * Comments for `SessionInfoProvider` * Separate `SessionInfoProvider` from `State` * `cache_session_info_for_head` becomes freestanding function * Remove unneeded `mut` usage * fn session_info -> fn get_session_info() to avoid name clashes. The function also tries to initialize `SessionInfoProvider` * Fix SessionInfo retrieval * Code cleanup * Don't wrap `SessionInfoProvider` in an `Option` * Remove `earliest_session()` * Remove pre-caching -> wip * Fix some tests and code cleanup * Fix all tests * Fixes in tests * Fix comments, variable names and small style changes * Fix a warning * impl From<SessionWindowSize> for NonZeroUsize * Fix logging for `get_session_info` - remove redundant logs and decrease log level to DEBUG * Code review feedback * Storage migration removing `COL_SESSION_WINDOW_DATA` from parachains db * Remove `col_session_data` usages * Storage migration clearing columns w/o removing them * Remove session data column usages from `approval-voting` and `dispute-coordinator` tests * Add some test cases from `RollingSessionWindow` to `dispute-coordinator` tests * Fix formatting in initialized.rs * Fix a corner case in `SessionInfo` caching for `dispute-coordinator` * Remove `RollingSessionWindow` ;( * Revert "Fix formatting in initialized.rs" This reverts commit 0f94664ec9f3a7e3737a30291195990e1e7065fc. * v2 to v3 migration drops `COL_DISPUTE_COORDINATOR_DATA` instead of clearing it * Fix `NUM_COLUMNS` in `approval-voting` * Use `columns::v3::NUM_COLUMNS` when opening db * Update node/service/src/parachains_db/upgrade.rs Co-authored-by: Andrei Sandu <54316454+sandreim@users.noreply.github.com> * Don't write in `COL_DISPUTE_COORDINATOR_DATA` for `test_rocksdb_migrate_2_to_3` * Fix `NUM+COLUMNS` in approval_voting * Fix formatting * Fix columns usage * Clarification comments about the different db versions --------- Co-authored-by: Andrei Sandu <54316454+sandreim@users.noreply.github.com> * pallet-para-config: Remove remnant WeightInfo functions (#7308) * pallet-para-config: Remove remnant WeightInfo functions Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * set_config_with_weight begone Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * ".git/.scripts/commands/bench/bench.sh" runtime kusama-dev runtime_parachains::configuration --------- Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> Co-authored-by: command-bot <> * XCM: PayOverXcm config (#6900) * Move XCM query functionality to trait * Fix tests * Add PayOverXcm implementation * fix the PayOverXcm trait to compile * moved doc comment out of trait implmeentation and to the trait * PayOverXCM documentation * Change documentation a bit * Added empty benchmark methods implementation and changed docs * update PayOverXCM to convert AccountIds to MultiLocations * Implement benchmarking method * Change v3 to latest * Descend origin to an asset sender (#6970) * descend origin to an asset sender * sender as tuple of dest and sender * Add more variants to the QueryResponseStatus enum * Change Beneficiary to Into<[u8; 32]> * update PayOverXcm to return concrete errors and use AccountId as sender * use polkadot-primitives for AccountId * fix dependency to use polkadot-core-primitives * force Unpaid instruction to the top of the instructions list * modify report_outcome to accept interior argument * use new_query directly for building final xcm query, instead of report_outcome * fix usage of new_query to use the XcmQueryHandler * fix usage of new_query to use the XcmQueryHandler * tiny method calling fix * xcm query handler (#7198) * drop redundant query status * rename ReportQueryStatus to OuterQueryStatus * revert rename of QueryResponseStatus * update mapping * Update xcm/xcm-builder/src/pay.rs Co-authored-by: Gavin Wood <gavin@parity.io> * Updates * Docs * Fix benchmarking stuff * Destination can be determined based on asset_kind * Tweaking API to minimise clones * Some repotting and docs --------- Co-authored-by: Anthony Alaribe <anthonyalaribe@gmail.com> Co-authored-by: Muharem Ismailov <ismailov.m.h@gmail.com> Co-authored-by: Anthony Alaribe <anthony.alaribe@parity.io> Co-authored-by: Gavin Wood <gavin@parity.io> * Companion for #14265 (#7307) * Update Cargo.lock Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Update Cargo.lock Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> --------- Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> Co-authored-by: parity-processbot <> * bump serde to 1.0.163 (#7315) * bump serde to 1.0.163 * bump ci * update lockfile for {"substrate"} --------- Co-authored-by: parity-processbot <> * fmt * Updated fmt * Removing changes accidentally pulled from master * fix another master pull issue * Another master pull fix * fmt * Fixing implementers guide build * Revert "Merge branch 'rh-async-backing-feature-while-frozen' of https://github.com/paritytech/polkadot into brad-rename-parathread" This reverts commit bebc24af52ab61155e3fe02cb3ce66a592bce49c, reversing changes made to 1b2de662dfb11173679d6da5bd0da9d149c85547. --------- Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> Signed-off-by: acatangiu <adrian@parity.io> Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> Co-authored-by: Marcin S <marcin@realemail.net> Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> Co-authored-by: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Co-authored-by: Adrian Catangiu <adrian@parity.io> Co-authored-by: ordian <write@reusable.software> Co-authored-by: Marcin S. <marcin@bytedude.com> Co-authored-by: Andrei Sandu <54316454+sandreim@users.noreply.github.com> Co-authored-by: Francisco Aguirre <franciscoaguirreperez@gmail.com> Co-authored-by: Bastian Köcher <git@kchr.de> Co-authored-by: Alexandru Vasile <60601340+lexnv@users.noreply.github.com> Co-authored-by: Sam Johnson <sam@durosoft.com> Co-authored-by: Tsvetomir Dimitrov <tsvetomir@parity.io> Co-authored-by: Anthony Alaribe <anthonyalaribe@gmail.com> Co-authored-by: Muharem Ismailov <ismailov.m.h@gmail.com> Co-authored-by: Anthony Alaribe <anthony.alaribe@parity.io> Co-authored-by: Gavin Wood <gavin@parity.io> * fix bitfield distribution test * approval distribution tests * fix bridge tests * update Cargo.lock * [async-backing-branch] Optimize collator-protocol validator-side request fetching (#7457) * Optimize collator-protocol validator-side request fetching * address feedback: replace tuples with structs * feedback: add doc comments * move collation types to subfolder --------- Signed-off-by: alindima <alin@parity.io> * Update collation generation for asynchronous backing (#7405) * break candidate receipt construction and distribution into own function * update implementers' guide to include SubmitCollation * implement SubmitCollation for collation-generation * fmt * fix test compilation & remove unnecessary submodule * add some TODOs for a test suite. * Update roadmap/implementers-guide/src/types/overseer-protocol.md Co-authored-by: Andrei Sandu <54316454+sandreim@users.noreply.github.com> * add new test harness and first test * refactor to avoid requiring background sender * ensure collation gets packaged and distributed * tests for the fallback case with no hint * add parent rp-number hint tests * fmt * update uses of CollationGenerationConfig * fix remaining test * address review comments * use subsystemsender for background tasks * fmt * remove ValidationCodeHashHint and related tests --------- Co-authored-by: Andrei Sandu <54316454+sandreim@users.noreply.github.com> * fix some more fallout from merge * fmt * remove staging APIs from Rococo & Westend (#7513) * send network messages on main protocol name (#7515) * misc async backing improvements for allowed ancestry blocks (#7532) * shared: fix acquire_info * backwards-compat test for prospective parachains * same relay parent is allowed * provisioner: request candidate receipt by relay parent (#7527) * return candidates hash from prospective parachains * update provisioner * update tests * guide changes * send a single message to backing * fix test * revert to old `handle_new_activations` logic in some cases (#7514) * revert to old `handle_new_activations` logic * gate sending messages on scheduled cores to max_depth >= 2 * fmt * 2->1 * Omnibus asynchronous backing bugfix PR (#7529) * fix a bug in backing * add some more logs * prospective parachains: take ancestry only up to session bounds * add test * fix zombienet tests (#7614) Signed-off-by: Andrei Sandu <andrei-mihail@parity.io> * fix runtime compilation * make bitfield distribution tests compile * attempt to fix zombienet disputes (#7618) * update metric name * update some metric names * avoid cycles when creating fake candidates * make undying collator more friendly to malformed parents * fix a bug in malus * fmt * clippy * add RUN_IN_CONTAINER to new ZombieNet tests (#7631) * remove duplicated migration happened because of master-merge --------- Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> Signed-off-by: acatangiu <adrian@parity.io> Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> Signed-off-by: alindima <alin@parity.io> Signed-off-by: Andrei Sandu <andrei-mihail@parity.io> Co-authored-by: Chris Sosnin <chris125_@live.com> Co-authored-by: Parity Bot <admin@parity.io> Co-authored-by: Chris Sosnin <48099298+slumber@users.noreply.github.com> Co-authored-by: Robert Klotzner <robert.klotzner@gmx.at> Co-authored-by: Robert Klotzner <eskimor@users.noreply.github.com> Co-authored-by: Marcin S <marcin@bytedude.com> Co-authored-by: Marcin S <marcin@realemail.net> Co-authored-by: Mattia L.V. Bradascio <28816406+bredamatt@users.noreply.github.com> Co-authored-by: Bradley Olson <34992650+BradleyOlson64@users.noreply.github.com> Co-authored-by: alexgparity <115470171+alexgparity@users.noreply.github.com> Co-authored-by: BradleyOlson64 <lotrftw9@gmail.com> Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> Co-authored-by: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Co-authored-by: Adrian Catangiu <adrian@parity.io> Co-authored-by: ordian <write@reusable.software> Co-authored-by: Andrei Sandu <54316454+sandreim@users.noreply.github.com> Co-authored-by: Francisco Aguirre <franciscoaguirreperez@gmail.com> Co-authored-by: Bastian Köcher <git@kchr.de> Co-authored-by: Alexandru Vasile <60601340+lexnv@users.noreply.github.com> Co-authored-by: Sam Johnson <sam@durosoft.com> Co-authored-by: Tsvetomir Dimitrov <tsvetomir@parity.io> Co-authored-by: Anthony Alaribe <anthonyalaribe@gmail.com> Co-authored-by: Muharem Ismailov <ismailov.m.h@gmail.com> Co-authored-by: Anthony Alaribe <anthony.alaribe@parity.io> Co-authored-by: Gavin Wood <gavin@parity.io> Co-authored-by: Alin Dima <alin@parity.io>
2643 lines
77 KiB
Rust
2643 lines
77 KiB
Rust
// Copyright (C) Parity Technologies (UK) Ltd.
|
|
// This file is part of Polkadot.
|
|
|
|
// Polkadot is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
|
|
// Polkadot is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
use super::*;
|
|
use assert_matches::assert_matches;
|
|
use futures::{executor, future, Future};
|
|
use polkadot_node_network_protocol::{
|
|
grid_topology::{SessionGridTopology, TopologyPeerInfo},
|
|
our_view,
|
|
peer_set::ValidationVersion,
|
|
view, ObservedRole,
|
|
};
|
|
use polkadot_node_primitives::approval::{
|
|
AssignmentCertKind, VrfOutput, VrfProof, VrfSignature, RELAY_VRF_MODULO_CONTEXT,
|
|
};
|
|
use polkadot_node_subsystem::messages::{
|
|
network_bridge_event, AllMessages, ApprovalCheckError, ReportPeerMessage,
|
|
};
|
|
use polkadot_node_subsystem_test_helpers as test_helpers;
|
|
use polkadot_node_subsystem_util::{reputation::add_reputation, TimeoutExt as _};
|
|
use polkadot_primitives::{AuthorityDiscoveryId, BlakeTwo256, HashT};
|
|
use polkadot_primitives_test_helpers::dummy_signature;
|
|
use rand::SeedableRng;
|
|
use sp_authority_discovery::AuthorityPair as AuthorityDiscoveryPair;
|
|
use sp_core::crypto::Pair as PairT;
|
|
use std::time::Duration;
|
|
|
|
type VirtualOverseer = test_helpers::TestSubsystemContextHandle<ApprovalDistributionMessage>;
|
|
|
|
fn test_harness<T: Future<Output = VirtualOverseer>>(
|
|
mut state: State,
|
|
test_fn: impl FnOnce(VirtualOverseer) -> T,
|
|
) -> State {
|
|
let _ = env_logger::builder()
|
|
.is_test(true)
|
|
.filter(Some(LOG_TARGET), log::LevelFilter::Trace)
|
|
.try_init();
|
|
|
|
let pool = sp_core::testing::TaskExecutor::new();
|
|
let (context, virtual_overseer) = test_helpers::make_subsystem_context(pool.clone());
|
|
|
|
let subsystem = ApprovalDistribution::new(Default::default());
|
|
{
|
|
let mut rng = rand_chacha::ChaCha12Rng::seed_from_u64(12345);
|
|
|
|
let subsystem =
|
|
subsystem.run_inner(context, &mut state, REPUTATION_CHANGE_TEST_INTERVAL, &mut rng);
|
|
|
|
let test_fut = test_fn(virtual_overseer);
|
|
|
|
futures::pin_mut!(test_fut);
|
|
futures::pin_mut!(subsystem);
|
|
|
|
executor::block_on(future::join(
|
|
async move {
|
|
let mut overseer = test_fut.await;
|
|
overseer
|
|
.send(FromOrchestra::Signal(OverseerSignal::Conclude))
|
|
.timeout(TIMEOUT)
|
|
.await
|
|
.expect("Conclude send timeout");
|
|
},
|
|
subsystem,
|
|
));
|
|
}
|
|
|
|
state
|
|
}
|
|
|
|
const TIMEOUT: Duration = Duration::from_millis(200);
|
|
const REPUTATION_CHANGE_TEST_INTERVAL: Duration = Duration::from_millis(1);
|
|
|
|
async fn overseer_send(overseer: &mut VirtualOverseer, msg: ApprovalDistributionMessage) {
|
|
gum::trace!(msg = ?msg, "Sending message");
|
|
overseer
|
|
.send(FromOrchestra::Communication { msg })
|
|
.timeout(TIMEOUT)
|
|
.await
|
|
.expect("msg send timeout");
|
|
}
|
|
|
|
async fn overseer_signal_block_finalized(overseer: &mut VirtualOverseer, number: BlockNumber) {
|
|
gum::trace!(?number, "Sending a finalized signal");
|
|
// we don't care about the block hash
|
|
overseer
|
|
.send(FromOrchestra::Signal(OverseerSignal::BlockFinalized(Hash::zero(), number)))
|
|
.timeout(TIMEOUT)
|
|
.await
|
|
.expect("signal send timeout");
|
|
}
|
|
|
|
async fn overseer_recv(overseer: &mut VirtualOverseer) -> AllMessages {
|
|
gum::trace!("Waiting for a message");
|
|
let msg = overseer.recv().timeout(TIMEOUT).await.expect("msg recv timeout");
|
|
|
|
gum::trace!(msg = ?msg, "Received message");
|
|
|
|
msg
|
|
}
|
|
|
|
fn make_peers_and_authority_ids(n: usize) -> Vec<(PeerId, AuthorityDiscoveryId)> {
|
|
(0..n)
|
|
.map(|_| {
|
|
let peer_id = PeerId::random();
|
|
let authority_id = AuthorityDiscoveryPair::generate().0.public();
|
|
|
|
(peer_id, authority_id)
|
|
})
|
|
.collect()
|
|
}
|
|
|
|
fn make_gossip_topology(
|
|
session: SessionIndex,
|
|
all_peers: &[(PeerId, AuthorityDiscoveryId)],
|
|
neighbors_x: &[usize],
|
|
neighbors_y: &[usize],
|
|
) -> network_bridge_event::NewGossipTopology {
|
|
// This builds a grid topology which is a square matrix.
|
|
// The local validator occupies the top left-hand corner.
|
|
// The X peers occupy the same row and the Y peers occupy
|
|
// the same column.
|
|
|
|
let local_index = 1;
|
|
|
|
assert_eq!(
|
|
neighbors_x.len(),
|
|
neighbors_y.len(),
|
|
"mocking grid topology only implemented for squares",
|
|
);
|
|
|
|
let d = neighbors_x.len() + 1;
|
|
|
|
let grid_size = d * d;
|
|
assert!(grid_size > 0);
|
|
assert!(all_peers.len() >= grid_size);
|
|
|
|
let peer_info = |i: usize| TopologyPeerInfo {
|
|
peer_ids: vec![all_peers[i].0],
|
|
validator_index: ValidatorIndex::from(i as u32),
|
|
discovery_id: all_peers[i].1.clone(),
|
|
};
|
|
|
|
let mut canonical_shuffling: Vec<_> = (0..)
|
|
.filter(|i| local_index != *i)
|
|
.filter(|i| !neighbors_x.contains(i))
|
|
.filter(|i| !neighbors_y.contains(i))
|
|
.take(grid_size)
|
|
.map(peer_info)
|
|
.collect();
|
|
|
|
// filled with junk except for own.
|
|
let mut shuffled_indices = vec![d + 1; grid_size];
|
|
shuffled_indices[local_index] = 0;
|
|
canonical_shuffling[0] = peer_info(local_index);
|
|
|
|
for (x_pos, v) in neighbors_x.iter().enumerate() {
|
|
let pos = 1 + x_pos;
|
|
canonical_shuffling[pos] = peer_info(*v);
|
|
}
|
|
|
|
for (y_pos, v) in neighbors_y.iter().enumerate() {
|
|
let pos = d * (1 + y_pos);
|
|
canonical_shuffling[pos] = peer_info(*v);
|
|
}
|
|
|
|
let topology = SessionGridTopology::new(shuffled_indices, canonical_shuffling);
|
|
|
|
// sanity check.
|
|
{
|
|
let g_n = topology
|
|
.compute_grid_neighbors_for(ValidatorIndex(local_index as _))
|
|
.expect("topology just constructed with this validator index");
|
|
|
|
assert_eq!(g_n.validator_indices_x.len(), neighbors_x.len());
|
|
assert_eq!(g_n.validator_indices_y.len(), neighbors_y.len());
|
|
|
|
for i in neighbors_x {
|
|
assert!(g_n.validator_indices_x.contains(&ValidatorIndex(*i as _)));
|
|
}
|
|
|
|
for i in neighbors_y {
|
|
assert!(g_n.validator_indices_y.contains(&ValidatorIndex(*i as _)));
|
|
}
|
|
}
|
|
|
|
network_bridge_event::NewGossipTopology {
|
|
session,
|
|
topology,
|
|
local_index: Some(ValidatorIndex(local_index as _)),
|
|
}
|
|
}
|
|
|
|
async fn setup_gossip_topology(
|
|
virtual_overseer: &mut VirtualOverseer,
|
|
gossip_topology: network_bridge_event::NewGossipTopology,
|
|
) {
|
|
overseer_send(
|
|
virtual_overseer,
|
|
ApprovalDistributionMessage::NetworkBridgeUpdate(NetworkBridgeEvent::NewGossipTopology(
|
|
gossip_topology,
|
|
)),
|
|
)
|
|
.await;
|
|
}
|
|
|
|
async fn setup_peer_with_view(
|
|
virtual_overseer: &mut VirtualOverseer,
|
|
peer_id: &PeerId,
|
|
validation_version: ValidationVersion,
|
|
view: View,
|
|
) {
|
|
overseer_send(
|
|
virtual_overseer,
|
|
ApprovalDistributionMessage::NetworkBridgeUpdate(NetworkBridgeEvent::PeerConnected(
|
|
*peer_id,
|
|
ObservedRole::Full,
|
|
validation_version.into(),
|
|
None,
|
|
)),
|
|
)
|
|
.await;
|
|
overseer_send(
|
|
virtual_overseer,
|
|
ApprovalDistributionMessage::NetworkBridgeUpdate(NetworkBridgeEvent::PeerViewChange(
|
|
*peer_id, view,
|
|
)),
|
|
)
|
|
.await;
|
|
}
|
|
|
|
async fn send_message_from_peer(
|
|
virtual_overseer: &mut VirtualOverseer,
|
|
peer_id: &PeerId,
|
|
msg: net_protocol::ApprovalDistributionMessage,
|
|
) {
|
|
overseer_send(
|
|
virtual_overseer,
|
|
ApprovalDistributionMessage::NetworkBridgeUpdate(NetworkBridgeEvent::PeerMessage(
|
|
*peer_id, msg,
|
|
)),
|
|
)
|
|
.await;
|
|
}
|
|
|
|
fn fake_assignment_cert(block_hash: Hash, validator: ValidatorIndex) -> IndirectAssignmentCert {
|
|
let ctx = schnorrkel::signing_context(RELAY_VRF_MODULO_CONTEXT);
|
|
let msg = b"WhenParachains?";
|
|
let mut prng = rand_core::OsRng;
|
|
let keypair = schnorrkel::Keypair::generate_with(&mut prng);
|
|
let (inout, proof, _) = keypair.vrf_sign(ctx.bytes(msg));
|
|
let out = inout.to_output();
|
|
|
|
IndirectAssignmentCert {
|
|
block_hash,
|
|
validator,
|
|
cert: AssignmentCert {
|
|
kind: AssignmentCertKind::RelayVRFModulo { sample: 1 },
|
|
vrf: VrfSignature { output: VrfOutput(out), proof: VrfProof(proof) },
|
|
},
|
|
}
|
|
}
|
|
|
|
async fn expect_reputation_change(
|
|
virtual_overseer: &mut VirtualOverseer,
|
|
peer_id: &PeerId,
|
|
rep: Rep,
|
|
) {
|
|
assert_matches!(
|
|
overseer_recv(virtual_overseer).await,
|
|
AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::ReportPeer(
|
|
ReportPeerMessage::Single(p, r),
|
|
)) => {
|
|
assert_eq!(p, *peer_id);
|
|
assert_eq!(r, rep.into());
|
|
}
|
|
);
|
|
}
|
|
|
|
async fn expect_reputation_changes(
|
|
virtual_overseer: &mut VirtualOverseer,
|
|
peer_id: &PeerId,
|
|
reps: Vec<Rep>,
|
|
) {
|
|
let mut acc = HashMap::new();
|
|
for rep in reps {
|
|
add_reputation(&mut acc, *peer_id, rep);
|
|
}
|
|
assert_matches!(
|
|
overseer_recv(virtual_overseer).await,
|
|
AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::ReportPeer(
|
|
ReportPeerMessage::Batch(v),
|
|
)) => {
|
|
assert_eq!(v, acc);
|
|
}
|
|
);
|
|
}
|
|
|
|
fn state_without_reputation_delay() -> State {
|
|
State { reputation: ReputationAggregator::new(|_| true), ..Default::default() }
|
|
}
|
|
|
|
fn state_with_reputation_delay() -> State {
|
|
State { reputation: ReputationAggregator::new(|_| false), ..Default::default() }
|
|
}
|
|
|
|
/// import an assignment
|
|
/// connect a new peer
|
|
/// the new peer sends us the same assignment
|
|
#[test]
|
|
fn try_import_the_same_assignment() {
|
|
let peer_a = PeerId::random();
|
|
let peer_b = PeerId::random();
|
|
let peer_c = PeerId::random();
|
|
let peer_d = PeerId::random();
|
|
let parent_hash = Hash::repeat_byte(0xFF);
|
|
let hash = Hash::repeat_byte(0xAA);
|
|
|
|
let _ = test_harness(state_without_reputation_delay(), |mut virtual_overseer| async move {
|
|
let overseer = &mut virtual_overseer;
|
|
// setup peers
|
|
setup_peer_with_view(overseer, &peer_a, ValidationVersion::V1, view![]).await;
|
|
setup_peer_with_view(overseer, &peer_b, ValidationVersion::V1, view![hash]).await;
|
|
setup_peer_with_view(overseer, &peer_c, ValidationVersion::V1, view![hash]).await;
|
|
|
|
// new block `hash_a` with 1 candidates
|
|
let meta = BlockApprovalMeta {
|
|
hash,
|
|
parent_hash,
|
|
number: 2,
|
|
candidates: vec![Default::default(); 1],
|
|
slot: 1.into(),
|
|
session: 1,
|
|
};
|
|
let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]);
|
|
overseer_send(overseer, msg).await;
|
|
|
|
// send the assignment related to `hash`
|
|
let validator_index = ValidatorIndex(0);
|
|
let cert = fake_assignment_cert(hash, validator_index);
|
|
let assignments = vec![(cert.clone(), 0u32)];
|
|
|
|
let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone());
|
|
send_message_from_peer(overseer, &peer_a, Versioned::V1(msg)).await;
|
|
|
|
expect_reputation_change(overseer, &peer_a, COST_UNEXPECTED_MESSAGE).await;
|
|
|
|
// send an `Accept` message from the Approval Voting subsystem
|
|
assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment(
|
|
assignment,
|
|
0u32,
|
|
tx,
|
|
)) => {
|
|
assert_eq!(assignment, cert);
|
|
tx.send(AssignmentCheckResult::Accepted).unwrap();
|
|
}
|
|
);
|
|
|
|
expect_reputation_change(overseer, &peer_a, BENEFIT_VALID_MESSAGE_FIRST).await;
|
|
|
|
assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage(
|
|
peers,
|
|
Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution(
|
|
protocol_v1::ApprovalDistributionMessage::Assignments(assignments)
|
|
))
|
|
)) => {
|
|
assert_eq!(peers.len(), 2);
|
|
assert_eq!(assignments.len(), 1);
|
|
}
|
|
);
|
|
|
|
// setup new peer
|
|
setup_peer_with_view(overseer, &peer_d, ValidationVersion::V1, view![]).await;
|
|
|
|
// send the same assignment from peer_d
|
|
let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments);
|
|
send_message_from_peer(overseer, &peer_d, Versioned::V1(msg)).await;
|
|
|
|
expect_reputation_change(overseer, &peer_d, COST_UNEXPECTED_MESSAGE).await;
|
|
expect_reputation_change(overseer, &peer_d, BENEFIT_VALID_MESSAGE).await;
|
|
|
|
assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent");
|
|
virtual_overseer
|
|
});
|
|
}
|
|
|
|
/// import an assignment
|
|
/// connect a new peer
|
|
/// state sends aggregated reputation change
|
|
#[test]
|
|
fn delay_reputation_change() {
|
|
let peer = PeerId::random();
|
|
let parent_hash = Hash::repeat_byte(0xFF);
|
|
let hash = Hash::repeat_byte(0xAA);
|
|
|
|
let _ = test_harness(state_with_reputation_delay(), |mut virtual_overseer| async move {
|
|
let overseer = &mut virtual_overseer;
|
|
|
|
// Setup peers
|
|
setup_peer_with_view(overseer, &peer, ValidationVersion::V1, view![]).await;
|
|
|
|
// new block `hash_a` with 1 candidates
|
|
let meta = BlockApprovalMeta {
|
|
hash,
|
|
parent_hash,
|
|
number: 2,
|
|
candidates: vec![Default::default(); 1],
|
|
slot: 1.into(),
|
|
session: 1,
|
|
};
|
|
let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]);
|
|
overseer_send(overseer, msg).await;
|
|
|
|
// send the assignment related to `hash`
|
|
let validator_index = ValidatorIndex(0);
|
|
let cert = fake_assignment_cert(hash, validator_index);
|
|
let assignments = vec![(cert.clone(), 0u32)];
|
|
|
|
let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone());
|
|
send_message_from_peer(overseer, &peer, Versioned::V1(msg)).await;
|
|
|
|
// send an `Accept` message from the Approval Voting subsystem
|
|
assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment(
|
|
assignment,
|
|
0u32,
|
|
tx,
|
|
)) => {
|
|
assert_eq!(assignment, cert);
|
|
tx.send(AssignmentCheckResult::Accepted).unwrap();
|
|
}
|
|
);
|
|
expect_reputation_changes(
|
|
overseer,
|
|
&peer,
|
|
vec![COST_UNEXPECTED_MESSAGE, BENEFIT_VALID_MESSAGE_FIRST],
|
|
)
|
|
.await;
|
|
assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent");
|
|
|
|
virtual_overseer
|
|
});
|
|
}
|
|
|
|
/// <https://github.com/paritytech/polkadot/pull/2160#discussion_r547594835>
|
|
///
|
|
/// 1. Send a view update that removes block B from their view.
|
|
/// 2. Send a message from B that they incur `COST_UNEXPECTED_MESSAGE` for, but then they receive
|
|
/// `BENEFIT_VALID_MESSAGE`.
|
|
/// 3. Send all other messages related to B.
|
|
#[test]
|
|
fn spam_attack_results_in_negative_reputation_change() {
|
|
let parent_hash = Hash::repeat_byte(0xFF);
|
|
let peer_a = PeerId::random();
|
|
let hash_b = Hash::repeat_byte(0xBB);
|
|
|
|
let _ = test_harness(state_without_reputation_delay(), |mut virtual_overseer| async move {
|
|
let overseer = &mut virtual_overseer;
|
|
let peer = &peer_a;
|
|
setup_peer_with_view(overseer, peer, ValidationVersion::V1, view![]).await;
|
|
|
|
// new block `hash_b` with 20 candidates
|
|
let candidates_count = 20;
|
|
let meta = BlockApprovalMeta {
|
|
hash: hash_b,
|
|
parent_hash,
|
|
number: 2,
|
|
candidates: vec![Default::default(); candidates_count],
|
|
slot: 1.into(),
|
|
session: 1,
|
|
};
|
|
|
|
let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]);
|
|
overseer_send(overseer, msg).await;
|
|
|
|
// send 20 assignments related to `hash_b`
|
|
// to populate our knowledge
|
|
let assignments: Vec<_> = (0..candidates_count)
|
|
.map(|candidate_index| {
|
|
let validator_index = ValidatorIndex(candidate_index as u32);
|
|
let cert = fake_assignment_cert(hash_b, validator_index);
|
|
(cert, candidate_index as u32)
|
|
})
|
|
.collect();
|
|
|
|
let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone());
|
|
send_message_from_peer(overseer, peer, Versioned::V1(msg.clone())).await;
|
|
|
|
for i in 0..candidates_count {
|
|
expect_reputation_change(overseer, peer, COST_UNEXPECTED_MESSAGE).await;
|
|
|
|
assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment(
|
|
assignment,
|
|
claimed_candidate_index,
|
|
tx,
|
|
)) => {
|
|
assert_eq!(assignment, assignments[i].0);
|
|
assert_eq!(claimed_candidate_index, assignments[i].1);
|
|
tx.send(AssignmentCheckResult::Accepted).unwrap();
|
|
}
|
|
);
|
|
|
|
expect_reputation_change(overseer, peer, BENEFIT_VALID_MESSAGE_FIRST).await;
|
|
}
|
|
|
|
// send a view update that removes block B from peer's view by bumping the finalized_number
|
|
overseer_send(
|
|
overseer,
|
|
ApprovalDistributionMessage::NetworkBridgeUpdate(NetworkBridgeEvent::PeerViewChange(
|
|
*peer,
|
|
View::with_finalized(2),
|
|
)),
|
|
)
|
|
.await;
|
|
|
|
// send the assignments again
|
|
send_message_from_peer(overseer, peer, Versioned::V1(msg.clone())).await;
|
|
|
|
// each of them will incur `COST_UNEXPECTED_MESSAGE`, not only the first one
|
|
for _ in 0..candidates_count {
|
|
expect_reputation_change(overseer, peer, COST_UNEXPECTED_MESSAGE).await;
|
|
expect_reputation_change(overseer, peer, BENEFIT_VALID_MESSAGE).await;
|
|
}
|
|
virtual_overseer
|
|
});
|
|
}
|
|
|
|
/// Imagine we send a message to peer A and peer B.
|
|
/// Upon receiving them, they both will try to send the message each other.
|
|
/// This test makes sure they will not punish each other for such duplicate messages.
|
|
///
|
|
/// See <https://github.com/paritytech/polkadot/issues/2499>.
|
|
#[test]
|
|
fn peer_sending_us_the_same_we_just_sent_them_is_ok() {
|
|
let parent_hash = Hash::repeat_byte(0xFF);
|
|
let peer_a = PeerId::random();
|
|
let hash = Hash::repeat_byte(0xAA);
|
|
|
|
let _ = test_harness(state_without_reputation_delay(), |mut virtual_overseer| async move {
|
|
let overseer = &mut virtual_overseer;
|
|
let peer = &peer_a;
|
|
setup_peer_with_view(overseer, peer, ValidationVersion::V1, view![]).await;
|
|
|
|
// new block `hash` with 1 candidates
|
|
let meta = BlockApprovalMeta {
|
|
hash,
|
|
parent_hash,
|
|
number: 1,
|
|
candidates: vec![Default::default(); 1],
|
|
slot: 1.into(),
|
|
session: 1,
|
|
};
|
|
let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]);
|
|
overseer_send(overseer, msg).await;
|
|
|
|
// import an assignment related to `hash` locally
|
|
let validator_index = ValidatorIndex(0);
|
|
let candidate_index = 0u32;
|
|
let cert = fake_assignment_cert(hash, validator_index);
|
|
overseer_send(
|
|
overseer,
|
|
ApprovalDistributionMessage::DistributeAssignment(cert.clone(), candidate_index),
|
|
)
|
|
.await;
|
|
|
|
// update peer view to include the hash
|
|
overseer_send(
|
|
overseer,
|
|
ApprovalDistributionMessage::NetworkBridgeUpdate(NetworkBridgeEvent::PeerViewChange(
|
|
*peer,
|
|
view![hash],
|
|
)),
|
|
)
|
|
.await;
|
|
|
|
// we should send them the assignment
|
|
assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage(
|
|
peers,
|
|
Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution(
|
|
protocol_v1::ApprovalDistributionMessage::Assignments(assignments)
|
|
))
|
|
)) => {
|
|
assert_eq!(peers.len(), 1);
|
|
assert_eq!(assignments.len(), 1);
|
|
}
|
|
);
|
|
|
|
// but if someone else is sending it the same assignment
|
|
// the peer could send us it as well
|
|
let assignments = vec![(cert, candidate_index)];
|
|
let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments);
|
|
send_message_from_peer(overseer, peer, Versioned::V1(msg.clone())).await;
|
|
|
|
assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "we should not punish the peer");
|
|
|
|
// send the assignments again
|
|
send_message_from_peer(overseer, peer, Versioned::V1(msg)).await;
|
|
|
|
// now we should
|
|
expect_reputation_change(overseer, peer, COST_DUPLICATE_MESSAGE).await;
|
|
virtual_overseer
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn import_approval_happy_path() {
|
|
let peer_a = PeerId::random();
|
|
let peer_b = PeerId::random();
|
|
let peer_c = PeerId::random();
|
|
let parent_hash = Hash::repeat_byte(0xFF);
|
|
let hash = Hash::repeat_byte(0xAA);
|
|
|
|
let _ = test_harness(state_without_reputation_delay(), |mut virtual_overseer| async move {
|
|
let overseer = &mut virtual_overseer;
|
|
// setup peers
|
|
setup_peer_with_view(overseer, &peer_a, ValidationVersion::V1, view![]).await;
|
|
setup_peer_with_view(overseer, &peer_b, ValidationVersion::V1, view![hash]).await;
|
|
setup_peer_with_view(overseer, &peer_c, ValidationVersion::V1, view![hash]).await;
|
|
|
|
// new block `hash_a` with 1 candidates
|
|
let meta = BlockApprovalMeta {
|
|
hash,
|
|
parent_hash,
|
|
number: 1,
|
|
candidates: vec![Default::default(); 1],
|
|
slot: 1.into(),
|
|
session: 1,
|
|
};
|
|
let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]);
|
|
overseer_send(overseer, msg).await;
|
|
|
|
// import an assignment related to `hash` locally
|
|
let validator_index = ValidatorIndex(0);
|
|
let candidate_index = 0u32;
|
|
let cert = fake_assignment_cert(hash, validator_index);
|
|
overseer_send(
|
|
overseer,
|
|
ApprovalDistributionMessage::DistributeAssignment(cert, candidate_index),
|
|
)
|
|
.await;
|
|
|
|
assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage(
|
|
peers,
|
|
Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution(
|
|
protocol_v1::ApprovalDistributionMessage::Assignments(assignments)
|
|
))
|
|
)) => {
|
|
assert_eq!(peers.len(), 2);
|
|
assert_eq!(assignments.len(), 1);
|
|
}
|
|
);
|
|
|
|
// send the an approval from peer_b
|
|
let approval = IndirectSignedApprovalVote {
|
|
block_hash: hash,
|
|
candidate_index,
|
|
validator: validator_index,
|
|
signature: dummy_signature(),
|
|
};
|
|
let msg = protocol_v1::ApprovalDistributionMessage::Approvals(vec![approval.clone()]);
|
|
send_message_from_peer(overseer, &peer_b, Versioned::V1(msg)).await;
|
|
|
|
assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportApproval(
|
|
vote,
|
|
tx,
|
|
)) => {
|
|
assert_eq!(vote, approval);
|
|
tx.send(ApprovalCheckResult::Accepted).unwrap();
|
|
}
|
|
);
|
|
|
|
expect_reputation_change(overseer, &peer_b, BENEFIT_VALID_MESSAGE_FIRST).await;
|
|
|
|
assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage(
|
|
peers,
|
|
Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution(
|
|
protocol_v1::ApprovalDistributionMessage::Approvals(approvals)
|
|
))
|
|
)) => {
|
|
assert_eq!(peers.len(), 1);
|
|
assert_eq!(approvals.len(), 1);
|
|
}
|
|
);
|
|
virtual_overseer
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn import_approval_bad() {
|
|
let peer_a = PeerId::random();
|
|
let peer_b = PeerId::random();
|
|
let parent_hash = Hash::repeat_byte(0xFF);
|
|
let hash = Hash::repeat_byte(0xAA);
|
|
|
|
let _ = test_harness(state_without_reputation_delay(), |mut virtual_overseer| async move {
|
|
let overseer = &mut virtual_overseer;
|
|
// setup peers
|
|
setup_peer_with_view(overseer, &peer_a, ValidationVersion::V1, view![]).await;
|
|
setup_peer_with_view(overseer, &peer_b, ValidationVersion::V1, view![hash]).await;
|
|
|
|
// new block `hash_a` with 1 candidates
|
|
let meta = BlockApprovalMeta {
|
|
hash,
|
|
parent_hash,
|
|
number: 1,
|
|
candidates: vec![Default::default(); 1],
|
|
slot: 1.into(),
|
|
session: 1,
|
|
};
|
|
let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]);
|
|
overseer_send(overseer, msg).await;
|
|
|
|
let validator_index = ValidatorIndex(0);
|
|
let candidate_index = 0u32;
|
|
let cert = fake_assignment_cert(hash, validator_index);
|
|
|
|
// send the an approval from peer_b, we don't have an assignment yet
|
|
let approval = IndirectSignedApprovalVote {
|
|
block_hash: hash,
|
|
candidate_index,
|
|
validator: validator_index,
|
|
signature: dummy_signature(),
|
|
};
|
|
let msg = protocol_v1::ApprovalDistributionMessage::Approvals(vec![approval.clone()]);
|
|
send_message_from_peer(overseer, &peer_b, Versioned::V1(msg)).await;
|
|
|
|
expect_reputation_change(overseer, &peer_b, COST_UNEXPECTED_MESSAGE).await;
|
|
|
|
// now import an assignment from peer_b
|
|
let assignments = vec![(cert.clone(), candidate_index)];
|
|
let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments);
|
|
send_message_from_peer(overseer, &peer_b, Versioned::V1(msg)).await;
|
|
|
|
assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment(
|
|
assignment,
|
|
i,
|
|
tx,
|
|
)) => {
|
|
assert_eq!(assignment, cert);
|
|
assert_eq!(i, candidate_index);
|
|
tx.send(AssignmentCheckResult::Accepted).unwrap();
|
|
}
|
|
);
|
|
|
|
expect_reputation_change(overseer, &peer_b, BENEFIT_VALID_MESSAGE_FIRST).await;
|
|
|
|
// and try again
|
|
let msg = protocol_v1::ApprovalDistributionMessage::Approvals(vec![approval.clone()]);
|
|
send_message_from_peer(overseer, &peer_b, Versioned::V1(msg)).await;
|
|
|
|
assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportApproval(
|
|
vote,
|
|
tx,
|
|
)) => {
|
|
assert_eq!(vote, approval);
|
|
tx.send(ApprovalCheckResult::Bad(ApprovalCheckError::UnknownBlock(hash))).unwrap();
|
|
}
|
|
);
|
|
|
|
expect_reputation_change(overseer, &peer_b, COST_INVALID_MESSAGE).await;
|
|
virtual_overseer
|
|
});
|
|
}
|
|
|
|
/// make sure we clean up the state on block finalized
|
|
#[test]
|
|
fn update_our_view() {
|
|
let parent_hash = Hash::repeat_byte(0xFF);
|
|
let hash_a = Hash::repeat_byte(0xAA);
|
|
let hash_b = Hash::repeat_byte(0xBB);
|
|
let hash_c = Hash::repeat_byte(0xCC);
|
|
|
|
let state = test_harness(State::default(), |mut virtual_overseer| async move {
|
|
let overseer = &mut virtual_overseer;
|
|
// new block `hash_a` with 1 candidates
|
|
let meta_a = BlockApprovalMeta {
|
|
hash: hash_a,
|
|
parent_hash,
|
|
number: 1,
|
|
candidates: vec![Default::default(); 1],
|
|
slot: 1.into(),
|
|
session: 1,
|
|
};
|
|
let meta_b = BlockApprovalMeta {
|
|
hash: hash_b,
|
|
parent_hash: hash_a,
|
|
number: 2,
|
|
candidates: vec![Default::default(); 1],
|
|
slot: 1.into(),
|
|
session: 1,
|
|
};
|
|
let meta_c = BlockApprovalMeta {
|
|
hash: hash_c,
|
|
parent_hash: hash_b,
|
|
number: 3,
|
|
candidates: vec![Default::default(); 1],
|
|
slot: 1.into(),
|
|
session: 1,
|
|
};
|
|
|
|
let msg = ApprovalDistributionMessage::NewBlocks(vec![meta_a, meta_b, meta_c]);
|
|
overseer_send(overseer, msg).await;
|
|
virtual_overseer
|
|
});
|
|
|
|
assert!(state.blocks_by_number.get(&1).is_some());
|
|
assert!(state.blocks_by_number.get(&2).is_some());
|
|
assert!(state.blocks_by_number.get(&3).is_some());
|
|
assert!(state.blocks.get(&hash_a).is_some());
|
|
assert!(state.blocks.get(&hash_b).is_some());
|
|
assert!(state.blocks.get(&hash_c).is_some());
|
|
|
|
let state = test_harness(state, |mut virtual_overseer| async move {
|
|
let overseer = &mut virtual_overseer;
|
|
// finalize a block
|
|
overseer_signal_block_finalized(overseer, 2).await;
|
|
virtual_overseer
|
|
});
|
|
|
|
assert!(state.blocks_by_number.get(&1).is_none());
|
|
assert!(state.blocks_by_number.get(&2).is_none());
|
|
assert!(state.blocks_by_number.get(&3).is_some());
|
|
assert!(state.blocks.get(&hash_a).is_none());
|
|
assert!(state.blocks.get(&hash_b).is_none());
|
|
assert!(state.blocks.get(&hash_c).is_some());
|
|
|
|
let state = test_harness(state, |mut virtual_overseer| async move {
|
|
let overseer = &mut virtual_overseer;
|
|
// finalize a very high block
|
|
overseer_signal_block_finalized(overseer, 4_000_000_000).await;
|
|
virtual_overseer
|
|
});
|
|
|
|
assert!(state.blocks_by_number.get(&3).is_none());
|
|
assert!(state.blocks.get(&hash_c).is_none());
|
|
}
|
|
|
|
/// make sure we unify with peers and clean up the state
|
|
#[test]
|
|
fn update_peer_view() {
|
|
let parent_hash = Hash::repeat_byte(0xFF);
|
|
let hash_a = Hash::repeat_byte(0xAA);
|
|
let hash_b = Hash::repeat_byte(0xBB);
|
|
let hash_c = Hash::repeat_byte(0xCC);
|
|
let hash_d = Hash::repeat_byte(0xDD);
|
|
let peer_a = PeerId::random();
|
|
let peer = &peer_a;
|
|
|
|
let state = test_harness(State::default(), |mut virtual_overseer| async move {
|
|
let overseer = &mut virtual_overseer;
|
|
// new block `hash_a` with 1 candidates
|
|
let meta_a = BlockApprovalMeta {
|
|
hash: hash_a,
|
|
parent_hash,
|
|
number: 1,
|
|
candidates: vec![Default::default(); 1],
|
|
slot: 1.into(),
|
|
session: 1,
|
|
};
|
|
let meta_b = BlockApprovalMeta {
|
|
hash: hash_b,
|
|
parent_hash: hash_a,
|
|
number: 2,
|
|
candidates: vec![Default::default(); 1],
|
|
slot: 1.into(),
|
|
session: 1,
|
|
};
|
|
let meta_c = BlockApprovalMeta {
|
|
hash: hash_c,
|
|
parent_hash: hash_b,
|
|
number: 3,
|
|
candidates: vec![Default::default(); 1],
|
|
slot: 1.into(),
|
|
session: 1,
|
|
};
|
|
|
|
let msg = ApprovalDistributionMessage::NewBlocks(vec![meta_a, meta_b, meta_c]);
|
|
overseer_send(overseer, msg).await;
|
|
|
|
let cert_a = fake_assignment_cert(hash_a, ValidatorIndex(0));
|
|
let cert_b = fake_assignment_cert(hash_b, ValidatorIndex(0));
|
|
|
|
overseer_send(overseer, ApprovalDistributionMessage::DistributeAssignment(cert_a, 0)).await;
|
|
|
|
overseer_send(overseer, ApprovalDistributionMessage::DistributeAssignment(cert_b, 0)).await;
|
|
|
|
// connect a peer
|
|
setup_peer_with_view(overseer, peer, ValidationVersion::V1, view![hash_a]).await;
|
|
|
|
// we should send relevant assignments to the peer
|
|
assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage(
|
|
peers,
|
|
Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution(
|
|
protocol_v1::ApprovalDistributionMessage::Assignments(assignments)
|
|
))
|
|
)) => {
|
|
assert_eq!(peers.len(), 1);
|
|
assert_eq!(assignments.len(), 1);
|
|
}
|
|
);
|
|
virtual_overseer
|
|
});
|
|
|
|
assert_eq!(state.peer_data.get(peer).map(|data| data.view.finalized_number), Some(0));
|
|
assert_eq!(
|
|
state
|
|
.blocks
|
|
.get(&hash_a)
|
|
.unwrap()
|
|
.known_by
|
|
.get(peer)
|
|
.unwrap()
|
|
.sent
|
|
.known_messages
|
|
.len(),
|
|
1,
|
|
);
|
|
|
|
let state = test_harness(state, |mut virtual_overseer| async move {
|
|
let overseer = &mut virtual_overseer;
|
|
// update peer's view
|
|
overseer_send(
|
|
overseer,
|
|
ApprovalDistributionMessage::NetworkBridgeUpdate(NetworkBridgeEvent::PeerViewChange(
|
|
*peer,
|
|
View::new(vec![hash_b, hash_c, hash_d], 2),
|
|
)),
|
|
)
|
|
.await;
|
|
|
|
let cert_c = fake_assignment_cert(hash_c, ValidatorIndex(0));
|
|
|
|
overseer_send(
|
|
overseer,
|
|
ApprovalDistributionMessage::DistributeAssignment(cert_c.clone(), 0),
|
|
)
|
|
.await;
|
|
|
|
// we should send relevant assignments to the peer
|
|
assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage(
|
|
peers,
|
|
Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution(
|
|
protocol_v1::ApprovalDistributionMessage::Assignments(assignments)
|
|
))
|
|
)) => {
|
|
assert_eq!(peers.len(), 1);
|
|
assert_eq!(assignments.len(), 1);
|
|
assert_eq!(assignments[0].0, cert_c);
|
|
}
|
|
);
|
|
virtual_overseer
|
|
});
|
|
|
|
assert_eq!(state.peer_data.get(peer).map(|data| data.view.finalized_number), Some(2));
|
|
assert_eq!(
|
|
state
|
|
.blocks
|
|
.get(&hash_c)
|
|
.unwrap()
|
|
.known_by
|
|
.get(peer)
|
|
.unwrap()
|
|
.sent
|
|
.known_messages
|
|
.len(),
|
|
1,
|
|
);
|
|
|
|
let finalized_number = 4_000_000_000;
|
|
let state = test_harness(state, |mut virtual_overseer| async move {
|
|
let overseer = &mut virtual_overseer;
|
|
// update peer's view
|
|
overseer_send(
|
|
overseer,
|
|
ApprovalDistributionMessage::NetworkBridgeUpdate(NetworkBridgeEvent::PeerViewChange(
|
|
*peer,
|
|
View::with_finalized(finalized_number),
|
|
)),
|
|
)
|
|
.await;
|
|
virtual_overseer
|
|
});
|
|
|
|
assert_eq!(
|
|
state.peer_data.get(peer).map(|data| data.view.finalized_number),
|
|
Some(finalized_number)
|
|
);
|
|
assert!(state.blocks.get(&hash_c).unwrap().known_by.get(peer).is_none());
|
|
}
|
|
|
|
/// E.g. if someone copies the keys...
|
|
#[test]
|
|
fn import_remotely_then_locally() {
|
|
let peer_a = PeerId::random();
|
|
let parent_hash = Hash::repeat_byte(0xFF);
|
|
let hash = Hash::repeat_byte(0xAA);
|
|
let peer = &peer_a;
|
|
|
|
let _ = test_harness(state_without_reputation_delay(), |mut virtual_overseer| async move {
|
|
let overseer = &mut virtual_overseer;
|
|
// setup the peer
|
|
setup_peer_with_view(overseer, peer, ValidationVersion::V1, view![hash]).await;
|
|
|
|
// new block `hash_a` with 1 candidates
|
|
let meta = BlockApprovalMeta {
|
|
hash,
|
|
parent_hash,
|
|
number: 1,
|
|
candidates: vec![Default::default(); 1],
|
|
slot: 1.into(),
|
|
session: 1,
|
|
};
|
|
let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]);
|
|
overseer_send(overseer, msg).await;
|
|
|
|
// import the assignment remotely first
|
|
let validator_index = ValidatorIndex(0);
|
|
let candidate_index = 0u32;
|
|
let cert = fake_assignment_cert(hash, validator_index);
|
|
let assignments = vec![(cert.clone(), candidate_index)];
|
|
let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone());
|
|
send_message_from_peer(overseer, peer, Versioned::V1(msg)).await;
|
|
|
|
// send an `Accept` message from the Approval Voting subsystem
|
|
assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment(
|
|
assignment,
|
|
i,
|
|
tx,
|
|
)) => {
|
|
assert_eq!(assignment, cert);
|
|
assert_eq!(i, candidate_index);
|
|
tx.send(AssignmentCheckResult::Accepted).unwrap();
|
|
}
|
|
);
|
|
|
|
expect_reputation_change(overseer, peer, BENEFIT_VALID_MESSAGE_FIRST).await;
|
|
|
|
// import the same assignment locally
|
|
overseer_send(
|
|
overseer,
|
|
ApprovalDistributionMessage::DistributeAssignment(cert, candidate_index),
|
|
)
|
|
.await;
|
|
|
|
assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent");
|
|
|
|
// send the approval remotely
|
|
let approval = IndirectSignedApprovalVote {
|
|
block_hash: hash,
|
|
candidate_index,
|
|
validator: validator_index,
|
|
signature: dummy_signature(),
|
|
};
|
|
let msg = protocol_v1::ApprovalDistributionMessage::Approvals(vec![approval.clone()]);
|
|
send_message_from_peer(overseer, peer, Versioned::V1(msg)).await;
|
|
|
|
assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportApproval(
|
|
vote,
|
|
tx,
|
|
)) => {
|
|
assert_eq!(vote, approval);
|
|
tx.send(ApprovalCheckResult::Accepted).unwrap();
|
|
}
|
|
);
|
|
expect_reputation_change(overseer, peer, BENEFIT_VALID_MESSAGE_FIRST).await;
|
|
|
|
// import the same approval locally
|
|
overseer_send(overseer, ApprovalDistributionMessage::DistributeApproval(approval)).await;
|
|
|
|
assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent");
|
|
virtual_overseer
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn sends_assignments_even_when_state_is_approved() {
|
|
let peer_a = PeerId::random();
|
|
let parent_hash = Hash::repeat_byte(0xFF);
|
|
let hash = Hash::repeat_byte(0xAA);
|
|
let peer = &peer_a;
|
|
|
|
let _ = test_harness(State::default(), |mut virtual_overseer| async move {
|
|
let overseer = &mut virtual_overseer;
|
|
|
|
// new block `hash_a` with 1 candidates
|
|
let meta = BlockApprovalMeta {
|
|
hash,
|
|
parent_hash,
|
|
number: 1,
|
|
candidates: vec![Default::default(); 1],
|
|
slot: 1.into(),
|
|
session: 1,
|
|
};
|
|
let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]);
|
|
overseer_send(overseer, msg).await;
|
|
|
|
let validator_index = ValidatorIndex(0);
|
|
let candidate_index = 0u32;
|
|
|
|
// import an assignment and approval locally.
|
|
let cert = fake_assignment_cert(hash, validator_index);
|
|
let approval = IndirectSignedApprovalVote {
|
|
block_hash: hash,
|
|
candidate_index,
|
|
validator: validator_index,
|
|
signature: dummy_signature(),
|
|
};
|
|
|
|
overseer_send(
|
|
overseer,
|
|
ApprovalDistributionMessage::DistributeAssignment(cert.clone(), candidate_index),
|
|
)
|
|
.await;
|
|
|
|
overseer_send(overseer, ApprovalDistributionMessage::DistributeApproval(approval.clone()))
|
|
.await;
|
|
|
|
// connect the peer.
|
|
setup_peer_with_view(overseer, peer, ValidationVersion::V1, view![hash]).await;
|
|
|
|
let assignments = vec![(cert.clone(), candidate_index)];
|
|
let approvals = vec![approval.clone()];
|
|
|
|
assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage(
|
|
peers,
|
|
Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution(
|
|
protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments)
|
|
))
|
|
)) => {
|
|
assert_eq!(peers, vec![*peer]);
|
|
assert_eq!(sent_assignments, assignments);
|
|
}
|
|
);
|
|
|
|
assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage(
|
|
peers,
|
|
Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution(
|
|
protocol_v1::ApprovalDistributionMessage::Approvals(sent_approvals)
|
|
))
|
|
)) => {
|
|
assert_eq!(peers, vec![*peer]);
|
|
assert_eq!(sent_approvals, approvals);
|
|
}
|
|
);
|
|
|
|
assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent");
|
|
virtual_overseer
|
|
});
|
|
}
|
|
|
|
/// <https://github.com/paritytech/polkadot/pull/5089>
|
|
///
|
|
/// 1. Receive remote peer view update with an unknown head
|
|
/// 2. Receive assignments for that unknown head
|
|
/// 3. Update our view and import the new block
|
|
/// 4. Expect that no reputation with `COST_UNEXPECTED_MESSAGE` is applied
|
|
#[test]
|
|
fn race_condition_in_local_vs_remote_view_update() {
|
|
let parent_hash = Hash::repeat_byte(0xFF);
|
|
let peer_a = PeerId::random();
|
|
let hash_b = Hash::repeat_byte(0xBB);
|
|
|
|
let _ = test_harness(state_without_reputation_delay(), |mut virtual_overseer| async move {
|
|
let overseer = &mut virtual_overseer;
|
|
let peer = &peer_a;
|
|
|
|
// Test a small number of candidates
|
|
let candidates_count = 1;
|
|
let meta = BlockApprovalMeta {
|
|
hash: hash_b,
|
|
parent_hash,
|
|
number: 2,
|
|
candidates: vec![Default::default(); candidates_count],
|
|
slot: 1.into(),
|
|
session: 1,
|
|
};
|
|
|
|
// This will send a peer view that is ahead of our view
|
|
setup_peer_with_view(overseer, peer, ValidationVersion::V1, view![hash_b]).await;
|
|
|
|
// Send our view update to include a new head
|
|
overseer_send(
|
|
overseer,
|
|
ApprovalDistributionMessage::NetworkBridgeUpdate(NetworkBridgeEvent::OurViewChange(
|
|
our_view![hash_b],
|
|
)),
|
|
)
|
|
.await;
|
|
|
|
// send assignments related to `hash_b` but they will come to the MessagesPending
|
|
let assignments: Vec<_> = (0..candidates_count)
|
|
.map(|candidate_index| {
|
|
let validator_index = ValidatorIndex(candidate_index as u32);
|
|
let cert = fake_assignment_cert(hash_b, validator_index);
|
|
(cert, candidate_index as u32)
|
|
})
|
|
.collect();
|
|
|
|
let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone());
|
|
send_message_from_peer(overseer, peer, Versioned::V1(msg.clone())).await;
|
|
|
|
// This will handle pending messages being processed
|
|
let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]);
|
|
overseer_send(overseer, msg).await;
|
|
|
|
for i in 0..candidates_count {
|
|
// Previously, this has caused out-of-view assignments/approvals
|
|
//expect_reputation_change(overseer, peer, COST_UNEXPECTED_MESSAGE).await;
|
|
|
|
assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment(
|
|
assignment,
|
|
claimed_candidate_index,
|
|
tx,
|
|
)) => {
|
|
assert_eq!(assignment, assignments[i].0);
|
|
assert_eq!(claimed_candidate_index, assignments[i].1);
|
|
tx.send(AssignmentCheckResult::Accepted).unwrap();
|
|
}
|
|
);
|
|
|
|
// Since we have a valid statement pending, this should always occur
|
|
expect_reputation_change(overseer, peer, BENEFIT_VALID_MESSAGE_FIRST).await;
|
|
}
|
|
virtual_overseer
|
|
});
|
|
}
|
|
|
|
// Tests that local messages propagate to both dimensions.
|
|
#[test]
|
|
fn propagates_locally_generated_assignment_to_both_dimensions() {
|
|
let parent_hash = Hash::repeat_byte(0xFF);
|
|
let hash = Hash::repeat_byte(0xAA);
|
|
|
|
let peers = make_peers_and_authority_ids(100);
|
|
|
|
let _ = test_harness(State::default(), |mut virtual_overseer| async move {
|
|
let overseer = &mut virtual_overseer;
|
|
|
|
// Connect all peers.
|
|
for (peer, _) in &peers {
|
|
setup_peer_with_view(overseer, peer, ValidationVersion::V1, view![hash]).await;
|
|
}
|
|
|
|
// Set up a gossip topology.
|
|
setup_gossip_topology(
|
|
overseer,
|
|
make_gossip_topology(1, &peers, &[0, 10, 20, 30], &[50, 51, 52, 53]),
|
|
)
|
|
.await;
|
|
|
|
let expected_indices = [
|
|
// Both dimensions in the gossip topology
|
|
0, 10, 20, 30, 50, 51, 52, 53,
|
|
];
|
|
|
|
// new block `hash_a` with 1 candidates
|
|
let meta = BlockApprovalMeta {
|
|
hash,
|
|
parent_hash,
|
|
number: 1,
|
|
candidates: vec![Default::default(); 1],
|
|
slot: 1.into(),
|
|
session: 1,
|
|
};
|
|
|
|
let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]);
|
|
overseer_send(overseer, msg).await;
|
|
|
|
let validator_index = ValidatorIndex(0);
|
|
let candidate_index = 0u32;
|
|
|
|
// import an assignment and approval locally.
|
|
let cert = fake_assignment_cert(hash, validator_index);
|
|
let approval = IndirectSignedApprovalVote {
|
|
block_hash: hash,
|
|
candidate_index,
|
|
validator: validator_index,
|
|
signature: dummy_signature(),
|
|
};
|
|
|
|
overseer_send(
|
|
overseer,
|
|
ApprovalDistributionMessage::DistributeAssignment(cert.clone(), candidate_index),
|
|
)
|
|
.await;
|
|
|
|
overseer_send(overseer, ApprovalDistributionMessage::DistributeApproval(approval.clone()))
|
|
.await;
|
|
|
|
let assignments = vec![(cert.clone(), candidate_index)];
|
|
let approvals = vec![approval.clone()];
|
|
|
|
let assignment_sent_peers = assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage(
|
|
sent_peers,
|
|
Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution(
|
|
protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments)
|
|
))
|
|
)) => {
|
|
assert_eq!(sent_peers.len(), expected_indices.len() + 4);
|
|
for &i in &expected_indices {
|
|
assert!(
|
|
sent_peers.contains(&peers[i].0),
|
|
"Message not sent to expected peer {}",
|
|
i,
|
|
);
|
|
}
|
|
assert_eq!(sent_assignments, assignments);
|
|
sent_peers
|
|
}
|
|
);
|
|
|
|
assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage(
|
|
sent_peers,
|
|
Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution(
|
|
protocol_v1::ApprovalDistributionMessage::Approvals(sent_approvals)
|
|
))
|
|
)) => {
|
|
// Random sampling is reused from the assignment.
|
|
assert_eq!(sent_peers, assignment_sent_peers);
|
|
assert_eq!(sent_approvals, approvals);
|
|
}
|
|
);
|
|
|
|
assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent");
|
|
virtual_overseer
|
|
});
|
|
}
|
|
|
|
// Tests that messages propagate to the unshared dimension.
|
|
#[test]
|
|
fn propagates_assignments_along_unshared_dimension() {
|
|
let parent_hash = Hash::repeat_byte(0xFF);
|
|
let hash = Hash::repeat_byte(0xAA);
|
|
|
|
let peers = make_peers_and_authority_ids(100);
|
|
|
|
let _ = test_harness(state_without_reputation_delay(), |mut virtual_overseer| async move {
|
|
let overseer = &mut virtual_overseer;
|
|
|
|
// Connect all peers.
|
|
for (peer, _) in &peers {
|
|
setup_peer_with_view(overseer, peer, ValidationVersion::V1, view![hash]).await;
|
|
}
|
|
|
|
// Set up a gossip topology.
|
|
setup_gossip_topology(
|
|
overseer,
|
|
make_gossip_topology(1, &peers, &[0, 10, 20, 30], &[50, 51, 52, 53]),
|
|
)
|
|
.await;
|
|
|
|
// new block `hash_a` with 1 candidates
|
|
let meta = BlockApprovalMeta {
|
|
hash,
|
|
parent_hash,
|
|
number: 1,
|
|
candidates: vec![Default::default(); 1],
|
|
slot: 1.into(),
|
|
session: 1,
|
|
};
|
|
|
|
let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]);
|
|
overseer_send(overseer, msg).await;
|
|
|
|
// Test messages from X direction go to Y peers
|
|
{
|
|
let validator_index = ValidatorIndex(0);
|
|
let candidate_index = 0u32;
|
|
|
|
// import an assignment and approval locally.
|
|
let cert = fake_assignment_cert(hash, validator_index);
|
|
let assignments = vec![(cert.clone(), candidate_index)];
|
|
|
|
let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone());
|
|
|
|
// Issuer of the message is important, not the peer we receive from.
|
|
// 99 deliberately chosen because it's not in X or Y.
|
|
send_message_from_peer(overseer, &peers[99].0, Versioned::V1(msg)).await;
|
|
assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment(
|
|
_,
|
|
_,
|
|
tx,
|
|
)) => {
|
|
tx.send(AssignmentCheckResult::Accepted).unwrap();
|
|
}
|
|
);
|
|
expect_reputation_change(overseer, &peers[99].0, BENEFIT_VALID_MESSAGE_FIRST).await;
|
|
|
|
let expected_y = [50, 51, 52, 53];
|
|
|
|
assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage(
|
|
sent_peers,
|
|
Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution(
|
|
protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments)
|
|
))
|
|
)) => {
|
|
assert_eq!(sent_peers.len(), expected_y.len() + 4);
|
|
for &i in &expected_y {
|
|
assert!(
|
|
sent_peers.contains(&peers[i].0),
|
|
"Message not sent to expected peer {}",
|
|
i,
|
|
);
|
|
}
|
|
assert_eq!(sent_assignments, assignments);
|
|
}
|
|
);
|
|
};
|
|
|
|
// Test messages from X direction go to Y peers
|
|
{
|
|
let validator_index = ValidatorIndex(50);
|
|
let candidate_index = 0u32;
|
|
|
|
// import an assignment and approval locally.
|
|
let cert = fake_assignment_cert(hash, validator_index);
|
|
let assignments = vec![(cert.clone(), candidate_index)];
|
|
|
|
let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone());
|
|
|
|
// Issuer of the message is important, not the peer we receive from.
|
|
// 99 deliberately chosen because it's not in X or Y.
|
|
send_message_from_peer(overseer, &peers[99].0, Versioned::V1(msg)).await;
|
|
assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment(
|
|
_,
|
|
_,
|
|
tx,
|
|
)) => {
|
|
tx.send(AssignmentCheckResult::Accepted).unwrap();
|
|
}
|
|
);
|
|
expect_reputation_change(overseer, &peers[99].0, BENEFIT_VALID_MESSAGE_FIRST).await;
|
|
|
|
let expected_x = [0, 10, 20, 30];
|
|
|
|
assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage(
|
|
sent_peers,
|
|
Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution(
|
|
protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments)
|
|
))
|
|
)) => {
|
|
assert_eq!(sent_peers.len(), expected_x.len() + 4);
|
|
for &i in &expected_x {
|
|
assert!(
|
|
sent_peers.contains(&peers[i].0),
|
|
"Message not sent to expected peer {}",
|
|
i,
|
|
);
|
|
}
|
|
assert_eq!(sent_assignments, assignments);
|
|
}
|
|
);
|
|
};
|
|
|
|
assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent");
|
|
virtual_overseer
|
|
});
|
|
}
|
|
|
|
// tests that messages are propagated to necessary peers after they connect
|
|
#[test]
|
|
fn propagates_to_required_after_connect() {
|
|
let parent_hash = Hash::repeat_byte(0xFF);
|
|
let hash = Hash::repeat_byte(0xAA);
|
|
|
|
let peers = make_peers_and_authority_ids(100);
|
|
|
|
let _ = test_harness(State::default(), |mut virtual_overseer| async move {
|
|
let overseer = &mut virtual_overseer;
|
|
|
|
let omitted = [0, 10, 50, 51];
|
|
|
|
// Connect all peers except omitted.
|
|
for (i, (peer, _)) in peers.iter().enumerate() {
|
|
if !omitted.contains(&i) {
|
|
setup_peer_with_view(overseer, peer, ValidationVersion::V1, view![hash]).await;
|
|
}
|
|
}
|
|
|
|
// Set up a gossip topology.
|
|
setup_gossip_topology(
|
|
overseer,
|
|
make_gossip_topology(1, &peers, &[0, 10, 20, 30], &[50, 51, 52, 53]),
|
|
)
|
|
.await;
|
|
|
|
let expected_indices = [
|
|
// Both dimensions in the gossip topology, minus omitted.
|
|
20, 30, 52, 53,
|
|
];
|
|
|
|
// new block `hash_a` with 1 candidates
|
|
let meta = BlockApprovalMeta {
|
|
hash,
|
|
parent_hash,
|
|
number: 1,
|
|
candidates: vec![Default::default(); 1],
|
|
slot: 1.into(),
|
|
session: 1,
|
|
};
|
|
|
|
let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]);
|
|
overseer_send(overseer, msg).await;
|
|
|
|
let validator_index = ValidatorIndex(0);
|
|
let candidate_index = 0u32;
|
|
|
|
// import an assignment and approval locally.
|
|
let cert = fake_assignment_cert(hash, validator_index);
|
|
let approval = IndirectSignedApprovalVote {
|
|
block_hash: hash,
|
|
candidate_index,
|
|
validator: validator_index,
|
|
signature: dummy_signature(),
|
|
};
|
|
|
|
overseer_send(
|
|
overseer,
|
|
ApprovalDistributionMessage::DistributeAssignment(cert.clone(), candidate_index),
|
|
)
|
|
.await;
|
|
|
|
overseer_send(overseer, ApprovalDistributionMessage::DistributeApproval(approval.clone()))
|
|
.await;
|
|
|
|
let assignments = vec![(cert.clone(), candidate_index)];
|
|
let approvals = vec![approval.clone()];
|
|
|
|
let assignment_sent_peers = assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage(
|
|
sent_peers,
|
|
Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution(
|
|
protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments)
|
|
))
|
|
)) => {
|
|
assert_eq!(sent_peers.len(), expected_indices.len() + 4);
|
|
for &i in &expected_indices {
|
|
assert!(
|
|
sent_peers.contains(&peers[i].0),
|
|
"Message not sent to expected peer {}",
|
|
i,
|
|
);
|
|
}
|
|
assert_eq!(sent_assignments, assignments);
|
|
sent_peers
|
|
}
|
|
);
|
|
|
|
assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage(
|
|
sent_peers,
|
|
Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution(
|
|
protocol_v1::ApprovalDistributionMessage::Approvals(sent_approvals)
|
|
))
|
|
)) => {
|
|
// Random sampling is reused from the assignment.
|
|
assert_eq!(sent_peers, assignment_sent_peers);
|
|
assert_eq!(sent_approvals, approvals);
|
|
}
|
|
);
|
|
|
|
for i in omitted.iter().copied() {
|
|
setup_peer_with_view(overseer, &peers[i].0, ValidationVersion::V1, view![hash]).await;
|
|
|
|
assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage(
|
|
sent_peers,
|
|
Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution(
|
|
protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments)
|
|
))
|
|
)) => {
|
|
assert_eq!(sent_peers.len(), 1);
|
|
assert_eq!(&sent_peers[0], &peers[i].0);
|
|
assert_eq!(sent_assignments, assignments);
|
|
}
|
|
);
|
|
|
|
assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage(
|
|
sent_peers,
|
|
Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution(
|
|
protocol_v1::ApprovalDistributionMessage::Approvals(sent_approvals)
|
|
))
|
|
)) => {
|
|
assert_eq!(sent_peers.len(), 1);
|
|
assert_eq!(&sent_peers[0], &peers[i].0);
|
|
assert_eq!(sent_approvals, approvals);
|
|
}
|
|
);
|
|
}
|
|
|
|
assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent");
|
|
virtual_overseer
|
|
});
|
|
}
|
|
|
|
// test that new gossip topology triggers send of messages.
|
|
#[test]
|
|
fn sends_to_more_peers_after_getting_topology() {
|
|
let parent_hash = Hash::repeat_byte(0xFF);
|
|
let hash = Hash::repeat_byte(0xAA);
|
|
|
|
let peers = make_peers_and_authority_ids(100);
|
|
|
|
let _ = test_harness(State::default(), |mut virtual_overseer| async move {
|
|
let overseer = &mut virtual_overseer;
|
|
|
|
// Connect all peers except omitted.
|
|
for (peer, _) in &peers {
|
|
setup_peer_with_view(overseer, peer, ValidationVersion::V1, view![hash]).await;
|
|
}
|
|
|
|
// new block `hash_a` with 1 candidates
|
|
let meta = BlockApprovalMeta {
|
|
hash,
|
|
parent_hash,
|
|
number: 1,
|
|
candidates: vec![Default::default(); 1],
|
|
slot: 1.into(),
|
|
session: 1,
|
|
};
|
|
|
|
let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]);
|
|
overseer_send(overseer, msg).await;
|
|
|
|
let validator_index = ValidatorIndex(0);
|
|
let candidate_index = 0u32;
|
|
|
|
// import an assignment and approval locally.
|
|
let cert = fake_assignment_cert(hash, validator_index);
|
|
let approval = IndirectSignedApprovalVote {
|
|
block_hash: hash,
|
|
candidate_index,
|
|
validator: validator_index,
|
|
signature: dummy_signature(),
|
|
};
|
|
|
|
overseer_send(
|
|
overseer,
|
|
ApprovalDistributionMessage::DistributeAssignment(cert.clone(), candidate_index),
|
|
)
|
|
.await;
|
|
|
|
overseer_send(overseer, ApprovalDistributionMessage::DistributeApproval(approval.clone()))
|
|
.await;
|
|
|
|
let assignments = vec![(cert.clone(), candidate_index)];
|
|
let approvals = vec![approval.clone()];
|
|
|
|
let mut expected_indices = vec![0, 10, 20, 30, 50, 51, 52, 53];
|
|
let assignment_sent_peers = assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage(
|
|
sent_peers,
|
|
Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution(
|
|
protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments)
|
|
))
|
|
)) => {
|
|
// Only sends to random peers.
|
|
assert_eq!(sent_peers.len(), 4);
|
|
for peer in &sent_peers {
|
|
let i = peers.iter().position(|p| peer == &p.0).unwrap();
|
|
// Random gossip before topology can send to topology-targeted peers.
|
|
// Remove them from the expected indices so we don't expect
|
|
// them to get the messages again after the assignment.
|
|
expected_indices.retain(|&i2| i2 != i);
|
|
}
|
|
assert_eq!(sent_assignments, assignments);
|
|
sent_peers
|
|
}
|
|
);
|
|
|
|
assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage(
|
|
sent_peers,
|
|
Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution(
|
|
protocol_v1::ApprovalDistributionMessage::Approvals(sent_approvals)
|
|
))
|
|
)) => {
|
|
// Random sampling is reused from the assignment.
|
|
assert_eq!(sent_peers, assignment_sent_peers);
|
|
assert_eq!(sent_approvals, approvals);
|
|
}
|
|
);
|
|
|
|
// Set up a gossip topology.
|
|
setup_gossip_topology(
|
|
overseer,
|
|
make_gossip_topology(1, &peers, &[0, 10, 20, 30], &[50, 51, 52, 53]),
|
|
)
|
|
.await;
|
|
|
|
let mut expected_indices_assignments = expected_indices.clone();
|
|
let mut expected_indices_approvals = expected_indices.clone();
|
|
|
|
for _ in 0..expected_indices_assignments.len() {
|
|
assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage(
|
|
sent_peers,
|
|
Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution(
|
|
protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments)
|
|
))
|
|
)) => {
|
|
// Sends to all expected peers.
|
|
assert_eq!(sent_peers.len(), 1);
|
|
assert_eq!(sent_assignments, assignments);
|
|
|
|
let pos = expected_indices_assignments.iter()
|
|
.position(|i| &peers[*i].0 == &sent_peers[0])
|
|
.unwrap();
|
|
expected_indices_assignments.remove(pos);
|
|
}
|
|
);
|
|
}
|
|
|
|
for _ in 0..expected_indices_approvals.len() {
|
|
assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage(
|
|
sent_peers,
|
|
Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution(
|
|
protocol_v1::ApprovalDistributionMessage::Approvals(sent_approvals)
|
|
))
|
|
)) => {
|
|
// Sends to all expected peers.
|
|
assert_eq!(sent_peers.len(), 1);
|
|
assert_eq!(sent_approvals, approvals);
|
|
|
|
let pos = expected_indices_approvals.iter()
|
|
.position(|i| &peers[*i].0 == &sent_peers[0])
|
|
.unwrap();
|
|
|
|
expected_indices_approvals.remove(pos);
|
|
}
|
|
);
|
|
}
|
|
|
|
assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent");
|
|
virtual_overseer
|
|
});
|
|
}
|
|
|
|
// test aggression L1
|
|
#[test]
|
|
fn originator_aggression_l1() {
|
|
let parent_hash = Hash::repeat_byte(0xFF);
|
|
let hash = Hash::repeat_byte(0xAA);
|
|
|
|
let peers = make_peers_and_authority_ids(100);
|
|
|
|
let mut state = State::default();
|
|
state.aggression_config.resend_unfinalized_period = None;
|
|
let aggression_l1_threshold = state.aggression_config.l1_threshold.unwrap();
|
|
|
|
let _ = test_harness(state, |mut virtual_overseer| async move {
|
|
let overseer = &mut virtual_overseer;
|
|
|
|
// Connect all peers except omitted.
|
|
for (peer, _) in &peers {
|
|
setup_peer_with_view(overseer, peer, ValidationVersion::V1, view![hash]).await;
|
|
}
|
|
|
|
// new block `hash_a` with 1 candidates
|
|
let meta = BlockApprovalMeta {
|
|
hash,
|
|
parent_hash,
|
|
number: 1,
|
|
candidates: vec![Default::default(); 1],
|
|
slot: 1.into(),
|
|
session: 1,
|
|
};
|
|
|
|
let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]);
|
|
overseer_send(overseer, msg).await;
|
|
|
|
let validator_index = ValidatorIndex(0);
|
|
let candidate_index = 0u32;
|
|
|
|
// import an assignment and approval locally.
|
|
let cert = fake_assignment_cert(hash, validator_index);
|
|
let approval = IndirectSignedApprovalVote {
|
|
block_hash: hash,
|
|
candidate_index,
|
|
validator: validator_index,
|
|
signature: dummy_signature(),
|
|
};
|
|
|
|
// Set up a gossip topology.
|
|
setup_gossip_topology(
|
|
overseer,
|
|
make_gossip_topology(1, &peers, &[0, 10, 20, 30], &[50, 51, 52, 53]),
|
|
)
|
|
.await;
|
|
|
|
overseer_send(
|
|
overseer,
|
|
ApprovalDistributionMessage::DistributeAssignment(cert.clone(), candidate_index),
|
|
)
|
|
.await;
|
|
|
|
overseer_send(overseer, ApprovalDistributionMessage::DistributeApproval(approval.clone()))
|
|
.await;
|
|
|
|
let assignments = vec![(cert.clone(), candidate_index)];
|
|
let approvals = vec![approval.clone()];
|
|
|
|
let prev_sent_indices = assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage(
|
|
sent_peers,
|
|
Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution(
|
|
protocol_v1::ApprovalDistributionMessage::Assignments(_)
|
|
))
|
|
)) => {
|
|
sent_peers.into_iter()
|
|
.filter_map(|sp| peers.iter().position(|p| &p.0 == &sp))
|
|
.collect::<Vec<_>>()
|
|
}
|
|
);
|
|
|
|
assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage(
|
|
_,
|
|
Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution(
|
|
protocol_v1::ApprovalDistributionMessage::Approvals(_)
|
|
))
|
|
)) => { }
|
|
);
|
|
|
|
// Add blocks until aggression L1 is triggered.
|
|
{
|
|
let mut parent_hash = hash;
|
|
for level in 0..aggression_l1_threshold {
|
|
let number = 1 + level + 1; // first block had number 1
|
|
let hash = BlakeTwo256::hash_of(&(parent_hash, number));
|
|
let meta = BlockApprovalMeta {
|
|
hash,
|
|
parent_hash,
|
|
number,
|
|
candidates: vec![],
|
|
slot: (level as u64).into(),
|
|
session: 1,
|
|
};
|
|
|
|
let msg = ApprovalDistributionMessage::ApprovalCheckingLagUpdate(level + 1);
|
|
overseer_send(overseer, msg).await;
|
|
|
|
let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]);
|
|
overseer_send(overseer, msg).await;
|
|
|
|
parent_hash = hash;
|
|
}
|
|
}
|
|
|
|
let unsent_indices =
|
|
(0..peers.len()).filter(|i| !prev_sent_indices.contains(&i)).collect::<Vec<_>>();
|
|
|
|
for _ in 0..unsent_indices.len() {
|
|
assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage(
|
|
sent_peers,
|
|
Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution(
|
|
protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments)
|
|
))
|
|
)) => {
|
|
// Sends to all expected peers.
|
|
assert_eq!(sent_peers.len(), 1);
|
|
assert_eq!(sent_assignments, assignments);
|
|
|
|
assert!(unsent_indices.iter()
|
|
.any(|i| &peers[*i].0 == &sent_peers[0]));
|
|
}
|
|
);
|
|
}
|
|
|
|
for _ in 0..unsent_indices.len() {
|
|
assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage(
|
|
sent_peers,
|
|
Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution(
|
|
protocol_v1::ApprovalDistributionMessage::Approvals(sent_approvals)
|
|
))
|
|
)) => {
|
|
// Sends to all expected peers.
|
|
assert_eq!(sent_peers.len(), 1);
|
|
assert_eq!(sent_approvals, approvals);
|
|
|
|
assert!(unsent_indices.iter()
|
|
.any(|i| &peers[*i].0 == &sent_peers[0]));
|
|
}
|
|
);
|
|
}
|
|
|
|
assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent");
|
|
virtual_overseer
|
|
});
|
|
}
|
|
|
|
// test aggression L1
|
|
#[test]
|
|
fn non_originator_aggression_l1() {
|
|
let parent_hash = Hash::repeat_byte(0xFF);
|
|
let hash = Hash::repeat_byte(0xAA);
|
|
|
|
let peers = make_peers_and_authority_ids(100);
|
|
|
|
let mut state = state_without_reputation_delay();
|
|
state.aggression_config.resend_unfinalized_period = None;
|
|
let aggression_l1_threshold = state.aggression_config.l1_threshold.unwrap();
|
|
|
|
let _ = test_harness(state, |mut virtual_overseer| async move {
|
|
let overseer = &mut virtual_overseer;
|
|
|
|
// Connect all peers except omitted.
|
|
for (peer, _) in &peers {
|
|
setup_peer_with_view(overseer, peer, ValidationVersion::V1, view![hash]).await;
|
|
}
|
|
|
|
// new block `hash_a` with 1 candidates
|
|
let meta = BlockApprovalMeta {
|
|
hash,
|
|
parent_hash,
|
|
number: 1,
|
|
candidates: vec![Default::default(); 1],
|
|
slot: 1.into(),
|
|
session: 1,
|
|
};
|
|
|
|
let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]);
|
|
overseer_send(overseer, msg).await;
|
|
|
|
let validator_index = ValidatorIndex(0);
|
|
let candidate_index = 0u32;
|
|
|
|
// import an assignment and approval locally.
|
|
let cert = fake_assignment_cert(hash, validator_index);
|
|
|
|
// Set up a gossip topology.
|
|
setup_gossip_topology(
|
|
overseer,
|
|
make_gossip_topology(1, &peers, &[0, 10, 20, 30], &[50, 51, 52, 53]),
|
|
)
|
|
.await;
|
|
|
|
let assignments = vec![(cert.clone(), candidate_index)];
|
|
let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone());
|
|
|
|
// Issuer of the message is important, not the peer we receive from.
|
|
// 99 deliberately chosen because it's not in X or Y.
|
|
send_message_from_peer(overseer, &peers[99].0, Versioned::V1(msg)).await;
|
|
assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment(
|
|
_,
|
|
_,
|
|
tx,
|
|
)) => {
|
|
tx.send(AssignmentCheckResult::Accepted).unwrap();
|
|
}
|
|
);
|
|
|
|
expect_reputation_change(overseer, &peers[99].0, BENEFIT_VALID_MESSAGE_FIRST).await;
|
|
|
|
assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage(
|
|
_,
|
|
Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution(
|
|
protocol_v1::ApprovalDistributionMessage::Assignments(_)
|
|
))
|
|
)) => { }
|
|
);
|
|
|
|
// Add blocks until aggression L1 is triggered.
|
|
{
|
|
let mut parent_hash = hash;
|
|
for level in 0..aggression_l1_threshold {
|
|
let number = 1 + level + 1; // first block had number 1
|
|
let hash = BlakeTwo256::hash_of(&(parent_hash, number));
|
|
let meta = BlockApprovalMeta {
|
|
hash,
|
|
parent_hash,
|
|
number,
|
|
candidates: vec![],
|
|
slot: (level as u64).into(),
|
|
session: 1,
|
|
};
|
|
|
|
let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]);
|
|
overseer_send(overseer, msg).await;
|
|
|
|
parent_hash = hash;
|
|
}
|
|
}
|
|
|
|
// No-op on non-originator
|
|
|
|
assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent");
|
|
virtual_overseer
|
|
});
|
|
}
|
|
|
|
// test aggression L2 on non-originator
|
|
#[test]
|
|
fn non_originator_aggression_l2() {
|
|
let parent_hash = Hash::repeat_byte(0xFF);
|
|
let hash = Hash::repeat_byte(0xAA);
|
|
|
|
let peers = make_peers_and_authority_ids(100);
|
|
|
|
let mut state = state_without_reputation_delay();
|
|
state.aggression_config.resend_unfinalized_period = None;
|
|
|
|
let aggression_l1_threshold = state.aggression_config.l1_threshold.unwrap();
|
|
let aggression_l2_threshold = state.aggression_config.l2_threshold.unwrap();
|
|
let _ = test_harness(state, |mut virtual_overseer| async move {
|
|
let overseer = &mut virtual_overseer;
|
|
|
|
// Connect all peers except omitted.
|
|
for (peer, _) in &peers {
|
|
setup_peer_with_view(overseer, peer, ValidationVersion::V1, view![hash]).await;
|
|
}
|
|
|
|
// new block `hash_a` with 1 candidates
|
|
let meta = BlockApprovalMeta {
|
|
hash,
|
|
parent_hash,
|
|
number: 1,
|
|
candidates: vec![Default::default(); 1],
|
|
slot: 1.into(),
|
|
session: 1,
|
|
};
|
|
|
|
let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]);
|
|
overseer_send(overseer, msg).await;
|
|
|
|
let validator_index = ValidatorIndex(0);
|
|
let candidate_index = 0u32;
|
|
|
|
// import an assignment and approval locally.
|
|
let cert = fake_assignment_cert(hash, validator_index);
|
|
|
|
// Set up a gossip topology.
|
|
setup_gossip_topology(
|
|
overseer,
|
|
make_gossip_topology(1, &peers, &[0, 10, 20, 30], &[50, 51, 52, 53]),
|
|
)
|
|
.await;
|
|
|
|
let assignments = vec![(cert.clone(), candidate_index)];
|
|
let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone());
|
|
|
|
// Issuer of the message is important, not the peer we receive from.
|
|
// 99 deliberately chosen because it's not in X or Y.
|
|
send_message_from_peer(overseer, &peers[99].0, Versioned::V1(msg)).await;
|
|
assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment(
|
|
_,
|
|
_,
|
|
tx,
|
|
)) => {
|
|
tx.send(AssignmentCheckResult::Accepted).unwrap();
|
|
}
|
|
);
|
|
|
|
expect_reputation_change(overseer, &peers[99].0, BENEFIT_VALID_MESSAGE_FIRST).await;
|
|
|
|
let prev_sent_indices = assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage(
|
|
sent_peers,
|
|
Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution(
|
|
protocol_v1::ApprovalDistributionMessage::Assignments(_)
|
|
))
|
|
)) => {
|
|
sent_peers.into_iter()
|
|
.filter_map(|sp| peers.iter().position(|p| &p.0 == &sp))
|
|
.collect::<Vec<_>>()
|
|
}
|
|
);
|
|
|
|
// Add blocks until aggression L1 is triggered.
|
|
let chain_head = {
|
|
let mut parent_hash = hash;
|
|
for level in 0..aggression_l1_threshold {
|
|
let number = 1 + level + 1; // first block had number 1
|
|
let hash = BlakeTwo256::hash_of(&(parent_hash, number));
|
|
let meta = BlockApprovalMeta {
|
|
hash,
|
|
parent_hash,
|
|
number,
|
|
candidates: vec![],
|
|
slot: (level as u64).into(),
|
|
session: 1,
|
|
};
|
|
|
|
let msg = ApprovalDistributionMessage::ApprovalCheckingLagUpdate(level + 1);
|
|
overseer_send(overseer, msg).await;
|
|
|
|
let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]);
|
|
overseer_send(overseer, msg).await;
|
|
|
|
parent_hash = hash;
|
|
}
|
|
|
|
parent_hash
|
|
};
|
|
|
|
// No-op on non-originator
|
|
|
|
// Add blocks until aggression L2 is triggered.
|
|
{
|
|
let mut parent_hash = chain_head;
|
|
for level in 0..aggression_l2_threshold - aggression_l1_threshold {
|
|
let number = aggression_l1_threshold + level + 1 + 1; // first block had number 1
|
|
let hash = BlakeTwo256::hash_of(&(parent_hash, number));
|
|
let meta = BlockApprovalMeta {
|
|
hash,
|
|
parent_hash,
|
|
number,
|
|
candidates: vec![],
|
|
slot: (level as u64).into(),
|
|
session: 1,
|
|
};
|
|
|
|
let msg = ApprovalDistributionMessage::ApprovalCheckingLagUpdate(
|
|
aggression_l1_threshold + level + 1,
|
|
);
|
|
overseer_send(overseer, msg).await;
|
|
let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]);
|
|
overseer_send(overseer, msg).await;
|
|
|
|
parent_hash = hash;
|
|
}
|
|
}
|
|
|
|
// XY dimension - previously sent.
|
|
let unsent_indices = [0, 10, 20, 30, 50, 51, 52, 53]
|
|
.iter()
|
|
.cloned()
|
|
.filter(|i| !prev_sent_indices.contains(&i))
|
|
.collect::<Vec<_>>();
|
|
|
|
for _ in 0..unsent_indices.len() {
|
|
assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage(
|
|
sent_peers,
|
|
Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution(
|
|
protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments)
|
|
))
|
|
)) => {
|
|
// Sends to all expected peers.
|
|
assert_eq!(sent_peers.len(), 1);
|
|
assert_eq!(sent_assignments, assignments);
|
|
|
|
assert!(unsent_indices.iter()
|
|
.any(|i| &peers[*i].0 == &sent_peers[0]));
|
|
}
|
|
);
|
|
}
|
|
|
|
assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent");
|
|
virtual_overseer
|
|
});
|
|
}
|
|
|
|
// Tests that messages propagate to the unshared dimension.
|
|
#[test]
|
|
fn resends_messages_periodically() {
|
|
let parent_hash = Hash::repeat_byte(0xFF);
|
|
let hash = Hash::repeat_byte(0xAA);
|
|
|
|
let peers = make_peers_and_authority_ids(100);
|
|
|
|
let mut state = state_without_reputation_delay();
|
|
state.aggression_config.l1_threshold = None;
|
|
state.aggression_config.l2_threshold = None;
|
|
state.aggression_config.resend_unfinalized_period = Some(2);
|
|
let _ = test_harness(state, |mut virtual_overseer| async move {
|
|
let overseer = &mut virtual_overseer;
|
|
|
|
// Connect all peers.
|
|
for (peer, _) in &peers {
|
|
setup_peer_with_view(overseer, peer, ValidationVersion::V1, view![hash]).await;
|
|
}
|
|
|
|
// Set up a gossip topology.
|
|
setup_gossip_topology(
|
|
overseer,
|
|
make_gossip_topology(1, &peers, &[0, 10, 20, 30], &[50, 51, 52, 53]),
|
|
)
|
|
.await;
|
|
|
|
// new block `hash_a` with 1 candidates
|
|
let meta = BlockApprovalMeta {
|
|
hash,
|
|
parent_hash,
|
|
number: 1,
|
|
candidates: vec![Default::default(); 1],
|
|
slot: 1.into(),
|
|
session: 1,
|
|
};
|
|
|
|
let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]);
|
|
overseer_send(overseer, msg).await;
|
|
|
|
let validator_index = ValidatorIndex(0);
|
|
let candidate_index = 0u32;
|
|
|
|
// import an assignment and approval locally.
|
|
let cert = fake_assignment_cert(hash, validator_index);
|
|
let assignments = vec![(cert.clone(), candidate_index)];
|
|
|
|
{
|
|
let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone());
|
|
|
|
// Issuer of the message is important, not the peer we receive from.
|
|
// 99 deliberately chosen because it's not in X or Y.
|
|
send_message_from_peer(overseer, &peers[99].0, Versioned::V1(msg)).await;
|
|
assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment(
|
|
_,
|
|
_,
|
|
tx,
|
|
)) => {
|
|
tx.send(AssignmentCheckResult::Accepted).unwrap();
|
|
}
|
|
);
|
|
expect_reputation_change(overseer, &peers[99].0, BENEFIT_VALID_MESSAGE_FIRST).await;
|
|
|
|
let expected_y = [50, 51, 52, 53];
|
|
|
|
assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage(
|
|
sent_peers,
|
|
Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution(
|
|
protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments)
|
|
))
|
|
)) => {
|
|
assert_eq!(sent_peers.len(), expected_y.len() + 4);
|
|
for &i in &expected_y {
|
|
assert!(
|
|
sent_peers.contains(&peers[i].0),
|
|
"Message not sent to expected peer {}",
|
|
i,
|
|
);
|
|
}
|
|
assert_eq!(sent_assignments, assignments);
|
|
}
|
|
);
|
|
};
|
|
|
|
let mut number = 1;
|
|
for _ in 0..10 {
|
|
// Add blocks until resend is done.
|
|
{
|
|
let mut parent_hash = hash;
|
|
for level in 0..2 {
|
|
number = number + 1;
|
|
let hash = BlakeTwo256::hash_of(&(parent_hash, number));
|
|
let meta = BlockApprovalMeta {
|
|
hash,
|
|
parent_hash,
|
|
number,
|
|
candidates: vec![],
|
|
slot: (level as u64).into(),
|
|
session: 1,
|
|
};
|
|
|
|
let msg = ApprovalDistributionMessage::ApprovalCheckingLagUpdate(2);
|
|
overseer_send(overseer, msg).await;
|
|
let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]);
|
|
overseer_send(overseer, msg).await;
|
|
|
|
parent_hash = hash;
|
|
}
|
|
}
|
|
|
|
let mut expected_y = vec![50, 51, 52, 53];
|
|
|
|
// Expect messages sent only to topology peers, one by one.
|
|
for _ in 0..expected_y.len() {
|
|
assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage(
|
|
sent_peers,
|
|
Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution(
|
|
protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments)
|
|
))
|
|
)) => {
|
|
assert_eq!(sent_peers.len(), 1);
|
|
let expected_pos = expected_y.iter()
|
|
.position(|&i| &peers[i].0 == &sent_peers[0])
|
|
.unwrap();
|
|
|
|
expected_y.remove(expected_pos);
|
|
assert_eq!(sent_assignments, assignments);
|
|
}
|
|
);
|
|
}
|
|
}
|
|
|
|
assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent");
|
|
virtual_overseer
|
|
});
|
|
}
|
|
|
|
/// Tests that peers correctly receive versioned messages.
|
|
#[test]
|
|
fn import_versioned_approval() {
|
|
let peer_a = PeerId::random();
|
|
let peer_b = PeerId::random();
|
|
let peer_c = PeerId::random();
|
|
let parent_hash = Hash::repeat_byte(0xFF);
|
|
let hash = Hash::repeat_byte(0xAA);
|
|
|
|
let state = state_without_reputation_delay();
|
|
let _ = test_harness(state, |mut virtual_overseer| async move {
|
|
let overseer = &mut virtual_overseer;
|
|
// All peers are aware of relay parent.
|
|
setup_peer_with_view(overseer, &peer_a, ValidationVersion::VStaging, view![hash]).await;
|
|
setup_peer_with_view(overseer, &peer_b, ValidationVersion::V1, view![hash]).await;
|
|
setup_peer_with_view(overseer, &peer_c, ValidationVersion::VStaging, view![hash]).await;
|
|
|
|
// new block `hash_a` with 1 candidates
|
|
let meta = BlockApprovalMeta {
|
|
hash,
|
|
parent_hash,
|
|
number: 1,
|
|
candidates: vec![Default::default(); 1],
|
|
slot: 1.into(),
|
|
session: 1,
|
|
};
|
|
let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]);
|
|
overseer_send(overseer, msg).await;
|
|
|
|
// import an assignment related to `hash` locally
|
|
let validator_index = ValidatorIndex(0);
|
|
let candidate_index = 0u32;
|
|
let cert = fake_assignment_cert(hash, validator_index);
|
|
overseer_send(
|
|
overseer,
|
|
ApprovalDistributionMessage::DistributeAssignment(cert, candidate_index),
|
|
)
|
|
.await;
|
|
|
|
assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage(
|
|
peers,
|
|
Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution(
|
|
protocol_v1::ApprovalDistributionMessage::Assignments(assignments)
|
|
))
|
|
)) => {
|
|
assert_eq!(peers, vec![peer_b]);
|
|
assert_eq!(assignments.len(), 1);
|
|
}
|
|
);
|
|
|
|
assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage(
|
|
peers,
|
|
Versioned::VStaging(protocol_vstaging::ValidationProtocol::ApprovalDistribution(
|
|
protocol_vstaging::ApprovalDistributionMessage::Assignments(assignments)
|
|
))
|
|
)) => {
|
|
assert_eq!(peers.len(), 2);
|
|
assert!(peers.contains(&peer_a));
|
|
assert!(peers.contains(&peer_c));
|
|
|
|
assert_eq!(assignments.len(), 1);
|
|
}
|
|
);
|
|
|
|
// send the an approval from peer_a
|
|
let approval = IndirectSignedApprovalVote {
|
|
block_hash: hash,
|
|
candidate_index,
|
|
validator: validator_index,
|
|
signature: dummy_signature(),
|
|
};
|
|
let msg = protocol_vstaging::ApprovalDistributionMessage::Approvals(vec![approval.clone()]);
|
|
send_message_from_peer(overseer, &peer_a, Versioned::VStaging(msg)).await;
|
|
|
|
assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportApproval(
|
|
vote,
|
|
tx,
|
|
)) => {
|
|
assert_eq!(vote, approval);
|
|
tx.send(ApprovalCheckResult::Accepted).unwrap();
|
|
}
|
|
);
|
|
|
|
expect_reputation_change(overseer, &peer_a, BENEFIT_VALID_MESSAGE_FIRST).await;
|
|
|
|
// Peers b and c receive versioned approval messages.
|
|
assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage(
|
|
peers,
|
|
Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution(
|
|
protocol_v1::ApprovalDistributionMessage::Approvals(approvals)
|
|
))
|
|
)) => {
|
|
assert_eq!(peers, vec![peer_b]);
|
|
assert_eq!(approvals.len(), 1);
|
|
}
|
|
);
|
|
assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage(
|
|
peers,
|
|
Versioned::VStaging(protocol_vstaging::ValidationProtocol::ApprovalDistribution(
|
|
protocol_vstaging::ApprovalDistributionMessage::Approvals(approvals)
|
|
))
|
|
)) => {
|
|
assert_eq!(peers, vec![peer_c]);
|
|
assert_eq!(approvals.len(), 1);
|
|
}
|
|
);
|
|
virtual_overseer
|
|
});
|
|
}
|
|
|
|
fn batch_test_round(message_count: usize) {
|
|
use polkadot_node_subsystem::SubsystemContext;
|
|
let pool = sp_core::testing::TaskExecutor::new();
|
|
let mut state = State::default();
|
|
|
|
let (mut context, mut virtual_overseer) = test_helpers::make_subsystem_context(pool.clone());
|
|
let subsystem = ApprovalDistribution::new(Default::default());
|
|
let mut rng = rand_chacha::ChaCha12Rng::seed_from_u64(12345);
|
|
let mut sender = context.sender().clone();
|
|
let subsystem =
|
|
subsystem.run_inner(context, &mut state, REPUTATION_CHANGE_TEST_INTERVAL, &mut rng);
|
|
|
|
let test_fut = async move {
|
|
let overseer = &mut virtual_overseer;
|
|
let validators = 0..message_count;
|
|
let assignments: Vec<_> = validators
|
|
.clone()
|
|
.map(|index| (fake_assignment_cert(Hash::zero(), ValidatorIndex(index as u32)), 0))
|
|
.collect();
|
|
|
|
let approvals: Vec<_> = validators
|
|
.map(|index| IndirectSignedApprovalVote {
|
|
block_hash: Hash::zero(),
|
|
candidate_index: 0,
|
|
validator: ValidatorIndex(index as u32),
|
|
signature: dummy_signature(),
|
|
})
|
|
.collect();
|
|
|
|
let peer = PeerId::random();
|
|
send_assignments_batched(&mut sender, assignments.clone(), peer, ValidationVersion::V1)
|
|
.await;
|
|
send_approvals_batched(&mut sender, approvals.clone(), peer, ValidationVersion::V1).await;
|
|
|
|
// Check expected assignments batches.
|
|
for assignment_index in (0..assignments.len()).step_by(super::MAX_ASSIGNMENT_BATCH_SIZE) {
|
|
assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage(
|
|
peers,
|
|
Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution(
|
|
protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments)
|
|
))
|
|
)) => {
|
|
// Last batch should cover all remaining messages.
|
|
if sent_assignments.len() < super::MAX_ASSIGNMENT_BATCH_SIZE {
|
|
assert_eq!(sent_assignments.len() + assignment_index, assignments.len());
|
|
} else {
|
|
assert_eq!(sent_assignments.len(), super::MAX_ASSIGNMENT_BATCH_SIZE);
|
|
}
|
|
|
|
assert_eq!(peers.len(), 1);
|
|
|
|
for (message_index, assignment) in sent_assignments.iter().enumerate() {
|
|
assert_eq!(assignment.0, assignments[assignment_index + message_index].0);
|
|
assert_eq!(assignment.1, 0);
|
|
}
|
|
}
|
|
);
|
|
}
|
|
|
|
// Check approval vote batching.
|
|
for approval_index in (0..approvals.len()).step_by(super::MAX_APPROVAL_BATCH_SIZE) {
|
|
assert_matches!(
|
|
overseer_recv(overseer).await,
|
|
AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage(
|
|
peers,
|
|
Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution(
|
|
protocol_v1::ApprovalDistributionMessage::Approvals(sent_approvals)
|
|
))
|
|
)) => {
|
|
// Last batch should cover all remaining messages.
|
|
if sent_approvals.len() < super::MAX_APPROVAL_BATCH_SIZE {
|
|
assert_eq!(sent_approvals.len() + approval_index, approvals.len());
|
|
} else {
|
|
assert_eq!(sent_approvals.len(), super::MAX_APPROVAL_BATCH_SIZE);
|
|
}
|
|
|
|
assert_eq!(peers.len(), 1);
|
|
|
|
for (message_index, approval) in sent_approvals.iter().enumerate() {
|
|
assert_eq!(approval, &approvals[approval_index + message_index]);
|
|
}
|
|
}
|
|
);
|
|
}
|
|
virtual_overseer
|
|
};
|
|
|
|
futures::pin_mut!(test_fut);
|
|
futures::pin_mut!(subsystem);
|
|
|
|
executor::block_on(future::join(
|
|
async move {
|
|
let mut overseer = test_fut.await;
|
|
overseer
|
|
.send(FromOrchestra::Signal(OverseerSignal::Conclude))
|
|
.timeout(TIMEOUT)
|
|
.await
|
|
.expect("Conclude send timeout");
|
|
},
|
|
subsystem,
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn batch_sending_1_msg() {
|
|
batch_test_round(1);
|
|
}
|
|
|
|
#[test]
|
|
fn batch_sending_exactly_one_batch() {
|
|
batch_test_round(super::MAX_APPROVAL_BATCH_SIZE);
|
|
batch_test_round(super::MAX_ASSIGNMENT_BATCH_SIZE);
|
|
}
|
|
|
|
#[test]
|
|
fn batch_sending_partial_batch() {
|
|
batch_test_round(super::MAX_APPROVAL_BATCH_SIZE * 2 + 4);
|
|
batch_test_round(super::MAX_ASSIGNMENT_BATCH_SIZE * 2 + 4);
|
|
}
|
|
|
|
#[test]
|
|
fn batch_sending_multiple_same_len() {
|
|
batch_test_round(super::MAX_APPROVAL_BATCH_SIZE * 10);
|
|
batch_test_round(super::MAX_ASSIGNMENT_BATCH_SIZE * 10);
|
|
}
|
|
|
|
#[test]
|
|
fn batch_sending_half_batch() {
|
|
batch_test_round(super::MAX_APPROVAL_BATCH_SIZE / 2);
|
|
batch_test_round(super::MAX_ASSIGNMENT_BATCH_SIZE / 2);
|
|
}
|
|
|
|
#[test]
|
|
#[should_panic]
|
|
fn const_batch_size_panics_if_zero() {
|
|
crate::ensure_size_not_zero(0);
|
|
}
|
|
|
|
#[test]
|
|
fn const_ensure_size_not_zero() {
|
|
crate::ensure_size_not_zero(super::MAX_ASSIGNMENT_BATCH_SIZE);
|
|
crate::ensure_size_not_zero(super::MAX_APPROVAL_BATCH_SIZE);
|
|
}
|