mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-07-06 12:37:23 +00:00
07d548a107
* Make use of `runtime_interface` for parachain externalities This also changes the encoding of the `ValidationResult` return value to match the default encoding used in Substrate. * Bump versions (#655) * Bump version * Update Cargo lock and bump runtime version * Remove balance transfer disabler. * Fix checking that `get_heads` exists (#657) * Tweak some Kusama params (#659) * Tweak some Kusama params - Council elections daily - No treasury burn - Bonding/slash defer periods of 7 days * Bump runtime * Bump version * Update Substrate (#661) * Make compat with exit-future updates * Update exit-future entirely * Tidy * Bump Substrate * Update branch * Add back sudo and fixing compilation * Increase nick deposit (10 KSM) and require 2 councillors to slash (#663) * Bump Substrate (#664) * Replace Substrate mentions in license headers with Polkadot (#674) * Fix typo in comment (#671) * Bump Substrate, add Identity module (#676) * Bump Substrate, add Identity module * Bump Substrate again * Update futures and tokio for browser light client (#673) * Make availability-store compile for WASM * Use --manifest-path instead * Make validation work on wasm! * Switch to Spawn trait * Migrate validation to std futures * Migrate network to std futures * Final changes to validation * Tidy up network * Tidy up validation * Switch branch * Migrate service * Get polkadot to compile via wasm! * Add browser-demo * Add initial browser file * Add browser-demo * Tidy * Temp switch back to substrate/master * tidy * Fix wasm build * Re-add release flag * Switch to polkadot-master * Revert cli tokio version to avoid libp2p panic * Update tokio version * Fix availability store tests * Fix validation tests * Remove futures01 from availability-store * Fix network tests * Small changes * Fix collator * Fix typo * Revert removal of tokio_executor that causes tokio version mismatch panic * Fix adder test parachain * Revert "Revert removal of tokio_executor that causes tokio version mismatch panic" This reverts commit cfeb50c01d8df5e209483406a711e64761b44ae9. * Update availability-store/src/worker.rs Co-Authored-By: Pierre Krieger <pierre.krieger1708@gmail.com> * Update network/src/lib.rs Co-Authored-By: Pierre Krieger <pierre.krieger1708@gmail.com> * Update network/src/lib.rs Co-Authored-By: Pierre Krieger <pierre.krieger1708@gmail.com> * Box pin changes * Asyncify network functions * Clean up browser validation worker error * Fix av store test * Nits * Fix validation test * Switch favicon * Fix validation test again * Revert "Asyncify network functions" This reverts commit f20ae6548dc482cb1e75bc80641cfe55c6131a53. * Add async blocks back in * Fix typo in comment (#672) * Add the /ws bootnode to the chain specs (#681) * Revert "Revert "Revert removal of tokio_executor that causes tokio version mismatch panic"" (#685) This reverts commit 938f411a9365e9c5fb16bfedb62aacac4403d063. * Fix bootnodes PeerIds (#683) * Fixes a flaky test (#675) * Fixes a flaky test * Renames a var * Do not unit the errors in tests * Bump Substrate (#686) * update to latest renames * Bump Substrate * Bump substrate * Merge some things from `ashley-compile-to-wasm` (#687) * Make availability-store compile for WASM * Use --manifest-path instead * Make validation work on wasm! * Switch to Spawn trait * Migrate validation to std futures * Migrate network to std futures * Final changes to validation * Tidy up network * Tidy up validation * Switch branch * Migrate service * Get polkadot to compile via wasm! * Add browser-demo * Add initial browser file * Add browser-demo * Tidy * Temp switch back to substrate/master * tidy * Fix wasm build * Re-add release flag * Add the /ws bootnode to the chain specs * Copy changes from master * Switch branch * Switch libp2p and add wasm-timer * Switch back libp2p and add rand * Fix bootnodes PeerIds * use browser indexdb * Reduce changeset * Add matrix release alert script and gitlab job (#688) * Add matrix release alert script and gitlab job * Update .gitlab-ci.yml Co-Authored-By: Kirill Pimenov <kirill@parity.io> * Rewrite some Future structs as async functions (#679) * Squashed commit of the following: commit e97a17157ae0887320994661e2f816275fc75b76 Author: Ashley <ashley.ruglys@gmail.com> Date: Tue Dec 10 15:06:28 2019 +0100 Rewrite some functions as async commit 970e485179f1e087cf0a51c6a4e71f923e87df45 Merge: f98966acdf3ea965Author: Ashley <ashley.ruglys@gmail.com> Date: Tue Dec 10 11:19:37 2019 +0100 Merge remote-tracking branch 'parity/master' into ashley-futures-update commit f98966ac188067158071d1e3e243c34ea5738f56 Author: Ashley <ashley.ruglys@gmail.com> Date: Mon Dec 9 23:40:20 2019 +0100 Add async blocks back in commit 7fa88af0271db659de9274c94cb8e7eead0e4289 Author: Ashley <ashley.ruglys@gmail.com> Date: Mon Dec 9 23:17:02 2019 +0100 Revert "Asyncify network functions" This reverts commit f20ae6548dc482cb1e75bc80641cfe55c6131a53. commit 82413550cdac40bd14a09f62df12de49dd7e55af Author: Ashley <ashley.ruglys@gmail.com> Date: Mon Dec 9 19:09:55 2019 +0100 Fix validation test again commit 47e002b08369c9c775b92aea9b6f6ed81b30241b Author: Ashley <ashley.ruglys@gmail.com> Date: Mon Dec 9 19:07:43 2019 +0100 Switch favicon commit 0c5c1409078fc57120a39e40ec5cb1763d67d593 Author: Ashley <ashley.ruglys@gmail.com> Date: Mon Dec 9 18:54:10 2019 +0100 Fix validation test commit 8bb6a0189fe824da09054cbf5b06f11a0f87072d Author: Ashley <ashley.ruglys@gmail.com> Date: Mon Dec 9 18:53:54 2019 +0100 Nits commit 33410f3a4910d3e688956cecfcca02cc2dfa6a7a Author: Ashley <ashley.ruglys@gmail.com> Date: Mon Dec 9 18:43:09 2019 +0100 Fix av store test commit f0c517eb240c42848cdb3305e0b554ef407bdfaa Merge: 938f411a 60e72111 Author: Ashley <ashley.ruglys@gmail.com> Date: Mon Dec 9 18:21:39 2019 +0100 Merge branch 'ashley-futures-updates' into ashley-futures-update commit 60e72111651f2b366592c1e56756c6bf5d8ce2f1 Author: Ashley <ashley.ruglys@gmail.com> Date: Mon Dec 9 18:19:40 2019 +0100 Clean up browser validation worker error commit f20ae6548dc482cb1e75bc80641cfe55c6131a53 Author: Ashley <ashley.ruglys@gmail.com> Date: Mon Dec 9 18:16:40 2019 +0100 Asyncify network functions commit b22758d0a3852d701923bd238484e1c9eabec5e2 Merge: 2e8b05edef562cd7Author: Ashley <ashley.ruglys@gmail.com> Date: Mon Dec 9 17:47:26 2019 +0100 Merge remote-tracking branch 'parity/master' into ashley-futures-updates commit 2e8b05edf1a1fadd6943f967c27b6d34675ba06a Author: Ashley <ashley.ruglys@gmail.com> Date: Mon Dec 9 17:45:52 2019 +0100 Box pin changes commit 08bfdf7f2d27721abffee49221213304ebc4fd47 Author: Ashley <ashley.ruglys@gmail.com> Date: Mon Dec 9 17:15:38 2019 +0100 Update network/src/lib.rs Co-Authored-By: Pierre Krieger <pierre.krieger1708@gmail.com> commit d8be456c508d5e5a03178db45d9f272b302a8a65 Author: Ashley <ashley.ruglys@gmail.com> Date: Mon Dec 9 17:15:32 2019 +0100 Update network/src/lib.rs Co-Authored-By: Pierre Krieger <pierre.krieger1708@gmail.com> commit ec7367276fdd374b19f41555fd5985454c559600 Author: Ashley <ashley.ruglys@gmail.com> Date: Mon Dec 9 17:14:36 2019 +0100 Update availability-store/src/worker.rs Co-Authored-By: Pierre Krieger <pierre.krieger1708@gmail.com> commit 938f411a9365e9c5fb16bfedb62aacac4403d063 Author: Ashley <ashley.ruglys@gmail.com> Date: Mon Dec 9 17:05:05 2019 +0100 Revert "Revert removal of tokio_executor that causes tokio version mismatch panic" This reverts commit cfeb50c01d8df5e209483406a711e64761b44ae9. commit f92f58044b4fe04bde73a60820d154080dd64b16 Author: Ashley <ashley.ruglys@gmail.com> Date: Mon Dec 9 15:47:35 2019 +0100 Fix adder test parachain commit cfeb50c01d8df5e209483406a711e64761b44ae9 Author: Ashley <ashley.ruglys@gmail.com> Date: Mon Dec 9 15:31:36 2019 +0100 Revert removal of tokio_executor that causes tokio version mismatch panic commit 5bcb83a122b9a30f240a238ca670c6b658f4ddf1 Author: Ashley <ashley.ruglys@gmail.com> Date: Mon Dec 9 15:17:55 2019 +0100 Fix typo commit fc02b1dc16e277649677396833a8d70e8588a56c Author: Ashley <ashley.ruglys@gmail.com> Date: Mon Dec 9 15:02:50 2019 +0100 Fix collator commit 6c4ff5b3bf1084a618ffec2d864090c9c8077f0f Author: Ashley <ashley.ruglys@gmail.com> Date: Mon Dec 9 14:35:37 2019 +0100 Small changes commit e1338cb4450df5377d8c911da56445914d667472 Author: Ashley <ashley.ruglys@gmail.com> Date: Mon Dec 9 14:24:42 2019 +0100 Fix network tests commit 4e458f7a91c1ed5c986795f40ed55e596d176c4b Author: Ashley <ashley.ruglys@gmail.com> Date: Mon Dec 9 12:25:26 2019 +0100 Remove futures01 from availability-store commit 5729f6cd6b53f061ff155320c815509feb02309e Author: Ashley <ashley.ruglys@gmail.com> Date: Mon Dec 9 12:22:33 2019 +0100 Fix validation tests commit a820612565b42780f8b6c09c9c1c30f06a9985ba Author: Ashley <ashley.ruglys@gmail.com> Date: Mon Dec 9 12:01:48 2019 +0100 Fix availability store tests commit 112344faeee5f8f03b3b87c6baf7036a7fcbe415 Author: Ashley <ashley.ruglys@gmail.com> Date: Mon Dec 9 11:36:03 2019 +0100 Update tokio version commit d2de6d8b3f0c3682679fe437d5459ac50a3c3895 Author: Ashley <ashley.ruglys@gmail.com> Date: Mon Dec 9 11:33:25 2019 +0100 Revert cli tokio version to avoid libp2p panic commit 0c5f24e0c1131ac58a947448456e7fb62c869702 Author: Ashley <ashley.ruglys@gmail.com> Date: Mon Dec 9 11:27:13 2019 +0100 Switch to polkadot-master commit 2e2311e33a4af87c2c545094ea8cb595cd6cfe2d Author: Ashley <ashley.ruglys@gmail.com> Date: Fri Dec 6 15:07:21 2019 +0100 Re-add release flag commit 6adc1b6114e154a590acf82acfaf0c1265409518 Merge: 9767f832533c80adAuthor: Ashley <ashley.ruglys@gmail.com> Date: Fri Dec 6 13:36:35 2019 +0100 Merge remote-tracking branch 'parity/master' into ashley-compile-to-wasm commit 9767f8325c33211065ef6830becdac0e3cf852de Merge: c528dc6d0bf7d294Author: Ashley <ashley.ruglys@gmail.com> Date: Wed Dec 4 17:11:39 2019 +0100 Merge remote-tracking branch 'parity/master' into ashley-compile-to-wasm commit c528dc6df8fc31cdcbc10889636355241398debd Author: Ashley <ashley.ruglys@gmail.com> Date: Wed Dec 4 17:07:00 2019 +0100 Fix wasm build commit da233a122c678dc7767dac7cc6e2564575b15cc8 Author: Ashley <ashley.ruglys@gmail.com> Date: Wed Dec 4 16:25:49 2019 +0100 tidy commit 832f8054df78afbcef1903e0f9e7e246b348c10d Merge: 4e1da888121c917dAuthor: Ashley <ashley.ruglys@gmail.com> Date: Wed Dec 4 15:56:56 2019 +0100 Merge remote-tracking branch 'parity/master' into ashley-compile-to-wasm commit 4e1da8888dd2160064dd453782fb05513c65ade4 Author: Ashley <ashley.ruglys@gmail.com> Date: Tue Dec 3 16:47:02 2019 +0100 Temp switch back to substrate/master commit af88a87338688797bbc52315fdd0fc22cf23c6cf Merge: a03a980c7832ad93Author: Ashley <ashley.ruglys@gmail.com> Date: Mon Dec 2 19:33:14 2019 +0100 Merge remote-tracking branch 'parity/master' into ashley-compile-to-wasm commit a03a980ce417ec7b446bfcbe7a66ec0ed6458135 Merge: 31a88a930c1ef335Author: Ashley <ashley.ruglys@gmail.com> Date: Mon Dec 2 13:52:37 2019 +0100 Merge remote-tracking branch 'parity/master' into ashley-compile-to-wasm commit 31a88a930ffdf5da72b3e587ec8c0e6b00922e3e Author: Ashley <ashley.ruglys@gmail.com> Date: Mon Dec 2 13:52:35 2019 +0100 Tidy commit 5b33b7a7af08d7a3aa3853b8e4995484fb640d52 Author: Ashley <ashley.ruglys@gmail.com> Date: Mon Dec 2 11:55:51 2019 +0100 Add browser-demo commit 868f6e51dfdc0a64252acd9adabe7b9ba436b1f4 Author: Ashley <ashley.ruglys@gmail.com> Date: Mon Dec 2 10:51:57 2019 +0100 Add initial browser file commit e5e399c20f1dc4e1023ee57773dcdd9ab2a0a14b Author: Ashley <ashley.ruglys@gmail.com> Date: Mon Dec 2 10:45:02 2019 +0100 Add browser-demo commit 408288b05292d952944a6b8e1f2bcf9cf259a040 Author: Ashley <ashley.ruglys@gmail.com> Date: Sun Dec 1 19:28:33 2019 +0100 Get polkadot to compile via wasm! commit 04ffe72e868be57841d31f01eec1b90423a595d6 Author: Ashley <ashley.ruglys@gmail.com> Date: Sun Dec 1 19:28:16 2019 +0100 Migrate service commit 119f0829a53b825a3ebc9efdefa76ae7eabb04aa Merge: 93fb642837fec553Author: Ashley <ashley.ruglys@gmail.com> Date: Sun Dec 1 17:43:49 2019 +0100 Merge remote-tracking branch 'parity/master' into ashley-compile-to-wasm commit 93fb6428501bac612a1675cf3b6e3d26f5bbc7c2 Author: Ashley <ashley.ruglys@gmail.com> Date: Sun Dec 1 12:21:25 2019 +0100 Switch branch commit 0c4fe8331bdc9665ac2427eb8c795112ac728d70 Author: Ashley <ashley.ruglys@gmail.com> Date: Sat Nov 30 11:45:59 2019 +0100 Tidy up validation commit 73563253d95962657108820ae130a8d3f3093ee8 Author: Ashley <ashley.ruglys@gmail.com> Date: Sat Nov 30 11:39:09 2019 +0100 Tidy up network commit 1c9cf0427c0e2d15c4b6d52b91d67d4a3963e30d Author: Ashley <ashley.ruglys@gmail.com> Date: Sat Nov 30 01:16:35 2019 +0100 Final changes to validation commit 322cca5224fdca0a29d88ff91700ef704a9d0c2a Author: Ashley <ashley.ruglys@gmail.com> Date: Sat Nov 30 00:31:55 2019 +0100 Migrate network to std futures commit 96f1a99491f5ae2957effa58cc1e385014575a32 Author: Ashley <ashley.ruglys@gmail.com> Date: Fri Nov 29 23:31:04 2019 +0100 Migrate validation to std futures commit aaf5e55fffd1367c05687eb34f4365a24e3a34c0 Author: Ashley <ashley.ruglys@gmail.com> Date: Fri Nov 29 17:10:11 2019 +0100 Switch to Spawn trait commit 2ab282f57e8b9a55cf8d285b283cf009216511d2 Merge: cceb6b72ed7ee572Author: Ashley <ashley.ruglys@gmail.com> Date: Fri Nov 29 16:31:24 2019 +0100 Merge remote-tracking branch 'parity/master' into ashley-compile-to-wasm commit cceb6b72f5677a1c43d2cd61bd525539054f0c01 Author: Ashley <ashley.ruglys@gmail.com> Date: Fri Nov 29 15:47:14 2019 +0100 Make validation work on wasm! commit b45a95cf7d829a916bf2ad6936d1e7f4b6f3ef77 Merge:4ec635eedb7eaa6b Author: Ashley <ashley.ruglys@gmail.com> Date: Fri Nov 29 13:57:23 2019 +0100 Merge remote-tracking branch 'tomaka/wasm-start' into HEAD commit db7eaa6bd5d3bbcea829570fb47ab4d06f3558ce Merge: 6f97dbb7f826ce53Author: Pierre Krieger <pierre.krieger1708@gmail.com> Date: Thu Nov 28 13:58:15 2019 +0100 Merge branch 'master' into wasm-start commit 6f97dbb786750d854cf8f7a56c6a336ea5979228 Author: Pierre Krieger <pierre.krieger1708@gmail.com> Date: Thu Nov 28 12:47:45 2019 +0100 Use --manifest-path instead commit 20104e98ff1713b6c81b0251b43d060d4e672d55 Author: Pierre Krieger <pierre.krieger1708@gmail.com> Date: Thu Nov 28 10:44:51 2019 +0100 Make availability-store compile for WASM * Fix build * Fix futures blocking panic in validators (again) * Deindent * Supercede 'Propagate Substrate#4284 to Polkadot' (#695) * Propagate Substrate#4284 to Polkadot * Fix tests * Fixes * Use hash part of fund id as child unique id. * Add comma * Switch branch * run cargo update * Update polkadot-master only * Fix collator * More update * Fix compilation * Some stylistic cleanups * Increase the minimum treasury bond to reduce silly proposals (#701) * Increase the minimum treasury bond to reduce silly proposals * Bump substrate * Bump version * Update to latest Substrate master (#703) * Update to latest Substrate master * Fix tests * Introduce an event for when transaction fees are paid (#702) * Introduce an event for when transaction fees are paid * Fix Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> * Bump to latest Substrate (#706) * Updates, but won't build. * Bump version. * Fix * Fix test * ci: increase git cloning depth to 100 (#707) * ci: fetch master branch for runtime diff (#708) * Bump Substrate and runtime version (#712) * Update to latest Substrate master * Bump Substrate and runtime version Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> * ci: fix release tagging after tags are added on the master branch (#714) * ci: fix release tagging after tags are added on the master branch * ci: latest tag applied to latest tag * Update kvdb version in availability-store (#709) * update availability store * also fix warning * update Cargo.lock * Support both polkadot and kusama runtimes (#704) * Allow both polkadot and kusama runtimes * Allow both polkadot and kusama runtimes * Make `collator` build * Removed kusama runtime * Introduced common runtime * Updated for latest substrate * Updated CI targets * Updated CI version check * Removed unused dependency * Pulled latests substrate * Pulled latest substrate * Fixed version * Apply suggestions from code review Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * NEW_HEADS_IDENTIFIER moved to primitives * Updated CI check script * Fixed script * Set epoch duration for polkadot * ci: check_runtime for both runtimes Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> Co-authored-by: gabriel klawitter <gabreal@users.noreply.github.com> * Bump Substrate & runtime version (#715) * Bump Substrate & runtime version * Attempt at fix * Update runtime/kusama/src/lib.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> * Update copyright year (#718) * Fixes after master merge * service: support setting fork blocks in config (#719) * ci: make sure master branch is available for check_runtime (#720) * service/src/lib.rs: Register network event stream for authority disc (#678) * service/src/lib.rs: Register network event stream for authority disc Previously one would create a sender and receiver channel pair, pass the sender to the build_network_future through the service builder and funnel network events returned from polling the network service into the sender to be consumed by the authority discovery module owning the receiver. With recent changes it is now possible to register an event_stream with the network service directly, thus one does not need to make the detour through the build_network_future. This commit is an adjusted clone of one targeting the Substrate repository. * service/src/lib.rs: Fix futures::stream imports * [TMP] *: Replace polkadot-upstream with feature branch * Revert "[TMP] *: Replace polkadot-upstream with feature branch" This reverts commit 0c947b04ab80488bfca16c5aeac9657b77a93a44. * Hotfix for Kusama (#724) * cli: revert borked kusama chain on startup * Docs. * cli: fix reversal of bork kusama fork * cli: force always can author * Version bump * service: support setting fork blocks in config * service: add support for bad blocks extension * service: add badBlocks to kusama chainspec * Bump Substrate to hotfix version. * service: add bad block to kusama chain spec * cleanup kusama hotfix * add kusama grandpa hotfix * Bump substrate * Bump spec_version * Rebump * cli: remove unnecessary dependencies * service: revert can_author_with fix * service: remove unnecessary method * Don't try to track polkadot runtime verion yet * Versions * service: better grandpa fix detection Co-authored-by: André Silva <andre.beat@gmail.com> * allow release alerts to fail (#725) * Fix the can-author issue by defaulting to Kusama when no chainspec given. (#728) * Fix can_author by defaulting correctly. * Comments * Better logging * Bump Substrate * Minor updates to readme. * service: reset grandpa into a future round (not past) (#726) * service: reset grandpa into a future round (not past) * update substrate version * service: create grandpa reset round variable * service: fine grained grandpa reset on startup Co-authored-by: Gavin Wood <github@gavwood.com> * Bump version (#729) * polkadot v0.7.13 (#730) * bump substrate version * bump version to 0.7.13 * Fix pending-release alert script (#734) ... parity/tools doesn't have /bin/bash * Select native runtime based on chain spec (#733) * Select native runtime based on chain spec * Bumped substrate * Add sudo module to `polkadot-runtime` (#735) * Bump versions (#736) * Fix up Kusama balance constants * Bump versions * Fix for the --dev option * remove kusama fixes (#738) * Tweak Polkadot constants & bump Substrate (#739) * Tweak Polkadot constants. * Bump and remove warning * Bump impl version * Bump Substrate * Bump Substrate again * Some fixes * Fix compilation * Remove TODO * Remove old dir * Companion PR for Substrate#4585 (#748) * Companion PR for Substrate#4394 (#723) * service/src/lib.rs: Register network event stream for authority disc Previously one would create a sender and receiver channel pair, pass the sender to the build_network_future through the service builder and funnel network events returned from polling the network service into the sender to be consumed by the authority discovery module owning the receiver. With recent changes it is now possible to register an event_stream with the network service directly, thus one does not need to make the detour through the build_network_future. This commit is an adjusted clone of one targeting the Substrate repository. * service/src/lib.rs: Fix futures::stream imports * [TMP] *: Replace polkadot-upstream with feature branch * Switch branch * Small change * Companion PR to substrate#4542 * Revert "Merge remote-tracking branch 'tomaka/companion-4542' into ashley-browser-utils" This reverts commit 17f00afe483ee65cb3cf4a0faca27034e6d6523a, reversing changes made to 928cbb9c55542baff56b53accd9a5a45f12f01f1. * ashley-browser-utils -> ashley-browser-utils-polkadot * Switch branches back Co-authored-by: Max Inden <mail@max-inden.de> Co-authored-by: Pierre Krieger <pierre.krieger1708@gmail.com> * Companion PR to substrate#4542 (#732) * Companion PR for Substrate#4585 Co-authored-by: Ashley <ashley.ruglys@gmail.com> Co-authored-by: Max Inden <mail@max-inden.de> Co-authored-by: Pierre Krieger <pierre.krieger1708@gmail.com> * Reset branch and make it compile * Review feedback * Make the bounds a bit cleaner. Co-authored-by: Gavin Wood <github@gavwood.com> Co-authored-by: Stanislav Tkach <stanislav.tkach@gmail.com> Co-authored-by: Leo Arias <elopio@openzeppelin.com> Co-authored-by: Ashley <ashley.ruglys@gmail.com> Co-authored-by: Pierre Krieger <pierre.krieger1708@gmail.com> Co-authored-by: Fedor Sakharov <fedor.sakharov@gmail.com> Co-authored-by: s3krit <pugh@s3kr.it> Co-authored-by: Kirill Pimenov <kirushik@gmail.com> Co-authored-by: gabriel klawitter <gabreal@users.noreply.github.com> Co-authored-by: Nikolay Volf <nikvolf@gmail.com> Co-authored-by: Arkadiy Paronyan <arkady.paronyan@gmail.com> Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com> Co-authored-by: André Silva <andre.beat@gmail.com> Co-authored-by: Max Inden <mail@max-inden.de> Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com>
1392 lines
42 KiB
Rust
1392 lines
42 KiB
Rust
// Copyright 2019-2020 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/>.
|
|
|
|
//! Gossip messages and the message validator.
|
|
//!
|
|
//! At the moment, this module houses 2 gossip protocols central to Polkadot.
|
|
//!
|
|
//! The first is the attestation-gossip system, which aims to circulate parachain
|
|
//! candidate attestations by validators at leaves of the block-DAG.
|
|
//!
|
|
//! The second is the inter-chain message queue routing gossip, which aims to
|
|
//! circulate message queues between parachains, which remain un-routed as of
|
|
//! recent leaves.
|
|
//!
|
|
//! These gossip systems do not have any form of sybil-resistance in terms
|
|
//! of the nodes which can participate. It could be imposed e.g. by limiting only to
|
|
//! validators, but this would prevent message queues from getting into the hands
|
|
//! of collators and of attestations from getting into the hands of fishermen.
|
|
//! As such, we take certain precautions which allow arbitrary full nodes to
|
|
//! join the gossip graph, as well as validators (who are likely to be well-connected
|
|
//! amongst themselves).
|
|
//!
|
|
//! The first is the notion of a neighbor packet. This is a packet sent between
|
|
//! neighbors of the gossip graph to inform each other of their current protocol
|
|
//! state. As of this writing, for both attestation and message-routing gossip,
|
|
//! the only necessary information here is a (length-limited) set of perceived
|
|
//! leaves of the block-DAG.
|
|
//!
|
|
//! These leaves can be used to derive what information a node is willing to accept
|
|
//! There is typically an unbounded amount of possible "future" information relative to
|
|
//! any protocol state. For example, attestations or unrouted message queues from millions
|
|
//! of blocks after a known protocol state. The neighbor packet is meant to avoid being
|
|
//! spammed by illegitimate future information, while informing neighbors of when
|
|
//! previously-future and now current gossip messages would be accepted.
|
|
//!
|
|
//! Peers who send information which was not allowed under a recent neighbor packet
|
|
//! will be noted as non-beneficial to Substrate's peer-set management utility.
|
|
|
|
use sp_runtime::{generic::BlockId, traits::{BlakeTwo256, Hash as HashT}};
|
|
use sp_blockchain::Error as ClientError;
|
|
use sc_network::{config::Roles, Context, PeerId, ReputationChange};
|
|
use sc_network_gossip::{
|
|
ValidationResult as GossipValidationResult,
|
|
ValidatorContext, MessageIntent,
|
|
};
|
|
use polkadot_validation::{SignedStatement};
|
|
use polkadot_primitives::{Block, Hash};
|
|
use polkadot_primitives::parachain::{
|
|
ParachainHost, ValidatorId, Message as ParachainMessage, ErasureChunk as PrimitiveChunk
|
|
};
|
|
use polkadot_erasure_coding::{self as erasure};
|
|
use codec::{Decode, Encode};
|
|
use sp_api::ProvideRuntimeApi;
|
|
|
|
use std::collections::HashMap;
|
|
use std::sync::Arc;
|
|
|
|
use arrayvec::ArrayVec;
|
|
use futures::prelude::*;
|
|
use parking_lot::RwLock;
|
|
use log::warn;
|
|
|
|
use super::PolkadotNetworkService;
|
|
use crate::{GossipMessageStream, NetworkService, PolkadotProtocol, router::attestation_topic};
|
|
|
|
use attestation::{View as AttestationView, PeerData as AttestationPeerData};
|
|
use message_routing::{View as MessageRoutingView};
|
|
|
|
mod attestation;
|
|
mod message_routing;
|
|
|
|
/// The engine ID of the polkadot attestation system.
|
|
pub const POLKADOT_ENGINE_ID: sp_runtime::ConsensusEngineId = *b"dot1";
|
|
|
|
// arbitrary; in practice this should not be more than 2.
|
|
pub(crate) const MAX_CHAIN_HEADS: usize = 5;
|
|
|
|
/// Type alias for a bounded vector of leaves.
|
|
pub type LeavesVec = ArrayVec<[Hash; MAX_CHAIN_HEADS]>;
|
|
|
|
mod benefit {
|
|
use sc_network::ReputationChange as Rep;
|
|
/// When a peer sends us a previously-unknown candidate statement.
|
|
pub const NEW_CANDIDATE: Rep = Rep::new(100, "Polkadot: New candidate");
|
|
/// When a peer sends us a previously-unknown attestation.
|
|
pub const NEW_ATTESTATION: Rep = Rep::new(50, "Polkadot: New attestation");
|
|
/// When a peer sends us a previously-unknown erasure chunk.
|
|
pub const NEW_ERASURE_CHUNK: Rep = Rep::new(10, "Polkadot: New erasure chunk");
|
|
/// When a peer sends us a previously-unknown message packet.
|
|
pub const NEW_ICMP_MESSAGES: Rep = Rep::new(50, "Polkadot: New ICMP messages");
|
|
}
|
|
|
|
mod cost {
|
|
use sc_network::ReputationChange as Rep;
|
|
/// No cost. This will not be reported.
|
|
pub const NONE: Rep = Rep::new(0, "");
|
|
/// A peer sent us an attestation and we don't know the candidate.
|
|
pub const ATTESTATION_NO_CANDIDATE: Rep = Rep::new(-100, "Polkadot: No candidate");
|
|
/// A peer sent us a statement we consider in the future.
|
|
pub const FUTURE_MESSAGE: Rep = Rep::new(-100, "Polkadot: Future message");
|
|
/// A peer sent us a statement from the past.
|
|
pub const PAST_MESSAGE: Rep = Rep::new(-30, "Polkadot: Past message");
|
|
/// A peer sent us a malformed message.
|
|
pub const MALFORMED_MESSAGE: Rep = Rep::new(-500, "Polkadot: Malformed message");
|
|
/// A peer sent us a wrongly signed message.
|
|
pub const BAD_SIGNATURE: Rep = Rep::new(-500, "Polkadot: Bad signature");
|
|
/// A peer sent us a bad neighbor packet.
|
|
pub const BAD_NEIGHBOR_PACKET: Rep = Rep::new(-300, "Polkadot: Bad neighbor");
|
|
/// A peer sent us an ICMP queue we haven't advertised a need for.
|
|
pub const UNNEEDED_ICMP_MESSAGES: Rep = Rep::new(-100, "Polkadot: Unexpected ICMP message");
|
|
/// A peer sent us an erasure chunk referring to a candidate that we are not aware of.
|
|
pub const ORPHANED_ERASURE_CHUNK: Rep = Rep::new(-10, "An erasure chunk from unknown candidate");
|
|
/// A peer sent us an erasure chunk that does not match candidate's erasure root.
|
|
pub const ERASURE_CHUNK_WRONG_ROOT: Rep = Rep::new(-100, "Chunk doesn't match encoding root");
|
|
|
|
/// A peer sent us an ICMP queue with a bad root.
|
|
pub fn icmp_messages_root_mismatch(n_messages: usize) -> Rep {
|
|
const PER_MESSAGE: i32 = -150;
|
|
|
|
Rep::new((0..n_messages).map(|_| PER_MESSAGE).sum(), "Polkadot: ICMP root mismatch")
|
|
}
|
|
}
|
|
|
|
/// A gossip message.
|
|
#[derive(Encode, Decode, Clone, PartialEq)]
|
|
pub enum GossipMessage {
|
|
/// A packet sent to a neighbor but not relayed.
|
|
#[codec(index = "1")]
|
|
Neighbor(VersionedNeighborPacket),
|
|
/// An attestation-statement about the candidate.
|
|
/// Non-candidate statements should only be sent to peers who are aware of the candidate.
|
|
#[codec(index = "2")]
|
|
Statement(GossipStatement),
|
|
/// A packet of messages from one parachain to another.
|
|
#[codec(index = "3")]
|
|
ParachainMessages(GossipParachainMessages),
|
|
// TODO: https://github.com/paritytech/polkadot/issues/253
|
|
/// A packet containing one of the erasure-coding chunks of one candidate.
|
|
#[codec(index = "4")]
|
|
ErasureChunk(ErasureChunkMessage),
|
|
}
|
|
|
|
impl From<NeighborPacket> for GossipMessage {
|
|
fn from(packet: NeighborPacket) -> Self {
|
|
GossipMessage::Neighbor(VersionedNeighborPacket::V1(packet))
|
|
}
|
|
}
|
|
|
|
impl From<GossipStatement> for GossipMessage {
|
|
fn from(stmt: GossipStatement) -> Self {
|
|
GossipMessage::Statement(stmt)
|
|
}
|
|
}
|
|
|
|
impl From<GossipParachainMessages> for GossipMessage {
|
|
fn from(messages: GossipParachainMessages) -> Self {
|
|
GossipMessage::ParachainMessages(messages)
|
|
}
|
|
}
|
|
|
|
/// A gossip message containing a statement.
|
|
#[derive(Encode, Decode, Clone, PartialEq)]
|
|
pub struct GossipStatement {
|
|
/// The block hash of the relay chain being referred to. In context, this should
|
|
/// be a leaf.
|
|
pub relay_chain_leaf: Hash,
|
|
/// The signed statement being gossipped.
|
|
pub signed_statement: SignedStatement,
|
|
}
|
|
|
|
impl GossipStatement {
|
|
/// Create a new instance.
|
|
pub fn new(relay_chain_leaf: Hash, signed_statement: SignedStatement) -> Self {
|
|
Self {
|
|
relay_chain_leaf,
|
|
signed_statement,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A gossip message containing one erasure chunk of a candidate block.
|
|
/// For each chunk of block erasure encoding one of this messages is constructed.
|
|
#[derive(Encode, Decode, Clone, Debug, PartialEq)]
|
|
pub struct ErasureChunkMessage {
|
|
/// The chunk itself.
|
|
pub chunk: PrimitiveChunk,
|
|
/// The relay parent of the block this chunk belongs to.
|
|
pub relay_parent: Hash,
|
|
/// The hash of the candidate receipt of the block this chunk belongs to.
|
|
pub candidate_hash: Hash,
|
|
}
|
|
|
|
impl From<ErasureChunkMessage> for GossipMessage {
|
|
fn from(chk: ErasureChunkMessage) -> Self {
|
|
GossipMessage::ErasureChunk(chk)
|
|
}
|
|
}
|
|
|
|
/// A packet of messages from one parachain to another.
|
|
///
|
|
/// These are all the messages posted from one parachain to another during the
|
|
/// execution of a single parachain block. Since this parachain block may have been
|
|
/// included in many forks of the relay chain, there is no relay-chain leaf parameter.
|
|
#[derive(Encode, Decode, Clone, PartialEq)]
|
|
pub struct GossipParachainMessages {
|
|
/// The root of the message queue.
|
|
pub queue_root: Hash,
|
|
/// The messages themselves.
|
|
pub messages: Vec<ParachainMessage>,
|
|
}
|
|
|
|
impl GossipParachainMessages {
|
|
// confirms that the queue-root in the struct correctly matches
|
|
// the messages.
|
|
fn queue_root_is_correct(&self) -> bool {
|
|
let root = polkadot_validation::message_queue_root(
|
|
self.messages.iter().map(|m| &m.0)
|
|
);
|
|
root == self.queue_root
|
|
}
|
|
}
|
|
|
|
/// A versioned neighbor message.
|
|
#[derive(Encode, Decode, Clone, PartialEq)]
|
|
pub enum VersionedNeighborPacket {
|
|
#[codec(index = "1")]
|
|
V1(NeighborPacket),
|
|
}
|
|
|
|
/// Contains information on which chain heads the peer is
|
|
/// accepting messages for.
|
|
#[derive(Encode, Decode, Clone, PartialEq)]
|
|
pub struct NeighborPacket {
|
|
chain_heads: Vec<Hash>,
|
|
}
|
|
|
|
/// whether a block is known.
|
|
#[derive(Clone, Copy, PartialEq)]
|
|
pub enum Known {
|
|
/// The block is a known leaf.
|
|
Leaf,
|
|
/// The block is known to be old.
|
|
Old,
|
|
/// The block is known to be bad.
|
|
Bad,
|
|
}
|
|
|
|
/// Context to the underlying polkadot chain.
|
|
pub trait ChainContext: Send + Sync {
|
|
/// Provide a closure which is invoked for every unrouted queue hash at a given leaf.
|
|
fn leaf_unrouted_roots(
|
|
&self,
|
|
leaf: &Hash,
|
|
with_queue_root: &mut dyn FnMut(&Hash),
|
|
) -> Result<(), ClientError>;
|
|
|
|
/// whether a block is known. If it's not, returns `None`.
|
|
fn is_known(&self, block_hash: &Hash) -> Option<Known>;
|
|
}
|
|
|
|
impl<F, P> ChainContext for (F, P) where
|
|
F: Fn(&Hash) -> Option<Known> + Send + Sync,
|
|
P: Send + Sync + std::ops::Deref,
|
|
P::Target: ProvideRuntimeApi<Block>,
|
|
<P::Target as ProvideRuntimeApi<Block>>::Api: ParachainHost<Block, Error = ClientError>,
|
|
{
|
|
fn is_known(&self, block_hash: &Hash) -> Option<Known> {
|
|
(self.0)(block_hash)
|
|
}
|
|
|
|
fn leaf_unrouted_roots(
|
|
&self,
|
|
&leaf: &Hash,
|
|
with_queue_root: &mut dyn FnMut(&Hash),
|
|
) -> Result<(), ClientError> {
|
|
let api = self.1.runtime_api();
|
|
|
|
let leaf_id = BlockId::Hash(leaf);
|
|
let active_parachains = api.active_parachains(&leaf_id)?;
|
|
|
|
// TODO: https://github.com/paritytech/polkadot/issues/467
|
|
for (para_id, _) in active_parachains {
|
|
if let Some(ingress) = api.ingress(&leaf_id, para_id, None)? {
|
|
for (_height, _from, queue_root) in ingress.iter() {
|
|
with_queue_root(queue_root);
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
/// Register a gossip validator on the network service.
|
|
// NOTE: since RegisteredMessageValidator is meant to be a type-safe proof
|
|
// that we've actually done the registration, this should be the only way
|
|
// to construct it outside of tests.
|
|
pub fn register_validator<C: ChainContext + 'static>(
|
|
service: Arc<PolkadotNetworkService>,
|
|
chain: C,
|
|
executor: &impl futures::task::Spawn,
|
|
) -> RegisteredMessageValidator
|
|
{
|
|
let s = service.clone();
|
|
let report_handle = Box::new(move |peer: &PeerId, cost_benefit: ReputationChange| {
|
|
if cost_benefit.value != 0 {
|
|
s.report_peer(peer.clone(), cost_benefit);
|
|
}
|
|
});
|
|
let validator = Arc::new(MessageValidator {
|
|
report_handle,
|
|
inner: RwLock::new(Inner {
|
|
peers: HashMap::new(),
|
|
attestation_view: Default::default(),
|
|
message_routing_view: Default::default(),
|
|
availability_store: None,
|
|
chain,
|
|
})
|
|
});
|
|
|
|
let gossip_side = validator.clone();
|
|
let gossip_engine = sc_network_gossip::GossipEngine::new(
|
|
service.clone(),
|
|
executor,
|
|
POLKADOT_ENGINE_ID,
|
|
gossip_side,
|
|
);
|
|
|
|
RegisteredMessageValidator {
|
|
inner: validator as _,
|
|
service: Some(service),
|
|
gossip_engine: Some(gossip_engine),
|
|
}
|
|
}
|
|
|
|
#[derive(PartialEq)]
|
|
enum NewLeafAction {
|
|
// (who, message)
|
|
TargetedMessage(PeerId, GossipMessage),
|
|
// (topic, message)
|
|
Multicast(Hash, GossipMessage),
|
|
}
|
|
|
|
/// Actions to take after noting a new block-DAG leaf.
|
|
///
|
|
/// This should be consumed by passing a consensus-gossip handle to `perform`.
|
|
#[must_use = "New chain-head gossip actions must be performed"]
|
|
pub struct NewLeafActions {
|
|
actions: Vec<NewLeafAction>,
|
|
}
|
|
|
|
impl NewLeafActions {
|
|
/// Perform the queued actions, feeding into gossip.
|
|
pub fn perform(
|
|
self,
|
|
gossip: &dyn crate::NetworkService,
|
|
) {
|
|
for action in self.actions {
|
|
match action {
|
|
NewLeafAction::TargetedMessage(who, message)
|
|
=> gossip.send_message(who, message),
|
|
NewLeafAction::Multicast(topic, message)
|
|
=> gossip.gossip_message(topic, message),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A registered message validator.
|
|
///
|
|
/// Create this using `register_validator`.
|
|
#[derive(Clone)]
|
|
pub struct RegisteredMessageValidator {
|
|
inner: Arc<MessageValidator<dyn ChainContext>>,
|
|
// Note: this is always `Some` in real code and `None` in tests.
|
|
service: Option<Arc<PolkadotNetworkService>>,
|
|
// Note: this is always `Some` in real code and `None` in tests.
|
|
gossip_engine: Option<sc_network_gossip::GossipEngine<Block>>,
|
|
}
|
|
|
|
impl RegisteredMessageValidator {
|
|
#[cfg(test)]
|
|
pub(crate) fn new_test<C: ChainContext + 'static>(
|
|
chain: C,
|
|
report_handle: Box<dyn Fn(&PeerId, ReputationChange) + Send + Sync>,
|
|
) -> Self {
|
|
let validator = Arc::new(MessageValidator::new_test(chain, report_handle));
|
|
|
|
RegisteredMessageValidator {
|
|
inner: validator as _,
|
|
service: None,
|
|
gossip_engine: None,
|
|
}
|
|
}
|
|
|
|
pub fn register_availability_store(&mut self, availability_store: av_store::Store) {
|
|
self.inner.inner.write().availability_store = Some(availability_store);
|
|
}
|
|
|
|
/// Note that we perceive a new leaf of the block-DAG. We will notify our neighbors that
|
|
/// we now accept parachain candidate attestations and incoming message queues
|
|
/// relevant to this leaf.
|
|
pub(crate) fn new_local_leaf(
|
|
&self,
|
|
relay_chain_leaf: Hash,
|
|
validation: MessageValidationData,
|
|
lookup_queue_by_root: impl Fn(&Hash) -> Option<Vec<ParachainMessage>>,
|
|
) -> NewLeafActions {
|
|
// add an entry in attestation_view
|
|
// prune any entries from attestation_view which are no longer leaves
|
|
let mut inner = self.inner.inner.write();
|
|
inner.attestation_view.new_local_leaf(relay_chain_leaf, validation);
|
|
|
|
let mut actions = Vec::new();
|
|
|
|
{
|
|
let &mut Inner {
|
|
ref chain,
|
|
ref mut attestation_view,
|
|
ref mut message_routing_view,
|
|
..
|
|
} = &mut *inner;
|
|
|
|
attestation_view.prune_old_leaves(|hash| match chain.is_known(hash) {
|
|
Some(Known::Leaf) => true,
|
|
_ => false,
|
|
});
|
|
|
|
if let Err(e) = message_routing_view.update_leaves(chain, attestation_view.neighbor_info()) {
|
|
warn!("Unable to fully update leaf-state: {:?}", e);
|
|
}
|
|
}
|
|
|
|
|
|
// send neighbor packets to peers
|
|
inner.multicast_neighbor_packet(
|
|
|who, message| actions.push(NewLeafAction::TargetedMessage(who.clone(), message))
|
|
);
|
|
|
|
// feed any new unrouted queues into the propagation pool.
|
|
inner.message_routing_view.sweep_unknown_queues(|topic, queue_root|
|
|
match lookup_queue_by_root(queue_root) {
|
|
Some(messages) => {
|
|
let message = GossipMessage::from(GossipParachainMessages {
|
|
queue_root: *queue_root,
|
|
messages,
|
|
});
|
|
|
|
actions.push(NewLeafAction::Multicast(*topic, message));
|
|
|
|
true
|
|
}
|
|
None => false,
|
|
}
|
|
);
|
|
|
|
NewLeafActions { actions }
|
|
}
|
|
}
|
|
|
|
impl NetworkService for RegisteredMessageValidator {
|
|
fn gossip_messages_for(&self, topic: Hash) -> GossipMessageStream {
|
|
let topic_stream = if let Some(gossip_engine) = self.gossip_engine.as_ref() {
|
|
gossip_engine.messages_for(topic)
|
|
} else {
|
|
log::error!("Called gossip_messages_for on a test engine");
|
|
futures::channel::mpsc::unbounded().1
|
|
};
|
|
|
|
GossipMessageStream::new(topic_stream.boxed())
|
|
}
|
|
|
|
fn gossip_message(&self, topic: Hash, message: GossipMessage) {
|
|
if let Some(gossip_engine) = self.gossip_engine.as_ref() {
|
|
gossip_engine.gossip_message(
|
|
topic,
|
|
message.encode(),
|
|
false,
|
|
);
|
|
} else {
|
|
log::error!("Called gossip_message on a test engine");
|
|
}
|
|
}
|
|
|
|
fn send_message(&self, who: PeerId, message: GossipMessage) {
|
|
if let Some(gossip_engine) = self.gossip_engine.as_ref() {
|
|
gossip_engine.send_message(vec![who], message.encode());
|
|
} else {
|
|
log::error!("Called send_message on a test engine");
|
|
}
|
|
}
|
|
|
|
fn with_spec<F: Send + 'static>(&self, with: F)
|
|
where F: FnOnce(&mut PolkadotProtocol, &mut dyn Context<Block>)
|
|
{
|
|
if let Some(service) = self.service.as_ref() {
|
|
service.with_spec(with)
|
|
} else {
|
|
log::error!("Called with_spec on a test engine");
|
|
}
|
|
}
|
|
}
|
|
|
|
/// The data needed for validating gossip messages.
|
|
#[derive(Default)]
|
|
pub(crate) struct MessageValidationData {
|
|
/// The authorities' parachain validation keys at a block.
|
|
pub(crate) authorities: Vec<ValidatorId>,
|
|
}
|
|
|
|
impl MessageValidationData {
|
|
// check a statement's signature.
|
|
fn check_statement(&self, relay_chain_leaf: &Hash, statement: &SignedStatement) -> Result<(), ()> {
|
|
let sender = match self.authorities.get(statement.sender as usize) {
|
|
Some(val) => val,
|
|
None => return Err(()),
|
|
};
|
|
|
|
let good = self.authorities.contains(&sender) &&
|
|
::polkadot_validation::check_statement(
|
|
&statement.statement,
|
|
&statement.signature,
|
|
sender.clone(),
|
|
relay_chain_leaf,
|
|
);
|
|
|
|
if good {
|
|
Ok(())
|
|
} else {
|
|
Err(())
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Default)]
|
|
struct PeerData {
|
|
attestation: AttestationPeerData,
|
|
}
|
|
|
|
impl PeerData {
|
|
fn leaves(&self) -> impl Iterator<Item = &Hash> {
|
|
self.attestation.leaves()
|
|
}
|
|
}
|
|
|
|
struct Inner<C: ?Sized> {
|
|
peers: HashMap<PeerId, PeerData>,
|
|
attestation_view: AttestationView,
|
|
message_routing_view: MessageRoutingView,
|
|
availability_store: Option<av_store::Store>,
|
|
chain: C,
|
|
}
|
|
|
|
impl<C: ?Sized + ChainContext> Inner<C> {
|
|
fn validate_neighbor_packet(&mut self, sender: &PeerId, packet: NeighborPacket)
|
|
-> (GossipValidationResult<Hash>, ReputationChange, Vec<Hash>)
|
|
{
|
|
let chain_heads = packet.chain_heads;
|
|
if chain_heads.len() > MAX_CHAIN_HEADS {
|
|
(GossipValidationResult::Discard, cost::BAD_NEIGHBOR_PACKET, Vec::new())
|
|
} else {
|
|
let chain_heads: LeavesVec = chain_heads.into_iter().collect();
|
|
let new_topics = if let Some(ref mut peer) = self.peers.get_mut(sender) {
|
|
let new_leaves = peer.attestation.update_leaves(&chain_heads);
|
|
let new_attestation_topics = new_leaves.iter().cloned().map(attestation_topic);
|
|
|
|
// find all topics which are from the intersection of our leaves with the peer's
|
|
// new leaves.
|
|
let new_message_routing_topics = self.message_routing_view.intersection_topics(&new_leaves);
|
|
|
|
new_attestation_topics.chain(new_message_routing_topics).collect()
|
|
} else {
|
|
Vec::new()
|
|
};
|
|
|
|
(GossipValidationResult::Discard, cost::NONE, new_topics)
|
|
}
|
|
}
|
|
|
|
fn validate_erasure_chunk_packet(&mut self, msg: ErasureChunkMessage)
|
|
-> (GossipValidationResult<Hash>, ReputationChange)
|
|
{
|
|
if let Some(store) = &self.availability_store {
|
|
if let Some(receipt) = store.get_candidate(&msg.candidate_hash) {
|
|
let chunk_hash = erasure::branch_hash(
|
|
&receipt.erasure_root,
|
|
&msg.chunk.proof,
|
|
msg.chunk.index as usize
|
|
);
|
|
|
|
if chunk_hash != Ok(BlakeTwo256::hash(&msg.chunk.chunk)) {
|
|
(
|
|
GossipValidationResult::Discard,
|
|
cost::ERASURE_CHUNK_WRONG_ROOT
|
|
)
|
|
} else {
|
|
if let Some(awaited_chunks) = store.awaited_chunks() {
|
|
if awaited_chunks.contains(&(
|
|
msg.relay_parent,
|
|
receipt.erasure_root,
|
|
receipt.hash(),
|
|
msg.chunk.index,
|
|
)) {
|
|
let topic = av_store::erasure_coding_topic(
|
|
msg.relay_parent,
|
|
receipt.erasure_root,
|
|
msg.chunk.index,
|
|
);
|
|
|
|
return (
|
|
GossipValidationResult::ProcessAndKeep(topic),
|
|
benefit::NEW_ERASURE_CHUNK,
|
|
);
|
|
}
|
|
}
|
|
(GossipValidationResult::Discard, cost::NONE)
|
|
}
|
|
} else {
|
|
(GossipValidationResult::Discard, cost::ORPHANED_ERASURE_CHUNK)
|
|
}
|
|
} else {
|
|
(GossipValidationResult::Discard, cost::NONE)
|
|
}
|
|
}
|
|
|
|
fn multicast_neighbor_packet<F: FnMut(&PeerId, GossipMessage)>(
|
|
&self,
|
|
mut send_neighbor_packet: F,
|
|
) {
|
|
let neighbor_packet = GossipMessage::from(NeighborPacket {
|
|
chain_heads: self.attestation_view.neighbor_info().collect(),
|
|
});
|
|
|
|
for peer in self.peers.keys() {
|
|
send_neighbor_packet(peer, neighbor_packet.clone())
|
|
}
|
|
}
|
|
}
|
|
|
|
/// An unregistered message validator. Register this with `register_validator`.
|
|
pub struct MessageValidator<C: ?Sized> {
|
|
report_handle: Box<dyn Fn(&PeerId, ReputationChange) + Send + Sync>,
|
|
inner: RwLock<Inner<C>>,
|
|
}
|
|
|
|
impl<C: ChainContext + ?Sized> MessageValidator<C> {
|
|
#[cfg(test)]
|
|
fn new_test(
|
|
chain: C,
|
|
report_handle: Box<dyn Fn(&PeerId, ReputationChange) + Send + Sync>,
|
|
) -> Self where C: Sized {
|
|
MessageValidator {
|
|
report_handle,
|
|
inner: RwLock::new(Inner {
|
|
peers: HashMap::new(),
|
|
attestation_view: Default::default(),
|
|
message_routing_view: Default::default(),
|
|
availability_store: None,
|
|
chain,
|
|
}),
|
|
}
|
|
}
|
|
|
|
fn report(&self, who: &PeerId, cost_benefit: ReputationChange) {
|
|
(self.report_handle)(who, cost_benefit)
|
|
}
|
|
}
|
|
|
|
impl<C: ChainContext + ?Sized> sc_network_gossip::Validator<Block> for MessageValidator<C> {
|
|
fn new_peer(&self, _context: &mut dyn ValidatorContext<Block>, who: &PeerId, _roles: Roles) {
|
|
let mut inner = self.inner.write();
|
|
inner.peers.insert(who.clone(), PeerData::default());
|
|
}
|
|
|
|
fn peer_disconnected(&self, _context: &mut dyn ValidatorContext<Block>, who: &PeerId) {
|
|
let mut inner = self.inner.write();
|
|
inner.peers.remove(who);
|
|
}
|
|
|
|
fn validate(&self, context: &mut dyn ValidatorContext<Block>, sender: &PeerId, data: &[u8])
|
|
-> GossipValidationResult<Hash>
|
|
{
|
|
let mut decode_data = data;
|
|
let (res, cost_benefit) = match GossipMessage::decode(&mut decode_data) {
|
|
Err(_) => (GossipValidationResult::Discard, cost::MALFORMED_MESSAGE),
|
|
Ok(GossipMessage::Neighbor(VersionedNeighborPacket::V1(packet))) => {
|
|
let (res, cb, topics) = self.inner.write().validate_neighbor_packet(sender, packet);
|
|
for new_topic in topics {
|
|
context.send_topic(sender, new_topic, false);
|
|
}
|
|
(res, cb)
|
|
}
|
|
Ok(GossipMessage::Statement(statement)) => {
|
|
let (res, cb) = {
|
|
let mut inner = self.inner.write();
|
|
let inner = &mut *inner;
|
|
inner.attestation_view.validate_statement_signature(statement, &inner.chain)
|
|
};
|
|
|
|
if let GossipValidationResult::ProcessAndKeep(ref topic) = res {
|
|
context.broadcast_message(topic.clone(), data.to_vec(), false);
|
|
}
|
|
(res, cb)
|
|
}
|
|
Ok(GossipMessage::ParachainMessages(messages)) => {
|
|
let (res, cb) = {
|
|
let mut inner = self.inner.write();
|
|
let inner = &mut *inner;
|
|
inner.message_routing_view.validate_queue_and_note_known(&messages)
|
|
};
|
|
|
|
if let GossipValidationResult::ProcessAndKeep(ref topic) = res {
|
|
context.broadcast_message(topic.clone(), data.to_vec(), false);
|
|
}
|
|
(res, cb)
|
|
}
|
|
Ok(GossipMessage::ErasureChunk(chunk)) => {
|
|
self.inner.write().validate_erasure_chunk_packet(chunk)
|
|
}
|
|
};
|
|
|
|
self.report(sender, cost_benefit);
|
|
res
|
|
}
|
|
|
|
fn message_expired<'a>(&'a self) -> Box<dyn FnMut(Hash, &[u8]) -> bool + 'a> {
|
|
let inner = self.inner.read();
|
|
|
|
Box::new(move |topic, _data| {
|
|
// check that messages from this topic are considered live by one of our protocols.
|
|
// everything else is expired
|
|
let live = inner.attestation_view.is_topic_live(&topic)
|
|
|| !inner.message_routing_view.is_topic_live(&topic);
|
|
|
|
!live // = expired
|
|
})
|
|
}
|
|
|
|
fn message_allowed<'a>(&'a self) -> Box<dyn FnMut(&PeerId, MessageIntent, &Hash, &[u8]) -> bool + 'a> {
|
|
let mut inner = self.inner.write();
|
|
Box::new(move |who, intent, topic, data| {
|
|
let &mut Inner {
|
|
ref mut peers,
|
|
ref mut attestation_view,
|
|
ref mut message_routing_view,
|
|
..
|
|
} = &mut *inner;
|
|
|
|
match intent {
|
|
MessageIntent::PeriodicRebroadcast => return false,
|
|
_ => {},
|
|
}
|
|
|
|
let attestation_head = attestation_view.topic_block(topic).map(|x| x.clone());
|
|
let peer = peers.get_mut(who);
|
|
|
|
match GossipMessage::decode(&mut &data[..]) {
|
|
Ok(GossipMessage::Statement(ref statement)) => {
|
|
// to allow statements, we need peer knowledge.
|
|
let peer_knowledge = peer.and_then(move |p| attestation_head.map(|r| (p, r)))
|
|
.and_then(|(p, r)| p.attestation.knowledge_at_mut(&r).map(|k| (k, r)));
|
|
|
|
peer_knowledge.map_or(false, |(knowledge, attestation_head)| {
|
|
attestation_view.statement_allowed(
|
|
statement,
|
|
&attestation_head,
|
|
knowledge,
|
|
)
|
|
})
|
|
}
|
|
Ok(GossipMessage::ParachainMessages(_)) => match peer {
|
|
None => false,
|
|
Some(peer) => {
|
|
let their_leaves: LeavesVec = peer.leaves().cloned().collect();
|
|
message_routing_view.allowed_intersecting(&their_leaves, topic)
|
|
}
|
|
}
|
|
_ => false,
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use sc_network_gossip::Validator as ValidatorT;
|
|
use std::sync::mpsc;
|
|
use parking_lot::Mutex;
|
|
use polkadot_primitives::parachain::{CandidateReceipt, HeadData};
|
|
use sp_core::crypto::UncheckedInto;
|
|
use sp_core::sr25519::{Public as Sr25519Public, Signature as Sr25519Signature};
|
|
use polkadot_validation::GenericStatement;
|
|
use super::message_routing::queue_topic;
|
|
|
|
use crate::tests::TestChainContext;
|
|
|
|
#[derive(PartialEq, Clone, Debug)]
|
|
enum ContextEvent {
|
|
BroadcastTopic(Hash, bool),
|
|
BroadcastMessage(Hash, Vec<u8>, bool),
|
|
SendMessage(PeerId, Vec<u8>),
|
|
SendTopic(PeerId, Hash, bool),
|
|
}
|
|
|
|
#[derive(Default)]
|
|
struct MockValidatorContext {
|
|
events: Vec<ContextEvent>,
|
|
}
|
|
|
|
impl MockValidatorContext {
|
|
fn clear(&mut self) {
|
|
self.events.clear()
|
|
}
|
|
}
|
|
|
|
impl sc_network_gossip::ValidatorContext<Block> for MockValidatorContext {
|
|
fn broadcast_topic(&mut self, topic: Hash, force: bool) {
|
|
self.events.push(ContextEvent::BroadcastTopic(topic, force));
|
|
}
|
|
fn broadcast_message(&mut self, topic: Hash, message: Vec<u8>, force: bool) {
|
|
self.events.push(ContextEvent::BroadcastMessage(topic, message, force));
|
|
}
|
|
fn send_message(&mut self, who: &PeerId, message: Vec<u8>) {
|
|
self.events.push(ContextEvent::SendMessage(who.clone(), message));
|
|
}
|
|
fn send_topic(&mut self, who: &PeerId, topic: Hash, force: bool) {
|
|
self.events.push(ContextEvent::SendTopic(who.clone(), topic, force));
|
|
}
|
|
}
|
|
|
|
impl NewLeafActions {
|
|
fn has_message(&self, who: PeerId, message: GossipMessage) -> bool {
|
|
let x = NewLeafAction::TargetedMessage(who, message);
|
|
self.actions.iter().find(|&m| m == &x).is_some()
|
|
}
|
|
|
|
fn has_multicast(&self, topic: Hash, message: GossipMessage) -> bool {
|
|
let x = NewLeafAction::Multicast(topic, message);
|
|
self.actions.iter().find(|&m| m == &x).is_some()
|
|
}
|
|
}
|
|
|
|
fn validator_id(raw: [u8; 32]) -> ValidatorId {
|
|
Sr25519Public::from_raw(raw).into()
|
|
}
|
|
|
|
#[test]
|
|
fn message_allowed() {
|
|
let (tx, _rx) = mpsc::channel();
|
|
let tx = Mutex::new(tx);
|
|
let report_handle = Box::new(move |peer: &PeerId, cb: ReputationChange| tx.lock().send((peer.clone(), cb)).unwrap());
|
|
let validator = MessageValidator::new_test(
|
|
TestChainContext::default(),
|
|
report_handle,
|
|
);
|
|
|
|
let peer_a = PeerId::random();
|
|
|
|
let mut validator_context = MockValidatorContext::default();
|
|
validator.new_peer(&mut validator_context, &peer_a, Roles::FULL);
|
|
assert!(validator_context.events.is_empty());
|
|
validator_context.clear();
|
|
|
|
let hash_a = [1u8; 32].into();
|
|
let hash_b = [2u8; 32].into();
|
|
let hash_c = [3u8; 32].into();
|
|
|
|
let message = GossipMessage::from(NeighborPacket {
|
|
chain_heads: vec![hash_a, hash_b],
|
|
}).encode();
|
|
let res = validator.validate(
|
|
&mut validator_context,
|
|
&peer_a,
|
|
&message[..],
|
|
);
|
|
|
|
match res {
|
|
GossipValidationResult::Discard => {},
|
|
_ => panic!("wrong result"),
|
|
}
|
|
assert_eq!(
|
|
validator_context.events,
|
|
vec![
|
|
ContextEvent::SendTopic(peer_a.clone(), attestation_topic(hash_a), false),
|
|
ContextEvent::SendTopic(peer_a.clone(), attestation_topic(hash_b), false),
|
|
],
|
|
);
|
|
|
|
validator_context.clear();
|
|
|
|
let candidate_receipt = CandidateReceipt {
|
|
parachain_index: 5.into(),
|
|
collator: [255; 32].unchecked_into(),
|
|
head_data: HeadData(vec![9, 9, 9]),
|
|
signature: Default::default(),
|
|
egress_queue_roots: Vec::new(),
|
|
fees: 1_000_000,
|
|
block_data_hash: [20u8; 32].into(),
|
|
upward_messages: Vec::new(),
|
|
erasure_root: [1u8; 32].into(),
|
|
};
|
|
|
|
let statement = GossipMessage::Statement(GossipStatement {
|
|
relay_chain_leaf: hash_a,
|
|
signed_statement: SignedStatement {
|
|
statement: GenericStatement::Candidate(candidate_receipt),
|
|
signature: Sr25519Signature([255u8; 64]).into(),
|
|
sender: 1,
|
|
}
|
|
});
|
|
let encoded = statement.encode();
|
|
|
|
let topic_a = attestation_topic(hash_a);
|
|
let topic_b = attestation_topic(hash_b);
|
|
let topic_c = attestation_topic(hash_c);
|
|
|
|
// topic_a is in all 3 views -> succeed
|
|
validator.inner.write().attestation_view.new_local_leaf(hash_a, MessageValidationData::default());
|
|
// topic_b is in the neighbor's view but not ours -> fail
|
|
// topic_c is not in either -> fail
|
|
|
|
{
|
|
let mut message_allowed = validator.message_allowed();
|
|
let intent = MessageIntent::Broadcast;
|
|
assert!(message_allowed(&peer_a, intent, &topic_a, &encoded));
|
|
assert!(!message_allowed(&peer_a, intent, &topic_b, &encoded));
|
|
assert!(!message_allowed(&peer_a, intent, &topic_c, &encoded));
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn too_many_chain_heads_is_report() {
|
|
let (tx, rx) = mpsc::channel();
|
|
let tx = Mutex::new(tx);
|
|
let report_handle = Box::new(move |peer: &PeerId, cb: ReputationChange| tx.lock().send((peer.clone(), cb)).unwrap());
|
|
let validator = MessageValidator::new_test(
|
|
TestChainContext::default(),
|
|
report_handle,
|
|
);
|
|
|
|
let peer_a = PeerId::random();
|
|
|
|
let mut validator_context = MockValidatorContext::default();
|
|
validator.new_peer(&mut validator_context, &peer_a, Roles::FULL);
|
|
assert!(validator_context.events.is_empty());
|
|
validator_context.clear();
|
|
|
|
let chain_heads = (0..MAX_CHAIN_HEADS+1).map(|i| [i as u8; 32].into()).collect();
|
|
|
|
let message = GossipMessage::from(NeighborPacket {
|
|
chain_heads,
|
|
}).encode();
|
|
let res = validator.validate(
|
|
&mut validator_context,
|
|
&peer_a,
|
|
&message[..],
|
|
);
|
|
|
|
match res {
|
|
GossipValidationResult::Discard => {},
|
|
_ => panic!("wrong result"),
|
|
}
|
|
assert_eq!(
|
|
validator_context.events,
|
|
Vec::new(),
|
|
);
|
|
|
|
drop(validator);
|
|
|
|
assert_eq!(rx.iter().collect::<Vec<_>>(), vec![(peer_a, cost::BAD_NEIGHBOR_PACKET)]);
|
|
}
|
|
|
|
#[test]
|
|
fn statement_only_sent_when_candidate_known() {
|
|
let (tx, _rx) = mpsc::channel();
|
|
let tx = Mutex::new(tx);
|
|
let report_handle = Box::new(move |peer: &PeerId, cb: ReputationChange| tx.lock().send((peer.clone(), cb)).unwrap());
|
|
let validator = MessageValidator::new_test(
|
|
TestChainContext::default(),
|
|
report_handle,
|
|
);
|
|
|
|
let peer_a = PeerId::random();
|
|
|
|
let mut validator_context = MockValidatorContext::default();
|
|
validator.new_peer(&mut validator_context, &peer_a, Roles::FULL);
|
|
assert!(validator_context.events.is_empty());
|
|
validator_context.clear();
|
|
|
|
let hash_a = [1u8; 32].into();
|
|
let hash_b = [2u8; 32].into();
|
|
|
|
let message = GossipMessage::from(NeighborPacket {
|
|
chain_heads: vec![hash_a, hash_b],
|
|
}).encode();
|
|
|
|
let res = validator.validate(
|
|
&mut validator_context,
|
|
&peer_a,
|
|
&message[..],
|
|
);
|
|
|
|
match res {
|
|
GossipValidationResult::Discard => {},
|
|
_ => panic!("wrong result"),
|
|
}
|
|
assert_eq!(
|
|
validator_context.events,
|
|
vec![
|
|
ContextEvent::SendTopic(peer_a.clone(), attestation_topic(hash_a), false),
|
|
ContextEvent::SendTopic(peer_a.clone(), attestation_topic(hash_b), false),
|
|
],
|
|
);
|
|
|
|
validator_context.clear();
|
|
|
|
let topic_a = attestation_topic(hash_a);
|
|
let c_hash = [99u8; 32].into();
|
|
|
|
let statement = GossipMessage::Statement(GossipStatement {
|
|
relay_chain_leaf: hash_a,
|
|
signed_statement: SignedStatement {
|
|
statement: GenericStatement::Valid(c_hash),
|
|
signature: Sr25519Signature([255u8; 64]).into(),
|
|
sender: 1,
|
|
}
|
|
});
|
|
let encoded = statement.encode();
|
|
validator.inner.write().attestation_view.new_local_leaf(hash_a, MessageValidationData::default());
|
|
|
|
{
|
|
let mut message_allowed = validator.message_allowed();
|
|
assert!(!message_allowed(&peer_a, MessageIntent::Broadcast, &topic_a, &encoded[..]));
|
|
}
|
|
|
|
validator
|
|
.inner
|
|
.write()
|
|
.peers
|
|
.get_mut(&peer_a)
|
|
.unwrap()
|
|
.attestation
|
|
.note_aware_under_leaf(&hash_a, c_hash);
|
|
{
|
|
let mut message_allowed = validator.message_allowed();
|
|
assert!(message_allowed(&peer_a, MessageIntent::Broadcast, &topic_a, &encoded[..]));
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn multicasts_icmp_queues_when_building_on_new_leaf() {
|
|
let (tx, _rx) = mpsc::channel();
|
|
let tx = Mutex::new(tx);
|
|
let report_handle = Box::new(move |peer: &PeerId, cb: ReputationChange| tx.lock().send((peer.clone(), cb)).unwrap());
|
|
|
|
let hash_a = [1u8; 32].into();
|
|
let root_a = [11u8; 32].into();
|
|
let root_a_topic = queue_topic(root_a);
|
|
|
|
let root_a_messages = vec![
|
|
ParachainMessage(vec![1, 2, 3]),
|
|
ParachainMessage(vec![4, 5, 6]),
|
|
];
|
|
|
|
let chain = {
|
|
let mut chain = TestChainContext::default();
|
|
chain.known_map.insert(hash_a, Known::Leaf);
|
|
chain.ingress_roots.insert(hash_a, vec![root_a]);
|
|
chain
|
|
};
|
|
|
|
let validator = RegisteredMessageValidator::new_test(chain, report_handle);
|
|
|
|
let authorities: Vec<ValidatorId> = vec![validator_id([0; 32]), validator_id([10; 32])];
|
|
|
|
let peer_a = PeerId::random();
|
|
let peer_b = PeerId::random();
|
|
|
|
let mut validator_context = MockValidatorContext::default();
|
|
validator.inner.new_peer(&mut validator_context, &peer_a, Roles::FULL);
|
|
validator.inner.new_peer(&mut validator_context, &peer_b, Roles::FULL);
|
|
assert!(validator_context.events.is_empty());
|
|
validator_context.clear();
|
|
|
|
|
|
{
|
|
let message = GossipMessage::from(NeighborPacket {
|
|
chain_heads: vec![hash_a],
|
|
}).encode();
|
|
let res = validator.inner.validate(
|
|
&mut validator_context,
|
|
&peer_a,
|
|
&message[..],
|
|
);
|
|
|
|
match res {
|
|
GossipValidationResult::Discard => {},
|
|
_ => panic!("wrong result"),
|
|
}
|
|
assert_eq!(
|
|
validator_context.events,
|
|
vec![
|
|
ContextEvent::SendTopic(peer_a.clone(), attestation_topic(hash_a), false),
|
|
],
|
|
);
|
|
}
|
|
|
|
// ensure that we attempt to multicast all relevant queues after noting a leaf.
|
|
{
|
|
let actions = validator.new_local_leaf(
|
|
hash_a,
|
|
MessageValidationData { authorities },
|
|
|root| if root == &root_a {
|
|
Some(root_a_messages.clone())
|
|
} else {
|
|
None
|
|
},
|
|
);
|
|
|
|
assert!(actions.has_message(peer_a.clone(), GossipMessage::from(NeighborPacket {
|
|
chain_heads: vec![hash_a],
|
|
})));
|
|
|
|
assert!(actions.has_multicast(root_a_topic, GossipMessage::from(GossipParachainMessages {
|
|
queue_root: root_a,
|
|
messages: root_a_messages.clone(),
|
|
})));
|
|
}
|
|
|
|
// ensure that we are allowed to multicast to a peer with same chain head,
|
|
// but not to one without.
|
|
{
|
|
let message = GossipMessage::from(GossipParachainMessages {
|
|
queue_root: root_a,
|
|
messages: root_a_messages.clone(),
|
|
}).encode();
|
|
|
|
let mut allowed = validator.inner.message_allowed();
|
|
let intent = MessageIntent::Broadcast;
|
|
assert!(allowed(&peer_a, intent, &root_a_topic, &message[..]));
|
|
assert!(!allowed(&peer_b, intent, &root_a_topic, &message[..]));
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn multicasts_icmp_queues_on_neighbor_update() {
|
|
let (tx, _rx) = mpsc::channel();
|
|
let tx = Mutex::new(tx);
|
|
let report_handle = Box::new(move |peer: &PeerId, cb: ReputationChange| tx.lock().send((peer.clone(), cb)).unwrap());
|
|
|
|
let hash_a = [1u8; 32].into();
|
|
let root_a = [11u8; 32].into();
|
|
let root_a_topic = queue_topic(root_a);
|
|
|
|
let root_a_messages = vec![
|
|
ParachainMessage(vec![1, 2, 3]),
|
|
ParachainMessage(vec![4, 5, 6]),
|
|
];
|
|
|
|
let chain = {
|
|
let mut chain = TestChainContext::default();
|
|
chain.known_map.insert(hash_a, Known::Leaf);
|
|
chain.ingress_roots.insert(hash_a, vec![root_a]);
|
|
chain
|
|
};
|
|
|
|
let validator = RegisteredMessageValidator::new_test(chain, report_handle);
|
|
|
|
let authorities: Vec<ValidatorId> = vec![validator_id([0; 32]), validator_id([10; 32])];
|
|
|
|
let peer_a = PeerId::random();
|
|
let peer_b = PeerId::random();
|
|
|
|
let mut validator_context = MockValidatorContext::default();
|
|
validator.inner.new_peer(&mut validator_context, &peer_a, Roles::FULL);
|
|
validator.inner.new_peer(&mut validator_context, &peer_b, Roles::FULL);
|
|
assert!(validator_context.events.is_empty());
|
|
validator_context.clear();
|
|
|
|
// ensure that we attempt to multicast all relevant queues after noting a leaf.
|
|
{
|
|
let actions = validator.new_local_leaf(
|
|
hash_a,
|
|
MessageValidationData { authorities },
|
|
|root| if root == &root_a {
|
|
Some(root_a_messages.clone())
|
|
} else {
|
|
None
|
|
},
|
|
);
|
|
|
|
assert!(actions.has_message(peer_a.clone(), GossipMessage::from(NeighborPacket {
|
|
chain_heads: vec![hash_a],
|
|
})));
|
|
|
|
assert!(actions.has_multicast(root_a_topic, GossipMessage::from(GossipParachainMessages {
|
|
queue_root: root_a,
|
|
messages: root_a_messages.clone(),
|
|
})));
|
|
}
|
|
|
|
// ensure that we are not allowed to multicast to either peer, as they
|
|
// don't have the chain head.
|
|
{
|
|
let message = GossipMessage::from(GossipParachainMessages {
|
|
queue_root: root_a,
|
|
messages: root_a_messages.clone(),
|
|
});
|
|
|
|
let mut allowed = validator.inner.message_allowed();
|
|
let intent = MessageIntent::Broadcast;
|
|
assert!(!allowed(&peer_a, intent, &root_a_topic, &message.encode()));
|
|
assert!(!allowed(&peer_b, intent, &root_a_topic, &message.encode()));
|
|
}
|
|
|
|
// peer A gets updated to the chain head. now we'll attempt to broadcast
|
|
// all queues to it.
|
|
{
|
|
let message = GossipMessage::from(NeighborPacket {
|
|
chain_heads: vec![hash_a],
|
|
}).encode();
|
|
let res = validator.inner.validate(
|
|
&mut validator_context,
|
|
&peer_a,
|
|
&message[..],
|
|
);
|
|
|
|
match res {
|
|
GossipValidationResult::Discard => {},
|
|
_ => panic!("wrong result"),
|
|
}
|
|
assert_eq!(
|
|
validator_context.events,
|
|
vec![
|
|
ContextEvent::SendTopic(peer_a.clone(), attestation_topic(hash_a), false),
|
|
ContextEvent::SendTopic(peer_a.clone(), root_a_topic, false),
|
|
],
|
|
);
|
|
}
|
|
|
|
// ensure that we are allowed to multicast to a peer with same chain head,
|
|
// but not to one without.
|
|
{
|
|
let message = GossipMessage::from(GossipParachainMessages {
|
|
queue_root: root_a,
|
|
messages: root_a_messages.clone(),
|
|
}).encode();
|
|
|
|
let mut allowed = validator.inner.message_allowed();
|
|
let intent = MessageIntent::Broadcast;
|
|
assert!(allowed(&peer_a, intent, &root_a_topic, &message[..]));
|
|
assert!(!allowed(&peer_b, intent, &root_a_topic, &message[..]));
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn accepts_needed_unknown_icmp_message_queue() {
|
|
let (tx, _rx) = mpsc::channel();
|
|
let tx = Mutex::new(tx);
|
|
let report_handle = Box::new(move |peer: &PeerId, cb: ReputationChange| tx.lock().send((peer.clone(), cb)).unwrap());
|
|
|
|
let hash_a = [1u8; 32].into();
|
|
let root_a_messages = vec![
|
|
ParachainMessage(vec![1, 2, 3]),
|
|
ParachainMessage(vec![4, 5, 6]),
|
|
];
|
|
let not_root_a_messages = vec![
|
|
ParachainMessage(vec![1, 1, 1, 1, 1, 1, 1, 1, 1, 1]),
|
|
ParachainMessage(vec![4, 5, 6]),
|
|
];
|
|
|
|
let root_a = polkadot_validation::message_queue_root(
|
|
root_a_messages.iter().map(|m| &m.0)
|
|
);
|
|
let not_root_a = [69u8; 32].into();
|
|
let root_a_topic = queue_topic(root_a);
|
|
|
|
let chain = {
|
|
let mut chain = TestChainContext::default();
|
|
chain.known_map.insert(hash_a, Known::Leaf);
|
|
chain.ingress_roots.insert(hash_a, vec![root_a]);
|
|
chain
|
|
};
|
|
|
|
let validator = RegisteredMessageValidator::new_test(chain, report_handle);
|
|
|
|
let authorities: Vec<ValidatorId> = vec![validator_id([0; 32]), validator_id([10; 32])];
|
|
|
|
let peer_a = PeerId::random();
|
|
let mut validator_context = MockValidatorContext::default();
|
|
|
|
validator.inner.new_peer(&mut validator_context, &peer_a, Roles::FULL);
|
|
assert!(validator_context.events.is_empty());
|
|
validator_context.clear();
|
|
|
|
let queue_messages = GossipMessage::from(GossipParachainMessages {
|
|
queue_root: root_a,
|
|
messages: root_a_messages.clone(),
|
|
});
|
|
|
|
let not_queue_messages = GossipMessage::from(GossipParachainMessages {
|
|
queue_root: root_a,
|
|
messages: not_root_a_messages.clone(),
|
|
});
|
|
|
|
let queue_messages_wrong_root = GossipMessage::from(GossipParachainMessages {
|
|
queue_root: not_root_a,
|
|
messages: root_a_messages.clone(),
|
|
});
|
|
|
|
// ensure that we attempt to multicast all relevant queues after noting a leaf.
|
|
{
|
|
let actions = validator.new_local_leaf(
|
|
hash_a,
|
|
MessageValidationData { authorities },
|
|
|_root| None,
|
|
);
|
|
|
|
assert!(actions.has_message(peer_a.clone(), GossipMessage::from(NeighborPacket {
|
|
chain_heads: vec![hash_a],
|
|
})));
|
|
|
|
// we don't know this queue! no broadcast :(
|
|
assert!(!actions.has_multicast(root_a_topic, queue_messages.clone()));
|
|
}
|
|
|
|
// rejects right queue with unknown root.
|
|
{
|
|
let res = validator.inner.validate(
|
|
&mut validator_context,
|
|
&peer_a,
|
|
&queue_messages_wrong_root.encode(),
|
|
);
|
|
|
|
match res {
|
|
GossipValidationResult::Discard => {},
|
|
_ => panic!("wrong result"),
|
|
}
|
|
|
|
assert_eq!(validator_context.events, Vec::new());
|
|
}
|
|
|
|
// rejects bad queue.
|
|
{
|
|
let res = validator.inner.validate(
|
|
&mut validator_context,
|
|
&peer_a,
|
|
¬_queue_messages.encode(),
|
|
);
|
|
|
|
match res {
|
|
GossipValidationResult::Discard => {},
|
|
_ => panic!("wrong result"),
|
|
}
|
|
|
|
assert_eq!(validator_context.events, Vec::new());
|
|
}
|
|
|
|
// accepts the right queue.
|
|
{
|
|
let res = validator.inner.validate(
|
|
&mut validator_context,
|
|
&peer_a,
|
|
&queue_messages.encode(),
|
|
);
|
|
|
|
match res {
|
|
GossipValidationResult::ProcessAndKeep(topic) if topic == root_a_topic => {},
|
|
_ => panic!("wrong result"),
|
|
}
|
|
|
|
assert_eq!(validator_context.events, vec![
|
|
ContextEvent::BroadcastMessage(root_a_topic, queue_messages.encode(), false),
|
|
]);
|
|
}
|
|
}
|
|
}
|