From b3532393b8f6ffda28e0579c2ab07eea84e2d572 Mon Sep 17 00:00:00 2001 From: "Mattia L.V. Bradascio" <28816406+bredamatt@users.noreply.github.com> Date: Thu, 13 Oct 2022 10:48:48 +0100 Subject: [PATCH] Malus: add disputed block percentage (#6100) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Malus: add disputed block percentage * Bump clap to support value_parser with range * Add rand crate and use Bernoulli and Distribution * Add conditional logic based on sampled value from Bernoulli distribution * Add SuggestGarbageCandidateOptions struct * Cleanup tests * * Replace unwrap with expect and meaningful error message * * Remove Inner * Remove intercept_outgoing * * Rename sampled variable * Move info! logs to include candidate hash of malicious candidate * * Add percentage option to dispute_ancestor * * Support static probability for `ReplaceValidationResult` proxy * Update some comments and docs * * Add `--percentage` to `back-garbage-candidate` variant * Rename structs for consistency * * Add probabilistic behavior to `dispute-ancestor` variant * Add probabilistic behavior to `back-garbage-candidate` variant * Rename structs in dispute variant * * More descriptive comments * * cargo +nightly fmt --all * * Move Bernoulli distributrion to ReplaceValidationResult constructor * Rename random_bool to behave_maliciously * * Remove dangling comment * * Consistent log * * Add logs based on sampled value * * Cargo +nightly fmt --all * * Remove unused percentage attributed after moving Bernoulli to constructor * Squashed commit of the following: commit e4361b6d80d64e65d74a8e31a3d1cdc1948ee8a4 Author: Chris Sosnin <48099298+slumber@users.noreply.github.com> Date: Mon Oct 10 10:06:44 2022 +0400 Fix flaky test (#6131) * Split test + decrease test timeout * fmt * spellcheck commit f614752c22270c3d948aec08a234637e1834269c Author: girazoki <gorka.irazoki@gmail.com> Date: Mon Oct 10 06:39:30 2022 +0200 Add event to asset claim (#6029) commit 71197818a4c40c7fdd79e233f8608e8f513e3bfc Author: Leszek Wiesner <leszek@jsgenesis.com> Date: Mon Oct 10 00:23:54 2022 +0200 Companion for 12109 (#5929) * Update following `pallet-vesting` configurable `WithdrawReasons` * Update lib.rs * Update lib.rs * Update lib.rs * update lockfile for {"substrate"} * fix warning Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com> Co-authored-by: parity-processbot <> commit 607350449c553cd6e17c4e0bca2b553cd8648ca6 Author: Bastian Köcher <info@kchr.de> Date: Fri Oct 7 13:40:40 2022 +0200 Companion for upgrading pin-project (#6118) * Companion for upgrading pin-project This will remove some warnings with the latest rustc nightly/stable. https://github.com/paritytech/substrate/pull/12426 * update lockfile for {"substrate"} Co-authored-by: parity-processbot <> commit c8151aed3c72293a7d93bec140b376a36658cfcb Author: Sergej Sakac <73715684+Szegoo@users.noreply.github.com> Date: Thu Oct 6 19:20:58 2022 +0200 Maximum value for `MultiplierUpdate` (#6021) * update multiplier * fix * update lockfile for {"substrate"} * fmt * fix typo Co-authored-by: parity-processbot <> commit 8d1c16dc0d899e4e47bdcc4d4d9658c0628f37d7 Author: Adrian Catangiu <adrian@parity.io> Date: Thu Oct 6 12:58:39 2022 +0300 service: use MmrRootProvider as custom BEEFY payload provider (companion for 12428) (#6112) * service: use MmrRootProvider as custom BEEFY payload provider * update lockfile for {"substrate"} Co-authored-by: parity-processbot <> commit 910e21847f4d75efbd978c860f3f1b8c946ea5fa Author: Branislav Kontur <bkontur@gmail.com> Date: Thu Oct 6 10:03:34 2022 +0200 Skip `unexpected metric type` * Dump more info for `unexpected metric type` * Skip `unexpected metric type` commit af6a5cd96ada80c574346f69f2720bf9a6a556a4 Author: Andronik <write@reusable.software> Date: Thu Oct 6 00:36:51 2022 +0200 update kvdb & co (#6111) * toml changes * REVERTME: patch * adapt parachains db interface * fix Cargo.toml patch after master rebase * fix av-store * fix chain-selection * fix parachains-db? * Revert "fix Cargo.toml patch after master rebase" This reverts commit 3afcbf033c86027b3f2b909d83ec703591bdd287. * Revert "REVERTME: patch" This reverts commit 464b717cf4142d3d09c3d77b83700b632d8c5f54. * Use `Ok` imported from prelude Co-authored-by: Bastian Köcher <info@kchr.de> * update lockfile for {"substrate"} * Revert "update lockfile for {"substrate"}" This reverts commit fdc623de226f7645741b86c4b1a7d030fed2172d. * cargo update -p sp-io Co-authored-by: Bastian Köcher <info@kchr.de> Co-authored-by: parity-processbot <> commit 9a3cf4cd1f3c68b620b8a2ad4c3c22418b9b83a7 Author: Gavin Wood <gavin@parity.io> Date: Wed Oct 5 22:17:59 2022 +0100 Companion for #11649: Bound uses of `Call` (#5729) * Fixes * Clear out old weights Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Resolve merges Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Fix weight traits Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * polkadot runtime: Clippy Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * rococo runtime: update pallet configs Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Add preimage migration Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Add all migrations Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Democracy is not on Westend Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * [Migration] Refund stored multisig calls (#6075) * Add Preimages to referenda config Needed since Gov V2 just merged. Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Update weights Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Add multisig migration to Westend+Rococo Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Fix Executive syntax Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Bump Substrate Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> Co-authored-by: parity-processbot <> Co-authored-by: Roman Useinov <roman.useinov@gmail.com> commit 4aea71a95f6b67a640057a6f44e85415a605582f Author: Alexander Theißen <alex.theissen@me.com> Date: Wed Oct 5 15:15:07 2022 +0200 Pass through `runtime-benchmark` feature (#6110) commit 42c043d7f4e2ae5dbeb162c4eb7d4df16dde6910 Author: Keith Yeung <kungfukeith11@gmail.com> Date: Wed Oct 5 17:47:15 2022 +0800 Properly migrate weights to v2 (#6091) * Create migration for config pallet * Use XcmWeight in XCM pallet extrinsics * Link to PR in doc comment * cargo fmt * Fix tests * Fix tests * Remove unused import * Update runtime/parachains/src/configuration/migration.rs Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Add missing on_runtime_upgrade implementation * Use new migration API * cargo fmt * Fix log message Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> commit b13e07bc47073c2972b1d1d82321cb359803874c Author: Chris Sosnin <48099298+slumber@users.noreply.github.com> Date: Wed Oct 5 11:48:50 2022 +0400 Buffered connection management for collator-protocol (#6022) * Extract metrics into a separate module * Introduce validators buffer * Integrate buffer into the subsystem * Only reconnect on new advertisements * Test * comma * doc comment * Make capacity buffer compile time non-zero * Add doc comments * nits * remove derives * review * better naming * check timeout * Extract interval stream into lib * Ensure collator disconnects after timeout * spellcheck * rename buf * Remove double interval * Add a log on timeout * Cleanup buffer on timeout commit e0e836671f068bde4c3f356ab1dede8ca51e7b50 Author: Robert Klotzner <eskimor@users.noreply.github.com> Date: Tue Oct 4 18:47:52 2022 +0200 Add unknown words (#6105) commit 938bc96a2cfd881ad309f51e16294ed87813b998 Author: Robert Klotzner <eskimor@users.noreply.github.com> Date: Tue Oct 4 18:02:05 2022 +0200 Batch vote import in dispute-distribution (#5894) * Start work on batching in dispute-distribution. * Guide work. * More guide changes. Still very much WIP. * Finish guide changes. * Clarification * Adjust argument about slashing. * WIP: Add constants to receiver. * Maintain order of disputes. * dispute-distribuion sender Rate limit. * Cleanup * WIP: dispute-distribution receiver. - [ ] Rate limiting - [ ] Batching * WIP: Batching. * fmt * Update `PeerQueues` to maintain more invariants. * WIP: Batching. * Small cleanup * Batching logic. * Some integration work. * Finish. Missing: Tests * Typo. * Docs. * Report missing metric. * Doc pass. * Tests for waiting_queue. * Speed up some crypto by 10x. * Fix redundant import. * Add some tracing. * Better sender rate limit * Some tests. * Tests * Add logging to rate limiter * Update roadmap/implementers-guide/src/node/disputes/dispute-distribution.md Co-authored-by: Tsvetomir Dimitrov <tsvetomir@parity.io> * Update roadmap/implementers-guide/src/node/disputes/dispute-distribution.md Co-authored-by: Tsvetomir Dimitrov <tsvetomir@parity.io> * Update node/network/dispute-distribution/src/receiver/mod.rs Co-authored-by: Tsvetomir Dimitrov <tsvetomir@parity.io> * Review feedback. * Also log peer in log messages. * Fix indentation. * waker -> timer * Guide improvement. * Remove obsolete comment. * waker -> timer * Fix spell complaints. * Fix Cargo.lock Co-authored-by: Tsvetomir Dimitrov <tsvetomir@parity.io> commit a64cc4a8600dd7cf7839c170b5f795dc8a21a8ec Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue Oct 4 11:28:21 2022 +0000 Bump lru from 0.7.8 to 0.8.0 (#6060) * Bump lru from 0.7.8 to 0.8.0 Bumps [lru](https://github.com/jeromefroe/lru-rs) from 0.7.8 to 0.8.0. - [Release notes](https://github.com/jeromefroe/lru-rs/releases) - [Changelog](https://github.com/jeromefroe/lru-rs/blob/master/CHANGELOG.md) - [Commits](https://github.com/jeromefroe/lru-rs/compare/0.7.8...0.8.0) --- updated-dependencies: - dependency-name: lru dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> * Change `LruCache` paramerter to `NonZeroUsize` * Change type of `session_cache_lru_size` to `NonZeroUsize` * Add expects instead of unwrap Co-authored-by: Bastian Köcher <info@kchr.de> * Use match to get rid of expects Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Sebastian Kunert <skunert49@gmail.com> Co-authored-by: Bastian Köcher <info@kchr.de> commit 7114a8cfcac07025db9815ef6819d4e2ddd5e9da Author: Andrei Sandu <54316454+sandreim@users.noreply.github.com> Date: Tue Oct 4 13:36:42 2022 +0300 Keep sessions in window for the full unfinalized chain (#6054) * Impl dynamic window size. Keep sessions for unfinalized chain Signed-off-by: Andrei Sandu <andrei-mihail@parity.io> * feedback Signed-off-by: Andrei Sandu <andrei-mihail@parity.io> * Stretch also in contructor plus tests Signed-off-by: Andrei Sandu <andrei-mihail@parity.io> * review feedback Signed-off-by: Andrei Sandu <andrei-mihail@parity.io> * fix approval-voting tests Signed-off-by: Andrei Sandu <andrei-mihail@parity.io> * grunting: dispute coordinator tests Signed-off-by: Andrei Sandu <andrei-mihail@parity.io> Signed-off-by: Andrei Sandu <andrei-mihail@parity.io> commit ab8f04f827b56ef1da8e0766b242c766cb0725b6 Author: Serban Iorga <serban@parity.io> Date: Tue Oct 4 12:25:48 2022 +0300 Companion for BEEFY: Simplify hashing for pallet-beefy-mmr (#6098) * beefy-mmr: Simplify hashing * update lockfile for {"substrate"} Co-authored-by: parity-processbot <> * Revert "Squashed commit of the following:" This reverts commit 5001fa5d1dcd366029d156f81c40b99ca29d8f77. * Companion for BEEFY: Simplify hashing for pallet-beefy-mmr (#6098) * beefy-mmr: Simplify hashing * update lockfile for {"substrate"} Co-authored-by: parity-processbot <> * Keep sessions in window for the full unfinalized chain (#6054) * Impl dynamic window size. Keep sessions for unfinalized chain Signed-off-by: Andrei Sandu <andrei-mihail@parity.io> * feedback Signed-off-by: Andrei Sandu <andrei-mihail@parity.io> * Stretch also in contructor plus tests Signed-off-by: Andrei Sandu <andrei-mihail@parity.io> * review feedback Signed-off-by: Andrei Sandu <andrei-mihail@parity.io> * fix approval-voting tests Signed-off-by: Andrei Sandu <andrei-mihail@parity.io> * grunting: dispute coordinator tests Signed-off-by: Andrei Sandu <andrei-mihail@parity.io> Signed-off-by: Andrei Sandu <andrei-mihail@parity.io> * Bump lru from 0.7.8 to 0.8.0 (#6060) * Bump lru from 0.7.8 to 0.8.0 Bumps [lru](https://github.com/jeromefroe/lru-rs) from 0.7.8 to 0.8.0. - [Release notes](https://github.com/jeromefroe/lru-rs/releases) - [Changelog](https://github.com/jeromefroe/lru-rs/blob/master/CHANGELOG.md) - [Commits](https://github.com/jeromefroe/lru-rs/compare/0.7.8...0.8.0) --- updated-dependencies: - dependency-name: lru dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> * Change `LruCache` paramerter to `NonZeroUsize` * Change type of `session_cache_lru_size` to `NonZeroUsize` * Add expects instead of unwrap Co-authored-by: Bastian Köcher <info@kchr.de> * Use match to get rid of expects Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Sebastian Kunert <skunert49@gmail.com> Co-authored-by: Bastian Köcher <info@kchr.de> * Batch vote import in dispute-distribution (#5894) * Start work on batching in dispute-distribution. * Guide work. * More guide changes. Still very much WIP. * Finish guide changes. * Clarification * Adjust argument about slashing. * WIP: Add constants to receiver. * Maintain order of disputes. * dispute-distribuion sender Rate limit. * Cleanup * WIP: dispute-distribution receiver. - [ ] Rate limiting - [ ] Batching * WIP: Batching. * fmt * Update `PeerQueues` to maintain more invariants. * WIP: Batching. * Small cleanup * Batching logic. * Some integration work. * Finish. Missing: Tests * Typo. * Docs. * Report missing metric. * Doc pass. * Tests for waiting_queue. * Speed up some crypto by 10x. * Fix redundant import. * Add some tracing. * Better sender rate limit * Some tests. * Tests * Add logging to rate limiter * Update roadmap/implementers-guide/src/node/disputes/dispute-distribution.md Co-authored-by: Tsvetomir Dimitrov <tsvetomir@parity.io> * Update roadmap/implementers-guide/src/node/disputes/dispute-distribution.md Co-authored-by: Tsvetomir Dimitrov <tsvetomir@parity.io> * Update node/network/dispute-distribution/src/receiver/mod.rs Co-authored-by: Tsvetomir Dimitrov <tsvetomir@parity.io> * Review feedback. * Also log peer in log messages. * Fix indentation. * waker -> timer * Guide improvement. * Remove obsolete comment. * waker -> timer * Fix spell complaints. * Fix Cargo.lock Co-authored-by: Tsvetomir Dimitrov <tsvetomir@parity.io> * Add unknown words (#6105) * Buffered connection management for collator-protocol (#6022) * Extract metrics into a separate module * Introduce validators buffer * Integrate buffer into the subsystem * Only reconnect on new advertisements * Test * comma * doc comment * Make capacity buffer compile time non-zero * Add doc comments * nits * remove derives * review * better naming * check timeout * Extract interval stream into lib * Ensure collator disconnects after timeout * spellcheck * rename buf * Remove double interval * Add a log on timeout * Cleanup buffer on timeout * Properly migrate weights to v2 (#6091) * Create migration for config pallet * Use XcmWeight in XCM pallet extrinsics * Link to PR in doc comment * cargo fmt * Fix tests * Fix tests * Remove unused import * Update runtime/parachains/src/configuration/migration.rs Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Add missing on_runtime_upgrade implementation * Use new migration API * cargo fmt * Fix log message Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Pass through `runtime-benchmark` feature (#6110) * Companion for #11649: Bound uses of `Call` (#5729) * Fixes * Clear out old weights Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Resolve merges Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Fix weight traits Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * polkadot runtime: Clippy Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * rococo runtime: update pallet configs Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Add preimage migration Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Add all migrations Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Democracy is not on Westend Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * [Migration] Refund stored multisig calls (#6075) * Add Preimages to referenda config Needed since Gov V2 just merged. Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Update weights Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Add multisig migration to Westend+Rococo Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Fix Executive syntax Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Bump Substrate Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> Co-authored-by: parity-processbot <> Co-authored-by: Roman Useinov <roman.useinov@gmail.com> * update kvdb & co (#6111) * toml changes * REVERTME: patch * adapt parachains db interface * fix Cargo.toml patch after master rebase * fix av-store * fix chain-selection * fix parachains-db? * Revert "fix Cargo.toml patch after master rebase" This reverts commit 3afcbf033c86027b3f2b909d83ec703591bdd287. * Revert "REVERTME: patch" This reverts commit 464b717cf4142d3d09c3d77b83700b632d8c5f54. * Use `Ok` imported from prelude Co-authored-by: Bastian Köcher <info@kchr.de> * update lockfile for {"substrate"} * Revert "update lockfile for {"substrate"}" This reverts commit fdc623de226f7645741b86c4b1a7d030fed2172d. * cargo update -p sp-io Co-authored-by: Bastian Köcher <info@kchr.de> Co-authored-by: parity-processbot <> * Skip `unexpected metric type` * Dump more info for `unexpected metric type` * Skip `unexpected metric type` * service: use MmrRootProvider as custom BEEFY payload provider (companion for 12428) (#6112) * service: use MmrRootProvider as custom BEEFY payload provider * update lockfile for {"substrate"} Co-authored-by: parity-processbot <> * Maximum value for `MultiplierUpdate` (#6021) * update multiplier * fix * update lockfile for {"substrate"} * fmt * fix typo Co-authored-by: parity-processbot <> * Companion for upgrading pin-project (#6118) * Companion for upgrading pin-project This will remove some warnings with the latest rustc nightly/stable. https://github.com/paritytech/substrate/pull/12426 * update lockfile for {"substrate"} Co-authored-by: parity-processbot <> * Companion for 12109 (#5929) * Update following `pallet-vesting` configurable `WithdrawReasons` * Update lib.rs * Update lib.rs * Update lib.rs * update lockfile for {"substrate"} * fix warning Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com> Co-authored-by: parity-processbot <> * Add event to asset claim (#6029) * Fix flaky test (#6131) * Split test + decrease test timeout * fmt * spellcheck * ci/guide: install mdbook-graphviz (#6119) * ci/guide: install mdbook-graphviz * install graphviz in build-implementers-guide * Update scripts/ci/gitlab/pipeline/build.yml Co-authored-by: Alexander Samusev <41779041+alvicsam@users.noreply.github.com> * Revert "Squashed commit of the following:" This reverts commit 5001fa5d1dcd366029d156f81c40b99ca29d8f77. * * Remove unused imports * * cargo +nightly fmt --all * Make tweaks based on PR comments * unit test related to gum formatting * cargo +nightly fmt --all * Resolve merge conflicts * cargo +nightly fmt --all * Fix tests so they use cli rather than cmd * CI unused import check fix * Move info! log to startup * make info log more comprehensible Signed-off-by: Andrei Sandu <andrei-mihail@parity.io> Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> Co-authored-by: Serban Iorga <serban@parity.io> Co-authored-by: Andrei Sandu <54316454+sandreim@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Sebastian Kunert <skunert49@gmail.com> Co-authored-by: Bastian Köcher <info@kchr.de> Co-authored-by: Robert Klotzner <eskimor@users.noreply.github.com> Co-authored-by: Tsvetomir Dimitrov <tsvetomir@parity.io> Co-authored-by: Chris Sosnin <48099298+slumber@users.noreply.github.com> Co-authored-by: Keith Yeung <kungfukeith11@gmail.com> Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> Co-authored-by: Alexander Theißen <alex.theissen@me.com> Co-authored-by: Gavin Wood <gavin@parity.io> Co-authored-by: Roman Useinov <roman.useinov@gmail.com> Co-authored-by: Andronik <write@reusable.software> Co-authored-by: Branislav Kontur <bkontur@gmail.com> Co-authored-by: Adrian Catangiu <adrian@parity.io> Co-authored-by: Sergej Sakac <73715684+Szegoo@users.noreply.github.com> Co-authored-by: Leszek Wiesner <leszek@jsgenesis.com> Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com> Co-authored-by: girazoki <gorka.irazoki@gmail.com> Co-authored-by: Alexander Samusev <41779041+alvicsam@users.noreply.github.com> --- polkadot/Cargo.lock | 19 +- polkadot/node/malus/Cargo.toml | 3 +- polkadot/node/malus/src/malus.rs | 105 +++++- .../src/variants/back_garbage_candidate.rs | 22 +- polkadot/node/malus/src/variants/common.rs | 177 ++++++++-- .../src/variants/dispute_valid_candidates.rs | 8 + polkadot/node/malus/src/variants/mod.rs | 4 +- .../src/variants/suggest_garbage_candidate.rs | 323 +++++++++--------- 8 files changed, 445 insertions(+), 216 deletions(-) diff --git a/polkadot/Cargo.lock b/polkadot/Cargo.lock index 278310281f5..ad6176d1acc 100644 --- a/polkadot/Cargo.lock +++ b/polkadot/Cargo.lock @@ -922,16 +922,16 @@ dependencies = [ [[package]] name = "clap" -version = "3.1.18" +version = "3.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2dbdf4bdacb33466e854ce889eee8dfd5729abf7ccd7664d0a2d60cd384440b" +checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750" dependencies = [ "atty", "bitflags", "clap_derive", "clap_lex", "indexmap", - "lazy_static", + "once_cell", "strsim", "termcolor", "textwrap", @@ -939,9 +939,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "3.1.18" +version = "3.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25320346e922cffe59c0bbc5410c8d8784509efb321488971081313cb1e1a33c" +checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" dependencies = [ "heck", "proc-macro-error", @@ -952,9 +952,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.2.0" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a37c35f1112dad5e6e0b1adaff798507497a18fceeb30cceb3bae7d1427b9213" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" dependencies = [ "os_str_bytes", ] @@ -7470,6 +7470,7 @@ dependencies = [ "polkadot-node-subsystem-types", "polkadot-node-subsystem-util", "polkadot-primitives", + "rand 0.8.5", "sp-core", "sp-keystore", "tracing-gum", @@ -11391,9 +11392,9 @@ dependencies = [ [[package]] name = "textwrap" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" +checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16" [[package]] name = "thiserror" diff --git a/polkadot/node/malus/Cargo.toml b/polkadot/node/malus/Cargo.toml index c32fce56e4c..9548857a03e 100644 --- a/polkadot/node/malus/Cargo.toml +++ b/polkadot/node/malus/Cargo.toml @@ -29,11 +29,12 @@ assert_matches = "1.5" async-trait = "0.1.57" sp-keystore = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } -clap = { version = "3.1", features = ["derive"] } +clap = { version = "3.2.21", features = ["derive"] } futures = "0.3.21" futures-timer = "3.0.2" gum = { package = "tracing-gum", path = "../gum/" } erasure = { package = "polkadot-erasure-coding", path = "../../erasure-coding" } +rand = "0.8.5" [features] default = [] diff --git a/polkadot/node/malus/src/malus.rs b/polkadot/node/malus/src/malus.rs index aa14b8e3d38..13e232198ea 100644 --- a/polkadot/node/malus/src/malus.rs +++ b/polkadot/node/malus/src/malus.rs @@ -18,7 +18,6 @@ use clap::Parser; use color_eyre::eyre; -use polkadot_cli::Cli; pub(crate) mod interceptor; pub(crate) mod shared; @@ -33,9 +32,9 @@ use variants::*; #[clap(rename_all = "kebab-case")] enum NemesisVariant { /// Suggest a candidate with an invalid proof of validity. - SuggestGarbageCandidate(Cli), + SuggestGarbageCandidate(SuggestGarbageCandidateOptions), /// Back a candidate with a specifically crafted proof of validity. - BackGarbageCandidate(Cli), + BackGarbageCandidate(BackGarbageCandidateOptions), /// Delayed disputing of ancestors that are perfectly fine. DisputeAncestor(DisputeAncestorOptions), @@ -62,16 +61,31 @@ impl MalusCli { fn launch(self) -> eyre::Result<()> { let finality_delay = self.finality_delay; match self.variant { - NemesisVariant::BackGarbageCandidate(cli) => - polkadot_cli::run_node(cli, BackGarbageCandidate, finality_delay)?, - NemesisVariant::SuggestGarbageCandidate(cli) => - polkadot_cli::run_node(cli, BackGarbageCandidateWrapper, finality_delay)?, + NemesisVariant::BackGarbageCandidate(opts) => { + let BackGarbageCandidateOptions { percentage, cli } = opts; + + polkadot_cli::run_node(cli, BackGarbageCandidates { percentage }, finality_delay)? + }, + NemesisVariant::SuggestGarbageCandidate(opts) => { + let SuggestGarbageCandidateOptions { percentage, cli } = opts; + + polkadot_cli::run_node( + cli, + SuggestGarbageCandidates { percentage }, + finality_delay, + )? + }, NemesisVariant::DisputeAncestor(opts) => { - let DisputeAncestorOptions { fake_validation, fake_validation_error, cli } = opts; + let DisputeAncestorOptions { + fake_validation, + fake_validation_error, + percentage, + cli, + } = opts; polkadot_cli::run_node( cli, - DisputeValidCandidates { fake_validation, fake_validation_error }, + DisputeValidCandidates { fake_validation, fake_validation_error, percentage }, finality_delay, )? }, @@ -129,4 +143,77 @@ mod tests { assert!(run.cli.run.base.bob); }); } + + #[test] + fn percentage_works_suggest_garbage() { + let cli = MalusCli::try_parse_from(IntoIterator::into_iter([ + "malus", + "suggest-garbage-candidate", + "--percentage", + "100", + "--bob", + ])) + .unwrap(); + assert_matches::assert_matches!(cli, MalusCli { + variant: NemesisVariant::SuggestGarbageCandidate(run), + .. + } => { + assert!(run.cli.run.base.bob); + }); + } + + #[test] + fn percentage_works_dispute_ancestor() { + let cli = MalusCli::try_parse_from(IntoIterator::into_iter([ + "malus", + "dispute-ancestor", + "--percentage", + "100", + "--bob", + ])) + .unwrap(); + assert_matches::assert_matches!(cli, MalusCli { + variant: NemesisVariant::DisputeAncestor(run), + .. + } => { + assert!(run.cli.run.base.bob); + }); + } + + #[test] + fn percentage_works_back_garbage() { + let cli = MalusCli::try_parse_from(IntoIterator::into_iter([ + "malus", + "back-garbage-candidate", + "--percentage", + "100", + "--bob", + ])) + .unwrap(); + assert_matches::assert_matches!(cli, MalusCli { + variant: NemesisVariant::BackGarbageCandidate(run), + .. + } => { + assert!(run.cli.run.base.bob); + }); + } + + #[test] + #[should_panic] + fn validate_range_for_percentage() { + let cli = MalusCli::try_parse_from(IntoIterator::into_iter([ + "malus", + "suggest-garbage-candidate", + "--percentage", + "101", + "--bob", + ])) + .unwrap(); + assert_matches::assert_matches!(cli, MalusCli { + variant: NemesisVariant::DisputeAncestor(run), + .. + } => { + assert!(run.cli.run.base.bob); + }); + } } diff --git a/polkadot/node/malus/src/variants/back_garbage_candidate.rs b/polkadot/node/malus/src/variants/back_garbage_candidate.rs index cf72776b5f2..b17b8bca588 100644 --- a/polkadot/node/malus/src/variants/back_garbage_candidate.rs +++ b/polkadot/node/malus/src/variants/back_garbage_candidate.rs @@ -25,6 +25,7 @@ use polkadot_cli::{ OverseerConnector, OverseerGen, OverseerGenArgs, OverseerHandle, ParachainHost, ProvideRuntimeApi, }, + Cli, }; use polkadot_node_subsystem::SpawnGlue; use sp_core::traits::SpawnNamed; @@ -36,11 +37,27 @@ use crate::{ use std::sync::Arc; +#[derive(Debug, clap::Parser)] +#[clap(rename_all = "kebab-case")] +#[allow(missing_docs)] +pub struct BackGarbageCandidateOptions { + /// Determines the percentage of garbage candidates that should be backed. + /// Defaults to 100% of garbage candidates being backed. + #[clap(short, long, ignore_case = true, default_value_t = 100, value_parser = clap::value_parser!(u8).range(0..=100))] + pub percentage: u8, + + #[clap(flatten)] + pub cli: Cli, +} + /// Generates an overseer that replaces the candidate validation subsystem with our malicious /// variant. -pub(crate) struct BackGarbageCandidate; +pub(crate) struct BackGarbageCandidates { + /// The probability of behaving maliciously. + pub percentage: u8, +} -impl OverseerGen for BackGarbageCandidate { +impl OverseerGen for BackGarbageCandidates { fn generate<'a, Spawner, RuntimeClient>( &self, connector: OverseerConnector, @@ -55,6 +72,7 @@ impl OverseerGen for BackGarbageCandidate { let validation_filter = ReplaceValidationResult::new( FakeCandidateValidation::BackingAndApprovalValid, FakeCandidateValidationError::InvalidOutputs, + f64::from(self.percentage), SpawnGlue(spawner), ); diff --git a/polkadot/node/malus/src/variants/common.rs b/polkadot/node/malus/src/variants/common.rs index e112aa49f83..845dac0b6fe 100644 --- a/polkadot/node/malus/src/variants/common.rs +++ b/polkadot/node/malus/src/variants/common.rs @@ -34,6 +34,8 @@ use polkadot_primitives::v2::{ use futures::channel::oneshot; +use rand::distributions::{Bernoulli, Distribution}; + #[derive(clap::ArgEnum, Clone, Copy, Debug, PartialEq)] #[clap(rename_all = "kebab-case")] #[non_exhaustive] @@ -109,6 +111,7 @@ impl Into<InvalidCandidate> for FakeCandidateValidationError { pub struct ReplaceValidationResult<Spawner> { fake_validation: FakeCandidateValidation, fake_validation_error: FakeCandidateValidationError, + distribution: Bernoulli, spawner: Spawner, } @@ -119,9 +122,12 @@ where pub fn new( fake_validation: FakeCandidateValidation, fake_validation_error: FakeCandidateValidationError, + percentage: f64, spawner: Spawner, ) -> Self { - Self { fake_validation, fake_validation_error, spawner } + let distribution = Bernoulli::new(percentage / 100.0) + .expect("Invalid probability! Percentage must be in range [0..=100]."); + Self { fake_validation, fake_validation_error, distribution, spawner } } /// Creates and sends the validation response for a given candidate. Queries the runtime to obtain the validation data for the @@ -202,13 +208,14 @@ where { type Message = CandidateValidationMessage; - // Capture all candidate validation requests and depending on configuration fail them. + // Capture all (approval and backing) candidate validation requests and depending on configuration fail them. fn intercept_incoming( &self, subsystem_sender: &mut Sender, msg: FromOrchestra<Self::Message>, ) -> Option<FromOrchestra<Self::Message>> { match msg { + // Message sent by the approval voting subsystem FromOrchestra::Communication { msg: CandidateValidationMessage::ValidateFromExhaustive( @@ -236,28 +243,84 @@ where ), }) } - create_validation_response( - validation_data, - candidate_receipt.descriptor, - sender, - ); - None + // Create the fake response with probability `p` if the `PoV` is malicious, + // where 'p' defaults to 100% for suggest-garbage-candidate variant. + let behave_maliciously = self.distribution.sample(&mut rand::thread_rng()); + match behave_maliciously { + true => { + gum::info!( + target: MALUS, + ?behave_maliciously, + "😈 Creating malicious ValidationResult::Valid message with fake candidate commitments.", + ); + + create_validation_response( + validation_data, + candidate_receipt.descriptor, + sender, + ); + None + }, + false => { + // Behave normally with probability `(1-p)` for a malicious `PoV`. + gum::info!( + target: MALUS, + ?behave_maliciously, + "😈 Passing CandidateValidationMessage::ValidateFromExhaustive to the candidate validation subsystem.", + ); + + Some(FromOrchestra::Communication { + msg: CandidateValidationMessage::ValidateFromExhaustive( + validation_data, + validation_code, + candidate_receipt, + pov, + timeout, + sender, + ), + }) + }, + } }, FakeCandidateValidation::ApprovalInvalid | FakeCandidateValidation::BackingAndApprovalInvalid => { - let validation_result = - ValidationResult::Invalid(InvalidCandidate::InvalidOutputs); + // Set the validation result to invalid with probability `p` and trigger a dispute + let behave_maliciously = self.distribution.sample(&mut rand::thread_rng()); + match behave_maliciously { + true => { + let validation_result = + ValidationResult::Invalid(InvalidCandidate::InvalidOutputs); + + gum::info!( + target: MALUS, + ?behave_maliciously, + para_id = ?candidate_receipt.descriptor.para_id, + "😈 Maliciously sending invalid validation result: {:?}.", + &validation_result, + ); - gum::debug!( - target: MALUS, - para_id = ?candidate_receipt.descriptor.para_id, - "ValidateFromExhaustive result: {:?}", - &validation_result - ); - // We're not even checking the candidate, this makes us appear faster than honest validators. - sender.send(Ok(validation_result)).unwrap(); - None + // We're not even checking the candidate, this makes us appear faster than honest validators. + sender.send(Ok(validation_result)).unwrap(); + None + }, + false => { + // Behave normally with probability `(1-p)` + gum::info!(target: MALUS, "😈 'Decided' to not act maliciously.",); + + Some(FromOrchestra::Communication { + msg: CandidateValidationMessage::ValidateFromExhaustive( + validation_data, + validation_code, + candidate_receipt, + pov, + timeout, + sender, + ), + }) + }, + } }, + // Handle FakeCandidateValidation::Disabled _ => Some(FromOrchestra::Communication { msg: CandidateValidationMessage::ValidateFromExhaustive( validation_data, @@ -270,6 +333,7 @@ where }), } }, + // Behaviour related to the backing subsystem FromOrchestra::Communication { msg: CandidateValidationMessage::ValidateFromChainState( @@ -293,27 +357,68 @@ where ), }) } - self.send_validation_response( - candidate_receipt.descriptor, - subsystem_sender.clone(), - response_sender, - ); - None + // If the `PoV` is malicious, back the candidate with some probability `p`, + // where 'p' defaults to 100% for suggest-garbage-candidate variant. + let behave_maliciously = self.distribution.sample(&mut rand::thread_rng()); + match behave_maliciously { + true => { + gum::info!( + target: MALUS, + ?behave_maliciously, + "😈 Backing candidate with malicious PoV.", + ); + + self.send_validation_response( + candidate_receipt.descriptor, + subsystem_sender.clone(), + response_sender, + ); + None + }, + // If the `PoV` is malicious, we behave normally with some probability `(1-p)` + false => Some(FromOrchestra::Communication { + msg: CandidateValidationMessage::ValidateFromChainState( + candidate_receipt, + pov, + timeout, + response_sender, + ), + }), + } }, FakeCandidateValidation::BackingInvalid | FakeCandidateValidation::BackingAndApprovalInvalid => { - let validation_result = - ValidationResult::Invalid(self.fake_validation_error.clone().into()); - gum::debug!( - target: MALUS, - para_id = ?candidate_receipt.descriptor.para_id, - "ValidateFromChainState result: {:?}", - &validation_result - ); + // Maliciously set the validation result to invalid for a valid candidate with probability `p` + let behave_maliciously = self.distribution.sample(&mut rand::thread_rng()); + match behave_maliciously { + true => { + let validation_result = ValidationResult::Invalid( + self.fake_validation_error.clone().into(), + ); + gum::info!( + target: MALUS, + para_id = ?candidate_receipt.descriptor.para_id, + "😈 Maliciously sending invalid validation result: {:?}.", + &validation_result, + ); + // We're not even checking the candidate, this makes us appear faster than honest validators. + response_sender.send(Ok(validation_result)).unwrap(); + None + }, + // With some probability `(1-p)` we behave normally + false => { + gum::info!(target: MALUS, "😈 'Decided' to not act maliciously.",); - // We're not even checking the candidate, this makes us appear faster than honest validators. - response_sender.send(Ok(validation_result)).unwrap(); - None + Some(FromOrchestra::Communication { + msg: CandidateValidationMessage::ValidateFromChainState( + candidate_receipt, + pov, + timeout, + response_sender, + ), + }) + }, + } }, _ => Some(FromOrchestra::Communication { msg: CandidateValidationMessage::ValidateFromChainState( diff --git a/polkadot/node/malus/src/variants/dispute_valid_candidates.rs b/polkadot/node/malus/src/variants/dispute_valid_candidates.rs index 175cdecee91..c8e6afe643c 100644 --- a/polkadot/node/malus/src/variants/dispute_valid_candidates.rs +++ b/polkadot/node/malus/src/variants/dispute_valid_candidates.rs @@ -55,6 +55,11 @@ pub struct DisputeAncestorOptions { #[clap(long, arg_enum, ignore_case = true, default_value_t = FakeCandidateValidationError::InvalidOutputs)] pub fake_validation_error: FakeCandidateValidationError, + /// Determines the percentage of candidates that should be disputed. Allows for fine-tuning + /// the intensity of the behavior of the malicious node. Value must be in the range [0..=100]. + #[clap(short, long, ignore_case = true, default_value_t = 100, value_parser = clap::value_parser!(u8).range(0..=100))] + pub percentage: u8, + #[clap(flatten)] pub cli: Cli, } @@ -64,6 +69,8 @@ pub(crate) struct DisputeValidCandidates { pub fake_validation: FakeCandidateValidation, /// Fake validation error config. pub fake_validation_error: FakeCandidateValidationError, + /// The probability of behaving maliciously. + pub percentage: u8, } impl OverseerGen for DisputeValidCandidates { @@ -81,6 +88,7 @@ impl OverseerGen for DisputeValidCandidates { let validation_filter = ReplaceValidationResult::new( self.fake_validation, self.fake_validation_error, + f64::from(self.percentage), SpawnGlue(spawner.clone()), ); diff --git a/polkadot/node/malus/src/variants/mod.rs b/polkadot/node/malus/src/variants/mod.rs index d57580fdf8d..6f9a9359e02 100644 --- a/polkadot/node/malus/src/variants/mod.rs +++ b/polkadot/node/malus/src/variants/mod.rs @@ -22,8 +22,8 @@ mod dispute_valid_candidates; mod suggest_garbage_candidate; pub(crate) use self::{ - back_garbage_candidate::BackGarbageCandidate, + back_garbage_candidate::{BackGarbageCandidateOptions, BackGarbageCandidates}, dispute_valid_candidates::{DisputeAncestorOptions, DisputeValidCandidates}, - suggest_garbage_candidate::BackGarbageCandidateWrapper, + suggest_garbage_candidate::{SuggestGarbageCandidateOptions, SuggestGarbageCandidates}, }; pub(crate) use common::*; diff --git a/polkadot/node/malus/src/variants/suggest_garbage_candidate.rs b/polkadot/node/malus/src/variants/suggest_garbage_candidate.rs index b8aaaa18c10..86b0c49e712 100644 --- a/polkadot/node/malus/src/variants/suggest_garbage_candidate.rs +++ b/polkadot/node/malus/src/variants/suggest_garbage_candidate.rs @@ -29,14 +29,17 @@ use polkadot_cli::{ OverseerConnector, OverseerGen, OverseerGenArgs, OverseerHandle, ParachainHost, ProvideRuntimeApi, }, + Cli, }; use polkadot_node_core_candidate_validation::find_validation_data; use polkadot_node_primitives::{AvailableData, BlockData, PoV}; -use polkadot_primitives::v2::{CandidateDescriptor, CandidateHash}; +use polkadot_primitives::v2::CandidateDescriptor; use polkadot_node_subsystem_util::request_validators; use sp_core::traits::SpawnNamed; +use rand::distributions::{Bernoulli, Distribution}; + // Filter wrapping related types. use crate::{ interceptor::*, @@ -49,28 +52,16 @@ use crate::{ // Import extra types relevant to the particular // subsystem. -use polkadot_node_subsystem::{ - messages::{CandidateBackingMessage, CollatorProtocolMessage}, - SpawnGlue, -}; +use polkadot_node_subsystem::{messages::CandidateBackingMessage, SpawnGlue}; use polkadot_primitives::v2::CandidateReceipt; -use std::{ - collections::HashMap, - sync::{Arc, Mutex}, -}; - -struct Inner { - /// Maps malicious candidate hash to original candidate hash. - /// It is used to replace outgoing collator protocol seconded messages. - map: HashMap<CandidateHash, CandidateHash>, -} +use std::sync::Arc; /// Replace outgoing approval messages with disputes. #[derive(Clone)] struct NoteCandidate<Spawner> { - inner: Arc<Mutex<Inner>>, spawner: Spawner, + percentage: f64, } impl<Sender, Spawner> MessageInterceptor<Sender> for NoteCandidate<Spawner> @@ -80,7 +71,7 @@ where { type Message = CandidateBackingMessage; - /// Intercept incoming `Second` requests from the `collator-protocol` subsystem. We take + /// Intercept incoming `Second` requests from the `collator-protocol` subsystem. fn intercept_incoming( &self, subsystem_sender: &mut Sender, @@ -88,163 +79,174 @@ where ) -> Option<FromOrchestra<Self::Message>> { match msg { FromOrchestra::Communication { - msg: CandidateBackingMessage::Second(relay_parent, candidate, _pov), + msg: CandidateBackingMessage::Second(relay_parent, ref candidate, ref _pov), } => { gum::debug!( target: MALUS, candidate_hash = ?candidate.hash(), ?relay_parent, - "Received request to second candidate" - ); - - let pov = PoV { block_data: BlockData(MALICIOUS_POV.into()) }; - - let (sender, receiver) = std::sync::mpsc::channel(); - let mut new_sender = subsystem_sender.clone(); - let _candidate = candidate.clone(); - self.spawner.spawn_blocking( - "malus-get-validation-data", - Some("malus"), - Box::pin(async move { - gum::trace!(target: MALUS, "Requesting validators"); - let n_validators = request_validators(relay_parent, &mut new_sender) - .await - .await - .unwrap() - .unwrap() - .len(); - gum::trace!(target: MALUS, "Validators {}", n_validators); - match find_validation_data(&mut new_sender, &_candidate.descriptor()).await - { - Ok(Some((validation_data, validation_code))) => { - sender - .send((validation_data, validation_code, n_validators)) - .expect("channel is still open"); - }, - _ => { - panic!("Unable to fetch validation data"); - }, - } - }), - ); - - let (validation_data, validation_code, n_validators) = receiver.recv().unwrap(); - - let validation_data_hash = validation_data.hash(); - let validation_code_hash = validation_code.hash(); - let validation_data_relay_parent_number = validation_data.relay_parent_number; - - gum::trace!( - target: MALUS, - candidate_hash = ?candidate.hash(), - ?relay_parent, - ?n_validators, - ?validation_data_hash, - ?validation_code_hash, - ?validation_data_relay_parent_number, - "Fetched validation data." + "Received request to second candidate", ); - let malicious_available_data = - AvailableData { pov: Arc::new(pov.clone()), validation_data }; - - let pov_hash = pov.hash(); - let erasure_root = { - let chunks = - erasure::obtain_chunks_v1(n_validators as usize, &malicious_available_data) - .unwrap(); - - let branches = erasure::branches(chunks.as_ref()); - branches.root() - }; - - let (collator_id, collator_signature) = { - use polkadot_primitives::v2::CollatorPair; - use sp_core::crypto::Pair; - - let collator_pair = CollatorPair::generate().0; - let signature_payload = polkadot_primitives::v2::collator_signature_payload( - &relay_parent, - &candidate.descriptor().para_id, - &validation_data_hash, - &pov_hash, - &validation_code_hash, + // Need to draw value from Bernoulli distribution with given probability of success defined by the clap parameter. + // Note that clap parameter must be f64 since this is expected by the Bernoulli::new() function. + // It must be converted from u8, due to the lack of support for the .range() call on u64 in the clap crate. + let distribution = Bernoulli::new(self.percentage / 100.0) + .expect("Invalid probability! Percentage must be in range [0..=100]."); + + // Draw a random boolean from the Bernoulli distribution with probability of true equal to `p`. + // We use `rand::thread_rng` as the source of randomness. + let generate_malicious_candidate = distribution.sample(&mut rand::thread_rng()); + + if generate_malicious_candidate == true { + gum::debug!(target: MALUS, "😈 Suggesting malicious candidate.",); + + let pov = PoV { block_data: BlockData(MALICIOUS_POV.into()) }; + + let (sender, receiver) = std::sync::mpsc::channel(); + let mut new_sender = subsystem_sender.clone(); + let _candidate = candidate.clone(); + self.spawner.spawn_blocking( + "malus-get-validation-data", + Some("malus"), + Box::pin(async move { + gum::trace!(target: MALUS, "Requesting validators"); + let n_validators = request_validators(relay_parent, &mut new_sender) + .await + .await + .unwrap() + .unwrap() + .len(); + gum::trace!(target: MALUS, "Validators {}", n_validators); + match find_validation_data(&mut new_sender, &_candidate.descriptor()) + .await + { + Ok(Some((validation_data, validation_code))) => { + sender + .send((validation_data, validation_code, n_validators)) + .expect("channel is still open"); + }, + _ => { + panic!("Unable to fetch validation data"); + }, + } + }), ); - (collator_pair.public(), collator_pair.sign(&signature_payload)) - }; - - let malicious_commitments = - create_fake_candidate_commitments(&malicious_available_data.validation_data); - - let malicious_candidate = CandidateReceipt { - descriptor: CandidateDescriptor { - para_id: candidate.descriptor().para_id, - relay_parent, - collator: collator_id, - persisted_validation_data_hash: validation_data_hash, - pov_hash, - erasure_root, - signature: collator_signature, - para_head: malicious_commitments.head_data.hash(), - validation_code_hash, - }, - commitments_hash: malicious_commitments.hash(), - }; - let malicious_candidate_hash = malicious_candidate.hash(); - - gum::debug!( - target: MALUS, - candidate_hash = ?candidate.hash(), - ?malicious_candidate_hash, - "Created malicious candidate" - ); - - // Map malicious candidate to the original one. We need this mapping to send back the correct seconded statement - // to the collators. - self.inner - .lock() - .expect("bad lock") - .map - .insert(malicious_candidate_hash, candidate.hash()); + let (validation_data, validation_code, n_validators) = receiver.recv().unwrap(); + + let validation_data_hash = validation_data.hash(); + let validation_code_hash = validation_code.hash(); + let validation_data_relay_parent_number = validation_data.relay_parent_number; + + gum::trace!( + target: MALUS, + candidate_hash = ?candidate.hash(), + ?relay_parent, + ?n_validators, + ?validation_data_hash, + ?validation_code_hash, + ?validation_data_relay_parent_number, + "Fetched validation data." + ); - let message = FromOrchestra::Communication { - msg: CandidateBackingMessage::Second(relay_parent, malicious_candidate, pov), - }; + let malicious_available_data = + AvailableData { pov: Arc::new(pov.clone()), validation_data }; + + let pov_hash = pov.hash(); + let erasure_root = { + let chunks = erasure::obtain_chunks_v1( + n_validators as usize, + &malicious_available_data, + ) + .unwrap(); + + let branches = erasure::branches(chunks.as_ref()); + branches.root() + }; + + let (collator_id, collator_signature) = { + use polkadot_primitives::v2::CollatorPair; + use sp_core::crypto::Pair; + + let collator_pair = CollatorPair::generate().0; + let signature_payload = polkadot_primitives::v2::collator_signature_payload( + &relay_parent, + &candidate.descriptor().para_id, + &validation_data_hash, + &pov_hash, + &validation_code_hash, + ); + + (collator_pair.public(), collator_pair.sign(&signature_payload)) + }; + + let malicious_commitments = create_fake_candidate_commitments( + &malicious_available_data.validation_data, + ); - Some(message) + let malicious_candidate = CandidateReceipt { + descriptor: CandidateDescriptor { + para_id: candidate.descriptor().para_id, + relay_parent, + collator: collator_id, + persisted_validation_data_hash: validation_data_hash, + pov_hash, + erasure_root, + signature: collator_signature, + para_head: malicious_commitments.head_data.hash(), + validation_code_hash, + }, + commitments_hash: malicious_commitments.hash(), + }; + let malicious_candidate_hash = malicious_candidate.hash(); + + let message = FromOrchestra::Communication { + msg: CandidateBackingMessage::Second( + relay_parent, + malicious_candidate, + pov, + ), + }; + + gum::info!( + target: MALUS, + candidate_hash = ?candidate.hash(), + "😈 Intercepted CandidateBackingMessage::Second and created malicious candidate with hash: {:?}", + &malicious_candidate_hash + ); + Some(message) + } else { + Some(msg) + } }, FromOrchestra::Communication { msg } => Some(FromOrchestra::Communication { msg }), FromOrchestra::Signal(signal) => Some(FromOrchestra::Signal(signal)), } } +} - fn intercept_outgoing( - &self, - msg: overseer::CandidateBackingOutgoingMessages, - ) -> Option<overseer::CandidateBackingOutgoingMessages> { - let msg = match msg { - overseer::CandidateBackingOutgoingMessages::CollatorProtocolMessage( - CollatorProtocolMessage::Seconded(relay_parent, statement), - ) => { - // `parachain::collator-protocol: received an unexpected `CollationSeconded`: unknown statement statement=...` - // TODO: Fix this error. We get this on colaltors because `malicious backing` creates a candidate that gets backed/included. - // It is harmless for test parachain collators, but it will prevent cumulus based collators to make progress - // as they wait for the relay chain to confirm the seconding of the collation. - overseer::CandidateBackingOutgoingMessages::CollatorProtocolMessage( - CollatorProtocolMessage::Seconded(relay_parent, statement), - ) - }, - msg => msg, - }; - Some(msg) - } +#[derive(Debug, clap::Parser)] +#[clap(rename_all = "kebab-case")] +#[allow(missing_docs)] +pub struct SuggestGarbageCandidateOptions { + /// Determines the percentage of malicious candidates that are suggested by malus, + /// based on the total number of intercepted CandidateBacking + /// Must be in the range [0..=100]. + #[clap(short, long, ignore_case = true, default_value_t = 100, value_parser = clap::value_parser!(u8).range(0..=100))] + pub percentage: u8, + + #[clap(flatten)] + pub cli: Cli, } /// Garbage candidate implementation wrapper which implements `OverseerGen` glue. -pub(crate) struct BackGarbageCandidateWrapper; +pub(crate) struct SuggestGarbageCandidates { + /// The probability of behaving maliciously. + pub percentage: u8, +} -impl OverseerGen for BackGarbageCandidateWrapper { +impl OverseerGen for SuggestGarbageCandidates { fn generate<'a, Spawner, RuntimeClient>( &self, connector: OverseerConnector, @@ -255,14 +257,21 @@ impl OverseerGen for BackGarbageCandidateWrapper { RuntimeClient::Api: ParachainHost<Block> + BabeApi<Block> + AuthorityDiscoveryApi<Block>, Spawner: 'static + SpawnNamed + Clone + Unpin, { - let inner = Inner { map: std::collections::HashMap::new() }; - let inner_mut = Arc::new(Mutex::new(inner)); - let note_candidate = - NoteCandidate { inner: inner_mut.clone(), spawner: SpawnGlue(args.spawner.clone()) }; + gum::info!( + target: MALUS, + "😈 Started Malus node with a {:?} percent chance of behaving maliciously for a given candidate.", + &self.percentage, + ); + let note_candidate = NoteCandidate { + spawner: SpawnGlue(args.spawner.clone()), + percentage: f64::from(self.percentage), + }; + let fake_valid_probability = 100.0; let validation_filter = ReplaceValidationResult::new( FakeCandidateValidation::BackingAndApprovalValid, FakeCandidateValidationError::InvalidOutputs, + fake_valid_probability, SpawnGlue(args.spawner.clone()), ); -- GitLab