From 15a34838819f09f3bcfb3c05c08fb53934d8efc4 Mon Sep 17 00:00:00 2001 From: Richard Melkonian <35300528+0xmovses@users.noreply.github.com> Date: Thu, 2 Nov 2023 18:02:01 +0000 Subject: [PATCH 01/31] Create new trait for non-dedup storage decode (#1932) - This adds the new trait `StorageDecodeNonDedupLength` and implements them for `BTreeSet` and its bounded types. - New unit test has been added to cover the case. - See linked [issue](https://github.com/paritytech/polkadot-sdk/issues/126) which outlines the original issue. Note that the added trait here doesn't add new logic but improves semantics. --------- Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Co-authored-by: Oliver Tale-Yazdi Co-authored-by: command-bot <> --- .../support/src/storage/bounded_btree_map.rs | 11 ++ .../support/src/storage/bounded_btree_set.rs | 22 ++-- substrate/frame/support/src/storage/mod.rs | 122 +++++++++++++++++- .../support/src/storage/types/double_map.rs | 26 ++++ .../frame/support/src/storage/types/map.rs | 22 ++++ .../frame/support/src/storage/types/value.rs | 22 ++++ 6 files changed, 210 insertions(+), 15 deletions(-) diff --git a/substrate/frame/support/src/storage/bounded_btree_map.rs b/substrate/frame/support/src/storage/bounded_btree_map.rs index f2f32d890b87..91196be9e802 100644 --- a/substrate/frame/support/src/storage/bounded_btree_map.rs +++ b/substrate/frame/support/src/storage/bounded_btree_map.rs @@ -79,5 +79,16 @@ pub mod test { assert!(FooDoubleMap::decode_len(1, 2).is_none()); assert!(FooDoubleMap::decode_len(2, 2).is_none()); }); + + TestExternalities::default().execute_with(|| { + let bounded = boundedmap_from_keys::>(&[1, 2, 3]); + FooDoubleMap::insert(1, 1, bounded.clone()); + FooDoubleMap::insert(2, 2, bounded); // duplicate value + + assert_eq!(FooDoubleMap::decode_len(1, 1).unwrap(), 3); + assert_eq!(FooDoubleMap::decode_len(2, 2).unwrap(), 3); + assert!(FooDoubleMap::decode_len(2, 1).is_none()); + assert!(FooDoubleMap::decode_len(1, 2).is_none()); + }); } } diff --git a/substrate/frame/support/src/storage/bounded_btree_set.rs b/substrate/frame/support/src/storage/bounded_btree_set.rs index 52be1bb99f10..cf801eb47874 100644 --- a/substrate/frame/support/src/storage/bounded_btree_set.rs +++ b/substrate/frame/support/src/storage/bounded_btree_set.rs @@ -17,10 +17,10 @@ //! Traits, types and structs to support a bounded `BTreeSet`. -use crate::storage::StorageDecodeLength; +use frame_support::storage::StorageDecodeNonDedupLength; pub use sp_runtime::BoundedBTreeSet; -impl StorageDecodeLength for BoundedBTreeSet {} +impl StorageDecodeNonDedupLength for BoundedBTreeSet {} #[cfg(test)] pub mod test { @@ -56,28 +56,28 @@ pub mod test { } #[test] - fn decode_len_works() { + fn decode_non_dedup_len_works() { TestExternalities::default().execute_with(|| { let bounded = boundedset_from_keys::>(&[1, 2, 3]); Foo::put(bounded); - assert_eq!(Foo::decode_len().unwrap(), 3); + assert_eq!(Foo::decode_non_dedup_len().unwrap(), 3); }); TestExternalities::default().execute_with(|| { let bounded = boundedset_from_keys::>(&[1, 2, 3]); FooMap::insert(1, bounded); - assert_eq!(FooMap::decode_len(1).unwrap(), 3); - assert!(FooMap::decode_len(0).is_none()); - assert!(FooMap::decode_len(2).is_none()); + assert_eq!(FooMap::decode_non_dedup_len(1).unwrap(), 3); + assert!(FooMap::decode_non_dedup_len(0).is_none()); + assert!(FooMap::decode_non_dedup_len(2).is_none()); }); TestExternalities::default().execute_with(|| { let bounded = boundedset_from_keys::>(&[1, 2, 3]); FooDoubleMap::insert(1, 1, bounded); - assert_eq!(FooDoubleMap::decode_len(1, 1).unwrap(), 3); - assert!(FooDoubleMap::decode_len(2, 1).is_none()); - assert!(FooDoubleMap::decode_len(1, 2).is_none()); - assert!(FooDoubleMap::decode_len(2, 2).is_none()); + assert_eq!(FooDoubleMap::decode_non_dedup_len(1, 1).unwrap(), 3); + assert!(FooDoubleMap::decode_non_dedup_len(2, 1).is_none()); + assert!(FooDoubleMap::decode_non_dedup_len(1, 2).is_none()); + assert!(FooDoubleMap::decode_non_dedup_len(2, 2).is_none()); }); } } diff --git a/substrate/frame/support/src/storage/mod.rs b/substrate/frame/support/src/storage/mod.rs index 851b0687bd12..7f39a3fdad85 100644 --- a/substrate/frame/support/src/storage/mod.rs +++ b/substrate/frame/support/src/storage/mod.rs @@ -167,6 +167,32 @@ pub trait StorageValue { { T::decode_len(&Self::hashed_key()) } + + /// Read the length of the storage value without decoding the entire value. + /// + /// `T` is required to implement [`StorageDecodeNonDedupLength`]. + /// + /// If the value does not exists or it fails to decode the length, `None` is returned. + /// Otherwise `Some(len)` is returned. + /// + /// # Warning + /// + /// - The value returned is the non-deduplicated length of the underlying Vector in storage.This + /// means that any duplicate items are included. + /// + /// - `None` does not mean that `get()` does not return a value. The default value is completely + /// ignored by this function. + /// + /// # Example + #[doc = docify::embed!("src/storage/mod.rs", btree_set_decode_non_dedup_len)] + /// This demonstrates how `decode_non_dedup_len` will count even the duplicate values in the + /// storage (in this case, the number `4` is counted twice). + fn decode_non_dedup_len() -> Option + where + T: StorageDecodeNonDedupLength, + { + T::decode_non_dedup_len(&Self::hashed_key()) + } } /// A non-continuous container type. @@ -346,6 +372,27 @@ pub trait StorageMap { V::decode_len(&Self::hashed_key_for(key)) } + /// Read the length of the storage value without decoding the entire value. + /// + /// `V` is required to implement [`StorageDecodeNonDedupLength`]. + /// + /// If the value does not exists or it fails to decode the length, `None` is returned. + /// Otherwise `Some(len)` is returned. + /// + /// # Warning + /// + /// - `None` does not mean that `get()` does not return a value. The default value is completly + /// ignored by this function. + /// + /// - The value returned is the non-deduplicated length of the underlying Vector in storage.This + /// means that any duplicate items are included. + fn decode_non_dedup_len>(key: KeyArg) -> Option + where + V: StorageDecodeNonDedupLength, + { + V::decode_non_dedup_len(&Self::hashed_key_for(key)) + } + /// Migrate an item with the given `key` from a defunct `OldHasher` to the current hasher. /// /// If the key doesn't exist, then it's a no-op. If it does, then it returns its value. @@ -741,6 +788,27 @@ pub trait StorageDoubleMap { V::decode_len(&Self::hashed_key_for(key1, key2)) } + /// Read the length of the storage value without decoding the entire value under the + /// given `key1` and `key2`. + /// + /// `V` is required to implement [`StorageDecodeNonDedupLength`]. + /// + /// If the value does not exists or it fails to decode the length, `None` is returned. + /// Otherwise `Some(len)` is returned. + /// + /// # Warning + /// + /// `None` does not mean that `get()` does not return a value. The default value is completly + /// ignored by this function. + fn decode_non_dedup_len(key1: KArg1, key2: KArg2) -> Option + where + KArg1: EncodeLike, + KArg2: EncodeLike, + V: StorageDecodeNonDedupLength, + { + V::decode_non_dedup_len(&Self::hashed_key_for(key1, key2)) + } + /// Migrate an item with the given `key1` and `key2` from defunct `OldHasher1` and /// `OldHasher2` to the current hashers. /// @@ -1400,8 +1468,7 @@ pub trait StoragePrefixedMap { /// This trait is sealed. pub trait StorageAppend: private::Sealed {} -/// Marker trait that will be implemented for types that support to decode their length in an -/// efficient way. It is expected that the length is at the beginning of the encoded object +/// It is expected that the length is at the beginning of the encoded object /// and that the length is a `Compact`. /// /// This trait is sealed. @@ -1421,6 +1488,29 @@ pub trait StorageDecodeLength: private::Sealed + codec::DecodeLength { } } +/// It is expected that the length is at the beginning of the encoded objectand that the length is a +/// `Compact`. +/// +/// # Note +/// The length returned by this trait is not deduplicated, i.e. it is the length of the underlying +/// stored Vec. +/// +/// This trait is sealed. +pub trait StorageDecodeNonDedupLength: private::Sealed + codec::DecodeLength { + /// Decode the length of the storage value at `key`. + /// + /// This function assumes that the length is at the beginning of the encoded object and is a + /// `Compact`. + /// + /// Returns `None` if the storage value does not exist or the decoding failed. + fn decode_non_dedup_len(key: &[u8]) -> Option { + let mut data = [0u8; 5]; + let len = sp_io::storage::read(key, &mut data, 0)?; + let len = data.len().min(len as usize); + ::len(&data[..len]).ok() + } +} + /// Provides `Sealed` trait to prevent implementing trait `StorageAppend` & `StorageDecodeLength` /// & `EncodeLikeTuple` outside of this crate. mod private { @@ -1471,7 +1561,14 @@ impl StorageAppend for Vec {} impl StorageDecodeLength for Vec {} impl StorageAppend for BTreeSet {} -impl StorageDecodeLength for BTreeSet {} +impl StorageDecodeNonDedupLength for BTreeSet {} + +// Blanket implementation StorageDecodeNonDedupLength for all types that are StorageDecodeLength. +impl StorageDecodeNonDedupLength for T { + fn decode_non_dedup_len(key: &[u8]) -> Option { + T::decode_len(key) + } +} /// We abuse the fact that SCALE does not put any marker into the encoding, i.e. we only encode the /// internal vec and we can append to this vec. We have a test that ensures that if the `Digest` @@ -2026,7 +2123,24 @@ mod test { FooSet::append(6); FooSet::append(7); - assert_eq!(FooSet::decode_len().unwrap(), 7); + assert_eq!(FooSet::decode_non_dedup_len().unwrap(), 7); + }); + } + + #[docify::export] + #[test] + fn btree_set_decode_non_dedup_len() { + #[crate::storage_alias] + type Store = StorageValue>; + + TestExternalities::default().execute_with(|| { + Store::append(4); + Store::append(4); // duplicate value + Store::append(5); + + let length_with_dup_items = 3; + + assert_eq!(Store::decode_non_dedup_len().unwrap(), length_with_dup_items); }); } } diff --git a/substrate/frame/support/src/storage/types/double_map.rs b/substrate/frame/support/src/storage/types/double_map.rs index 1002222a895c..cb9479d491cf 100644 --- a/substrate/frame/support/src/storage/types/double_map.rs +++ b/substrate/frame/support/src/storage/types/double_map.rs @@ -27,6 +27,7 @@ use crate::{ StorageHasher, Twox128, }; use codec::{Decode, Encode, EncodeLike, FullCodec, MaxEncodedLen}; +use frame_support::storage::StorageDecodeNonDedupLength; use sp_arithmetic::traits::SaturatedConversion; use sp_metadata_ir::{StorageEntryMetadataIR, StorageEntryTypeIR}; use sp_std::prelude::*; @@ -455,6 +456,31 @@ where >::decode_len(key1, key2) } + /// Read the length of the storage value without decoding the entire value. + /// + /// `Value` is required to implement [`StorageDecodeNonDedupLength`]. + /// + /// If the value does not exists or it fails to decode the length, `None` is returned. + /// Otherwise `Some(len)` is returned. + /// + /// # Warning + /// + /// - `None` does not mean that `get()` does not return a value. The default value is completly + /// ignored by this function. + /// + /// - The value returned is the non-deduplicated length of the underlying Vector in storage.This + /// means that any duplicate items are included. + pub fn decode_non_dedup_len(key1: KArg1, key2: KArg2) -> Option + where + KArg1: EncodeLike, + KArg2: EncodeLike, + Value: StorageDecodeNonDedupLength, + { + >::decode_non_dedup_len( + key1, key2, + ) + } + /// Migrate an item with the given `key1` and `key2` from defunct `OldHasher1` and /// `OldHasher2` to the current hashers. /// diff --git a/substrate/frame/support/src/storage/types/map.rs b/substrate/frame/support/src/storage/types/map.rs index 75988220e3f7..d0149cf3fc87 100644 --- a/substrate/frame/support/src/storage/types/map.rs +++ b/substrate/frame/support/src/storage/types/map.rs @@ -27,6 +27,7 @@ use crate::{ StorageHasher, Twox128, }; use codec::{Decode, Encode, EncodeLike, FullCodec, MaxEncodedLen}; +use frame_support::storage::StorageDecodeNonDedupLength; use sp_arithmetic::traits::SaturatedConversion; use sp_metadata_ir::{StorageEntryMetadataIR, StorageEntryTypeIR}; use sp_std::prelude::*; @@ -285,6 +286,27 @@ where >::decode_len(key) } + /// Read the length of the storage value without decoding the entire value. + /// + /// `Value` is required to implement [`StorageDecodeNonDedupLength`]. + /// + /// If the value does not exists or it fails to decode the length, `None` is returned. + /// Otherwise `Some(len)` is returned. + /// + /// # Warning + /// + /// - `None` does not mean that `get()` does not return a value. The default value is completly + /// ignored by this function. + /// + /// - The value returned is the non-deduplicated length of the underlying Vector in storage.This + /// means that any duplicate items are included. + pub fn decode_non_dedup_len>(key: KeyArg) -> Option + where + Value: StorageDecodeNonDedupLength, + { + >::decode_non_dedup_len(key) + } + /// Migrate an item with the given `key` from a defunct `OldHasher` to the current hasher. /// /// If the key doesn't exist, then it's a no-op. If it does, then it returns its value. diff --git a/substrate/frame/support/src/storage/types/value.rs b/substrate/frame/support/src/storage/types/value.rs index 9fff1774d7b4..34cc3e24956b 100644 --- a/substrate/frame/support/src/storage/types/value.rs +++ b/substrate/frame/support/src/storage/types/value.rs @@ -26,6 +26,7 @@ use crate::{ traits::{GetDefault, StorageInfo, StorageInstance}, }; use codec::{Decode, Encode, EncodeLike, FullCodec, MaxEncodedLen}; +use frame_support::storage::StorageDecodeNonDedupLength; use sp_arithmetic::traits::SaturatedConversion; use sp_metadata_ir::{StorageEntryMetadataIR, StorageEntryTypeIR}; use sp_std::prelude::*; @@ -233,6 +234,27 @@ where >::decode_len() } + /// Read the length of the storage value without decoding the entire value. + /// + /// `Value` is required to implement [`StorageDecodeNonDedupLength`]. + /// + /// If the value does not exists or it fails to decode the length, `None` is returned. + /// Otherwise `Some(len)` is returned. + /// + /// # Warning + /// + /// - `None` does not mean that `get()` does not return a value. The default value is completly + /// ignored by this function. + /// + /// - The value returned is the non-deduplicated length of the underlying Vector in storage.This + /// means that any duplicate items are included. + pub fn decode_non_dedup_len() -> Option + where + Value: StorageDecodeNonDedupLength, + { + >::decode_non_dedup_len() + } + /// Try and append the given item to the value in the storage. /// /// Is only available if `Value` of the storage implements [`StorageTryAppend`]. From 0d3c67d96beda62b729e8328c5755358ac244246 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Fri, 3 Nov 2023 11:32:41 +0300 Subject: [PATCH 02/31] [testnet] Allow governance to control fees for Rococo <> Westend bridge (#2139) Right now governance could only control byte-fee component of Rococo <> Westend message fees (paid at Asset Hubs). This PR changes it a bit: 1) governance now allowed to control both fee components - byte fee and base fee; 2) base fee now includes cost of "default" delivery and confirmation transactions, in addition to `ExportMessage` instruction cost. --- Cargo.lock | 2 + .../src/messages_benchmarking.rs | 54 +- .../runtime-common/src/messages_generation.rs | 37 +- .../chain-asset-hub-rococo/src/lib.rs | 4 - .../chain-asset-hub-westend/src/lib.rs | 4 - .../chain-asset-hub-wococo/src/lib.rs | 4 - .../chain-bridge-hub-rococo/src/lib.rs | 15 + .../chain-bridge-hub-westend/src/lib.rs | 15 + .../chain-bridge-hub-wococo/src/lib.rs | 15 + .../assets/asset-hub-rococo/Cargo.toml | 2 + .../assets/asset-hub-rococo/src/xcm_config.rs | 30 +- .../assets/asset-hub-westend/Cargo.toml | 2 + .../asset-hub-westend/src/xcm_config.rs | 28 +- .../bridge-hub-rococo/tests/tests.rs | 117 +++- .../bridge-hub-westend/tests/tests.rs | 63 +- .../bridge-hubs/test-utils/src/test_cases.rs | 540 ++++++++++++++++-- 16 files changed, 801 insertions(+), 131 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 808a1976e079..bfe54bf8eea0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -910,6 +910,7 @@ dependencies = [ "bp-asset-hub-westend", "bp-asset-hub-wococo", "bp-bridge-hub-rococo", + "bp-bridge-hub-westend", "bp-bridge-hub-wococo", "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", @@ -1022,6 +1023,7 @@ dependencies = [ "assets-common", "bp-asset-hub-rococo", "bp-asset-hub-westend", + "bp-bridge-hub-rococo", "bp-bridge-hub-westend", "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", diff --git a/bridges/bin/runtime-common/src/messages_benchmarking.rs b/bridges/bin/runtime-common/src/messages_benchmarking.rs index d80a88f1068c..e7e7891461b2 100644 --- a/bridges/bin/runtime-common/src/messages_benchmarking.rs +++ b/bridges/bin/runtime-common/src/messages_benchmarking.rs @@ -22,24 +22,22 @@ use crate::{ messages::{ source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, - AccountIdOf, BridgedChain, HashOf, HasherOf, MessageBridge, ThisChain, + AccountIdOf, BridgedChain, HashOf, MessageBridge, ThisChain, }, messages_generation::{ - encode_all_messages, encode_lane_data, grow_trie_leaf_value, prepare_messages_storage_proof, + encode_all_messages, encode_lane_data, prepare_message_delivery_storage_proof, + prepare_messages_storage_proof, }, }; -use bp_messages::{storage_keys, MessagePayload}; +use bp_messages::MessagePayload; use bp_polkadot_core::parachains::ParaHash; -use bp_runtime::{ - record_all_trie_keys, Chain, Parachain, RawStorageProof, StorageProofSize, UnderlyingChainOf, -}; +use bp_runtime::{Chain, Parachain, StorageProofSize, UnderlyingChainOf}; use codec::Encode; use frame_support::weights::Weight; use pallet_bridge_messages::benchmarking::{MessageDeliveryProofParams, MessageProofParams}; use sp_runtime::traits::{Header, Zero}; use sp_std::prelude::*; -use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, TrieMut}; use xcm::v3::prelude::*; /// Prepare inbound bridge message according to given message proof parameters. @@ -172,7 +170,11 @@ where { // prepare storage proof let lane = params.lane; - let (state_root, storage_proof) = prepare_message_delivery_proof::(params); + let (state_root, storage_proof) = prepare_message_delivery_storage_proof::( + params.lane, + params.inbound_lane_data, + params.size, + ); // update runtime storage let (_, bridged_header_hash) = insert_header_to_grandpa_pallet::(state_root); @@ -200,7 +202,11 @@ where { // prepare storage proof let lane = params.lane; - let (state_root, storage_proof) = prepare_message_delivery_proof::(params); + let (state_root, storage_proof) = prepare_message_delivery_storage_proof::( + params.lane, + params.inbound_lane_data, + params.size, + ); // update runtime storage let (_, bridged_header_hash) = @@ -213,36 +219,6 @@ where } } -/// Prepare in-memory message delivery proof, without inserting anything to the runtime storage. -fn prepare_message_delivery_proof( - params: MessageDeliveryProofParams>>, -) -> (HashOf>, RawStorageProof) -where - B: MessageBridge, -{ - // prepare Bridged chain storage with inbound lane state - let storage_key = - storage_keys::inbound_lane_data_key(B::BRIDGED_MESSAGES_PALLET_NAME, ¶ms.lane).0; - let mut root = Default::default(); - let mut mdb = MemoryDB::default(); - { - let mut trie = - TrieDBMutBuilderV1::>>::new(&mut mdb, &mut root).build(); - let inbound_lane_data = - grow_trie_leaf_value(params.inbound_lane_data.encode(), params.size); - trie.insert(&storage_key, &inbound_lane_data) - .map_err(|_| "TrieMut::insert has failed") - .expect("TrieMut::insert should not fail in benchmarks"); - } - - // generate storage proof to be delivered to This chain - let storage_proof = record_all_trie_keys::>>, _>(&mdb, &root) - .map_err(|_| "record_all_trie_keys has failed") - .expect("record_all_trie_keys should not fail in benchmarks"); - - (root, storage_proof) -} - /// Insert header to the bridge GRANDPA pallet. pub(crate) fn insert_header_to_grandpa_pallet( state_root: bp_runtime::HashOf, diff --git a/bridges/bin/runtime-common/src/messages_generation.rs b/bridges/bin/runtime-common/src/messages_generation.rs index 3c550a9bd0fd..c37aaa5d4d53 100644 --- a/bridges/bin/runtime-common/src/messages_generation.rs +++ b/bridges/bin/runtime-common/src/messages_generation.rs @@ -16,10 +16,11 @@ //! Helpers for generating message storage proofs, that are used by tests and by benchmarks. -use crate::messages::{BridgedChain, HashOf, HasherOf, MessageBridge}; +use crate::messages::{AccountIdOf, BridgedChain, HashOf, HasherOf, MessageBridge, ThisChain}; use bp_messages::{ - storage_keys, LaneId, MessageKey, MessageNonce, MessagePayload, OutboundLaneData, + storage_keys, InboundLaneData, LaneId, MessageKey, MessageNonce, MessagePayload, + OutboundLaneData, }; use bp_runtime::{record_all_trie_keys, RawStorageProof, StorageProofSize}; use codec::Encode; @@ -104,6 +105,38 @@ where (root, storage_proof) } +/// Prepare storage proof of given messages delivery. +/// +/// Returns state trie root and nodes with prepared messages. +pub fn prepare_message_delivery_storage_proof( + lane: LaneId, + inbound_lane_data: InboundLaneData>>, + size: StorageProofSize, +) -> (HashOf>, RawStorageProof) +where + B: MessageBridge, +{ + // prepare Bridged chain storage with inbound lane state + let storage_key = storage_keys::inbound_lane_data_key(B::BRIDGED_MESSAGES_PALLET_NAME, &lane).0; + let mut root = Default::default(); + let mut mdb = MemoryDB::default(); + { + let mut trie = + TrieDBMutBuilderV1::>>::new(&mut mdb, &mut root).build(); + let inbound_lane_data = grow_trie_leaf_value(inbound_lane_data.encode(), size); + trie.insert(&storage_key, &inbound_lane_data) + .map_err(|_| "TrieMut::insert has failed") + .expect("TrieMut::insert should not fail in benchmarks"); + } + + // generate storage proof to be delivered to This chain + let storage_proof = record_all_trie_keys::>>, _>(&mdb, &root) + .map_err(|_| "record_all_trie_keys has failed") + .expect("record_all_trie_keys should not fail in benchmarks"); + + (root, storage_proof) +} + /// Add extra data to the trie leaf value so that it'll be of given size. pub fn grow_trie_leaf_value(mut value: Vec, size: StorageProofSize) -> Vec { match size { diff --git a/bridges/primitives/chain-asset-hub-rococo/src/lib.rs b/bridges/primitives/chain-asset-hub-rococo/src/lib.rs index 1a08ade2f4f2..6216b24d75c9 100644 --- a/bridges/primitives/chain-asset-hub-rococo/src/lib.rs +++ b/bridges/primitives/chain-asset-hub-rococo/src/lib.rs @@ -45,10 +45,6 @@ pub enum Call { frame_support::parameter_types! { /// Some sane weight to execute `xcm::Transact(pallet-xcm-bridge-hub-router::Call::report_bridge_status)`. pub const XcmBridgeHubRouterTransactCallMaxWeight: frame_support::weights::Weight = frame_support::weights::Weight::from_parts(200_000_000, 6144); - - /// Base delivery fee to `BridgeHubRococo`. - /// (initially was calculated by test `BridgeHubRococo::can_calculate_weight_for_paid_export_message_with_reserve_transfer` + `33%`) - pub const BridgeHubRococoBaseFeeInRocs: u128 = 1624803349; } /// Identifier of AssetHubRococo in the Rococo relay chain. diff --git a/bridges/primitives/chain-asset-hub-westend/src/lib.rs b/bridges/primitives/chain-asset-hub-westend/src/lib.rs index 2d317e4aee46..9de1c8809894 100644 --- a/bridges/primitives/chain-asset-hub-westend/src/lib.rs +++ b/bridges/primitives/chain-asset-hub-westend/src/lib.rs @@ -42,10 +42,6 @@ pub enum Call { frame_support::parameter_types! { /// Some sane weight to execute `xcm::Transact(pallet-xcm-bridge-hub-router::Call::report_bridge_status)`. pub const XcmBridgeHubRouterTransactCallMaxWeight: frame_support::weights::Weight = frame_support::weights::Weight::from_parts(200_000_000, 6144); - - /// Base delivery fee to `BridgeHubWestend`. - /// (initially was calculated by test `BridgeHubWestend::can_calculate_weight_for_paid_export_message_with_reserve_transfer` + `33%`) - pub const BridgeHubWestendBaseFeeInWnds: u128 = 487441010000; } /// Identifier of AssetHubWestend in the Westend relay chain. diff --git a/bridges/primitives/chain-asset-hub-wococo/src/lib.rs b/bridges/primitives/chain-asset-hub-wococo/src/lib.rs index 809e56934e96..c04eb04cce70 100644 --- a/bridges/primitives/chain-asset-hub-wococo/src/lib.rs +++ b/bridges/primitives/chain-asset-hub-wococo/src/lib.rs @@ -42,10 +42,6 @@ pub enum Call { frame_support::parameter_types! { /// Some sane weight to execute `xcm::Transact(pallet-xcm-bridge-hub-router::Call::report_bridge_status)`. pub const XcmBridgeHubRouterTransactCallMaxWeight: frame_support::weights::Weight = frame_support::weights::Weight::from_parts(200_000_000, 6144); - - /// Base delivery fee to `BridgeHubWococo`. - /// (initially was calculated by test `BridgeHubWococo::can_calculate_weight_for_paid_export_message_with_reserve_transfer` + `33%`) - pub const BridgeHubWococoBaseFeeInWocs: u128 = 1624803349; } /// Identifier of AssetHubWococo in the Wococo relay chain. diff --git a/bridges/primitives/chain-bridge-hub-rococo/src/lib.rs b/bridges/primitives/chain-bridge-hub-rococo/src/lib.rs index 039d681951cd..e72e711de927 100644 --- a/bridges/primitives/chain-bridge-hub-rococo/src/lib.rs +++ b/bridges/primitives/chain-bridge-hub-rococo/src/lib.rs @@ -82,3 +82,18 @@ pub const WITH_BRIDGE_ROCOCO_TO_WESTEND_MESSAGES_PALLET_INDEX: u8 = 51; decl_bridge_finality_runtime_apis!(bridge_hub_rococo); decl_bridge_messages_runtime_apis!(bridge_hub_rococo); + +frame_support::parameter_types! { + /// The XCM fee that is paid for executing XCM program (with `ExportMessage` instruction) at the Rococo + /// BridgeHub. + /// (initially was calculated by test `BridgeHubRococo::can_calculate_weight_for_paid_export_message_with_reserve_transfer` + `33%`) + pub const BridgeHubRococoBaseXcmFeeInRocs: u128 = 1628875538; + + /// Transaction fee that is paid at the Rococo BridgeHub for delivering single inbound message. + /// (initially was calculated by test `BridgeHubRococo::can_calculate_fee_for_complex_message_delivery_transaction` + `33%`) + pub const BridgeHubRococoBaseDeliveryFeeInRocs: u128 = 6417262881; + + /// Transaction fee that is paid at the Rococo BridgeHub for delivering single outbound message confirmation. + /// (initially was calculated by test `BridgeHubRococo::can_calculate_fee_for_complex_message_confirmation_transaction` + `33%`) + pub const BridgeHubRococoBaseConfirmationFeeInRocs: u128 = 6159996668; +} diff --git a/bridges/primitives/chain-bridge-hub-westend/src/lib.rs b/bridges/primitives/chain-bridge-hub-westend/src/lib.rs index a52e328b6875..0124e05bf887 100644 --- a/bridges/primitives/chain-bridge-hub-westend/src/lib.rs +++ b/bridges/primitives/chain-bridge-hub-westend/src/lib.rs @@ -73,3 +73,18 @@ pub const WITH_BRIDGE_WESTEND_TO_ROCOCO_MESSAGES_PALLET_INDEX: u8 = 44; decl_bridge_finality_runtime_apis!(bridge_hub_westend); decl_bridge_messages_runtime_apis!(bridge_hub_westend); + +frame_support::parameter_types! { + /// The XCM fee that is paid for executing XCM program (with `ExportMessage` instruction) at the Westend + /// BridgeHub. + /// (initially was calculated by test `BridgeHubWestend::can_calculate_weight_for_paid_export_message_with_reserve_transfer` + `33%`) + pub const BridgeHubWestendBaseXcmFeeInWnds: u128 = 488662666666; + + /// Transaction fee that is paid at the Westend BridgeHub for delivering single inbound message. + /// (initially was calculated by test `BridgeHubWestend::can_calculate_fee_for_complex_message_delivery_transaction` + `33%`) + pub const BridgeHubWestendBaseDeliveryFeeInWnds: u128 = 1925196628010; + + /// Transaction fee that is paid at the Westend BridgeHub for delivering single outbound message confirmation. + /// (initially was calculated by test `BridgeHubWestend::can_calculate_fee_for_complex_message_confirmation_transaction` + `33%`) + pub const BridgeHubWestendBaseConfirmationFeeInWnds: u128 = 1848016628010; +} diff --git a/bridges/primitives/chain-bridge-hub-wococo/src/lib.rs b/bridges/primitives/chain-bridge-hub-wococo/src/lib.rs index 750a8f7ecb9d..c8bd397cec56 100644 --- a/bridges/primitives/chain-bridge-hub-wococo/src/lib.rs +++ b/bridges/primitives/chain-bridge-hub-wococo/src/lib.rs @@ -73,3 +73,18 @@ pub const WITH_BRIDGE_WOCOCO_TO_ROCOCO_MESSAGES_PALLET_INDEX: u8 = 45; decl_bridge_finality_runtime_apis!(bridge_hub_wococo); decl_bridge_messages_runtime_apis!(bridge_hub_wococo); + +frame_support::parameter_types! { + /// The XCM fee that is paid for executing XCM program (with `ExportMessage` instruction) at the Wococo + /// BridgeHub. + /// (initially was calculated by test `BridgeHubWococo::can_calculate_weight_for_paid_export_message_with_reserve_transfer` + `33%`) + pub const BridgeHubWococoBaseXcmFeeInWocs: u128 = 1624803349; + + /// Transaction fee that is paid at the Wococo BridgeHub for delivering single inbound message. + /// (initially was calculated by test `BridgeHubWococo::can_calculate_fee_for_complex_message_delivery_transaction` + `33%`) + pub const BridgeHubWococoBaseDeliveryFeeInWocs: u128 = 6417262881; + + /// Transaction fee that is paid at the Wococo BridgeHub for delivering single outbound message confirmation. + /// (initially was calculated by test `BridgeHubWococo::can_calculate_fee_for_complex_message_confirmation_transaction` + `33%`) + pub const BridgeHubWococoBaseConfirmationFeeInWocs: u128 = 6159996668; +} diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml b/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml index 764d0eb989fa..7efcf1e1585d 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml @@ -88,6 +88,7 @@ bp-asset-hub-westend = { path = "../../../../../bridges/primitives/chain-asset-h bp-asset-hub-wococo = { path = "../../../../../bridges/primitives/chain-asset-hub-wococo", default-features = false } bp-bridge-hub-rococo = { path = "../../../../../bridges/primitives/chain-bridge-hub-rococo", default-features = false } bp-bridge-hub-wococo = { path = "../../../../../bridges/primitives/chain-bridge-hub-wococo", default-features = false } +bp-bridge-hub-westend = { path = "../../../../../bridges/primitives/chain-bridge-hub-westend", default-features = false } [dev-dependencies] asset-test-utils = { path = "../test-utils" } @@ -177,6 +178,7 @@ std = [ "bp-asset-hub-wococo/std", "bp-bridge-hub-rococo/std", "bp-bridge-hub-wococo/std", + "bp-bridge-hub-westend/std", "codec/std", "cumulus-pallet-aura-ext/std", "cumulus-pallet-dmp-queue/std", diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs index a17d0a6cf0c3..ff37ff74e750 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs @@ -283,6 +283,9 @@ impl Contains for SafeCallFilter { match call { RuntimeCall::System(frame_system::Call::set_storage { items }) if items.iter().all(|(k, _)| k.eq(&bridging::XcmBridgeHubRouterByteFee::key())) || + items + .iter() + .all(|(k, _)| k.eq(&bridging::XcmBridgeHubRouterBaseFee::key())) || items.iter().all(|(k, _)| k.eq(&Flavor::key())) => return true, _ => (), @@ -760,6 +763,25 @@ pub mod bridging { // common/shared parameters for Wococo/Rococo parameter_types! { + /// Base price of every byte of the Rococo -> Westend message. Can be adjusted via + /// governance `set_storage` call. + /// + /// Default value is our estimation of the: + /// + /// 1) an approximate cost of XCM execution (`ExportMessage` and surroundings) at Rococo bridge hub; + /// + /// 2) the approximate cost of Rococo -> Westend message delivery transaction on Westend Bridge Hub, + /// converted into ROCs using 1:1 conversion rate; + /// + /// 3) the approximate cost of Rococo -> Westend message confirmation transaction on Rococo Bridge Hub. + pub storage XcmBridgeHubRouterBaseFee: Balance = + bp_bridge_hub_rococo::BridgeHubRococoBaseXcmFeeInRocs::get() + .saturating_add(bp_bridge_hub_westend::BridgeHubWestendBaseDeliveryFeeInWnds::get()) + .saturating_add(bp_bridge_hub_rococo::BridgeHubRococoBaseConfirmationFeeInRocs::get()); + /// Price of every byte of the Rococo -> Westend message. Can be adjusted via + /// governance `set_storage` call. + pub storage XcmBridgeHubRouterByteFee: Balance = TransactionByteFee::get(); + pub SiblingBridgeHubParaId: u32 = match Flavor::get() { RuntimeFlavor::Rococo => bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, RuntimeFlavor::Wococo => bp_bridge_hub_wococo::BRIDGE_HUB_WOCOCO_PARACHAIN_ID, @@ -768,8 +790,6 @@ pub mod bridging { /// Router expects payment with this `AssetId`. /// (`AssetId` has to be aligned with `BridgeTable`) pub XcmBridgeHubRouterFeeAssetId: AssetId = TokenLocation::get().into(); - /// Price per byte - can be adjusted via governance `set_storage` call. - pub storage XcmBridgeHubRouterByteFee: Balance = TransactionByteFee::get(); pub BridgeTable: sp_std::vec::Vec = sp_std::vec::Vec::new().into_iter() @@ -814,7 +834,7 @@ pub mod bridging { // base delivery fee to local `BridgeHub` Some(( XcmBridgeHubRouterFeeAssetId::get(), - bp_asset_hub_rococo::BridgeHubRococoBaseFeeInRocs::get(), + XcmBridgeHubRouterBaseFee::get(), ).into()) ) ]; @@ -890,7 +910,7 @@ pub mod bridging { // base delivery fee to local `BridgeHub` Some(( XcmBridgeHubRouterFeeAssetId::get(), - bp_asset_hub_rococo::BridgeHubRococoBaseFeeInRocs::get(), + XcmBridgeHubRouterBaseFee::get(), ).into()) ) ]; @@ -966,7 +986,7 @@ pub mod bridging { // base delivery fee to local `BridgeHub` Some(( XcmBridgeHubRouterFeeAssetId::get(), - bp_asset_hub_wococo::BridgeHubWococoBaseFeeInWocs::get(), + XcmBridgeHubRouterBaseFee::get(), ).into()) ) ]; diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml b/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml index dbcb0be3cb7f..49b80b067cfb 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml @@ -84,6 +84,7 @@ assets-common = { path = "../common", default-features = false } pallet-xcm-bridge-hub-router = { path = "../../../../../bridges/modules/xcm-bridge-hub-router", default-features = false } bp-asset-hub-rococo = { path = "../../../../../bridges/primitives/chain-asset-hub-rococo", default-features = false } bp-asset-hub-westend = { path = "../../../../../bridges/primitives/chain-asset-hub-westend", default-features = false } +bp-bridge-hub-rococo = { path = "../../../../../bridges/primitives/chain-bridge-hub-rococo", default-features = false } bp-bridge-hub-westend = { path = "../../../../../bridges/primitives/chain-bridge-hub-westend", default-features = false } [dev-dependencies] @@ -167,6 +168,7 @@ std = [ "assets-common/std", "bp-asset-hub-rococo/std", "bp-asset-hub-westend/std", + "bp-bridge-hub-rococo/std", "bp-bridge-hub-westend/std", "codec/std", "cumulus-pallet-aura-ext/std", diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs index 526c8eb4af42..ec42618513a5 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs @@ -262,7 +262,10 @@ impl Contains for SafeCallFilter { // Allow to change dedicated storage items (called by governance-like) match call { RuntimeCall::System(frame_system::Call::set_storage { items }) - if items.iter().all(|(k, _)| k.eq(&bridging::XcmBridgeHubRouterByteFee::key())) => + if items.iter().all(|(k, _)| k.eq(&bridging::XcmBridgeHubRouterByteFee::key())) || + items + .iter() + .all(|(k, _)| k.eq(&bridging::XcmBridgeHubRouterBaseFee::key())) => return true, _ => (), }; @@ -725,13 +728,30 @@ pub mod bridging { use sp_std::collections::btree_set::BTreeSet; parameter_types! { + /// Base price of every byte of the Westend -> Rococo message. Can be adjusted via + /// governance `set_storage` call. + /// + /// Default value is our estimation of the: + /// + /// 1) an approximate cost of XCM execution (`ExportMessage` and surroundings) at Westend bridge hub; + /// + /// 2) the approximate cost of Westend -> Rococo message delivery transaction on Rococo Bridge Hub, + /// converted into WNDs using 1:1 conversion rate; + /// + /// 3) the approximate cost of Westend -> Rococo message confirmation transaction on Westend Bridge Hub. + pub storage XcmBridgeHubRouterBaseFee: Balance = + bp_bridge_hub_westend::BridgeHubWestendBaseXcmFeeInWnds::get() + .saturating_add(bp_bridge_hub_rococo::BridgeHubRococoBaseDeliveryFeeInRocs::get()) + .saturating_add(bp_bridge_hub_westend::BridgeHubWestendBaseConfirmationFeeInWnds::get()); + /// Price of every byte of the Westend -> Rococo message. Can be adjusted via + /// governance `set_storage` call. + pub storage XcmBridgeHubRouterByteFee: Balance = TransactionByteFee::get(); + pub SiblingBridgeHubParaId: u32 = bp_bridge_hub_westend::BRIDGE_HUB_WESTEND_PARACHAIN_ID; pub SiblingBridgeHub: MultiLocation = MultiLocation::new(1, X1(Parachain(SiblingBridgeHubParaId::get()))); /// Router expects payment with this `AssetId`. /// (`AssetId` has to be aligned with `BridgeTable`) pub XcmBridgeHubRouterFeeAssetId: AssetId = WestendLocation::get().into(); - /// Price per byte - can be adjusted via governance `set_storage` call. - pub storage XcmBridgeHubRouterByteFee: Balance = TransactionByteFee::get(); pub BridgeTable: sp_std::vec::Vec = sp_std::vec::Vec::new().into_iter() @@ -774,7 +794,7 @@ pub mod bridging { // base delivery fee to local `BridgeHub` Some(( XcmBridgeHubRouterFeeAssetId::get(), - bp_asset_hub_westend::BridgeHubWestendBaseFeeInWnds::get(), + XcmBridgeHubRouterBaseFee::get(), ).into()) ) ]; diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs index b5b1c3778c00..65cca67dac19 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs @@ -23,10 +23,10 @@ use bridge_hub_rococo_runtime::{ xcm_config::{RelayNetwork, TokenLocation, XcmConfig}, AllPalletsWithoutSystem, BridgeRejectObsoleteHeadersAndMessages, Executive, ExistentialDeposit, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, SessionKeys, SignedExtra, - UncheckedExtrinsic, + TransactionPayment, UncheckedExtrinsic, }; use codec::{Decode, Encode}; -use frame_support::parameter_types; +use frame_support::{dispatch::GetDispatchInfo, parameter_types}; use frame_system::pallet_prelude::HeaderFor; use parachains_common::{rococo::fee::WeightToFee, AccountId, AuraId, Balance}; use sp_keyring::AccountKeyring::Alice; @@ -83,6 +83,13 @@ fn construct_and_apply_extrinsic( r.unwrap() } +fn construct_and_estimate_extrinsic_fee(batch: pallet_utility::Call) -> Balance { + let batch_call = RuntimeCall::Utility(batch); + let batch_info = batch_call.get_dispatch_info(); + let xt = construct_extrinsic(Alice, batch_call); + TransactionPayment::compute_fee(xt.encoded_size() as _, &batch_info, 0) +} + fn executive_init_block(header: &HeaderFor) { Executive::initialize_block(header) } @@ -213,7 +220,7 @@ mod bridge_hub_rococo_tests { XCM_LANE_FOR_ASSET_HUB_ROCOCO_TO_ASSET_HUB_WOCOCO, Some((TokenLocation::get(), ExistentialDeposit::get()).into()), // value should be >= than value generated by `can_calculate_weight_for_paid_export_message_with_reserve_transfer` - Some((TokenLocation::get(), bp_asset_hub_rococo::BridgeHubRococoBaseFeeInRocs::get()).into()), + Some((TokenLocation::get(), bp_bridge_hub_rococo::BridgeHubRococoBaseXcmFeeInRocs::get()).into()), || (), ); // for Westend @@ -235,7 +242,7 @@ mod bridge_hub_rococo_tests { XCM_LANE_FOR_ASSET_HUB_ROCOCO_TO_ASSET_HUB_WESTEND, Some((TokenLocation::get(), ExistentialDeposit::get()).into()), // value should be >= than value generated by `can_calculate_weight_for_paid_export_message_with_reserve_transfer` - Some((TokenLocation::get(), bp_asset_hub_rococo::BridgeHubRococoBaseFeeInRocs::get()).into()), + Some((TokenLocation::get(), bp_bridge_hub_rococo::BridgeHubRococoBaseXcmFeeInRocs::get()).into()), || (), ) } @@ -401,10 +408,56 @@ mod bridge_hub_rococo_tests { >(); // check if estimated value is sane - let max_expected = bp_asset_hub_rococo::BridgeHubRococoBaseFeeInRocs::get(); + let max_expected = bp_bridge_hub_rococo::BridgeHubRococoBaseXcmFeeInRocs::get(); + assert!( + estimated <= max_expected, + "calculated: {:?}, max_expected: {:?}, please adjust `bp_bridge_hub_rococo::BridgeHubRococoBaseXcmFeeInRocs` value", + estimated, + max_expected + ); + } + + #[test] + pub fn can_calculate_fee_for_complex_message_delivery_transaction() { + let estimated = bridge_hub_test_utils::test_cases::can_calculate_fee_for_complex_message_delivery_transaction::< + Runtime, + BridgeGrandpaWestendInstance, + BridgeParachainWestendInstance, + WithBridgeHubWestendMessagesInstance, + WithBridgeHubWestendMessageBridge, + >( + collator_session_keys(), + construct_and_estimate_extrinsic_fee + ); + + // check if estimated value is sane + let max_expected = bp_bridge_hub_rococo::BridgeHubRococoBaseDeliveryFeeInRocs::get(); assert!( estimated <= max_expected, - "calculated: {:?}, max_expected: {:?}, please adjust `bp_asset_hub_rococo::BridgeHubRococoBaseFeeInRocs` value", + "calculated: {:?}, max_expected: {:?}, please adjust `bp_bridge_hub_rococo::BridgeHubRococoBaseDeliveryFeeInRocs` value", + estimated, + max_expected + ); + } + + #[test] + pub fn can_calculate_fee_for_complex_message_confirmation_transaction() { + let estimated = bridge_hub_test_utils::test_cases::can_calculate_fee_for_complex_message_confirmation_transaction::< + Runtime, + BridgeGrandpaWestendInstance, + BridgeParachainWestendInstance, + WithBridgeHubWestendMessagesInstance, + WithBridgeHubWestendMessageBridge, + >( + collator_session_keys(), + construct_and_estimate_extrinsic_fee + ); + + // check if estimated value is sane + let max_expected = bp_bridge_hub_rococo::BridgeHubRococoBaseConfirmationFeeInRocs::get(); + assert!( + estimated <= max_expected, + "calculated: {:?}, max_expected: {:?}, please adjust `bp_bridge_hub_rococo::BridgeHubRococoBaseConfirmationFeeInRocs` value", estimated, max_expected ); @@ -540,7 +593,7 @@ mod bridge_hub_wococo_tests { XCM_LANE_FOR_ASSET_HUB_WOCOCO_TO_ASSET_HUB_ROCOCO, Some((TokenLocation::get(), ExistentialDeposit::get()).into()), // value should be >= than value generated by `can_calculate_weight_for_paid_export_message_with_reserve_transfer` - Some((TokenLocation::get(), bp_asset_hub_wococo::BridgeHubWococoBaseFeeInWocs::get()).into()), + Some((TokenLocation::get(), bp_bridge_hub_wococo::BridgeHubWococoBaseXcmFeeInWocs::get()).into()), set_wococo_flavor, ) } @@ -633,10 +686,56 @@ mod bridge_hub_wococo_tests { >(); // check if estimated value is sane - let max_expected = bp_asset_hub_wococo::BridgeHubWococoBaseFeeInWocs::get(); + let max_expected = bp_bridge_hub_wococo::BridgeHubWococoBaseXcmFeeInWocs::get(); + assert!( + estimated <= max_expected, + "calculated: {:?}, max_expected: {:?}, please adjust `bp_bridge_hub_wococo::BridgeHubWococoBaseXcmFeeInWocs` value", + estimated, + max_expected + ); + } + + #[test] + pub fn can_calculate_fee_for_complex_message_delivery_transaction() { + let estimated = bridge_hub_test_utils::test_cases::can_calculate_fee_for_complex_message_delivery_transaction::< + Runtime, + BridgeGrandpaRococoInstance, + BridgeParachainRococoInstance, + WithBridgeHubRococoMessagesInstance, + WithBridgeHubRococoMessageBridge, + >( + collator_session_keys(), + construct_and_estimate_extrinsic_fee + ); + + // check if estimated value is sane + let max_expected = bp_bridge_hub_wococo::BridgeHubWococoBaseDeliveryFeeInWocs::get(); + assert!( + estimated <= max_expected, + "calculated: {:?}, max_expected: {:?}, please adjust `bp_bridge_hub_wococo::BridgeHubWococoBaseDeliveryFeeInWocs` value", + estimated, + max_expected + ); + } + + #[test] + pub fn can_calculate_fee_for_complex_message_confirmation_transaction() { + let estimated = bridge_hub_test_utils::test_cases::can_calculate_fee_for_complex_message_confirmation_transaction::< + Runtime, + BridgeGrandpaRococoInstance, + BridgeParachainRococoInstance, + WithBridgeHubRococoMessagesInstance, + WithBridgeHubRococoMessageBridge, + >( + collator_session_keys(), + construct_and_estimate_extrinsic_fee + ); + + // check if estimated value is sane + let max_expected = bp_bridge_hub_wococo::BridgeHubWococoBaseConfirmationFeeInWocs::get(); assert!( estimated <= max_expected, - "calculated: {:?}, max_expected: {:?}, please adjust `bp_asset_hub_wococo::BridgeHubWococoBaseFeeInWocs` value", + "calculated: {:?}, max_expected: {:?}, please adjust `bp_bridge_hub_wococo::BridgeHubWococoBaseConfirmationFeeInWocs` value", estimated, max_expected ); diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs index babd579f0022..16dcd10a2ca4 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs @@ -23,7 +23,7 @@ use bridge_hub_westend_runtime::{ xcm_config::{RelayNetwork, WestendLocation, XcmConfig}, AllPalletsWithoutSystem, BridgeRejectObsoleteHeadersAndMessages, Executive, ExistentialDeposit, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, SessionKeys, SignedExtra, - UncheckedExtrinsic, + TransactionPayment, UncheckedExtrinsic, }; use bridge_to_rococo_config::{ BridgeGrandpaRococoInstance, BridgeHubRococoChainId, BridgeParachainRococoInstance, @@ -31,7 +31,7 @@ use bridge_to_rococo_config::{ XCM_LANE_FOR_ASSET_HUB_WESTEND_TO_ASSET_HUB_ROCOCO, }; use codec::{Decode, Encode}; -use frame_support::parameter_types; +use frame_support::{dispatch::GetDispatchInfo, parameter_types}; use frame_system::pallet_prelude::HeaderFor; use parachains_common::{westend::fee::WeightToFee, AccountId, AuraId, Balance}; use sp_keyring::AccountKeyring::Alice; @@ -84,6 +84,13 @@ fn construct_and_apply_extrinsic( r.unwrap() } +fn construct_and_estimate_extrinsic_fee(batch: pallet_utility::Call) -> Balance { + let batch_call = RuntimeCall::Utility(batch); + let batch_info = batch_call.get_dispatch_info(); + let xt = construct_extrinsic(Alice, batch_call); + TransactionPayment::compute_fee(xt.encoded_size() as _, &batch_info, 0) +} + fn executive_init_block(header: &HeaderFor) { Executive::initialize_block(header) } @@ -182,7 +189,7 @@ fn handle_export_message_from_system_parachain_add_to_outbound_queue_works() { XCM_LANE_FOR_ASSET_HUB_WESTEND_TO_ASSET_HUB_ROCOCO, Some((WestendLocation::get(), ExistentialDeposit::get()).into()), // value should be >= than value generated by `can_calculate_weight_for_paid_export_message_with_reserve_transfer` - Some((WestendLocation::get(), bp_asset_hub_westend::BridgeHubWestendBaseFeeInWnds::get()).into()), + Some((WestendLocation::get(), bp_bridge_hub_westend::BridgeHubWestendBaseXcmFeeInWnds::get()).into()), || (), ) } @@ -275,11 +282,57 @@ pub fn can_calculate_weight_for_paid_export_message_with_reserve_transfer() { >(); // check if estimated value is sane - let max_expected = bp_asset_hub_westend::BridgeHubWestendBaseFeeInWnds::get(); + let max_expected = bp_bridge_hub_westend::BridgeHubWestendBaseXcmFeeInWnds::get(); assert!( estimated <= max_expected, - "calculated: {:?}, max_expected: {:?}, please adjust `bp_asset_hub_westend::BridgeHubWestendBaseFeeInWnds` value", + "calculated: {:?}, max_expected: {:?}, please adjust `bp_bridge_hub_westend::BridgeHubWestendBaseXcmFeeInWnds` value", estimated, max_expected ); } + +#[test] +pub fn can_calculate_fee_for_complex_message_delivery_transaction() { + let estimated = bridge_hub_test_utils::test_cases::can_calculate_fee_for_complex_message_delivery_transaction::< + Runtime, + BridgeGrandpaRococoInstance, + BridgeParachainRococoInstance, + WithBridgeHubRococoMessagesInstance, + WithBridgeHubRococoMessageBridge, + >( + collator_session_keys(), + construct_and_estimate_extrinsic_fee + ); + + // check if estimated value is sane + let max_expected = bp_bridge_hub_westend::BridgeHubWestendBaseDeliveryFeeInWnds::get(); + assert!( + estimated <= max_expected, + "calculated: {:?}, max_expected: {:?}, please adjust `bp_bridge_hub_westend::BridgeHubWestendBaseDeliveryFeeInWnds` value", + estimated, + max_expected + ); +} + +#[test] +pub fn can_calculate_fee_for_complex_message_confirmation_transaction() { + let estimated = bridge_hub_test_utils::test_cases::can_calculate_fee_for_complex_message_confirmation_transaction::< + Runtime, + BridgeGrandpaRococoInstance, + BridgeParachainRococoInstance, + WithBridgeHubRococoMessagesInstance, + WithBridgeHubRococoMessageBridge, + >( + collator_session_keys(), + construct_and_estimate_extrinsic_fee + ); + + // check if estimated value is sane + let max_expected = bp_bridge_hub_westend::BridgeHubWestendBaseConfirmationFeeInWnds::get(); + assert!( + estimated <= max_expected, + "calculated: {:?}, max_expected: {:?}, please adjust `bp_bridge_hub_westend::BridgeHubWestendBaseConfirmationFeeInWnds` value", + estimated, + max_expected + ); +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases.rs index 7281680416b1..b421eea6bcf6 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases.rs @@ -17,8 +17,9 @@ //! Module contains predefined test-case scenarios for `Runtime` with bridging capabilities. use bp_messages::{ + source_chain::TargetHeaderChain, target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch, SourceHeaderChain}, - LaneId, MessageKey, OutboundLaneData, Weight, + LaneId, MessageKey, OutboundLaneData, UnrewardedRelayersState, Weight, }; use bp_parachains::{BestParaHeadHash, ParaInfo}; use bp_polkadot_core::parachains::{ParaHash, ParaId}; @@ -27,9 +28,13 @@ use bp_runtime::{HeaderOf, Parachain, StorageProofSize, UnderlyingChainOf}; use bp_test_utils::{make_default_justification, prepare_parachain_heads_proof}; use bridge_runtime_common::{ messages::{ - target::FromBridgedChainMessagesProof, BridgedChain as MessageBridgedChain, MessageBridge, + source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, + BridgedChain as MessageBridgedChain, MessageBridge, + }, + messages_generation::{ + encode_all_messages, encode_lane_data, prepare_message_delivery_storage_proof, + prepare_messages_storage_proof, }, - messages_generation::{encode_all_messages, encode_lane_data, prepare_messages_storage_proof}, messages_xcm_extension::{XcmAsPlainPayload, XcmBlobMessageDispatchResult}, }; use codec::Encode; @@ -399,6 +404,7 @@ pub fn relayed_incoming_message_works::BridgedChain: Send + Sync + 'static, + ::ThisChain: Send + Sync + 'static, UnderlyingChainOf>: bp_runtime::Chain + Parachain, XcmConfig: xcm_executor::Config, HrmpChannelOpener: frame_support::inherent::ProvideInherent< @@ -484,7 +490,11 @@ pub fn relayed_incoming_message_works, MB, ()>( + ) = test_data::make_complex_relayer_delivery_proofs::< + >::BridgedChain, + MB, + (), + >( lane_id, xcm.into(), message_nonce, @@ -626,6 +636,7 @@ pub fn complex_relay_extrinsic_works::BridgedChain: Send + Sync + 'static, + ::ThisChain: Send + Sync + 'static, UnderlyingChainOf>: bp_runtime::Chain + Parachain, XcmConfig: xcm_executor::Config, HrmpChannelOpener: frame_support::inherent::ProvideInherent< @@ -725,7 +736,11 @@ pub fn complex_relay_extrinsic_works, MB, ()>( + ) = test_data::make_complex_relayer_delivery_proofs::< + >::BridgedChain, + MB, + (), + >( lane_id, xcm.clone().into(), message_nonce, @@ -735,27 +750,15 @@ pub fn complex_relay_extrinsic_works::submit_finality_proof { - finality_target: Box::new(relay_chain_header.clone()), - justification: grandpa_justification, - }; - let submit_para_head = - pallet_bridge_parachains::Call::::submit_parachain_heads { - at_relay_block: (relay_header_number, relay_chain_header.hash().into()), - parachains: parachain_heads, - parachain_heads_proof: para_heads_proof, - }; - let submit_message = - pallet_bridge_messages::Call::::receive_messages_proof { - relayer_id_at_bridged_chain: relayer_id_on_source.into(), - proof: message_proof.into(), - messages_count: 1, - dispatch_weight: Weight::from_parts(1000000000, 0), - }; - let batch = pallet_utility::Call::::batch_all { - calls: vec![submit_grandpa.into(), submit_para_head.into(), submit_message.into()], - }; + let relay_chain_header_hash = relay_chain_header.hash(); + let batch = test_data::make_complex_relayer_delivery_batch::( + relay_chain_header, + grandpa_justification, + parachain_heads, + para_heads_proof, + message_proof, + relayer_id_on_source, + ); // sanity checks - before relayer extrinsic assert!(RuntimeHelper::>::take_xcm( @@ -790,10 +793,10 @@ pub fn complex_relay_extrinsic_works>::get().unwrap().1, - relay_chain_header.hash() + relay_chain_header_hash ); assert!(>::contains_key( - relay_chain_header.hash() + relay_chain_header_hash )); // verify parachain head proof correctly imported assert_eq!( @@ -839,7 +842,7 @@ pub fn complex_relay_extrinsic_works( + collator_session_key: CollatorSessionKeys, + compute_extrinsic_fee: fn(pallet_utility::Call::) -> u128, +) -> u128 +where + Runtime: frame_system::Config + + pallet_balances::Config + + pallet_session::Config + + pallet_xcm::Config + + parachain_info::Config + + pallet_collator_selection::Config + + cumulus_pallet_parachain_system::Config + + pallet_bridge_grandpa::Config + + pallet_bridge_parachains::Config + + pallet_bridge_messages::Config + + pallet_utility::Config, + GPI: 'static, + PPI: 'static, + MPI: 'static, + MB: MessageBridge, + ::BridgedChain: Send + Sync + 'static, + ::ThisChain: Send + Sync + 'static, + UnderlyingChainOf>: bp_runtime::Chain + Parachain, + ValidatorIdOf: From>, + <>::SourceHeaderChain as SourceHeaderChain>::MessagesProof: + From>, + <>::BridgedChain as bp_runtime::Chain>::Hash: From, + ParaHash: From<<>::BridgedChain as bp_runtime::Chain>::Hash>, + ::AccountId: + Into<<::RuntimeOrigin as OriginTrait>::AccountId>, + ::AccountId: From, + AccountIdOf: From, + >::InboundRelayer: From, + ::RuntimeCall: + From> + + From> + + From>, +{ + ExtBuilder::::default() + .with_collators(collator_session_key.collators()) + .with_session_keys(collator_session_key.session_keys()) + .with_safe_xcm_version(XCM_VERSION) + .with_para_id(1000.into()) + .with_tracing() + .build() + .execute_with(|| { + // generate bridged relay chain finality, parachain heads and message proofs, + // to be submitted by relayer to this chain. + // + // we don't care about parameter values here, apart from the XCM message size. But we + // do not need to have a large message here, because we're charging for every byte of + // the message additionally + let ( + relay_chain_header, + grandpa_justification, + _, + parachain_heads, + para_heads_proof, + message_proof, + ) = test_data::make_complex_relayer_delivery_proofs::< + >::BridgedChain, + MB, + (), + >( + LaneId::default(), + vec![xcm::v3::Instruction::<()>::ClearOrigin; 1_024].into(), + 1, + X2(GlobalConsensus(Polkadot), Parachain(1_000)), + 1, + 5, + 1_000, + ); + + // generate batch call that provides finality for bridged relay and parachains + message + // proof + let batch = test_data::make_complex_relayer_delivery_batch::( + relay_chain_header, + grandpa_justification, + parachain_heads, + para_heads_proof, + message_proof, + Dave.public().into(), + ); + let estimated_fee = compute_extrinsic_fee(batch); + + log::error!( + target: "bridges::estimate", + "Estimate fee: {:?} for single message delivery for runtime: {:?}", + estimated_fee, + Runtime::Version::get(), + ); + + estimated_fee + }) +} + +/// Estimates transaction fee for default message confirmation transaction (batched with required +/// proofs) from bridged parachain. +pub fn can_calculate_fee_for_complex_message_confirmation_transaction( + collator_session_key: CollatorSessionKeys, + compute_extrinsic_fee: fn(pallet_utility::Call::) -> u128, +) -> u128 +where + Runtime: frame_system::Config + + pallet_balances::Config + + pallet_session::Config + + pallet_xcm::Config + + parachain_info::Config + + pallet_collator_selection::Config + + cumulus_pallet_parachain_system::Config + + pallet_bridge_grandpa::Config + + pallet_bridge_parachains::Config + + pallet_bridge_messages::Config + + pallet_utility::Config, + GPI: 'static, + PPI: 'static, + MPI: 'static, + MB: MessageBridge, + ::BridgedChain: Send + Sync + 'static, + ::ThisChain: Send + Sync + 'static, + <::ThisChain as bp_runtime::Chain>::AccountId: From, + UnderlyingChainOf>: bp_runtime::Chain + Parachain, + ValidatorIdOf: From>, + <>::SourceHeaderChain as SourceHeaderChain>::MessagesProof: + From>, + <>::BridgedChain as bp_runtime::Chain>::Hash: From, + ParaHash: From<<>::BridgedChain as bp_runtime::Chain>::Hash>, + ::AccountId: + Into<<::RuntimeOrigin as OriginTrait>::AccountId>, + ::AccountId: From, + AccountIdOf: From, + >::InboundRelayer: From, + <>::TargetHeaderChain as TargetHeaderChain< + XcmAsPlainPayload, + Runtime::AccountId, + >>::MessagesDeliveryProof: From>, + ::RuntimeCall: + From> + + From> + + From>, +{ + ExtBuilder::::default() + .with_collators(collator_session_key.collators()) + .with_session_keys(collator_session_key.session_keys()) + .with_safe_xcm_version(XCM_VERSION) + .with_para_id(1000.into()) + .with_tracing() + .build() + .execute_with(|| { + // generate bridged relay chain finality, parachain heads and message proofs, + // to be submitted by relayer to this chain. + let unrewarded_relayers = UnrewardedRelayersState { + unrewarded_relayer_entries: 1, + total_messages: 1, + ..Default::default() + }; + let ( + relay_chain_header, + grandpa_justification, + _, + parachain_heads, + para_heads_proof, + message_delivery_proof, + ) = test_data::make_complex_relayer_confirmation_proofs::< + >::BridgedChain, + MB, + (), + >(LaneId::default(), 1, 5, 1_000, Alice.public().into(), unrewarded_relayers.clone()); + + // generate batch call that provides finality for bridged relay and parachains + message + // proof + let batch = test_data::make_complex_relayer_confirmation_batch::( + relay_chain_header, + grandpa_justification, + parachain_heads, + para_heads_proof, + message_delivery_proof, + unrewarded_relayers, + ); + let estimated_fee = compute_extrinsic_fee(batch); + + log::error!( + target: "bridges::estimate", + "Estimate fee: {:?} for single message confirmation for runtime: {:?}", + estimated_fee, + Runtime::Version::get(), + ); + + estimated_fee + }) +} + pub mod test_data { use super::*; - use bp_header_chain::justification::GrandpaJustification; - use bp_messages::MessageNonce; + use bp_header_chain::{justification::GrandpaJustification, ChainWithGrandpa}; + use bp_messages::{DeliveredMessages, InboundLaneData, MessageNonce, UnrewardedRelayer}; use bp_polkadot_core::parachains::{ParaHash, ParaHead, ParaHeadsProof, ParaId}; - use bp_runtime::BasicOperatingMode; + use bp_runtime::{BasicOperatingMode, HashOf}; use bp_test_utils::authority_list; + use sp_runtime::{DigestItem, SaturatedConversion}; use xcm_builder::{HaulBlob, HaulBlobError, HaulBlobExporter}; use xcm_executor::traits::{validate_export, ExportXcm}; @@ -968,7 +1166,117 @@ pub mod test_data { (location, xcm).encode().encode() } - pub fn make_complex_relayer_proofs( + /// Prepare a batch call with relay finality proof, parachain head proof and message proof. + pub fn make_complex_relayer_delivery_batch( + relay_chain_header: BridgedHeader, + grandpa_justification: GrandpaJustification>, + parachain_heads: Vec<(ParaId, ParaHash)>, + para_heads_proof: ParaHeadsProof, + message_proof: FromBridgedChainMessagesProof, + relayer_id_at_bridged_chain: AccountId32, + ) -> pallet_utility::Call where + Runtime:pallet_bridge_grandpa::Config + + pallet_bridge_parachains::Config + + pallet_bridge_messages::Config + + pallet_utility::Config, + GPI: 'static, + PPI: 'static, + MPI: 'static, + ParaHash: From<<>::BridgedChain as bp_runtime::Chain>::Hash>, + <>::BridgedChain as bp_runtime::Chain>::Hash: From, + <>::SourceHeaderChain as SourceHeaderChain>::MessagesProof: + From>, + >::InboundRelayer: From, + ::RuntimeCall: + From> + + From> + + From>, + { + let relay_chain_header_hash = relay_chain_header.hash(); + let relay_chain_header_number = *relay_chain_header.number(); + let submit_grandpa = pallet_bridge_grandpa::Call::::submit_finality_proof { + finality_target: Box::new(relay_chain_header), + justification: grandpa_justification, + }; + let submit_para_head = + pallet_bridge_parachains::Call::::submit_parachain_heads { + at_relay_block: ( + relay_chain_header_number.saturated_into(), + relay_chain_header_hash.into(), + ), + parachains: parachain_heads, + parachain_heads_proof: para_heads_proof, + }; + let submit_message = pallet_bridge_messages::Call::::receive_messages_proof { + relayer_id_at_bridged_chain: relayer_id_at_bridged_chain.into(), + proof: message_proof.into(), + messages_count: 1, + dispatch_weight: Weight::from_parts(1000000000, 0), + }; + pallet_utility::Call::::batch_all { + calls: vec![submit_grandpa.into(), submit_para_head.into(), submit_message.into()], + } + } + + /// Prepare a batch call with relay finality proof, parachain head proof and message delivery + /// proof. + pub fn make_complex_relayer_confirmation_batch( + relay_chain_header: BridgedHeader, + grandpa_justification: GrandpaJustification>, + parachain_heads: Vec<(ParaId, ParaHash)>, + para_heads_proof: ParaHeadsProof, + message_delivery_proof: FromBridgedChainMessagesDeliveryProof, + relayers_state: UnrewardedRelayersState, + ) -> pallet_utility::Call where + Runtime:pallet_bridge_grandpa::Config + + pallet_bridge_parachains::Config + + pallet_bridge_messages::Config + + pallet_utility::Config, + GPI: 'static, + PPI: 'static, + MPI: 'static, + ParaHash: From<<>::BridgedChain as bp_runtime::Chain>::Hash>, + <>::BridgedChain as bp_runtime::Chain>::Hash: From, + <>::TargetHeaderChain as TargetHeaderChain< + XcmAsPlainPayload, + Runtime::AccountId, + >>::MessagesDeliveryProof: From>, + ::RuntimeCall: + From> + + From> + + From>, + { + let relay_chain_header_hash = relay_chain_header.hash(); + let relay_chain_header_number = *relay_chain_header.number(); + let submit_grandpa = pallet_bridge_grandpa::Call::::submit_finality_proof { + finality_target: Box::new(relay_chain_header), + justification: grandpa_justification, + }; + let submit_para_head = + pallet_bridge_parachains::Call::::submit_parachain_heads { + at_relay_block: ( + relay_chain_header_number.saturated_into(), + relay_chain_header_hash.into(), + ), + parachains: parachain_heads, + parachain_heads_proof: para_heads_proof, + }; + let submit_message_delivery_proof = + pallet_bridge_messages::Call::::receive_messages_delivery_proof { + proof: message_delivery_proof.into(), + relayers_state, + }; + pallet_utility::Call::::batch_all { + calls: vec![ + submit_grandpa.into(), + submit_para_head.into(), + submit_message_delivery_proof.into(), + ], + } + } + + /// Prepare storage proofs of messages, stored at the source chain. + pub fn make_complex_relayer_delivery_proofs( lane_id: LaneId, xcm_message: Xcm, message_nonce: MessageNonce, @@ -977,18 +1285,19 @@ pub mod test_data { relay_header_number: u32, bridged_para_id: u32, ) -> ( - BridgedRelayHeader, - GrandpaJustification, + HeaderOf, + GrandpaJustification>, ParaHead, Vec<(ParaId, ParaHash)>, ParaHeadsProof, FromBridgedChainMessagesProof, ) where - BridgedRelayHeader: HeaderT, - ::Hash: From, + BridgedRelayChain: ChainWithGrandpa, + HashOf: From, MB: MessageBridge, ::BridgedChain: Send + Sync + 'static, + ::ThisChain: Send + Sync + 'static, UnderlyingChainOf>: bp_runtime::Chain + Parachain, { let message_payload = prepare_inbound_xcm(xcm_message, message_destination); @@ -1004,19 +1313,18 @@ pub mod test_data { encode_lane_data, ); - let bridged_para_head = ParaHead( - bp_test_utils::test_header_with_root::>( - para_header_number.into(), - para_state_root.into(), - ) - .encode(), + let ( + relay_chain_header, + justification, + bridged_para_head, + parachain_heads, + para_heads_proof, + ) = make_complex_bridged_heads_proof::( + para_state_root, + para_header_number, + relay_header_number, + bridged_para_id, ); - let (relay_state_root, para_heads_proof, parachain_heads) = - prepare_parachain_heads_proof::>(vec![( - bridged_para_id, - bridged_para_head.clone(), - )]); - assert_eq!(bridged_para_head.hash(), parachain_heads[0].1); let message_proof = FromBridgedChainMessagesProof { bridged_header_hash: bridged_para_head.hash(), @@ -1026,12 +1334,6 @@ pub mod test_data { nonces_end: message_nonce, }; - // import bridged relay chain block#1 with state root containing head#5 of bridged parachain - let relay_chain_header: BridgedRelayHeader = bp_test_utils::test_header_with_root( - relay_header_number.into(), - relay_state_root.into(), - ); - let justification = make_default_justification(&relay_chain_header); ( relay_chain_header, justification, @@ -1042,6 +1344,134 @@ pub mod test_data { ) } + /// Prepare storage proofs of message confirmations, stored at the target chain. + pub fn make_complex_relayer_confirmation_proofs( + lane_id: LaneId, + para_header_number: u32, + relay_header_number: u32, + bridged_para_id: u32, + relayer_id_at_this_chain: AccountId32, + relayers_state: UnrewardedRelayersState, + ) -> ( + HeaderOf, + GrandpaJustification>, + ParaHead, + Vec<(ParaId, ParaHash)>, + ParaHeadsProof, + FromBridgedChainMessagesDeliveryProof, + ) + where + BridgedRelayChain: ChainWithGrandpa, + HashOf: From, + MB: MessageBridge, + ::BridgedChain: Send + Sync + 'static, + ::ThisChain: Send + Sync + 'static, + <::ThisChain as bp_runtime::Chain>::AccountId: From, + UnderlyingChainOf>: bp_runtime::Chain + Parachain, + { + // prepare para storage proof containing message delivery proof + let (para_state_root, para_storage_proof) = prepare_message_delivery_storage_proof::( + lane_id, + InboundLaneData { + relayers: vec![ + UnrewardedRelayer { + relayer: relayer_id_at_this_chain.into(), + messages: DeliveredMessages::new(1) + }; + relayers_state.unrewarded_relayer_entries as usize + ] + .into(), + last_confirmed_nonce: 1, + }, + StorageProofSize::Minimal(0), + ); + + let ( + relay_chain_header, + justification, + bridged_para_head, + parachain_heads, + para_heads_proof, + ) = make_complex_bridged_heads_proof::( + para_state_root, + para_header_number, + relay_header_number, + bridged_para_id, + ); + + let message_delivery_proof = FromBridgedChainMessagesDeliveryProof { + bridged_header_hash: bridged_para_head.hash(), + storage_proof: para_storage_proof, + lane: lane_id, + }; + + ( + relay_chain_header, + justification, + bridged_para_head, + parachain_heads, + para_heads_proof, + message_delivery_proof, + ) + } + + /// Make bridged parachain header with given state root and relay header that is finalizing it. + pub fn make_complex_bridged_heads_proof( + para_state_root: ParaHash, + para_header_number: u32, + relay_header_number: u32, + bridged_para_id: u32, + ) -> ( + HeaderOf, + GrandpaJustification>, + ParaHead, + Vec<(ParaId, ParaHash)>, + ParaHeadsProof, + ) + where + BridgedRelayChain: ChainWithGrandpa, + HashOf: From, + MB: MessageBridge, + ::BridgedChain: Send + Sync + 'static, + ::ThisChain: Send + Sync + 'static, + UnderlyingChainOf>: bp_runtime::Chain + Parachain, + { + let bridged_para_head = ParaHead( + bp_test_utils::test_header_with_root::>( + para_header_number.into(), + para_state_root.into(), + ) + .encode(), + ); + let (relay_state_root, para_heads_proof, parachain_heads) = + prepare_parachain_heads_proof::>(vec![( + bridged_para_id, + bridged_para_head.clone(), + )]); + assert_eq!(bridged_para_head.hash(), parachain_heads[0].1); + + // import bridged relay chain block#1 with state root containing head#5 of bridged parachain + let mut relay_chain_header: BridgedRelayChain::Header = + bp_test_utils::test_header_with_root( + relay_header_number.into(), + relay_state_root.into(), + ); + // to compute proper cost of GRANDPA call, let's add some dummy bytes to header, so that the + // `submit_finality_proof` call size would be close to maximal expected (and refundable) + let expected_bytes_in_grandpa_call = BridgedRelayChain::AVERAGE_HEADER_SIZE_IN_JUSTIFICATION + .saturating_mul(BridgedRelayChain::REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY) + .saturating_add(BridgedRelayChain::MAX_HEADER_SIZE) + as usize; + let extra_bytes_required = + expected_bytes_in_grandpa_call.saturating_sub(relay_chain_header.encoded_size()); + relay_chain_header + .digest_mut() + .push(DigestItem::Other(vec![42; extra_bytes_required])); + + let justification = make_default_justification(&relay_chain_header); + (relay_chain_header, justification, bridged_para_head, parachain_heads, para_heads_proof) + } + /// Helper that creates InitializationData mock data, that can be used to initialize bridge /// GRANDPA pallet pub fn initialization_data< From e9987401f3e8edba1b831bd1874a44ace7d58d13 Mon Sep 17 00:00:00 2001 From: Javier Bullrich Date: Fri, 3 Nov 2023 13:43:29 +0100 Subject: [PATCH 03/31] skip trigger for review bot on draft PRs (#2145) Added if condition on review-bot's trigger so it does not trigger in `draft` PRs. --- .github/workflows/review-trigger.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/review-trigger.yml b/.github/workflows/review-trigger.yml index 40e2918df2b2..1ae6b79ffbda 100644 --- a/.github/workflows/review-trigger.yml +++ b/.github/workflows/review-trigger.yml @@ -13,6 +13,7 @@ on: jobs: trigger-review-bot: + if: github.event.pull_request.draft != true runs-on: ubuntu-latest name: trigger review bot steps: From dca142398e00786429d69eb8b9e4454499962b2a Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe <49718502+alexggh@users.noreply.github.com> Date: Fri, 3 Nov 2023 15:26:40 +0200 Subject: [PATCH 04/31] substrate: sysinfo: Expose failed hardware requirements (#2144) The check_hardware functions does not give us too much information as to what is failing, so let's return the list of failed metrics, so that callers can print it. This would make debugging easier, rather than try to guess which dimension is actually failing. Signed-off-by: Alexandru Gheorghe --- Cargo.lock | 1 + .../parachain-template/node/src/service.rs | 10 ++- cumulus/polkadot-parachain/src/service.rs | 7 +- polkadot/node/service/src/lib.rs | 12 ++-- substrate/bin/node/cli/src/service.rs | 12 ++-- substrate/client/sysinfo/Cargo.toml | 1 + substrate/client/sysinfo/src/sysinfo.rs | 66 +++++++++++++++++-- 7 files changed, 89 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bfe54bf8eea0..7d9e4bf725ca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15980,6 +15980,7 @@ dependencies = [ name = "sc-sysinfo" version = "6.0.0-dev" dependencies = [ + "derive_more", "futures", "libc", "log", diff --git a/cumulus/parachain-template/node/src/service.rs b/cumulus/parachain-template/node/src/service.rs index 84dcd6dd1b39..43d16ee0d5b7 100644 --- a/cumulus/parachain-template/node/src/service.rs +++ b/cumulus/parachain-template/node/src/service.rs @@ -255,10 +255,14 @@ async fn start_node_impl( // Here you can check whether the hardware meets your chains' requirements. Putting a link // in there and swapping out the requirements for your own are probably a good idea. The // requirements for a para-chain are dictated by its relay-chain. - if !SUBSTRATE_REFERENCE_HARDWARE.check_hardware(&hwbench) && validator { - log::warn!( - "⚠️ The hardware does not meet the minimal requirements for role 'Authority'." + match SUBSTRATE_REFERENCE_HARDWARE.check_hardware(&hwbench) { + Err(err) if validator => { + log::warn!( + "⚠️ The hardware does not meet the minimal requirements {} for role 'Authority'.", + err ); + }, + _ => {}, } if let Some(ref mut telemetry) = telemetry { diff --git a/cumulus/polkadot-parachain/src/service.rs b/cumulus/polkadot-parachain/src/service.rs index 438d09a4c777..3bcc9b7f60d3 100644 --- a/cumulus/polkadot-parachain/src/service.rs +++ b/cumulus/polkadot-parachain/src/service.rs @@ -1955,10 +1955,11 @@ pub async fn start_contracts_rococo_node( fn warn_if_slow_hardware(hwbench: &sc_sysinfo::HwBench) { // Polkadot para-chains should generally use these requirements to ensure that the relay-chain // will not take longer than expected to import its blocks. - if !frame_benchmarking_cli::SUBSTRATE_REFERENCE_HARDWARE.check_hardware(hwbench) { + if let Err(err) = frame_benchmarking_cli::SUBSTRATE_REFERENCE_HARDWARE.check_hardware(hwbench) { log::warn!( - "⚠️ The hardware does not meet the minimal requirements for role 'Authority' find out more at:\n\ - https://wiki.polkadot.network/docs/maintain-guides-how-to-validate-polkadot#reference-hardware" + "⚠️ The hardware does not meet the minimal requirements {} for role 'Authority' find out more at:\n\ + https://wiki.polkadot.network/docs/maintain-guides-how-to-validate-polkadot#reference-hardware", + err ); } } diff --git a/polkadot/node/service/src/lib.rs b/polkadot/node/service/src/lib.rs index ced89c3843a2..0ed7940b3e80 100644 --- a/polkadot/node/service/src/lib.rs +++ b/polkadot/node/service/src/lib.rs @@ -965,11 +965,15 @@ pub fn new_full( if let Some(hwbench) = hwbench { sc_sysinfo::print_hwbench(&hwbench); - if !SUBSTRATE_REFERENCE_HARDWARE.check_hardware(&hwbench) && role.is_authority() { - log::warn!( - "⚠️ The hardware does not meet the minimal requirements for role 'Authority' find out more at:\n\ - https://wiki.polkadot.network/docs/maintain-guides-how-to-validate-polkadot#reference-hardware" + match SUBSTRATE_REFERENCE_HARDWARE.check_hardware(&hwbench) { + Err(err) if role.is_authority() => { + log::warn!( + "⚠️ The hardware does not meet the minimal requirements {} for role 'Authority' find out more at:\n\ + https://wiki.polkadot.network/docs/maintain-guides-how-to-validate-polkadot#reference-hardware", + err ); + }, + _ => {}, } if let Some(ref mut telemetry) = telemetry { diff --git a/substrate/bin/node/cli/src/service.rs b/substrate/bin/node/cli/src/service.rs index 5a85f4cde0ae..153dda5c0a52 100644 --- a/substrate/bin/node/cli/src/service.rs +++ b/substrate/bin/node/cli/src/service.rs @@ -449,10 +449,14 @@ pub fn new_full_base( if let Some(hwbench) = hwbench { sc_sysinfo::print_hwbench(&hwbench); - if !SUBSTRATE_REFERENCE_HARDWARE.check_hardware(&hwbench) && role.is_authority() { - log::warn!( - "⚠️ The hardware does not meet the minimal requirements for role 'Authority'." - ); + match SUBSTRATE_REFERENCE_HARDWARE.check_hardware(&hwbench) { + Err(err) if role.is_authority() => { + log::warn!( + "⚠️ The hardware does not meet the minimal requirements {} for role 'Authority'.", + err + ); + }, + _ => {}, } if let Some(ref mut telemetry) = telemetry { diff --git a/substrate/client/sysinfo/Cargo.toml b/substrate/client/sysinfo/Cargo.toml index fdf987ed45f2..86f4d217dd9f 100644 --- a/substrate/client/sysinfo/Cargo.toml +++ b/substrate/client/sysinfo/Cargo.toml @@ -19,6 +19,7 @@ libc = "0.2" log = "0.4.17" rand = "0.8.5" rand_pcg = "0.3.1" +derive_more = "0.99" regex = "1" serde = { version = "1.0.188", features = ["derive"] } serde_json = "1.0.107" diff --git a/substrate/client/sysinfo/src/sysinfo.rs b/substrate/client/sysinfo/src/sysinfo.rs index 41161fc685d3..bef87a83e46f 100644 --- a/substrate/client/sysinfo/src/sysinfo.rs +++ b/substrate/client/sysinfo/src/sysinfo.rs @@ -23,9 +23,11 @@ use sp_core::{sr25519, Pair}; use sp_io::crypto::sr25519_verify; use sp_std::{fmt, fmt::Formatter, prelude::*}; +use derive_more::From; use rand::{seq::SliceRandom, Rng, RngCore}; use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer}; use std::{ + fmt::Display, fs::File, io::{Seek, SeekFrom, Write}, ops::{Deref, DerefMut}, @@ -48,6 +50,37 @@ pub enum Metric { DiskRndWrite, } +/// Describes a checking failure for the hardware requirements. +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct CheckFailure { + /// The metric that failed the check. + pub metric: Metric, + /// The expected minimum value. + pub expected: Throughput, + /// The measured value. + pub found: Throughput, +} + +/// A list of metrics that failed to meet the minimum hardware requirements. +#[derive(Debug, Clone, PartialEq, From)] +pub struct CheckFailures(pub Vec); + +impl Display for CheckFailures { + fn fmt(&self, formatter: &mut Formatter) -> fmt::Result { + write!(formatter, "Failed checks: ")?; + for failure in &self.0 { + write!( + formatter, + "{}(expected: {}, found: {}), ", + failure.metric.name(), + failure.expected, + failure.found + )? + } + Ok(()) + } +} + impl Metric { /// The category of the metric. pub fn category(&self) -> &'static str { @@ -626,33 +659,54 @@ pub fn gather_hwbench(scratch_directory: Option<&Path>) -> HwBench { impl Requirements { /// Whether the hardware requirements are met by the provided benchmark results. - pub fn check_hardware(&self, hwbench: &HwBench) -> bool { + pub fn check_hardware(&self, hwbench: &HwBench) -> Result<(), CheckFailures> { + let mut failures = Vec::new(); for requirement in self.0.iter() { match requirement.metric { Metric::Blake2256 => if requirement.minimum > hwbench.cpu_hashrate_score { - return false + failures.push(CheckFailure { + metric: requirement.metric, + expected: requirement.minimum, + found: hwbench.cpu_hashrate_score, + }); }, Metric::MemCopy => if requirement.minimum > hwbench.memory_memcpy_score { - return false + failures.push(CheckFailure { + metric: requirement.metric, + expected: requirement.minimum, + found: hwbench.memory_memcpy_score, + }); }, Metric::DiskSeqWrite => if let Some(score) = hwbench.disk_sequential_write_score { if requirement.minimum > score { - return false + failures.push(CheckFailure { + metric: requirement.metric, + expected: requirement.minimum, + found: score, + }); } }, Metric::DiskRndWrite => if let Some(score) = hwbench.disk_random_write_score { if requirement.minimum > score { - return false + failures.push(CheckFailure { + metric: requirement.metric, + expected: requirement.minimum, + found: score, + }); } }, Metric::Sr25519Verify => {}, } } - true + if failures.is_empty() { + Ok(()) + } else { + Err(failures.into()) + } } } From f6f4c5aac8a672512dbe1530e6184a7d07f0fb08 Mon Sep 17 00:00:00 2001 From: Anthony Lazam Date: Fri, 3 Nov 2023 16:04:39 +0100 Subject: [PATCH 05/31] Update Kusama Parachains Bootnode (#2148) # Description Update the bootnode of kusama parachains before decommissioning the nodes. This will avoid connecting to non-existing bootnodes. --- cumulus/parachains/chain-specs/asset-hub-kusama.json | 4 ---- cumulus/parachains/chain-specs/bridge-hub-kusama.json | 4 ---- 2 files changed, 8 deletions(-) diff --git a/cumulus/parachains/chain-specs/asset-hub-kusama.json b/cumulus/parachains/chain-specs/asset-hub-kusama.json index e50858795623..91a821efab9c 100644 --- a/cumulus/parachains/chain-specs/asset-hub-kusama.json +++ b/cumulus/parachains/chain-specs/asset-hub-kusama.json @@ -9,10 +9,6 @@ "/dns/kusama-asset-hub-connect-0.polkadot.io/tcp/443/wss/p2p/12D3KooWMzvdGcUXxacLdMQzRVrsP1mJrZHcrz8LtGbhLzve84Qx", "/dns/kusama-asset-hub-connect-1.polkadot.io/tcp/30334/p2p/12D3KooWQmGf5z3DU1kKcZoLzMNgdbP31ybjuwxS1VGLKMUjq5ez", "/dns/kusama-asset-hub-connect-1.polkadot.io/tcp/443/wss/p2p/12D3KooWQmGf5z3DU1kKcZoLzMNgdbP31ybjuwxS1VGLKMUjq5ez", - "/dns/kusama-asset-hub-connect-2.polkadot.io/tcp/30334/p2p/12D3KooWLm6iHcmA3YD4xn2zfbm4KLF5KSUqJJAnmt2UGr9o2PgB", - "/dns/kusama-asset-hub-connect-2.polkadot.io/tcp/443/wss/p2p/12D3KooWLm6iHcmA3YD4xn2zfbm4KLF5KSUqJJAnmt2UGr9o2PgB", - "/dns/kusama-asset-hub-connect-3.polkadot.io/tcp/30334/p2p/12D3KooWD8Bma5qPbq7N5qdED3Xy6GXHfvfk86TL8aVTQKxmWkHG", - "/dns/kusama-asset-hub-connect-3.polkadot.io/tcp/443/wss/p2p/12D3KooWD8Bma5qPbq7N5qdED3Xy6GXHfvfk86TL8aVTQKxmWkHG", "/dns/boot.stake.plus/tcp/34333/p2p/12D3KooWAzSSZ7jLqMw1WPomYEKCYANQaKemXQ8BKoFvNEvfmdqR", "/dns/boot.stake.plus/tcp/34334/wss/p2p/12D3KooWAzSSZ7jLqMw1WPomYEKCYANQaKemXQ8BKoFvNEvfmdqR", "/dns/boot.metaspan.io/tcp/26052/p2p/12D3KooW9z9hKqe3mqYAp5UJMhZiCqhkTHyiR43fegnGmTJ3JAba", diff --git a/cumulus/parachains/chain-specs/bridge-hub-kusama.json b/cumulus/parachains/chain-specs/bridge-hub-kusama.json index 5e45e1528a10..c4a7d6358f75 100644 --- a/cumulus/parachains/chain-specs/bridge-hub-kusama.json +++ b/cumulus/parachains/chain-specs/bridge-hub-kusama.json @@ -5,12 +5,8 @@ "bootNodes": [ "/dns/kusama-bridge-hub-connect-ew1-0.polkadot.io/tcp/30334/p2p/12D3KooWPQQPivrqQ51kRTDc2R1mtqwKT4GGtk2rapkY4FrwHrEp", "/dns/kusama-bridge-hub-connect-ew1-1.polkadot.io/tcp/30334/p2p/12D3KooWPcF9Yk4gYrMju9CyWCV69hAFXbYsnxCLogwLGu9QFTRn", - "/dns/kusama-bridge-hub-connect-ue4-0.polkadot.io/tcp/30334/p2p/12D3KooWMf1sVnJDTkKWtaThqvrgcSPLbfGXttSqbwhM2DJp9BUG", - "/dns/kusama-bridge-hub-connect-ue4-1.polkadot.io/tcp/30334/p2p/12D3KooWQaV7wMfNVKy2aMz4Lds3TTxgSDyZAUEnbAZMfD8rW3ow", "/dns/kusama-bridge-hub-connect-ew1-0.polkadot.io/tcp/443/wss/p2p/12D3KooWPQQPivrqQ51kRTDc2R1mtqwKT4GGtk2rapkY4FrwHrEp", "/dns/kusama-bridge-hub-connect-ew1-1.polkadot.io/tcp/443/wss/p2p/12D3KooWPcF9Yk4gYrMju9CyWCV69hAFXbYsnxCLogwLGu9QFTRn", - "/dns/kusama-bridge-hub-connect-ue4-0.polkadot.io/tcp/443/wss/p2p/12D3KooWMf1sVnJDTkKWtaThqvrgcSPLbfGXttSqbwhM2DJp9BUG", - "/dns/kusama-bridge-hub-connect-ue4-1.polkadot.io/tcp/443/wss/p2p/12D3KooWQaV7wMfNVKy2aMz4Lds3TTxgSDyZAUEnbAZMfD8rW3ow", "/dns/boot.stake.plus/tcp/41333/p2p/12D3KooWBzbs2jsXjG5dipktGPKaUm9XWvkmeJFsEAGkVt946Aa7", "/dns/boot.stake.plus/tcp/41334/wss/p2p/12D3KooWBzbs2jsXjG5dipktGPKaUm9XWvkmeJFsEAGkVt946Aa7", "/dns/boot.metaspan.io/tcp/26032/p2p/12D3KooWKfuSaZrLNz43PDgM4inMALXRHTSh2WBuqQtZRq8zmT1Z", From 8dc41ba49d0a63959b0b5e8b75ebd5d0a1084c07 Mon Sep 17 00:00:00 2001 From: Dmitry Markin Date: Fri, 3 Nov 2023 17:09:01 +0200 Subject: [PATCH 06/31] Do not request blocks below the common number when syncing (#2045) This changes `BlockCollection` logic so we don't download block ranges from peers with which we have these ranges already in sync. Improves situation with https://github.com/paritytech/polkadot-sdk/issues/1915. --- substrate/client/network/sync/src/blocks.rs | 232 +++++++++++++++++- .../client/network/sync/src/chain_sync.rs | 38 ++- .../network/sync/src/chain_sync/test.rs | 107 -------- 3 files changed, 255 insertions(+), 122 deletions(-) diff --git a/substrate/client/network/sync/src/blocks.rs b/substrate/client/network/sync/src/blocks.rs index 240c1ca1f8b2..539a8a5d612c 100644 --- a/substrate/client/network/sync/src/blocks.rs +++ b/substrate/client/network/sync/src/blocks.rs @@ -123,20 +123,36 @@ impl BlockCollection { let first_different = common + >::one(); let count = (count as u32).into(); let (mut range, downloading) = { + // Iterate through the ranges in `self.blocks` looking for a range to download let mut downloading_iter = self.blocks.iter().peekable(); let mut prev: Option<(&NumberFor, &BlockRangeState)> = None; loop { let next = downloading_iter.next(); break match (prev, next) { + // If we are already downloading this range, request it from `max_parallel` + // peers (`max_parallel = 5` by default). + // Do not request already downloading range from peers with common number above + // the range start. (Some((start, &BlockRangeState::Downloading { ref len, downloading })), _) - if downloading < max_parallel => + if downloading < max_parallel && *start >= first_different => (*start..*start + *len, downloading), - (Some((start, r)), Some((next_start, _))) if *start + r.len() < *next_start => - (*start + r.len()..cmp::min(*next_start, *start + r.len() + count), 0), // gap - (Some((start, r)), None) => (*start + r.len()..*start + r.len() + count, 0), /* last range */ - (None, None) => (first_different..first_different + count, 0), /* empty */ + // If there is a gap between ranges requested, download this gap unless the peer + // has common number above the gap start + (Some((start, r)), Some((next_start, _))) + if *start + r.len() < *next_start && + *start + r.len() >= first_different => + (*start + r.len()..cmp::min(*next_start, *start + r.len() + count), 0), + // Download `count` blocks after the last range requested unless the peer + // has common number above this new range + (Some((start, r)), None) if *start + r.len() >= first_different => + (*start + r.len()..*start + r.len() + count, 0), + // If there are no ranges currently requested, download `count` blocks after + // `common` number + (None, None) => (first_different..first_different + count, 0), + // If the first range starts above `common + 1`, download the gap at the start (None, Some((start, _))) if *start > first_different => - (first_different..cmp::min(first_different + count, *start), 0), /* gap at the start */ + (first_different..cmp::min(first_different + count, *start), 0), + // Move on to the next range pair _ => { prev = next; continue @@ -365,10 +381,10 @@ mod test { bc.blocks.insert(114305, BlockRangeState::Complete(blocks)); let peer0 = PeerId::random(); - assert_eq!(bc.needed_blocks(peer0, 128, 10000, 000, 1, 200), Some(1..100)); - assert_eq!(bc.needed_blocks(peer0, 128, 10000, 600, 1, 200), None); // too far ahead + assert_eq!(bc.needed_blocks(peer0, 128, 10000, 0, 1, 200), Some(1..100)); + assert_eq!(bc.needed_blocks(peer0, 128, 10000, 0, 1, 200), None); // too far ahead assert_eq!( - bc.needed_blocks(peer0, 128, 10000, 600, 1, 200000), + bc.needed_blocks(peer0, 128, 10000, 0, 1, 200000), Some(100 + 128..100 + 128 + 128) ); } @@ -431,4 +447,202 @@ mod test { assert!(bc.blocks.is_empty()); assert!(bc.queued_blocks.is_empty()); } + + #[test] + fn downloaded_range_is_requested_from_max_parallel_peers() { + let mut bc = BlockCollection::new(); + assert!(is_empty(&bc)); + + let count = 5; + // identical ranges requested from 2 peers + let max_parallel = 2; + let max_ahead = 200; + + let peer1 = PeerId::random(); + let peer2 = PeerId::random(); + let peer3 = PeerId::random(); + + // common for all peers + let best = 100; + let common = 10; + + assert_eq!( + bc.needed_blocks(peer1, count, best, common, max_parallel, max_ahead), + Some(11..16) + ); + assert_eq!( + bc.needed_blocks(peer2, count, best, common, max_parallel, max_ahead), + Some(11..16) + ); + assert_eq!( + bc.needed_blocks(peer3, count, best, common, max_parallel, max_ahead), + Some(16..21) + ); + } + #[test] + fn downloaded_range_not_requested_from_peers_with_higher_common_number() { + // A peer connects with a common number falling behind our best number + // (either a fork or lagging behind). + // We request a range from this peer starting at its common number + 1. + // Even though we have less than `max_parallel` downloads, we do not request + // this range from peers with a common number above the start of this range. + + let mut bc = BlockCollection::new(); + assert!(is_empty(&bc)); + + let count = 5; + let max_parallel = 2; + let max_ahead = 200; + + let peer1 = PeerId::random(); + let peer1_best = 20; + let peer1_common = 10; + + // `peer2` has first different above the start of the range downloaded from `peer1` + let peer2 = PeerId::random(); + let peer2_best = 20; + let peer2_common = 11; // first_different = 12 + + assert_eq!( + bc.needed_blocks(peer1, count, peer1_best, peer1_common, max_parallel, max_ahead), + Some(11..16), + ); + assert_eq!( + bc.needed_blocks(peer2, count, peer2_best, peer2_common, max_parallel, max_ahead), + Some(16..21), + ); + } + + #[test] + fn gap_above_common_number_requested() { + let mut bc = BlockCollection::new(); + assert!(is_empty(&bc)); + + let count = 5; + let best = 30; + // We need at least 3 ranges requested to have a gap, so to minimize the number of peers + // set `max_parallel = 1` + let max_parallel = 1; + let max_ahead = 200; + + let peer1 = PeerId::random(); + let peer2 = PeerId::random(); + let peer3 = PeerId::random(); + + let common = 10; + assert_eq!( + bc.needed_blocks(peer1, count, best, common, max_parallel, max_ahead), + Some(11..16), + ); + assert_eq!( + bc.needed_blocks(peer2, count, best, common, max_parallel, max_ahead), + Some(16..21), + ); + assert_eq!( + bc.needed_blocks(peer3, count, best, common, max_parallel, max_ahead), + Some(21..26), + ); + + // For some reason there is now a gap at 16..21. We just disconnect `peer2`, but it might + // also happen that 16..21 received first and got imported if our best is actually >= 15. + bc.clear_peer_download(&peer2); + + // Some peer connects with common number below the gap. The gap is requested from it. + assert_eq!( + bc.needed_blocks(peer2, count, best, common, max_parallel, max_ahead), + Some(16..21), + ); + } + + #[test] + fn gap_below_common_number_not_requested() { + let mut bc = BlockCollection::new(); + assert!(is_empty(&bc)); + + let count = 5; + let best = 30; + // We need at least 3 ranges requested to have a gap, so to minimize the number of peers + // set `max_parallel = 1` + let max_parallel = 1; + let max_ahead = 200; + + let peer1 = PeerId::random(); + let peer2 = PeerId::random(); + let peer3 = PeerId::random(); + + let common = 10; + assert_eq!( + bc.needed_blocks(peer1, count, best, common, max_parallel, max_ahead), + Some(11..16), + ); + assert_eq!( + bc.needed_blocks(peer2, count, best, common, max_parallel, max_ahead), + Some(16..21), + ); + assert_eq!( + bc.needed_blocks(peer3, count, best, common, max_parallel, max_ahead), + Some(21..26), + ); + + // For some reason there is now a gap at 16..21. We just disconnect `peer2`, but it might + // also happen that 16..21 received first and got imported if our best is actually >= 15. + bc.clear_peer_download(&peer2); + + // Some peer connects with common number above the gap. The gap is not requested from it. + let common = 23; + assert_eq!( + bc.needed_blocks(peer2, count, best, common, max_parallel, max_ahead), + Some(26..31), // not 16..21 + ); + } + + #[test] + fn range_at_the_end_above_common_number_requested() { + let mut bc = BlockCollection::new(); + assert!(is_empty(&bc)); + + let count = 5; + let best = 30; + let max_parallel = 1; + let max_ahead = 200; + + let peer1 = PeerId::random(); + let peer2 = PeerId::random(); + + let common = 10; + assert_eq!( + bc.needed_blocks(peer1, count, best, common, max_parallel, max_ahead), + Some(11..16), + ); + assert_eq!( + bc.needed_blocks(peer2, count, best, common, max_parallel, max_ahead), + Some(16..21), + ); + } + + #[test] + fn range_at_the_end_below_common_number_not_requested() { + let mut bc = BlockCollection::new(); + assert!(is_empty(&bc)); + + let count = 5; + let best = 30; + let max_parallel = 1; + let max_ahead = 200; + + let peer1 = PeerId::random(); + let peer2 = PeerId::random(); + + let common = 10; + assert_eq!( + bc.needed_blocks(peer1, count, best, common, max_parallel, max_ahead), + Some(11..16), + ); + + let common = 20; + assert_eq!( + bc.needed_blocks(peer2, count, best, common, max_parallel, max_ahead), + Some(21..26), // not 16..21 + ); + } } diff --git a/substrate/client/network/sync/src/chain_sync.rs b/substrate/client/network/sync/src/chain_sync.rs index 9cf0080e36ac..5721bd59a847 100644 --- a/substrate/client/network/sync/src/chain_sync.rs +++ b/substrate/client/network/sync/src/chain_sync.rs @@ -520,12 +520,15 @@ where Err(BadPeer(who, rep::BLOCKCHAIN_READ_ERROR)) }, Ok(BlockStatus::KnownBad) => { - info!("πŸ’” New peer with known bad best block {} ({}).", best_hash, best_number); + info!("πŸ’” New peer {who} with known bad best block {best_hash} ({best_number})."); Err(BadPeer(who, rep::BAD_BLOCK)) }, Ok(BlockStatus::Unknown) => { if best_number.is_zero() { - info!("πŸ’” New peer with unknown genesis hash {} ({}).", best_hash, best_number); + info!( + "πŸ’” New peer {} with unknown genesis hash {} ({}).", + who, best_hash, best_number, + ); return Err(BadPeer(who, rep::GENESIS_MISMATCH)) } @@ -535,7 +538,8 @@ where if self.queue_blocks.len() > MAJOR_SYNC_BLOCKS.into() { debug!( target:LOG_TARGET, - "New peer with unknown best hash {} ({}), assuming common block.", + "New peer {} with unknown best hash {} ({}), assuming common block.", + who, self.best_queued_hash, self.best_queued_number ); @@ -556,7 +560,7 @@ where let (state, req) = if self.best_queued_number.is_zero() { debug!( target:LOG_TARGET, - "New peer with best hash {best_hash} ({best_number}).", + "New peer {who} with best hash {best_hash} ({best_number}).", ); (PeerSyncState::Available, None) @@ -565,7 +569,8 @@ where debug!( target:LOG_TARGET, - "New peer with unknown best hash {} ({}), searching for common ancestor.", + "New peer {} with unknown best hash {} ({}), searching for common ancestor.", + who, best_hash, best_number ); @@ -613,7 +618,7 @@ where Ok(BlockStatus::InChainPruned) => { debug!( target: LOG_TARGET, - "New peer with known best hash {best_hash} ({best_number}).", + "New peer {who} with known best hash {best_hash} ({best_number}).", ); self.peers.insert( who, @@ -848,8 +853,22 @@ where // We've made progress on this chain since the search was started. // Opportunistically set common number to updated number // instead of the one that started the search. + trace!( + target: LOG_TARGET, + "Ancestry search: opportunistically updating peer {} common number from={} => to={}.", + *who, + peer.common_number, + self.best_queued_number, + ); peer.common_number = self.best_queued_number; } else if peer.common_number < *current { + trace!( + target: LOG_TARGET, + "Ancestry search: updating peer {} common number from={} => to={}.", + *who, + peer.common_number, + *current, + ); peer.common_number = *current; } } @@ -1338,6 +1357,13 @@ where // should be kept in that state. if let PeerSyncState::DownloadingJustification(_) = p.state { // We make sure our commmon number is at least something we have. + trace!( + target: LOG_TARGET, + "Keeping peer {} after restart, updating common number from={} => to={} (our best).", + peer_id, + p.common_number, + self.best_queued_number, + ); p.common_number = self.best_queued_number; self.peers.insert(peer_id, p); return None diff --git a/substrate/client/network/sync/src/chain_sync/test.rs b/substrate/client/network/sync/src/chain_sync/test.rs index 6f9fea1b161b..23316eaa1949 100644 --- a/substrate/client/network/sync/src/chain_sync/test.rs +++ b/substrate/client/network/sync/src/chain_sync/test.rs @@ -245,113 +245,6 @@ fn build_block(client: &mut Arc, at: Option, fork: bool) -> Bl block } -/// This test is a regression test as observed on a real network. -/// -/// The node is connected to multiple peers. Both of these peers are having a best block (1) -/// that is below our best block (3). Now peer 2 announces a fork of block 3 that we will -/// request from peer 2. After importing the fork, peer 2 and then peer 1 will announce block 4. -/// But as peer 1 in our view is still at block 1, we will request block 2 (which we already -/// have) from it. In the meanwhile peer 2 sends us block 4 and 3 and we send another request -/// for block 2 to peer 2. Peer 1 answers with block 2 and then peer 2. This will need to -/// succeed, as we have requested block 2 from both peers. -#[test] -fn do_not_report_peer_on_block_response_for_block_request() { - sp_tracing::try_init_simple(); - - let mut client = Arc::new(TestClientBuilder::new().build()); - let (_chain_sync_network_provider, chain_sync_network_handle) = NetworkServiceProvider::new(); - - let mut sync = ChainSync::new( - SyncMode::Full, - client.clone(), - ProtocolName::from("test-block-announce-protocol"), - 5, - 64, - None, - chain_sync_network_handle, - ) - .unwrap(); - - let peer_id1 = PeerId::random(); - let peer_id2 = PeerId::random(); - - let mut client2 = client.clone(); - let mut build_block_at = |at, import| { - let mut block_builder = client2.new_block_at(at, Default::default(), false).unwrap(); - // Make sure we generate a different block as fork - block_builder.push_storage_change(vec![1, 2, 3], Some(vec![4, 5, 6])).unwrap(); - - let block = block_builder.build().unwrap().block; - - if import { - block_on(client2.import(BlockOrigin::Own, block.clone())).unwrap(); - } - - block - }; - - let block1 = build_block(&mut client, None, false); - let block2 = build_block(&mut client, None, false); - let block3 = build_block(&mut client, None, false); - let block3_fork = build_block_at(block2.hash(), false); - - // Add two peers which are on block 1. - sync.new_peer(peer_id1, block1.hash(), 1).unwrap(); - sync.new_peer(peer_id2, block1.hash(), 1).unwrap(); - - // Tell sync that our best block is 3. - sync.update_chain_info(&block3.hash(), 3); - - // There should be no requests. - assert!(sync.block_requests().is_empty()); - - // Let peer2 announce a fork of block 3 - send_block_announce(block3_fork.header().clone(), peer_id2, &mut sync); - - // Import and tell sync that we now have the fork. - block_on(client.import(BlockOrigin::Own, block3_fork.clone())).unwrap(); - sync.update_chain_info(&block3_fork.hash(), 3); - - let block4 = build_block_at(block3_fork.hash(), false); - - // Let peer2 announce block 4 and check that sync wants to get the block. - send_block_announce(block4.header().clone(), peer_id2, &mut sync); - - let request = get_block_request(&mut sync, FromBlock::Hash(block4.hash()), 2, &peer_id2); - - // Peer1 announces the same block, but as the common block is still `1`, sync will request - // block 2 again. - send_block_announce(block4.header().clone(), peer_id1, &mut sync); - - let request2 = get_block_request(&mut sync, FromBlock::Number(2), 1, &peer_id1); - - let response = create_block_response(vec![block4.clone(), block3_fork.clone()]); - let res = sync.on_block_data(&peer_id2, Some(request), response).unwrap(); - - // We should not yet import the blocks, because there is still an open request for fetching - // block `2` which blocks the import. - assert!( - matches!(res, OnBlockData::Import(ImportBlocksAction{ origin: _, blocks }) if blocks.is_empty()) - ); - - let request3 = get_block_request(&mut sync, FromBlock::Number(2), 1, &peer_id2); - - let response = create_block_response(vec![block2.clone()]); - let res = sync.on_block_data(&peer_id1, Some(request2), response).unwrap(); - assert!(matches!( - res, - OnBlockData::Import(ImportBlocksAction{ origin: _, blocks }) - if blocks.iter().all(|b| [2, 3, 4].contains(b.header.as_ref().unwrap().number())) - )); - - let response = create_block_response(vec![block2.clone()]); - let res = sync.on_block_data(&peer_id2, Some(request3), response).unwrap(); - // Nothing to import - assert!( - matches!(res, OnBlockData::Import(ImportBlocksAction{ origin: _, blocks }) if blocks.is_empty()) - ); -} - fn unwrap_from_block_number(from: FromBlock) -> u64 { if let FromBlock::Number(from) = from { from From d512b3f00bca98ebf300608bf841e0558a82d7ad Mon Sep 17 00:00:00 2001 From: Dmitry Markin Date: Fri, 3 Nov 2023 17:10:24 +0200 Subject: [PATCH 07/31] Convert `SyncingEngine::run` to use `tokio::select!` instead of polling (#2132) --- substrate/client/network/sync/Cargo.toml | 1 + .../sync/src/block_announce_validator.rs | 12 +- substrate/client/network/sync/src/engine.rs | 403 +++++++++--------- .../network/sync/src/pending_responses.rs | 33 +- 4 files changed, 235 insertions(+), 214 deletions(-) diff --git a/substrate/client/network/sync/Cargo.toml b/substrate/client/network/sync/Cargo.toml index 39312cc4b327..a1ea39a852fc 100644 --- a/substrate/client/network/sync/Cargo.toml +++ b/substrate/client/network/sync/Cargo.toml @@ -30,6 +30,7 @@ schnellru = "0.2.1" smallvec = "1.11.0" thiserror = "1.0" tokio-stream = "0.1.14" +tokio = { version = "1.32.0", features = ["time", "macros"] } fork-tree = { path = "../../../utils/fork-tree" } prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../../utils/prometheus" } sc-client-api = { path = "../../api" } diff --git a/substrate/client/network/sync/src/block_announce_validator.rs b/substrate/client/network/sync/src/block_announce_validator.rs index f083f9e29e44..961b581cddce 100644 --- a/substrate/client/network/sync/src/block_announce_validator.rs +++ b/substrate/client/network/sync/src/block_announce_validator.rs @@ -16,10 +16,11 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -//! `BlockAnnounceValidator` is responsible for async validation of block announcements. +//! [`BlockAnnounceValidator`] is responsible for async validation of block announcements. +//! [`Stream`] implemented by [`BlockAnnounceValidator`] never terminates. use crate::futures_stream::FuturesStream; -use futures::{Future, FutureExt, Stream, StreamExt}; +use futures::{stream::FusedStream, Future, FutureExt, Stream, StreamExt}; use libp2p::PeerId; use log::{debug, error, trace, warn}; use sc_network_common::sync::message::BlockAnnounce; @@ -300,6 +301,13 @@ impl Stream for BlockAnnounceValidator { } } +// As [`BlockAnnounceValidator`] never terminates, we can easily implement [`FusedStream`] for it. +impl FusedStream for BlockAnnounceValidator { + fn is_terminated(&self) -> bool { + false + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/substrate/client/network/sync/src/engine.rs b/substrate/client/network/sync/src/engine.rs index 0f689742bc58..02a0dc2e9151 100644 --- a/substrate/client/network/sync/src/engine.rs +++ b/substrate/client/network/sync/src/engine.rs @@ -47,7 +47,6 @@ use futures::{ future::{BoxFuture, Fuse}, FutureExt, StreamExt, }; -use futures_timer::Delay; use libp2p::{request_response::OutboundFailure, PeerId}; use log::{debug, trace}; use prometheus_endpoint::{ @@ -56,6 +55,7 @@ use prometheus_endpoint::{ }; use prost::Message; use schnellru::{ByLength, LruMap}; +use tokio::time::{Interval, MissedTickBehavior}; use sc_client_api::{BlockBackend, HeaderBackend, ProofProvider}; use sc_consensus::import_queue::ImportQueueService; @@ -85,7 +85,6 @@ use std::{ atomic::{AtomicBool, AtomicUsize, Ordering}, Arc, }, - task::Poll, time::{Duration, Instant}, }; @@ -254,7 +253,7 @@ pub struct SyncingEngine { service_rx: TracingUnboundedReceiver>, /// Channel for receiving inbound connections from `Protocol`. - rx: sc_utils::mpsc::TracingUnboundedReceiver>, + sync_events_rx: sc_utils::mpsc::TracingUnboundedReceiver>, /// Assigned roles. roles: Roles, @@ -266,7 +265,7 @@ pub struct SyncingEngine { event_streams: Vec>, /// Interval at which we call `tick`. - tick_timeout: Delay, + tick_timeout: Interval, /// All connected peers. Contains both full and light node peers. peers: HashMap>, @@ -304,7 +303,7 @@ pub struct SyncingEngine { boot_node_ids: HashSet, /// A channel to get target block header if we skip over proofs downloading during warp sync. - warp_sync_target_block_header_rx: + warp_sync_target_block_header_rx_fused: Fuse>>, /// Protocol name used for block announcements @@ -363,7 +362,7 @@ where block_downloader: Arc>, state_request_protocol_name: ProtocolName, warp_sync_protocol_name: Option, - rx: sc_utils::mpsc::TracingUnboundedReceiver>, + sync_events_rx: sc_utils::mpsc::TracingUnboundedReceiver>, ) -> Result<(Self, SyncingService, NonDefaultSetConfig), ClientError> { let mode = net_config.network_config.sync_mode; let max_parallel_downloads = net_config.network_config.max_parallel_downloads; @@ -436,7 +435,7 @@ where // Make sure polling of the target block channel is a no-op if there is no block to // retrieve. - let warp_sync_target_block_header_rx = warp_sync_target_block_header_rx + let warp_sync_target_block_header_rx_fused = warp_sync_target_block_header_rx .map_or(futures::future::pending().boxed().fuse(), |rx| rx.boxed().fuse()); let block_announce_config = Self::get_block_announce_proto_config( @@ -478,6 +477,12 @@ where let max_out_peers = net_config.network_config.default_peers_set.out_peers; let max_in_peers = (max_full_peers - max_out_peers) as usize; + let tick_timeout = { + let mut interval = tokio::time::interval(TICK_TIMEOUT); + interval.set_missed_tick_behavior(MissedTickBehavior::Delay); + interval + }; + Ok(( Self { roles, @@ -493,11 +498,11 @@ where num_connected: num_connected.clone(), is_major_syncing: is_major_syncing.clone(), service_rx, - rx, + sync_events_rx, genesis_hash, important_peers, default_peers_set_no_slot_connected_peers: HashSet::new(), - warp_sync_target_block_header_rx, + warp_sync_target_block_header_rx_fused, boot_node_ids, default_peers_set_no_slot_peers, default_peers_set_num_full, @@ -505,7 +510,7 @@ where num_in_peers: 0usize, max_in_peers, event_streams: Vec::new(), - tick_timeout: Delay::new(TICK_TIMEOUT), + tick_timeout, syncing_started: None, last_notification_io: Instant::now(), metrics: if let Some(r) = metrics_registry { @@ -691,230 +696,222 @@ where self.syncing_started = Some(Instant::now()); loop { - futures::future::poll_fn(|cx| self.poll(cx)).await; + tokio::select! { + _ = self.tick_timeout.tick() => self.perform_periodic_actions(), + command = self.service_rx.select_next_some() => + self.process_service_command(command), + sync_event = self.sync_events_rx.select_next_some() => + self.process_sync_event(sync_event), + warp_target_block_header = &mut self.warp_sync_target_block_header_rx_fused => + self.pass_warp_sync_target_block_header(warp_target_block_header), + response_event = self.pending_responses.select_next_some() => + self.process_response_event(response_event), + validation_result = self.block_announce_validator.select_next_some() => + self.process_block_announce_validation_result(validation_result), + } + + // Update atomic variables + self.num_connected.store(self.peers.len(), Ordering::Relaxed); + self.is_major_syncing + .store(self.chain_sync.status().state.is_major_syncing(), Ordering::Relaxed); + + // Send outbound requests on `ChanSync`'s behalf. + self.send_chain_sync_requests(); } } - pub fn poll(&mut self, cx: &mut std::task::Context) -> Poll<()> { - self.num_connected.store(self.peers.len(), Ordering::Relaxed); - self.is_major_syncing - .store(self.chain_sync.status().state.is_major_syncing(), Ordering::Relaxed); - - while let Poll::Ready(()) = self.tick_timeout.poll_unpin(cx) { - self.report_metrics(); - self.tick_timeout.reset(TICK_TIMEOUT); - - // if `SyncingEngine` has just started, don't evict seemingly inactive peers right away - // as they may not have produced blocks not because they've disconnected but because - // they're still waiting to receive enough relaychain blocks to start producing blocks. - if let Some(started) = self.syncing_started { - if started.elapsed() < INITIAL_EVICTION_WAIT_PERIOD { - continue - } + fn perform_periodic_actions(&mut self) { + self.report_metrics(); - self.syncing_started = None; - self.last_notification_io = Instant::now(); + // if `SyncingEngine` has just started, don't evict seemingly inactive peers right away + // as they may not have produced blocks not because they've disconnected but because + // they're still waiting to receive enough relaychain blocks to start producing blocks. + if let Some(started) = self.syncing_started { + if started.elapsed() < INITIAL_EVICTION_WAIT_PERIOD { + return } - // if syncing hasn't sent or received any blocks within `INACTIVITY_EVICT_THRESHOLD`, - // it means the local node has stalled and is connected to peers who either don't - // consider it connected or are also all stalled. In order to unstall the node, - // disconnect all peers and allow `ProtocolController` to establish new connections. - if self.last_notification_io.elapsed() > INACTIVITY_EVICT_THRESHOLD { - log::debug!( - target: LOG_TARGET, - "syncing has halted due to inactivity, evicting all peers", - ); + self.syncing_started = None; + self.last_notification_io = Instant::now(); + } - for peer in self.peers.keys() { - self.network_service.report_peer(*peer, rep::INACTIVE_SUBSTREAM); - self.network_service - .disconnect_peer(*peer, self.block_announce_protocol_name.clone()); - } + // if syncing hasn't sent or received any blocks within `INACTIVITY_EVICT_THRESHOLD`, + // it means the local node has stalled and is connected to peers who either don't + // consider it connected or are also all stalled. In order to unstall the node, + // disconnect all peers and allow `ProtocolController` to establish new connections. + if self.last_notification_io.elapsed() > INACTIVITY_EVICT_THRESHOLD { + log::debug!( + target: LOG_TARGET, + "syncing has halted due to inactivity, evicting all peers", + ); - // after all the peers have been evicted, start timer again to prevent evicting - // new peers that join after the old peer have been evicted - self.last_notification_io = Instant::now(); + for peer in self.peers.keys() { + self.network_service.report_peer(*peer, rep::INACTIVE_SUBSTREAM); + self.network_service + .disconnect_peer(*peer, self.block_announce_protocol_name.clone()); } + + // after all the peers have been evicted, start timer again to prevent evicting + // new peers that join after the old peer have been evicted + self.last_notification_io = Instant::now(); } + } - while let Poll::Ready(Some(event)) = self.service_rx.poll_next_unpin(cx) { - match event { - ToServiceCommand::SetSyncForkRequest(peers, hash, number) => { - self.chain_sync.set_sync_fork_request(peers, &hash, number); - }, - ToServiceCommand::EventStream(tx) => self.event_streams.push(tx), - ToServiceCommand::RequestJustification(hash, number) => - self.chain_sync.request_justification(&hash, number), - ToServiceCommand::ClearJustificationRequests => - self.chain_sync.clear_justification_requests(), - ToServiceCommand::BlocksProcessed(imported, count, results) => { - for result in self.chain_sync.on_blocks_processed(imported, count, results) { - match result { - Ok(action) => match action { - BlockRequestAction::SendRequest { peer_id, request } => { - // drop obsolete pending response first - self.pending_responses.remove(&peer_id); - self.send_block_request(peer_id, request); - }, - BlockRequestAction::RemoveStale { peer_id } => { - self.pending_responses.remove(&peer_id); - }, + fn process_service_command(&mut self, command: ToServiceCommand) { + match command { + ToServiceCommand::SetSyncForkRequest(peers, hash, number) => { + self.chain_sync.set_sync_fork_request(peers, &hash, number); + }, + ToServiceCommand::EventStream(tx) => self.event_streams.push(tx), + ToServiceCommand::RequestJustification(hash, number) => + self.chain_sync.request_justification(&hash, number), + ToServiceCommand::ClearJustificationRequests => + self.chain_sync.clear_justification_requests(), + ToServiceCommand::BlocksProcessed(imported, count, results) => { + for result in self.chain_sync.on_blocks_processed(imported, count, results) { + match result { + Ok(action) => match action { + BlockRequestAction::SendRequest { peer_id, request } => { + // drop obsolete pending response first + self.pending_responses.remove(&peer_id); + self.send_block_request(peer_id, request); }, - Err(BadPeer(peer_id, repu)) => { + BlockRequestAction::RemoveStale { peer_id } => { self.pending_responses.remove(&peer_id); - self.network_service.disconnect_peer( - peer_id, - self.block_announce_protocol_name.clone(), - ); - self.network_service.report_peer(peer_id, repu) }, - } - } - }, - ToServiceCommand::JustificationImported(peer_id, hash, number, success) => { - self.chain_sync.on_justification_import(hash, number, success); - if !success { - log::info!( - target: LOG_TARGET, - "πŸ’” Invalid justification provided by {peer_id} for #{hash}", - ); - self.network_service - .disconnect_peer(peer_id, self.block_announce_protocol_name.clone()); - self.network_service.report_peer( - peer_id, - ReputationChange::new_fatal("Invalid justification"), - ); + }, + Err(BadPeer(peer_id, repu)) => { + self.pending_responses.remove(&peer_id); + self.network_service.disconnect_peer( + peer_id, + self.block_announce_protocol_name.clone(), + ); + self.network_service.report_peer(peer_id, repu) + }, } - }, - ToServiceCommand::AnnounceBlock(hash, data) => self.announce_block(hash, data), - ToServiceCommand::NewBestBlockImported(hash, number) => - self.new_best_block_imported(hash, number), - ToServiceCommand::Status(tx) => { - let mut status = self.chain_sync.status(); - status.num_connected_peers = self.peers.len() as u32; - let _ = tx.send(status); - }, - ToServiceCommand::NumActivePeers(tx) => { - let _ = tx.send(self.num_active_peers()); - }, - ToServiceCommand::SyncState(tx) => { - let _ = tx.send(self.chain_sync.status()); - }, - ToServiceCommand::BestSeenBlock(tx) => { - let _ = tx.send(self.chain_sync.status().best_seen_block); - }, - ToServiceCommand::NumSyncPeers(tx) => { - let _ = tx.send(self.chain_sync.status().num_peers); - }, - ToServiceCommand::NumQueuedBlocks(tx) => { - let _ = tx.send(self.chain_sync.status().queued_blocks); - }, - ToServiceCommand::NumDownloadedBlocks(tx) => { - let _ = tx.send(self.chain_sync.num_downloaded_blocks()); - }, - ToServiceCommand::NumSyncRequests(tx) => { - let _ = tx.send(self.chain_sync.num_sync_requests()); - }, - ToServiceCommand::PeersInfo(tx) => { - let peers_info = self - .peers - .iter() - .map(|(peer_id, peer)| (*peer_id, peer.info.clone())) - .collect(); - let _ = tx.send(peers_info); - }, - ToServiceCommand::OnBlockFinalized(hash, header) => - self.chain_sync.on_block_finalized(&hash, *header.number()), - } + } + }, + ToServiceCommand::JustificationImported(peer_id, hash, number, success) => { + self.chain_sync.on_justification_import(hash, number, success); + if !success { + log::info!( + target: LOG_TARGET, + "πŸ’” Invalid justification provided by {peer_id} for #{hash}", + ); + self.network_service + .disconnect_peer(peer_id, self.block_announce_protocol_name.clone()); + self.network_service + .report_peer(peer_id, ReputationChange::new_fatal("Invalid justification")); + } + }, + ToServiceCommand::AnnounceBlock(hash, data) => self.announce_block(hash, data), + ToServiceCommand::NewBestBlockImported(hash, number) => + self.new_best_block_imported(hash, number), + ToServiceCommand::Status(tx) => { + let mut status = self.chain_sync.status(); + status.num_connected_peers = self.peers.len() as u32; + let _ = tx.send(status); + }, + ToServiceCommand::NumActivePeers(tx) => { + let _ = tx.send(self.num_active_peers()); + }, + ToServiceCommand::SyncState(tx) => { + let _ = tx.send(self.chain_sync.status()); + }, + ToServiceCommand::BestSeenBlock(tx) => { + let _ = tx.send(self.chain_sync.status().best_seen_block); + }, + ToServiceCommand::NumSyncPeers(tx) => { + let _ = tx.send(self.chain_sync.status().num_peers); + }, + ToServiceCommand::NumQueuedBlocks(tx) => { + let _ = tx.send(self.chain_sync.status().queued_blocks); + }, + ToServiceCommand::NumDownloadedBlocks(tx) => { + let _ = tx.send(self.chain_sync.num_downloaded_blocks()); + }, + ToServiceCommand::NumSyncRequests(tx) => { + let _ = tx.send(self.chain_sync.num_sync_requests()); + }, + ToServiceCommand::PeersInfo(tx) => { + let peers_info = self + .peers + .iter() + .map(|(peer_id, peer)| (*peer_id, peer.info.clone())) + .collect(); + let _ = tx.send(peers_info); + }, + ToServiceCommand::OnBlockFinalized(hash, header) => + self.chain_sync.on_block_finalized(&hash, *header.number()), } + } - while let Poll::Ready(Some(event)) = self.rx.poll_next_unpin(cx) { - match event { - sc_network::SyncEvent::NotificationStreamOpened { - remote, - received_handshake, - sink, - inbound, - tx, - } => match self.on_sync_peer_connected(remote, &received_handshake, sink, inbound) { - Ok(()) => { - let _ = tx.send(true); - }, - Err(()) => { - log::debug!( - target: LOG_TARGET, - "Failed to register peer {remote:?}: {received_handshake:?}", - ); - let _ = tx.send(false); - }, + fn process_sync_event(&mut self, event: sc_network::SyncEvent) { + match event { + sc_network::SyncEvent::NotificationStreamOpened { + remote, + received_handshake, + sink, + inbound, + tx, + } => match self.on_sync_peer_connected(remote, &received_handshake, sink, inbound) { + Ok(()) => { + let _ = tx.send(true); }, - sc_network::SyncEvent::NotificationStreamClosed { remote } => { - if self.on_sync_peer_disconnected(remote).is_err() { - log::trace!( - target: LOG_TARGET, - "Disconnected peer which had earlier been refused by on_sync_peer_connected {}", - remote - ); - } + Err(()) => { + log::debug!( + target: LOG_TARGET, + "Failed to register peer {remote:?}: {received_handshake:?}", + ); + let _ = tx.send(false); }, - sc_network::SyncEvent::NotificationsReceived { remote, messages } => { - for message in messages { - if self.peers.contains_key(&remote) { - if let Ok(announce) = BlockAnnounce::decode(&mut message.as_ref()) { - self.last_notification_io = Instant::now(); - self.push_block_announce_validation(remote, announce); - } else { - log::warn!(target: "sub-libp2p", "Failed to decode block announce"); - } + }, + sc_network::SyncEvent::NotificationStreamClosed { remote } => { + if self.on_sync_peer_disconnected(remote).is_err() { + log::trace!( + target: LOG_TARGET, + "Disconnected peer which had earlier been refused by on_sync_peer_connected {}", + remote + ); + } + }, + sc_network::SyncEvent::NotificationsReceived { remote, messages } => { + for message in messages { + if self.peers.contains_key(&remote) { + if let Ok(announce) = BlockAnnounce::decode(&mut message.as_ref()) { + self.last_notification_io = Instant::now(); + self.push_block_announce_validation(remote, announce); } else { - log::trace!( - target: LOG_TARGET, - "Received sync for peer earlier refused by sync layer: {remote}", - ); + log::warn!(target: "sub-libp2p", "Failed to decode block announce"); } + } else { + log::trace!( + target: LOG_TARGET, + "Received sync for peer earlier refused by sync layer: {remote}", + ); } - }, - sc_network::SyncEvent::NotificationSinkReplaced { remote, sink } => { - if let Some(peer) = self.peers.get_mut(&remote) { - peer.sink = sink; - } - }, - } + } + }, + sc_network::SyncEvent::NotificationSinkReplaced { remote, sink } => { + if let Some(peer) = self.peers.get_mut(&remote) { + peer.sink = sink; + } + }, } + } - // Retreive warp sync target block header just before polling `ChainSync` - // to make progress as soon as we receive it. - match self.warp_sync_target_block_header_rx.poll_unpin(cx) { - Poll::Ready(Ok(target)) => { - self.chain_sync.set_warp_sync_target_block(target); + fn pass_warp_sync_target_block_header(&mut self, header: Result) { + match header { + Ok(header) => { + self.chain_sync.set_warp_sync_target_block(header); }, - Poll::Ready(Err(err)) => { + Err(err) => { log::error!( target: LOG_TARGET, "Failed to get target block for warp sync. Error: {err:?}", ); }, - Poll::Pending => {}, } - - // Send outbound requests on `ChanSync`'s behalf. - self.send_chain_sync_requests(); - - // Poll & process pending responses. - while let Poll::Ready(Some(event)) = self.pending_responses.poll_next_unpin(cx) { - self.process_response_event(event); - } - - // Poll block announce validations last, because if a block announcement was received - // through the event stream between `SyncingEngine` and `Protocol` and the validation - // finished right after it is queued, the resulting block request (if any) can be sent - // right away. - while let Poll::Ready(Some(result)) = self.block_announce_validator.poll_next_unpin(cx) { - self.process_block_announce_validation_result(result); - } - - Poll::Pending } /// Called by peer when it is disconnecting. diff --git a/substrate/client/network/sync/src/pending_responses.rs b/substrate/client/network/sync/src/pending_responses.rs index 9e2fd5cfd674..55308dfc1ea9 100644 --- a/substrate/client/network/sync/src/pending_responses.rs +++ b/substrate/client/network/sync/src/pending_responses.rs @@ -17,20 +17,20 @@ // along with this program. If not, see . //! [`PendingResponses`] is responsible for keeping track of pending responses and -//! polling them. +//! polling them. [`Stream`] implemented by [`PendingResponses`] never terminates. use crate::types::PeerRequest; use futures::{ channel::oneshot, future::BoxFuture, - stream::{BoxStream, Stream}, + stream::{BoxStream, FusedStream, Stream}, FutureExt, StreamExt, }; use libp2p::PeerId; use log::error; use sc_network::request_responses::RequestFailure; use sp_runtime::traits::Block as BlockT; -use std::task::{Context, Poll}; +use std::task::{Context, Poll, Waker}; use tokio_stream::StreamMap; /// Log target for this file. @@ -53,11 +53,13 @@ pub(crate) struct ResponseEvent { pub(crate) struct PendingResponses { /// Pending responses pending_responses: StreamMap, ResponseResult)>>, + /// Waker to implement never terminating stream + waker: Option, } impl PendingResponses { pub fn new() -> Self { - Self { pending_responses: StreamMap::new() } + Self { pending_responses: StreamMap::new(), waker: None } } pub fn insert( @@ -82,6 +84,10 @@ impl PendingResponses { ); debug_assert!(false); } + + if let Some(waker) = self.waker.take() { + waker.wake(); + } } pub fn remove(&mut self, peer_id: &PeerId) -> bool { @@ -93,8 +99,6 @@ impl PendingResponses { } } -impl Unpin for PendingResponses {} - impl Stream for PendingResponses { type Item = ResponseEvent; @@ -102,8 +106,8 @@ impl Stream for PendingResponses { mut self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - match futures::ready!(self.pending_responses.poll_next_unpin(cx)) { - Some((peer_id, (request, response))) => { + match self.pending_responses.poll_next_unpin(cx) { + Poll::Ready(Some((peer_id, (request, response)))) => { // We need to manually remove the stream, because `StreamMap` doesn't know yet that // it's going to yield `None`, so may not remove it before the next request is made // to the same peer. @@ -111,7 +115,18 @@ impl Stream for PendingResponses { Poll::Ready(Some(ResponseEvent { peer_id, request, response })) }, - None => Poll::Ready(None), + Poll::Ready(None) | Poll::Pending => { + self.waker = Some(cx.waker().clone()); + + Poll::Pending + }, } } } + +// As [`PendingResponses`] never terminates, we can easily implement [`FusedStream`] for it. +impl FusedStream for PendingResponses { + fn is_terminated(&self) -> bool { + false + } +} From 8cfbee706dab7bf55fe6c9fb3a8bfbe29e6776aa Mon Sep 17 00:00:00 2001 From: Juan Girini Date: Fri, 3 Nov 2023 16:36:48 +0100 Subject: [PATCH 08/31] Add deprecation checklist document for Substrate (#1583) fixes https://github.com/paritytech/polkadot-sdk/issues/182 This PR adds a document with recommendations of how deprecations should be handled. Initiated within FRAME, this checklist could be extended to the rest of the repo. I want to quote here a comment from @kianenigma that summarizes the spirit of this new document: > I would see it as a guideline of "what an extensive deprecation process looks like". As the author of a PR, you should match this against your "common sense" and see if it is needed or not. Someone else can nudge you to "hey, this is an important PR, you should go through the deprecation process". > > For some trivial things, all the steps might be an overkill. --------- Co-authored-by: Francisco Aguirre --- docs/DEPRECATION_CHECKLIST.md | 79 +++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 docs/DEPRECATION_CHECKLIST.md diff --git a/docs/DEPRECATION_CHECKLIST.md b/docs/DEPRECATION_CHECKLIST.md new file mode 100644 index 000000000000..fccf93d22737 --- /dev/null +++ b/docs/DEPRECATION_CHECKLIST.md @@ -0,0 +1,79 @@ +# Deprecation Checklist + +This deprecation checklist makes sense while we don’t use [SemVer](https://semver.org/). +After that, this document will most likely change. +As deprecation and removal of existing code can happen on any release, we need to be mindful that external builders +could be impacted by the changes we make. +The deprecation checklist tries to mitigate this impact, while still keeping the developer experience, the DevEx, as +smooth as possible. + +To start a deprecation process, a new issue with the label `T15-deprecation` needs to be created for correct tracking. +Then these are the actions to take: + +## Hard deprecate by adding a warning message + +The warning message shall include a removal month and year, which is suggested to be 6 months after the deprecation +notice is released. +This means that the code will be removed in a release within that month (or after, but never before). Please use this +template, doing so makes it easy to search through the code base: + +```rust +#[deprecated(note = "[DEPRECATED] will be removed after [DATE]. [ALTERNATIVE]")] +``` +`[ALTERNATIVE]` won't always be possible but offer it if it is. + +E.g. +```rust +#[deprecated(note = "`GenesisConfig` will be removed after December 2023. Use `RuntimeGenesisConfig` instead.")] +``` + +Some pieces of code cannot be labeled as deprecated, like [reexports](https://github.com/rust-lang/rust/issues/30827) +or [dispatchables](https://github.com/paritytech/polkadot-sdk/issues/182#issuecomment-1691684159), for instance. +In cases like that we can only make a visible enough comment, and make sure that we [announce the deprecation properly](#announce-the-deprecation-and-removal). + +## Remove usage of the deprecated code in the code base + +Just make sure that we are not using the deprecated code ourselves. +If you added the deprecation warning from the previous step, this can be done by making sure that warning is not shown +when building the code. + +## Update examples and tutorials + +Make sure that the rust docs are updated. +We also need [https://docs.substrate.io/](https://docs.substrate.io/) to be updated accordingly. The repo behind it is +[https://github.com/substrate-developer-hub/substrate-docs](https://github.com/substrate-developer-hub/substrate-docs). + +## Announce the deprecation and removal + +**At minimum they should be noted in the release log.** Please see how to document a PR [here](https://github.com/paritytech/polkadot-sdk/blob/master/docs/CONTRIBUTING.md#documentation). +There you can give instructions based on the audience and tell them what they need to do to upgrade the code. + +Some breaking changes have a bigger impact than others. When the impact is big the release note is not enough, though +it should still be the primary place for the notice. You can link back to the changelog files in other channels if you +want to announce it somewhere else. +Make sure you are as loud as you need to be for the magnitude of the breaking change. + +## Removal version is planned + +Depending on the removal date indicated in the deprecation warning in the [first step](#hard-deprecate-by-adding-a-warning-message), +the nature and the importance of the change, it might make sense to coordinate the release with other developers and +with the Release team. + +## Deprecated code is removed + +The deprecated code finally gets removed. +Don’t forget to [announce this accordingly](#announce-the-deprecation-and-removal). + +βœ… In order to not forget any of these steps, consider using this template in your deprecation issue: + +```markdown +### Tasks + +- [ ] Deprecate code by adding a warning message +- [ ] Remove usage of the deprecated code in the code base +- [ ] Update examples and tutorials +- [ ] Announce code deprecation +- [ ] Plan removal version +- [ ] Announce code removal +- [ ] Remove deprecated code +``` From cd2d5d2579f12dff6d6999b5c9e1f011dcd19a36 Mon Sep 17 00:00:00 2001 From: s0me0ne-unkn0wn <48632512+s0me0ne-unkn0wn@users.noreply.github.com> Date: Fri, 3 Nov 2023 16:48:41 +0100 Subject: [PATCH 09/31] Tracking/limiting memory allocator (#1192) --- Cargo.lock | 9 + polkadot/node/core/pvf/common/src/error.rs | 27 +- .../node/core/pvf/common/src/executor_intf.rs | 27 +- polkadot/node/core/pvf/common/src/prepare.rs | 4 +- .../node/core/pvf/prepare-worker/Cargo.toml | 13 + .../benches/prepare_rococo_runtime.rs | 65 +++++ .../pvf/prepare-worker/src/executor_intf.rs | 42 --- .../node/core/pvf/prepare-worker/src/lib.rs | 93 ++++++- polkadot/node/core/pvf/src/metrics.rs | 20 ++ polkadot/node/core/pvf/src/prepare/pool.rs | 14 + .../node/core/pvf/src/prepare/worker_intf.rs | 3 + polkadot/node/core/pvf/src/testing.rs | 2 +- polkadot/node/core/pvf/tests/it/adder.rs | 2 + polkadot/node/core/pvf/tests/it/main.rs | 44 +++- polkadot/node/malus/Cargo.toml | 2 +- polkadot/node/tracking-allocator/Cargo.toml | 6 + polkadot/node/tracking-allocator/src/lib.rs | 242 ++++++++++++++++++ polkadot/primitives/src/v6/executor_params.rs | 10 + polkadot/src/main.rs | 2 +- 19 files changed, 569 insertions(+), 58 deletions(-) create mode 100644 polkadot/node/core/pvf/prepare-worker/benches/prepare_rococo_runtime.rs delete mode 100644 polkadot/node/core/pvf/prepare-worker/src/executor_intf.rs create mode 100644 polkadot/node/tracking-allocator/Cargo.toml create mode 100644 polkadot/node/tracking-allocator/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 7d9e4bf725ca..ccd410f1415b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12421,15 +12421,20 @@ name = "polkadot-node-core-pvf-prepare-worker" version = "1.0.0" dependencies = [ "cfg-if", + "criterion 0.4.0", "libc", "parity-scale-codec", "polkadot-node-core-pvf-common", "polkadot-primitives", "rayon", + "rococo-runtime", "sc-executor-common", "sc-executor-wasmtime", + "sp-maybe-compressed-blob", "tikv-jemalloc-ctl", + "tikv-jemallocator", "tracing-gum", + "tracking-allocator", ] [[package]] @@ -19442,6 +19447,10 @@ dependencies = [ "tracing-serde", ] +[[package]] +name = "tracking-allocator" +version = "1.0.0" + [[package]] name = "trie-bench" version = "0.38.0" diff --git a/polkadot/node/core/pvf/common/src/error.rs b/polkadot/node/core/pvf/common/src/error.rs index 6fdd06057c8b..82b56562d8cc 100644 --- a/polkadot/node/core/pvf/common/src/error.rs +++ b/polkadot/node/core/pvf/common/src/error.rs @@ -23,27 +23,36 @@ use std::fmt; pub type PrepareResult = Result; /// An error that occurred during the prepare part of the PVF pipeline. +// Codec indexes are intended to stabilize pre-encoded payloads (see `OOM_PAYLOAD` below) #[derive(Debug, Clone, Encode, Decode)] pub enum PrepareError { /// During the prevalidation stage of preparation an issue was found with the PVF. + #[codec(index = 0)] Prevalidation(String), /// Compilation failed for the given PVF. + #[codec(index = 1)] Preparation(String), /// Instantiation of the WASM module instance failed. + #[codec(index = 2)] RuntimeConstruction(String), /// An unexpected panic has occurred in the preparation worker. + #[codec(index = 3)] Panic(String), /// Failed to prepare the PVF due to the time limit. + #[codec(index = 4)] TimedOut, /// An IO error occurred. This state is reported by either the validation host or by the /// worker. + #[codec(index = 5)] IoErr(String), /// The temporary file for the artifact could not be created at the given cache path. This /// state is reported by the validation host (not by the worker). + #[codec(index = 6)] CreateTmpFileErr(String), /// The response from the worker is received, but the file cannot be renamed (moved) to the /// final destination location. This state is reported by the validation host (not by the /// worker). + #[codec(index = 7)] RenameTmpFileErr { err: String, // Unfortunately `PathBuf` doesn't implement `Encode`/`Decode`, so we do a fallible @@ -51,12 +60,19 @@ pub enum PrepareError { src: Option, dest: Option, }, + /// Memory limit reached + #[codec(index = 8)] + OutOfMemory, /// The response from the worker is received, but the worker cache could not be cleared. The /// worker has to be killed to avoid jobs having access to data from other jobs. This state is /// reported by the validation host (not by the worker). + #[codec(index = 9)] ClearWorkerDir(String), } +/// Pre-encoded length-prefixed `PrepareResult::Err(PrepareError::OutOfMemory)` +pub const OOM_PAYLOAD: &[u8] = b"\x02\x00\x00\x00\x00\x00\x00\x00\x01\x08"; + impl PrepareError { /// Returns whether this is a deterministic error, i.e. one that should trigger reliably. Those /// errors depend on the PVF itself and the sc-executor/wasmtime logic. @@ -67,7 +83,7 @@ impl PrepareError { pub fn is_deterministic(&self) -> bool { use PrepareError::*; match self { - Prevalidation(_) | Preparation(_) | Panic(_) => true, + Prevalidation(_) | Preparation(_) | Panic(_) | OutOfMemory => true, TimedOut | IoErr(_) | CreateTmpFileErr(_) | @@ -92,6 +108,7 @@ impl fmt::Display for PrepareError { CreateTmpFileErr(err) => write!(f, "prepare: error creating tmp file: {}", err), RenameTmpFileErr { err, src, dest } => write!(f, "prepare: error renaming tmp file ({:?} -> {:?}): {}", src, dest, err), + OutOfMemory => write!(f, "prepare: out of memory"), ClearWorkerDir(err) => write!(f, "prepare: error clearing worker cache: {}", err), } } @@ -147,3 +164,11 @@ impl fmt::Display for InternalValidationError { } } } + +#[test] +fn pre_encoded_payloads() { + let oom_enc = PrepareResult::Err(PrepareError::OutOfMemory).encode(); + let mut oom_payload = oom_enc.len().to_le_bytes().to_vec(); + oom_payload.extend(oom_enc); + assert_eq!(oom_payload, OOM_PAYLOAD); +} diff --git a/polkadot/node/core/pvf/common/src/executor_intf.rs b/polkadot/node/core/pvf/common/src/executor_intf.rs index 58d2a90371cf..3a1d3ac1ba07 100644 --- a/polkadot/node/core/pvf/common/src/executor_intf.rs +++ b/polkadot/node/core/pvf/common/src/executor_intf.rs @@ -166,15 +166,36 @@ pub fn params_to_wasmtime_semantics(par: &ExecutorParams) -> Result stack_limit.logical_max = *slm, ExecutorParam::StackNativeMax(snm) => stack_limit.native_stack_max = *snm, ExecutorParam::WasmExtBulkMemory => sem.wasm_bulk_memory = true, - // TODO: Not implemented yet; . - ExecutorParam::PrecheckingMaxMemory(_) => (), - ExecutorParam::PvfPrepTimeout(_, _) | ExecutorParam::PvfExecTimeout(_, _) => (), /* Not used here */ + ExecutorParam::PrecheckingMaxMemory(_) | + ExecutorParam::PvfPrepTimeout(_, _) | + ExecutorParam::PvfExecTimeout(_, _) => (), /* Not used here */ } } sem.deterministic_stack_limit = Some(stack_limit); Ok(sem) } +/// Runs the prevalidation on the given code. Returns a [`RuntimeBlob`] if it succeeds. +pub fn prevalidate(code: &[u8]) -> Result { + let blob = RuntimeBlob::new(code)?; + // It's assumed this function will take care of any prevalidation logic + // that needs to be done. + // + // Do nothing for now. + Ok(blob) +} + +/// Runs preparation on the given runtime blob. If successful, it returns a serialized compiled +/// artifact which can then be used to pass into `Executor::execute` after writing it to the disk. +pub fn prepare( + blob: RuntimeBlob, + executor_params: &ExecutorParams, +) -> Result, sc_executor_common::error::WasmError> { + let semantics = params_to_wasmtime_semantics(executor_params) + .map_err(|e| sc_executor_common::error::WasmError::Other(e))?; + sc_executor_wasmtime::prepare_runtime_artifact(blob, &semantics) +} + /// Available host functions. We leave out: /// /// 1. storage related stuff (PVF doesn't have a notion of a persistent storage/trie) diff --git a/polkadot/node/core/pvf/common/src/prepare.rs b/polkadot/node/core/pvf/common/src/prepare.rs index c205eddfb8b1..4436ebe4861e 100644 --- a/polkadot/node/core/pvf/common/src/prepare.rs +++ b/polkadot/node/core/pvf/common/src/prepare.rs @@ -29,12 +29,14 @@ pub struct PrepareStats { /// supported by the OS, `ru_maxrss`. #[derive(Clone, Debug, Default, Encode, Decode)] pub struct MemoryStats { - /// Memory stats from `tikv_jemalloc_ctl`. + /// Memory stats from `tikv_jemalloc_ctl`, polling-based and not very precise. #[cfg(any(target_os = "linux", feature = "jemalloc-allocator"))] pub memory_tracker_stats: Option, /// `ru_maxrss` from `getrusage`. `None` if an error occurred. #[cfg(target_os = "linux")] pub max_rss: Option, + /// Peak allocation in bytes measured by tracking allocator + pub peak_tracked_alloc: u64, } /// Statistics of collected memory metrics. diff --git a/polkadot/node/core/pvf/prepare-worker/Cargo.toml b/polkadot/node/core/pvf/prepare-worker/Cargo.toml index e5a08f8a153d..dae88afc555d 100644 --- a/polkadot/node/core/pvf/prepare-worker/Cargo.toml +++ b/polkadot/node/core/pvf/prepare-worker/Cargo.toml @@ -11,7 +11,9 @@ cfg-if = "1.0" gum = { package = "tracing-gum", path = "../../../gum" } libc = "0.2.139" rayon = "1.5.1" +tracking-allocator = { path = "../../../tracking-allocator" } tikv-jemalloc-ctl = { version = "0.5.0", optional = true } +tikv-jemallocator = { version = "0.5.0", optional = true } parity-scale-codec = { version = "3.6.1", default-features = false, features = ["derive"] } @@ -22,11 +24,22 @@ sc-executor-common = { path = "../../../../../substrate/client/executor/common" sc-executor-wasmtime = { path = "../../../../../substrate/client/executor/wasmtime" } [target.'cfg(target_os = "linux")'.dependencies] +tikv-jemallocator = "0.5.0" tikv-jemalloc-ctl = "0.5.0" [features] builder = [] jemalloc-allocator = [ "dep:tikv-jemalloc-ctl", + "dep:tikv-jemallocator", "polkadot-node-core-pvf-common/jemalloc-allocator", ] + +[dev-dependencies] +criterion = { version = "0.4.0", default-features = false, features = ["cargo_bench_support"] } +rococo-runtime = { path = "../../../../runtime/rococo" } +sp-maybe-compressed-blob = { path = "../../../../../substrate/primitives/maybe-compressed-blob" } + +[[bench]] +name = "prepare_rococo_runtime" +harness = false diff --git a/polkadot/node/core/pvf/prepare-worker/benches/prepare_rococo_runtime.rs b/polkadot/node/core/pvf/prepare-worker/benches/prepare_rococo_runtime.rs new file mode 100644 index 000000000000..ba2568cd80cc --- /dev/null +++ b/polkadot/node/core/pvf/prepare-worker/benches/prepare_rococo_runtime.rs @@ -0,0 +1,65 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +use criterion::{criterion_group, criterion_main, Criterion, SamplingMode}; +use polkadot_node_core_pvf_common::{ + executor_intf::{prepare, prevalidate}, + prepare::PrepareJobKind, + pvf::PvfPrepData, +}; +use polkadot_primitives::ExecutorParams; +use std::time::Duration; + +fn do_prepare_runtime(pvf: PvfPrepData) { + let blob = match prevalidate(&pvf.code()) { + Err(err) => panic!("{:?}", err), + Ok(b) => b, + }; + + match prepare(blob, &pvf.executor_params()) { + Ok(_) => (), + Err(err) => panic!("{:?}", err), + } +} + +fn prepare_rococo_runtime(c: &mut Criterion) { + let blob = rococo_runtime::WASM_BINARY.unwrap(); + let pvf = match sp_maybe_compressed_blob::decompress(&blob, 64 * 1024 * 1024) { + Ok(code) => PvfPrepData::from_code( + code.into_owned(), + ExecutorParams::default(), + Duration::from_secs(360), + PrepareJobKind::Compilation, + ), + Err(e) => { + panic!("Cannot decompress blob: {:?}", e); + }, + }; + + let mut group = c.benchmark_group("rococo"); + group.sampling_mode(SamplingMode::Flat); + group.sample_size(20); + group.measurement_time(Duration::from_secs(240)); + group.bench_function("prepare Rococo runtime", |b| { + // `PvfPrepData` is designed to be cheap to clone, so cloning shouldn't affect the + // benchmark accuracy + b.iter(|| do_prepare_runtime(pvf.clone())) + }); + group.finish(); +} + +criterion_group!(preparation, prepare_rococo_runtime); +criterion_main!(preparation); diff --git a/polkadot/node/core/pvf/prepare-worker/src/executor_intf.rs b/polkadot/node/core/pvf/prepare-worker/src/executor_intf.rs deleted file mode 100644 index 1f88f6a6dd6e..000000000000 --- a/polkadot/node/core/pvf/prepare-worker/src/executor_intf.rs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Polkadot is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Polkadot. If not, see . - -//! Interface to the Substrate Executor - -use polkadot_node_core_pvf_common::executor_intf::params_to_wasmtime_semantics; -use polkadot_primitives::ExecutorParams; -use sc_executor_common::runtime_blob::RuntimeBlob; - -/// Runs the prevalidation on the given code. Returns a [`RuntimeBlob`] if it succeeds. -pub fn prevalidate(code: &[u8]) -> Result { - let blob = RuntimeBlob::new(code)?; - // It's assumed this function will take care of any prevalidation logic - // that needs to be done. - // - // Do nothing for now. - Ok(blob) -} - -/// Runs preparation on the given runtime blob. If successful, it returns a serialized compiled -/// artifact which can then be used to pass into `Executor::execute` after writing it to the disk. -pub fn prepare( - blob: RuntimeBlob, - executor_params: &ExecutorParams, -) -> Result, sc_executor_common::error::WasmError> { - let semantics = params_to_wasmtime_semantics(executor_params) - .map_err(|e| sc_executor_common::error::WasmError::Other(e))?; - sc_executor_wasmtime::prepare_runtime_artifact(blob, &semantics) -} diff --git a/polkadot/node/core/pvf/prepare-worker/src/lib.rs b/polkadot/node/core/pvf/prepare-worker/src/lib.rs index 926d9cabe182..37a4dd06075e 100644 --- a/polkadot/node/core/pvf/prepare-worker/src/lib.rs +++ b/polkadot/node/core/pvf/prepare-worker/src/lib.rs @@ -16,10 +16,9 @@ //! Contains the logic for preparing PVFs. Used by the polkadot-prepare-worker binary. -mod executor_intf; mod memory_stats; -pub use executor_intf::{prepare, prevalidate}; +use polkadot_node_core_pvf_common::executor_intf::{prepare, prevalidate}; // NOTE: Initializing logging in e.g. tests will not have an effect in the workers, as they are // separate spawned processes. Run with e.g. `RUST_LOG=parachain::pvf-prepare-worker=trace`. @@ -31,7 +30,7 @@ use crate::memory_stats::max_rss_stat::{extract_max_rss_stat, get_max_rss_thread use crate::memory_stats::memory_tracker::{get_memory_tracker_loop_stats, memory_tracker_loop}; use parity_scale_codec::{Decode, Encode}; use polkadot_node_core_pvf_common::{ - error::{PrepareError, PrepareResult}, + error::{PrepareError, PrepareResult, OOM_PAYLOAD}, executor_intf::create_runtime_from_artifact_bytes, framed_recv_blocking, framed_send_blocking, prepare::{MemoryStats, PrepareJobKind, PrepareStats}, @@ -46,11 +45,24 @@ use polkadot_node_core_pvf_common::{ use polkadot_primitives::ExecutorParams; use std::{ fs, io, - os::unix::net::UnixStream, + os::{ + fd::{AsRawFd, RawFd}, + unix::net::UnixStream, + }, path::PathBuf, sync::{mpsc::channel, Arc}, time::Duration, }; +use tracking_allocator::TrackingAllocator; + +#[cfg(any(target_os = "linux", feature = "jemalloc-allocator"))] +#[global_allocator] +static ALLOC: TrackingAllocator = + TrackingAllocator(tikv_jemallocator::Jemalloc); + +#[cfg(not(any(target_os = "linux", feature = "jemalloc-allocator")))] +#[global_allocator] +static ALLOC: TrackingAllocator = TrackingAllocator(std::alloc::System); /// Contains the bytes for a successfully compiled artifact. pub struct CompiledArtifact(Vec); @@ -83,6 +95,44 @@ fn send_response(stream: &mut UnixStream, result: PrepareResult) -> io::Result<( framed_send_blocking(stream, &result.encode()) } +fn start_memory_tracking(fd: RawFd, limit: Option) { + unsafe { + // SAFETY: Inside the failure handler, the allocator is locked and no allocations or + // deallocations are possible. For Linux, that always holds for the code below, so it's + // safe. For MacOS, that technically holds at the time of writing, but there are no future + // guarantees. + // The arguments of unsafe `libc` calls are valid, the payload validity is covered with + // a test. + ALLOC.start_tracking( + limit, + Some(Box::new(move || { + #[cfg(target_os = "linux")] + { + // Syscalls never allocate or deallocate, so this is safe. + libc::syscall(libc::SYS_write, fd, OOM_PAYLOAD.as_ptr(), OOM_PAYLOAD.len()); + libc::syscall(libc::SYS_close, fd); + libc::syscall(libc::SYS_exit, 1); + } + #[cfg(not(target_os = "linux"))] + { + // Syscalls are not available on MacOS, so we have to use `libc` wrappers. + // Technicaly, there may be allocations inside, although they shouldn't be + // there. In that case, we'll see deadlocks on MacOS after the OOM condition + // triggered. As we consider running a validator on MacOS unsafe, and this + // code is only run by a validator, it's a lesser evil. + libc::write(fd, OOM_PAYLOAD.as_ptr().cast(), OOM_PAYLOAD.len()); + libc::close(fd); + std::process::exit(1); + } + })), + ); + } +} + +fn end_memory_tracking() -> isize { + ALLOC.end_tracking() +} + /// The entrypoint that the spawned prepare worker should start with. /// /// # Parameters @@ -172,6 +222,22 @@ pub fn worker_entrypoint( Arc::clone(&condvar), WaitOutcome::TimedOut, )?; + + start_memory_tracking( + stream.as_raw_fd(), + executor_params.prechecking_max_memory().map(|v| { + v.try_into().unwrap_or_else(|_| { + gum::warn!( + LOG_TARGET, + %worker_pid, + "Illegal pre-checking max memory value {} discarded", + v, + ); + 0 + }) + }), + ); + // Spawn another thread for preparation. let prepare_thread = thread::spawn_worker_thread( "prepare thread", @@ -207,6 +273,17 @@ pub fn worker_entrypoint( let outcome = thread::wait_for_threads(condvar); + let peak_alloc = { + let peak = end_memory_tracking(); + gum::debug!( + target: LOG_TARGET, + %worker_pid, + "prepare job peak allocation is {} bytes", + peak, + ); + peak + }; + let result = match outcome { WaitOutcome::Finished => { let _ = cpu_time_monitor_tx.send(()); @@ -238,6 +315,14 @@ pub fn worker_entrypoint( memory_tracker_stats, #[cfg(target_os = "linux")] max_rss: extract_max_rss_stat(max_rss, worker_pid), + // Negative peak allocation values are legit; they are narrow + // corner cases and shouldn't affect overall statistics + // significantly + peak_tracked_alloc: if peak_alloc > 0 { + peak_alloc as u64 + } else { + 0u64 + }, }; // Write the serialized artifact into a temp file. diff --git a/polkadot/node/core/pvf/src/metrics.rs b/polkadot/node/core/pvf/src/metrics.rs index 3d792793498b..7fd876cf1740 100644 --- a/polkadot/node/core/pvf/src/metrics.rs +++ b/polkadot/node/core/pvf/src/metrics.rs @@ -93,6 +93,10 @@ impl Metrics { metrics.preparation_max_resident.observe(max_resident_kb); metrics.preparation_max_allocated.observe(max_allocated_kb); } + + metrics + .preparation_peak_tracked_allocation + .observe((memory_stats.peak_tracked_alloc / 1024) as f64); } } } @@ -110,10 +114,14 @@ struct MetricsInner { execution_time: prometheus::Histogram, #[cfg(target_os = "linux")] preparation_max_rss: prometheus::Histogram, + // Max. allocated memory, tracked by Jemallocator, polling-based #[cfg(any(target_os = "linux", feature = "jemalloc-allocator"))] preparation_max_allocated: prometheus::Histogram, + // Max. resident memory, tracked by Jemallocator, polling-based #[cfg(any(target_os = "linux", feature = "jemalloc-allocator"))] preparation_max_resident: prometheus::Histogram, + // Peak allocation value, tracked by tracking-allocator + preparation_peak_tracked_allocation: prometheus::Histogram, } impl metrics::Metrics for Metrics { @@ -271,6 +279,18 @@ impl metrics::Metrics for Metrics { )?, registry, )?, + preparation_peak_tracked_allocation: prometheus::register( + prometheus::Histogram::with_opts( + prometheus::HistogramOpts::new( + "polkadot_pvf_preparation_peak_tracked_allocation", + "peak allocation observed for preparation (in kilobytes)", + ).buckets( + prometheus::exponential_buckets(8192.0, 2.0, 10) + .expect("arguments are always valid; qed"), + ), + )?, + registry, + )?, }; Ok(Metrics(Some(inner))) } diff --git a/polkadot/node/core/pvf/src/prepare/pool.rs b/polkadot/node/core/pvf/src/prepare/pool.rs index 7933b0319a6f..6bb6ca5b6445 100644 --- a/polkadot/node/core/pvf/src/prepare/pool.rs +++ b/polkadot/node/core/pvf/src/prepare/pool.rs @@ -399,6 +399,20 @@ fn handle_mux( )?; } + Ok(()) + }, + Outcome::OutOfMemory => { + if attempt_retire(metrics, spawned, worker) { + reply( + from_pool, + FromPool::Concluded { + worker, + rip: true, + result: Err(PrepareError::OutOfMemory), + }, + )?; + } + Ok(()) }, } diff --git a/polkadot/node/core/pvf/src/prepare/worker_intf.rs b/polkadot/node/core/pvf/src/prepare/worker_intf.rs index 1f6ab0b76556..0e50caf1feb5 100644 --- a/polkadot/node/core/pvf/src/prepare/worker_intf.rs +++ b/polkadot/node/core/pvf/src/prepare/worker_intf.rs @@ -98,6 +98,8 @@ pub enum Outcome { /// /// This doesn't return an idle worker instance, thus this worker is no longer usable. IoErr(String), + /// The worker ran out of memory and is aborting. The worker should be ripped. + OutOfMemory, } /// Given the idle token of a worker and parameters of work, communicates with the worker and @@ -234,6 +236,7 @@ async fn handle_response( Ok(result) => result, // Timed out on the child. This should already be logged by the child. Err(PrepareError::TimedOut) => return Outcome::TimedOut, + Err(PrepareError::OutOfMemory) => return Outcome::OutOfMemory, Err(_) => return Outcome::Concluded { worker, result }, }; diff --git a/polkadot/node/core/pvf/src/testing.rs b/polkadot/node/core/pvf/src/testing.rs index 169b55d34b56..4c038896f7f9 100644 --- a/polkadot/node/core/pvf/src/testing.rs +++ b/polkadot/node/core/pvf/src/testing.rs @@ -36,8 +36,8 @@ pub fn validate_candidate( code: &[u8], params: &[u8], ) -> Result, Box> { + use polkadot_node_core_pvf_common::executor_intf::{prepare, prevalidate}; use polkadot_node_core_pvf_execute_worker::execute_artifact; - use polkadot_node_core_pvf_prepare_worker::{prepare, prevalidate}; let code = sp_maybe_compressed_blob::decompress(code, 10 * 1024 * 1024) .expect("Decompressing code failed"); diff --git a/polkadot/node/core/pvf/tests/it/adder.rs b/polkadot/node/core/pvf/tests/it/adder.rs index e8d8a9a6b63e..9a7ddcb40890 100644 --- a/polkadot/node/core/pvf/tests/it/adder.rs +++ b/polkadot/node/core/pvf/tests/it/adder.rs @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . +//! PVF host integration tests checking the chain production pipeline. + use super::TestHost; use adder::{hash_state, BlockData, HeadData}; use parity_scale_codec::{Decode, Encode}; diff --git a/polkadot/node/core/pvf/tests/it/main.rs b/polkadot/node/core/pvf/tests/it/main.rs index a69a488adb98..f4fd7f802f5e 100644 --- a/polkadot/node/core/pvf/tests/it/main.rs +++ b/polkadot/node/core/pvf/tests/it/main.rs @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . +//! General PVF host integration tests checking the functionality of the PVF host itself. + use assert_matches::assert_matches; use parity_scale_codec::Encode as _; use polkadot_node_core_pvf::{ @@ -22,13 +24,10 @@ use polkadot_node_core_pvf::{ JOB_TIMEOUT_WALL_CLOCK_FACTOR, }; use polkadot_parachain_primitives::primitives::{BlockData, ValidationParams, ValidationResult}; -use polkadot_primitives::ExecutorParams; +use polkadot_primitives::{ExecutorParam, ExecutorParams}; #[cfg(target_os = "linux")] use rusty_fork::rusty_fork_test; -#[cfg(feature = "ci-only-tests")] -use polkadot_primitives::ExecutorParam; - use std::time::Duration; use tokio::sync::Mutex; @@ -434,6 +433,43 @@ async fn deleting_prepared_artifact_does_not_dispute() { } } +// This test checks if the adder parachain runtime can be prepared with 10Mb preparation memory +// limit enforced. At the moment of writing, the limit if far enough to prepare the PVF. If it +// starts failing, either Wasmtime version has changed, or the PVF code itself has changed, and +// more memory is required now. Multi-threaded preparation, if ever enabled, may also affect +// memory consumption. +#[tokio::test] +async fn prechecking_within_memory_limits() { + let host = TestHost::new().await; + let result = host + .precheck_pvf( + ::adder::wasm_binary_unwrap(), + ExecutorParams::from(&[ExecutorParam::PrecheckingMaxMemory(10 * 1024 * 1024)][..]), + ) + .await; + + assert_matches!(result, Ok(_)); +} + +// This test checks if the adder parachain runtime can be prepared with 512Kb preparation memory +// limit enforced. At the moment of writing, the limit if not enough to prepare the PVF, and the +// preparation is supposed to generate an error. If the test starts failing, either Wasmtime +// version has changed, or the PVF code itself has changed, and less memory is required now. +#[tokio::test] +async fn prechecking_out_of_memory() { + use polkadot_node_core_pvf::PrepareError; + + let host = TestHost::new().await; + let result = host + .precheck_pvf( + ::adder::wasm_binary_unwrap(), + ExecutorParams::from(&[ExecutorParam::PrecheckingMaxMemory(512 * 1024)][..]), + ) + .await; + + assert_matches!(result, Err(PrepareError::OutOfMemory)); +} + // With one worker, run multiple preparation jobs serially. They should not conflict. #[tokio::test] async fn prepare_can_run_serially() { diff --git a/polkadot/node/malus/Cargo.toml b/polkadot/node/malus/Cargo.toml index 9ce725f16822..f52f0cc0282f 100644 --- a/polkadot/node/malus/Cargo.toml +++ b/polkadot/node/malus/Cargo.toml @@ -48,7 +48,7 @@ erasure = { package = "polkadot-erasure-coding", path = "../../erasure-coding" } rand = "0.8.5" # Required for worker binaries to build. -polkadot-node-core-pvf-common = { path = "../core/pvf/common", features = ["test-utils"] } +polkadot-node-core-pvf-common = { path = "../core/pvf/common" } polkadot-node-core-pvf-execute-worker = { path = "../core/pvf/execute-worker" } polkadot-node-core-pvf-prepare-worker = { path = "../core/pvf/prepare-worker" } diff --git a/polkadot/node/tracking-allocator/Cargo.toml b/polkadot/node/tracking-allocator/Cargo.toml new file mode 100644 index 000000000000..2ea3f69c7d0d --- /dev/null +++ b/polkadot/node/tracking-allocator/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "tracking-allocator" +description = "Tracking allocator to control the amount of memory consumed by the process" +version = "1.0.0" +authors.workspace = true +edition.workspace = true diff --git a/polkadot/node/tracking-allocator/src/lib.rs b/polkadot/node/tracking-allocator/src/lib.rs new file mode 100644 index 000000000000..8441bd1ffafa --- /dev/null +++ b/polkadot/node/tracking-allocator/src/lib.rs @@ -0,0 +1,242 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! Tracking/limiting global allocator. Calculates the peak allocation between two checkpoints for +//! the whole process. Accepts an optional limit and a failure handler which is called if the limit +//! is overflown. + +use core::{ + alloc::{GlobalAlloc, Layout}, + ops::{Deref, DerefMut}, +}; +use std::{ + cell::UnsafeCell, + ptr::null_mut, + sync::atomic::{AtomicBool, Ordering}, +}; + +struct Spinlock { + lock: AtomicBool, + data: UnsafeCell, +} + +struct SpinlockGuard<'a, T: 'a> { + lock: &'a Spinlock, +} + +// SAFETY: We require that the data inside of the `SpinLock` is `Send`, so it can be sent +// and accessed by any thread as long as it's accessed by only one thread at a time. +// The `SpinLock` provides an exclusive lock over it, so it guarantees that multiple +// threads cannot access it at the same time, hence it implements `Sync` (that is, it can be +// accessed concurrently from multiple threads, even though the `T` itself might not +// necessarily be `Sync` too). +unsafe impl Sync for Spinlock {} + +impl Spinlock { + pub const fn new(t: T) -> Spinlock { + Spinlock { lock: AtomicBool::new(false), data: UnsafeCell::new(t) } + } + + #[inline] + pub fn lock(&self) -> SpinlockGuard { + loop { + // Try to acquire the lock. + if self + .lock + .compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed) + .is_ok() + { + return SpinlockGuard { lock: self } + } + // We failed to acquire the lock; wait until it's unlocked. + // + // In theory this should result in less coherency traffic as unlike `compare_exchange` + // it is a read-only operation, so multiple cores can execute it simultaneously + // without taking an exclusive lock over the cache line. + while self.lock.load(Ordering::Relaxed) { + std::hint::spin_loop(); + } + } + } + + #[inline] + fn unlock(&self) { + self.lock.store(false, Ordering::Release); + } +} + +impl Deref for SpinlockGuard<'_, T> { + type Target = T; + + fn deref(&self) -> &T { + // SAFETY: It is safe to dereference a guard to the `UnsafeCell` underlying data as the + // presence of the guard means the data is already locked. + unsafe { &*self.lock.data.get() } + } +} + +impl DerefMut for SpinlockGuard<'_, T> { + fn deref_mut(&mut self) -> &mut T { + // SAFETY: Same as for `Deref::deref`. + unsafe { &mut *self.lock.data.get() } + } +} + +impl Drop for SpinlockGuard<'_, T> { + fn drop(&mut self) { + self.lock.unlock(); + } +} + +struct TrackingAllocatorData { + current: isize, + peak: isize, + limit: isize, + failure_handler: Option>, +} + +impl TrackingAllocatorData { + fn start_tracking( + mut guard: SpinlockGuard, + limit: isize, + failure_handler: Option>, + ) { + guard.current = 0; + guard.peak = 0; + guard.limit = limit; + // Cannot drop it yet, as it would trigger a deallocation + let old_handler = guard.failure_handler.take(); + guard.failure_handler = failure_handler; + drop(guard); + drop(old_handler); + } + + fn end_tracking(mut guard: SpinlockGuard) -> isize { + let peak = guard.peak; + guard.limit = 0; + // Cannot drop it yet, as it would trigger a deallocation + let old_handler = guard.failure_handler.take(); + drop(guard); + drop(old_handler); + peak + } + + #[inline] + fn track_and_check_limits( + mut guard: SpinlockGuard, + alloc: isize, + ) -> Option> { + guard.current += alloc; + if guard.current > guard.peak { + guard.peak = guard.current; + } + if guard.limit == 0 || guard.peak <= guard.limit { + None + } else { + Some(guard) + } + } +} + +static ALLOCATOR_DATA: Spinlock = + Spinlock::new(TrackingAllocatorData { current: 0, peak: 0, limit: 0, failure_handler: None }); + +pub struct TrackingAllocator(pub A); + +impl TrackingAllocator { + /// Start tracking memory allocations and deallocations. + /// + /// # Safety + /// + /// Failure handler is called with the allocator being in the locked state. Thus, no + /// allocations or deallocations are allowed inside the failure handler; otherwise, a + /// deadlock will occur. + pub unsafe fn start_tracking( + &self, + limit: Option, + failure_handler: Option>, + ) { + TrackingAllocatorData::start_tracking( + ALLOCATOR_DATA.lock(), + limit.unwrap_or(0), + failure_handler, + ); + } + + /// End tracking and return the peak allocation value in bytes (as `isize`). Peak allocation + /// value is not guaranteed to be neither non-zero nor positive. + pub fn end_tracking(&self) -> isize { + TrackingAllocatorData::end_tracking(ALLOCATOR_DATA.lock()) + } +} + +#[cold] +#[inline(never)] +unsafe fn fail_allocation(guard: SpinlockGuard) -> *mut u8 { + if let Some(failure_handler) = &guard.failure_handler { + failure_handler() + } + null_mut() +} + +unsafe impl GlobalAlloc for TrackingAllocator { + // SAFETY: + // * The wrapped methods are as safe as the underlying allocator implementation is + + #[inline] + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + let guard = ALLOCATOR_DATA.lock(); + if let Some(guard) = + TrackingAllocatorData::track_and_check_limits(guard, layout.size() as isize) + { + fail_allocation(guard) + } else { + self.0.alloc(layout) + } + } + + #[inline] + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { + let guard = ALLOCATOR_DATA.lock(); + if let Some(guard) = + TrackingAllocatorData::track_and_check_limits(guard, layout.size() as isize) + { + fail_allocation(guard) + } else { + self.0.alloc_zeroed(layout) + } + } + + #[inline] + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) -> () { + let guard = ALLOCATOR_DATA.lock(); + TrackingAllocatorData::track_and_check_limits(guard, -(layout.size() as isize)); + self.0.dealloc(ptr, layout) + } + + #[inline] + unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + let guard = ALLOCATOR_DATA.lock(); + if let Some(guard) = TrackingAllocatorData::track_and_check_limits( + guard, + (new_size as isize) - (layout.size() as isize), + ) { + fail_allocation(guard) + } else { + self.0.realloc(ptr, layout, new_size) + } + } +} diff --git a/polkadot/primitives/src/v6/executor_params.rs b/polkadot/primitives/src/v6/executor_params.rs index 8ea0889bd23b..bb9980f68796 100644 --- a/polkadot/primitives/src/v6/executor_params.rs +++ b/polkadot/primitives/src/v6/executor_params.rs @@ -197,6 +197,16 @@ impl ExecutorParams { None } + /// Returns pre-checking memory limit, if any + pub fn prechecking_max_memory(&self) -> Option { + for param in &self.0 { + if let ExecutorParam::PrecheckingMaxMemory(limit) = param { + return Some(*limit) + } + } + None + } + /// Check params coherence. pub fn check_consistency(&self) -> Result<(), ExecutorParamError> { use ExecutorParam::*; diff --git a/polkadot/src/main.rs b/polkadot/src/main.rs index 5986d8cea7bb..1a96bf8fb00f 100644 --- a/polkadot/src/main.rs +++ b/polkadot/src/main.rs @@ -24,7 +24,7 @@ use color_eyre::eyre; /// `memory_stats::MemoryAllocationTracker`. #[cfg(any(target_os = "linux", feature = "jemalloc-allocator"))] #[global_allocator] -pub static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; +static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; fn main() -> eyre::Result<()> { color_eyre::install()?; From ca5f10567a4647e53ba5a2f900c60994f6e855b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Fri, 3 Nov 2023 19:06:31 +0100 Subject: [PATCH 10/31] `sc-block-builder`: Remove `BlockBuilderProvider` (#2099) The `BlockBuilderProvider` was a trait that was defined in `sc-block-builder`. The trait was implemented for `Client`. This basically meant that you needed to import `sc-block-builder` any way to have access to the block builder. So, this trait was not providing any real value. This pull request is removing the said trait. Instead of the trait it introduces a builder for creating a `BlockBuilder`. The builder currently has the quite fabulous name `BlockBuilderBuilder` (I'm open to any better name :sweat_smile:). The rest of the pull request is about replacing the old trait with the new builder. # Downstream code changes If you used `new_block` or `new_block_at` before you now need to switch it over to the new `BlockBuilderBuilder` pattern: ```rust // `new` requires a type that implements `CallApiAt`. let mut block_builder = BlockBuilderBuilder::new(client) // Then you need to specify the hash of the parent block the block will be build on top of .on_parent_block(at) // The block builder also needs the block number of the parent block. // Here it is fetched from the given `client` using the `HeaderBackend` // However, there also exists `with_parent_block_number` for directly passing the number .fetch_parent_block_number(client) .unwrap() // Enable proof recording if required. This call is optional. .enable_proof_recording() // Pass the digests. This call is optional. .with_inherent_digests(digests) .build() .expect("Creates new block builder"); ``` --------- Co-authored-by: Sebastian Kunert Co-authored-by: command-bot <> --- Cargo.lock | 2 - cumulus/test/client/src/block_builder.rs | 28 +- cumulus/test/service/benches/block_import.rs | 10 +- .../service/benches/block_import_glutton.rs | 13 +- .../test/service/benches/block_production.rs | 25 +- .../benches/block_production_glutton.rs | 22 +- .../test/service/benches/validate_block.rs | 8 +- cumulus/test/service/src/bench_utils.rs | 16 +- .../node/test/client/src/block_builder.rs | 24 +- .../bin/node/cli/benches/block_production.rs | 32 +- substrate/bin/node/testing/src/bench.rs | 11 +- substrate/client/basic-authorship/Cargo.toml | 2 +- .../basic-authorship/src/basic_authorship.rs | 87 ++- substrate/client/block-builder/Cargo.toml | 1 - substrate/client/block-builder/src/lib.rs | 275 +++++---- substrate/client/consensus/aura/src/lib.rs | 11 +- substrate/client/consensus/babe/src/tests.rs | 13 +- substrate/client/consensus/beefy/src/tests.rs | 26 +- .../client/consensus/grandpa/rpc/src/lib.rs | 21 +- .../consensus/grandpa/src/finality_proof.rs | 11 +- .../client/consensus/grandpa/src/tests.rs | 26 +- .../consensus/grandpa/src/voting_rule.rs | 20 +- .../consensus/grandpa/src/warp_proof.rs | 8 +- .../merkle-mountain-range/src/test_utils.rs | 9 +- substrate/client/network/bitswap/src/lib.rs | 8 +- .../network/sync/src/chain_sync/test.rs | 36 +- .../client/network/test/src/block_import.rs | 10 +- substrate/client/network/test/src/lib.rs | 21 +- substrate/client/offchain/src/lib.rs | 17 +- .../client/rpc-spec-v2/src/archive/tests.rs | 73 ++- .../src/chain_head/subscription/inner.rs | 105 +++- .../rpc-spec-v2/src/chain_head/tests.rs | 522 ++++++++++++---- substrate/client/rpc/src/chain/tests.rs | 38 +- substrate/client/rpc/src/dev/tests.rs | 20 +- substrate/client/rpc/src/state/tests.rs | 20 +- substrate/client/service/Cargo.toml | 1 - substrate/client/service/src/client/client.rs | 43 +- .../client/service/test/src/client/mod.rs | 572 +++++++++++++----- .../client/transaction-pool/tests/pool.rs | 8 +- substrate/primitives/api/src/lib.rs | 28 + .../api/test/tests/runtime_calls.rs | 11 +- .../runtime/client/src/block_builder_ext.rs | 12 +- .../runtime/client/src/trait_tests.rs | 225 +++++-- substrate/test-utils/runtime/src/lib.rs | 8 +- .../frame/benchmarking-cli/src/block/bench.rs | 5 +- .../frame/benchmarking-cli/src/block/cmd.rs | 5 +- .../benchmarking-cli/src/extrinsic/bench.rs | 26 +- .../benchmarking-cli/src/extrinsic/cmd.rs | 14 +- .../benchmarking-cli/src/overhead/cmd.rs | 14 +- 49 files changed, 1807 insertions(+), 736 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ccd410f1415b..b4501b5d7512 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14769,7 +14769,6 @@ name = "sc-block-builder" version = "0.10.0-dev" dependencies = [ "parity-scale-codec", - "sc-client-api", "sp-api", "sp-block-builder", "sp-blockchain", @@ -15834,7 +15833,6 @@ dependencies = [ "parking_lot 0.12.1", "pin-project", "rand 0.8.5", - "sc-block-builder", "sc-chain-spec", "sc-client-api", "sc-client-db", diff --git a/cumulus/test/client/src/block_builder.rs b/cumulus/test/client/src/block_builder.rs index 2d930d1be597..929f9f5f48c4 100644 --- a/cumulus/test/client/src/block_builder.rs +++ b/cumulus/test/client/src/block_builder.rs @@ -14,13 +14,13 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use crate::{Backend, Client}; +use crate::Client; use cumulus_primitives_core::{ParachainBlockData, PersistedValidationData}; use cumulus_primitives_parachain_inherent::{ParachainInherentData, INHERENT_IDENTIFIER}; use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder; use cumulus_test_runtime::{Block, GetLastTimestamp, Hash, Header}; use polkadot_primitives::{BlockNumber as PBlockNumber, Hash as PHash}; -use sc_block_builder::{BlockBuilder, BlockBuilderProvider}; +use sc_block_builder::{BlockBuilder, BlockBuilderBuilder}; use sp_api::ProvideRuntimeApi; use sp_runtime::traits::{Block as BlockT, Header as HeaderT}; @@ -37,7 +37,7 @@ pub trait InitBlockBuilder { &self, validation_data: Option>, relay_sproof_builder: RelayStateSproofBuilder, - ) -> sc_block_builder::BlockBuilder; + ) -> sc_block_builder::BlockBuilder; /// Init a specific block builder at a specific block that works for the test runtime. /// @@ -48,7 +48,7 @@ pub trait InitBlockBuilder { at: Hash, validation_data: Option>, relay_sproof_builder: RelayStateSproofBuilder, - ) -> sc_block_builder::BlockBuilder; + ) -> sc_block_builder::BlockBuilder; /// Init a specific block builder that works for the test runtime. /// @@ -61,7 +61,7 @@ pub trait InitBlockBuilder { validation_data: Option>, relay_sproof_builder: RelayStateSproofBuilder, timestamp: u64, - ) -> sc_block_builder::BlockBuilder; + ) -> sc_block_builder::BlockBuilder; } fn init_block_builder( @@ -70,9 +70,13 @@ fn init_block_builder( validation_data: Option>, relay_sproof_builder: RelayStateSproofBuilder, timestamp: u64, -) -> BlockBuilder<'_, Block, Client, Backend> { - let mut block_builder = client - .new_block_at(at, Default::default(), true) +) -> BlockBuilder<'_, Block, Client> { + let mut block_builder = BlockBuilderBuilder::new(client) + .on_parent_block(at) + .fetch_parent_block_number(client) + .unwrap() + .enable_proof_recording() + .build() .expect("Creates new block builder for test runtime"); let mut inherent_data = sp_inherents::InherentData::new(); @@ -118,7 +122,7 @@ impl InitBlockBuilder for Client { &self, validation_data: Option>, relay_sproof_builder: RelayStateSproofBuilder, - ) -> BlockBuilder { + ) -> BlockBuilder { let chain_info = self.chain_info(); self.init_block_builder_at(chain_info.best_hash, validation_data, relay_sproof_builder) } @@ -128,7 +132,7 @@ impl InitBlockBuilder for Client { at: Hash, validation_data: Option>, relay_sproof_builder: RelayStateSproofBuilder, - ) -> BlockBuilder { + ) -> BlockBuilder { let last_timestamp = self.runtime_api().get_last_timestamp(at).expect("Get last timestamp"); let timestamp = last_timestamp + cumulus_test_runtime::MinimumPeriod::get(); @@ -142,7 +146,7 @@ impl InitBlockBuilder for Client { validation_data: Option>, relay_sproof_builder: RelayStateSproofBuilder, timestamp: u64, - ) -> sc_block_builder::BlockBuilder { + ) -> sc_block_builder::BlockBuilder { init_block_builder(self, at, validation_data, relay_sproof_builder, timestamp) } } @@ -155,7 +159,7 @@ pub trait BuildParachainBlockData { fn build_parachain_block(self, parent_state_root: Hash) -> ParachainBlockData; } -impl<'a> BuildParachainBlockData for sc_block_builder::BlockBuilder<'a, Block, Client, Backend> { +impl<'a> BuildParachainBlockData for sc_block_builder::BlockBuilder<'a, Block, Client> { fn build_parachain_block(self, parent_state_root: Hash) -> ParachainBlockData { let built_block = self.build().expect("Builds the block"); diff --git a/cumulus/test/service/benches/block_import.rs b/cumulus/test/service/benches/block_import.rs index b79598b15302..254e03b9263a 100644 --- a/cumulus/test/service/benches/block_import.rs +++ b/cumulus/test/service/benches/block_import.rs @@ -17,12 +17,12 @@ use criterion::{criterion_group, criterion_main, BatchSize, Criterion, Throughput}; +use sc_block_builder::BlockBuilderBuilder; use sc_client_api::UsageProvider; use core::time::Duration; use cumulus_primitives_core::ParaId; -use sc_block_builder::{BlockBuilderProvider, RecordProof}; use sp_api::{Core, ProvideRuntimeApi}; use sp_keyring::Sr25519Keyring::Alice; @@ -52,8 +52,12 @@ fn benchmark_block_import(c: &mut Criterion) { utils::create_benchmarking_transfer_extrinsics(&client, &src_accounts, &dst_accounts); let parent_hash = client.usage_info().chain.best_hash; - let mut block_builder = - client.new_block_at(parent_hash, Default::default(), RecordProof::No).unwrap(); + let mut block_builder = BlockBuilderBuilder::new(&*client) + .on_parent_block(parent_hash) + .fetch_parent_block_number(&*client) + .unwrap() + .build() + .unwrap(); for extrinsic in extrinsics { block_builder.push(extrinsic).unwrap(); } diff --git a/cumulus/test/service/benches/block_import_glutton.rs b/cumulus/test/service/benches/block_import_glutton.rs index b49db9f449e9..aeaf0722e724 100644 --- a/cumulus/test/service/benches/block_import_glutton.rs +++ b/cumulus/test/service/benches/block_import_glutton.rs @@ -17,7 +17,6 @@ use criterion::{criterion_group, criterion_main, BatchSize, Criterion}; -use sc_client_api::UsageProvider; use sp_api::{Core, ProvideRuntimeApi}; use sp_arithmetic::{ traits::{One, Zero}, @@ -27,7 +26,7 @@ use sp_arithmetic::{ use core::time::Duration; use cumulus_primitives_core::ParaId; -use sc_block_builder::{BlockBuilderProvider, RecordProof}; +use sc_block_builder::BlockBuilderBuilder; use sp_keyring::Sr25519Keyring::Alice; use cumulus_test_service::bench_utils as utils; @@ -61,10 +60,14 @@ fn benchmark_block_import(c: &mut Criterion) { runtime.block_on(utils::import_block(&client, &block, false)); // Build the block we will use for benchmarking - let parent_hash = client.usage_info().chain.best_hash; + let parent_hash = client.chain_info().best_hash; let parent_header = client.header(parent_hash).expect("Just fetched this hash.").unwrap(); - let mut block_builder = - client.new_block_at(parent_hash, Default::default(), RecordProof::No).unwrap(); + let mut block_builder = BlockBuilderBuilder::new(&*client) + .on_parent_block(parent_hash) + .fetch_parent_block_number(&*client) + .unwrap() + .build() + .unwrap(); block_builder .push(utils::extrinsic_set_validation_data(parent_header.clone()).clone()) .unwrap(); diff --git a/cumulus/test/service/benches/block_production.rs b/cumulus/test/service/benches/block_production.rs index 1b868d736302..3b0db578041f 100644 --- a/cumulus/test/service/benches/block_production.rs +++ b/cumulus/test/service/benches/block_production.rs @@ -21,7 +21,7 @@ use sc_client_api::UsageProvider; use core::time::Duration; use cumulus_primitives_core::ParaId; -use sc_block_builder::{BlockBuilderProvider, RecordProof}; +use sc_block_builder::BlockBuilderBuilder; use sp_keyring::Sr25519Keyring::Alice; @@ -50,7 +50,11 @@ fn benchmark_block_production(c: &mut Criterion) { let parent_header = client.header(parent_hash).expect("Just fetched this hash.").unwrap(); let set_validation_data_extrinsic = utils::extrinsic_set_validation_data(parent_header); - let mut block_builder = client.new_block(Default::default()).unwrap(); + let mut block_builder = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().best_hash) + .with_parent_block_number(client.chain_info().best_number) + .build() + .unwrap(); block_builder.push(utils::extrinsic_set_time(&client)).unwrap(); block_builder.push(set_validation_data_extrinsic).unwrap(); let built_block = block_builder.build().unwrap(); @@ -66,7 +70,7 @@ fn benchmark_block_production(c: &mut Criterion) { group.measurement_time(Duration::from_secs(120)); group.throughput(Throughput::Elements(max_transfer_count as u64)); - let best_hash = client.chain_info().best_hash; + let chain = client.chain_info(); group.bench_function( format!("(proof = true, transfers = {}) block production", max_transfer_count), @@ -74,9 +78,13 @@ fn benchmark_block_production(c: &mut Criterion) { b.iter_batched( || extrinsics.clone(), |extrinsics| { - let mut block_builder = client - .new_block_at(best_hash, Default::default(), RecordProof::Yes) + let mut block_builder = BlockBuilderBuilder::new(&*client) + .on_parent_block(chain.best_hash) + .with_parent_block_number(chain.best_number) + .enable_proof_recording() + .build() .unwrap(); + for extrinsic in extrinsics { block_builder.push(extrinsic).unwrap(); } @@ -93,9 +101,12 @@ fn benchmark_block_production(c: &mut Criterion) { b.iter_batched( || extrinsics.clone(), |extrinsics| { - let mut block_builder = client - .new_block_at(best_hash, Default::default(), RecordProof::No) + let mut block_builder = BlockBuilderBuilder::new(&*client) + .on_parent_block(chain.best_hash) + .with_parent_block_number(chain.best_number) + .build() .unwrap(); + for extrinsic in extrinsics { block_builder.push(extrinsic).unwrap(); } diff --git a/cumulus/test/service/benches/block_production_glutton.rs b/cumulus/test/service/benches/block_production_glutton.rs index 92a368c88c8d..6ab2c0e56bd1 100644 --- a/cumulus/test/service/benches/block_production_glutton.rs +++ b/cumulus/test/service/benches/block_production_glutton.rs @@ -17,7 +17,6 @@ use criterion::{criterion_group, criterion_main, BatchSize, Criterion}; -use sc_client_api::UsageProvider; use sp_arithmetic::{ traits::{One, Zero}, FixedPointNumber, @@ -26,7 +25,7 @@ use sp_arithmetic::{ use core::time::Duration; use cumulus_primitives_core::ParaId; -use sc_block_builder::{BlockBuilderProvider, RecordProof}; +use sc_block_builder::BlockBuilderBuilder; use sp_keyring::Sr25519Keyring::Alice; @@ -60,11 +59,11 @@ fn benchmark_block_production_compute(c: &mut Criterion) { runtime.block_on(utils::import_block(&client, &block, false)); initialize_glutton_pallet = false; - let parent_hash = client.usage_info().chain.best_hash; - let parent_header = client.header(parent_hash).expect("Just fetched this hash.").unwrap(); + let best_hash = client.chain_info().best_hash; + let best_number = client.chain_info().best_number; + let parent_header = client.header(best_hash).expect("Just fetched this hash.").unwrap(); let set_validation_data_extrinsic = utils::extrinsic_set_validation_data(parent_header); let set_time_extrinsic = utils::extrinsic_set_time(&client); - let best_hash = client.chain_info().best_hash; group.bench_function( format!( @@ -76,8 +75,11 @@ fn benchmark_block_production_compute(c: &mut Criterion) { b.iter_batched( || (set_validation_data_extrinsic.clone(), set_time_extrinsic.clone()), |(validation_data, time)| { - let mut block_builder = client - .new_block_at(best_hash, Default::default(), RecordProof::Yes) + let mut block_builder = BlockBuilderBuilder::new(&*client) + .on_parent_block(best_hash) + .with_parent_block_number(best_number) + .enable_proof_recording() + .build() .unwrap(); block_builder.push(validation_data).unwrap(); block_builder.push(time).unwrap(); @@ -98,8 +100,10 @@ fn benchmark_block_production_compute(c: &mut Criterion) { b.iter_batched( || (set_validation_data_extrinsic.clone(), set_time_extrinsic.clone()), |(validation_data, time)| { - let mut block_builder = client - .new_block_at(best_hash, Default::default(), RecordProof::No) + let mut block_builder = BlockBuilderBuilder::new(&*client) + .on_parent_block(best_hash) + .with_parent_block_number(best_number) + .build() .unwrap(); block_builder.push(validation_data).unwrap(); block_builder.push(time).unwrap(); diff --git a/cumulus/test/service/benches/validate_block.rs b/cumulus/test/service/benches/validate_block.rs index f3b4d0b12144..11a7c4376d4c 100644 --- a/cumulus/test/service/benches/validate_block.rs +++ b/cumulus/test/service/benches/validate_block.rs @@ -27,7 +27,7 @@ use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder; use cumulus_test_runtime::{BalancesCall, Block, Header, UncheckedExtrinsic}; use cumulus_test_service::bench_utils as utils; use polkadot_primitives::HeadData; -use sc_block_builder::BlockBuilderProvider; +use sc_block_builder::BlockBuilderBuilder; use sc_client_api::UsageProvider; use sc_executor_common::wasm_runtime::WasmModule; @@ -46,7 +46,11 @@ fn create_extrinsics( dst_accounts: &[sr25519::Pair], ) -> (usize, Vec) { // Add as many tranfer extrinsics as possible into a single block. - let mut block_builder = client.new_block(Default::default()).unwrap(); + let mut block_builder = BlockBuilderBuilder::new(client) + .on_parent_block(client.chain_info().best_hash) + .with_parent_block_number(client.chain_info().best_number) + .build() + .unwrap(); let mut max_transfer_count = 0; let mut extrinsics = Vec::new(); diff --git a/cumulus/test/service/src/bench_utils.rs b/cumulus/test/service/src/bench_utils.rs index 172c9e504196..82142f21695f 100644 --- a/cumulus/test/service/src/bench_utils.rs +++ b/cumulus/test/service/src/bench_utils.rs @@ -16,6 +16,7 @@ // limitations under the License. use codec::Encode; +use sc_block_builder::BlockBuilderBuilder; use crate::{construct_extrinsic, Client as TestClient}; use cumulus_primitives_core::{relay_chain::AccountId, PersistedValidationData}; @@ -26,7 +27,6 @@ use cumulus_test_runtime::{ }; use frame_system_rpc_runtime_api::AccountNonceApi; use polkadot_primitives::HeadData; -use sc_block_builder::BlockBuilderProvider; use sc_client_api::UsageProvider; use sc_consensus::{ block_import::{BlockImportParams, ForkChoiceStrategy}, @@ -126,8 +126,13 @@ pub fn create_benchmarking_transfer_extrinsics( src_accounts: &[sr25519::Pair], dst_accounts: &[sr25519::Pair], ) -> (usize, Vec) { + let chain = client.usage_info().chain; // Add as many transfer extrinsics as possible into a single block. - let mut block_builder = client.new_block(Default::default()).unwrap(); + let mut block_builder = BlockBuilderBuilder::new(client) + .on_parent_block(chain.best_hash) + .with_parent_block_number(chain.best_number) + .build() + .expect("Creates block builder"); let mut max_transfer_count = 0; let mut extrinsics = Vec::new(); // Every block needs one timestamp extrinsic. @@ -248,8 +253,13 @@ pub fn set_glutton_parameters( Some(last_nonce), ); extrinsics.push(set_storage); + let chain = client.usage_info().chain; - let mut block_builder = client.new_block(Default::default()).unwrap(); + let mut block_builder = BlockBuilderBuilder::new(client) + .on_parent_block(chain.best_hash) + .with_parent_block_number(chain.best_number) + .build() + .unwrap(); block_builder.push(extrinsic_set_time(client)).unwrap(); block_builder.push(extrinsic_set_validation_data(parent_header)).unwrap(); for extrinsic in extrinsics { diff --git a/polkadot/node/test/client/src/block_builder.rs b/polkadot/node/test/client/src/block_builder.rs index b4ff050ff152..57e6008917af 100644 --- a/polkadot/node/test/client/src/block_builder.rs +++ b/polkadot/node/test/client/src/block_builder.rs @@ -14,12 +14,12 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -use crate::{Client, FullBackend}; +use crate::Client; use parity_scale_codec::{Decode, Encode}; use polkadot_primitives::{Block, InherentData as ParachainsInherentData}; use polkadot_test_runtime::UncheckedExtrinsic; use polkadot_test_service::GetLastTimestamp; -use sc_block_builder::{BlockBuilder, BlockBuilderProvider}; +use sc_block_builder::{BlockBuilder, BlockBuilderBuilder}; use sp_api::ProvideRuntimeApi; use sp_consensus_babe::{ digests::{PreDigest, SecondaryPlainPreDigest}, @@ -34,9 +34,7 @@ pub trait InitPolkadotBlockBuilder { /// /// This will automatically create and push the inherents for you to make the block valid for /// the test runtime. - fn init_polkadot_block_builder( - &self, - ) -> sc_block_builder::BlockBuilder; + fn init_polkadot_block_builder(&self) -> sc_block_builder::BlockBuilder; /// Init a Polkadot specific block builder at a specific block that works for the test runtime. /// @@ -45,11 +43,11 @@ pub trait InitPolkadotBlockBuilder { fn init_polkadot_block_builder_at( &self, hash: ::Hash, - ) -> sc_block_builder::BlockBuilder; + ) -> sc_block_builder::BlockBuilder; } impl InitPolkadotBlockBuilder for Client { - fn init_polkadot_block_builder(&self) -> BlockBuilder { + fn init_polkadot_block_builder(&self) -> BlockBuilder { let chain_info = self.chain_info(); self.init_polkadot_block_builder_at(chain_info.best_hash) } @@ -57,7 +55,7 @@ impl InitPolkadotBlockBuilder for Client { fn init_polkadot_block_builder_at( &self, hash: ::Hash, - ) -> BlockBuilder { + ) -> BlockBuilder { let last_timestamp = self.runtime_api().get_last_timestamp(hash).expect("Get last timestamp"); @@ -90,8 +88,12 @@ impl InitPolkadotBlockBuilder for Client { )], }; - let mut block_builder = self - .new_block_at(hash, digest, false) + let mut block_builder = BlockBuilderBuilder::new(self) + .on_parent_block(hash) + .fetch_parent_block_number(&self) + .expect("Fetches parent block number") + .with_inherent_digests(digest) + .build() .expect("Creates new block builder for test runtime"); let mut inherent_data = sp_inherents::InherentData::new(); @@ -144,7 +146,7 @@ pub trait BlockBuilderExt { ) -> Result<(), sp_blockchain::Error>; } -impl BlockBuilderExt for BlockBuilder<'_, Block, Client, FullBackend> { +impl BlockBuilderExt for BlockBuilder<'_, Block, Client> { fn push_polkadot_extrinsic( &mut self, ext: UncheckedExtrinsic, diff --git a/substrate/bin/node/cli/benches/block_production.rs b/substrate/bin/node/cli/benches/block_production.rs index a22aa365e042..f59a125e1c05 100644 --- a/substrate/bin/node/cli/benches/block_production.rs +++ b/substrate/bin/node/cli/benches/block_production.rs @@ -20,7 +20,7 @@ use criterion::{criterion_group, criterion_main, BatchSize, Criterion, Throughpu use kitchensink_runtime::{constants::currency::*, BalancesCall}; use node_cli::service::{create_extrinsic, FullClient}; -use sc_block_builder::{BlockBuilderProvider, BuiltBlock, RecordProof}; +use sc_block_builder::{BlockBuilderBuilder, BuiltBlock}; use sc_consensus::{ block_import::{BlockImportParams, ForkChoiceStrategy}, BlockImport, StateAction, @@ -127,7 +127,11 @@ fn prepare_benchmark(client: &FullClient) -> (usize, Vec) { let mut max_transfer_count = 0; let mut extrinsics = Vec::new(); - let mut block_builder = client.new_block(Default::default()).unwrap(); + let mut block_builder = BlockBuilderBuilder::new(client) + .on_parent_block(client.chain_info().best_hash) + .with_parent_block_number(client.chain_info().best_number) + .build() + .unwrap(); // Every block needs one timestamp extrinsic. let extrinsic_set_time = extrinsic_set_time(1 + MINIMUM_PERIOD_FOR_BLOCKS); @@ -174,7 +178,11 @@ fn block_production(c: &mut Criterion) { // Buliding the very first block is around ~30x slower than any subsequent one, // so let's make sure it's built and imported before we benchmark anything. - let mut block_builder = client.new_block(Default::default()).unwrap(); + let mut block_builder = BlockBuilderBuilder::new(client) + .on_parent_block(client.chain_info().best_hash) + .with_parent_block_number(client.chain_info().best_number) + .build() + .unwrap(); block_builder.push(extrinsic_set_time(1)).unwrap(); import_block(client, block_builder.build().unwrap()); @@ -186,14 +194,19 @@ fn block_production(c: &mut Criterion) { group.sample_size(10); group.throughput(Throughput::Elements(max_transfer_count as u64)); - let best_hash = client.chain_info().best_hash; + let chain = client.chain_info(); + let best_hash = chain.best_hash; + let best_number = chain.best_number; group.bench_function(format!("{} transfers (no proof)", max_transfer_count), |b| { b.iter_batched( || extrinsics.clone(), |extrinsics| { - let mut block_builder = - client.new_block_at(best_hash, Default::default(), RecordProof::No).unwrap(); + let mut block_builder = BlockBuilderBuilder::new(client) + .on_parent_block(best_hash) + .with_parent_block_number(best_number) + .build() + .unwrap(); for extrinsic in extrinsics { block_builder.push(extrinsic).unwrap(); } @@ -207,8 +220,11 @@ fn block_production(c: &mut Criterion) { b.iter_batched( || extrinsics.clone(), |extrinsics| { - let mut block_builder = - client.new_block_at(best_hash, Default::default(), RecordProof::Yes).unwrap(); + let mut block_builder = BlockBuilderBuilder::new(client) + .on_parent_block(best_hash) + .with_parent_block_number(best_number) + .build() + .unwrap(); for extrinsic in extrinsics { block_builder.push(extrinsic).unwrap(); } diff --git a/substrate/bin/node/testing/src/bench.rs b/substrate/bin/node/testing/src/bench.rs index f1ab2212239b..af3701f218ac 100644 --- a/substrate/bin/node/testing/src/bench.rs +++ b/substrate/bin/node/testing/src/bench.rs @@ -39,8 +39,8 @@ use kitchensink_runtime::{ RuntimeCall, Signature, SystemCall, UncheckedExtrinsic, }; use node_primitives::Block; -use sc_block_builder::BlockBuilderProvider; -use sc_client_api::execution_extensions::ExecutionExtensions; +use sc_block_builder::BlockBuilderBuilder; +use sc_client_api::{execution_extensions::ExecutionExtensions, UsageProvider}; use sc_client_db::PruningMode; use sc_consensus::{BlockImport, BlockImportParams, ForkChoiceStrategy, ImportResult, ImportedAux}; use sc_executor::{NativeElseWasmExecutor, WasmExecutionMethod, WasmtimeInstantiationStrategy}; @@ -455,8 +455,13 @@ impl BenchDb { /// Generate new block using this database. pub fn generate_block(&mut self, content: BlockContent) -> Block { let client = self.client(); + let chain = client.usage_info().chain; - let mut block = client.new_block(Default::default()).expect("Block creation failed"); + let mut block = BlockBuilderBuilder::new(&client) + .on_parent_block(chain.best_hash) + .with_parent_block_number(chain.best_number) + .build() + .expect("Failed to create block builder."); for extrinsic in self.generate_inherents(&client) { block.push(extrinsic).expect("Push inherent failed"); diff --git a/substrate/client/basic-authorship/Cargo.toml b/substrate/client/basic-authorship/Cargo.toml index b65a59179554..1d60fc7f53e3 100644 --- a/substrate/client/basic-authorship/Cargo.toml +++ b/substrate/client/basic-authorship/Cargo.toml @@ -19,7 +19,6 @@ futures-timer = "3.0.1" log = "0.4.17" prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../utils/prometheus" } sc-block-builder = { path = "../block-builder" } -sc-client-api = { path = "../api" } sc-proposer-metrics = { path = "../proposer-metrics" } sc-telemetry = { path = "../telemetry" } sc-transaction-pool-api = { path = "../transaction-pool/api" } @@ -32,5 +31,6 @@ sp-runtime = { path = "../../primitives/runtime" } [dev-dependencies] parking_lot = "0.12.1" +sc-client-api = { path = "../api" } sc-transaction-pool = { path = "../transaction-pool" } substrate-test-runtime-client = { path = "../../test-utils/runtime/client" } diff --git a/substrate/client/basic-authorship/src/basic_authorship.rs b/substrate/client/basic-authorship/src/basic_authorship.rs index 4db2de61cb4c..c07f3e639c3e 100644 --- a/substrate/client/basic-authorship/src/basic_authorship.rs +++ b/substrate/client/basic-authorship/src/basic_authorship.rs @@ -28,11 +28,10 @@ use futures::{ select, }; use log::{debug, error, info, trace, warn}; -use sc_block_builder::{BlockBuilderApi, BlockBuilderProvider}; -use sc_client_api::backend; +use sc_block_builder::{BlockBuilderApi, BlockBuilderBuilder}; use sc_telemetry::{telemetry, TelemetryHandle, CONSENSUS_INFO}; use sc_transaction_pool_api::{InPoolTransaction, TransactionPool}; -use sp_api::{ApiExt, ProvideRuntimeApi}; +use sp_api::{ApiExt, CallApiAt, ProvideRuntimeApi}; use sp_blockchain::{ApplyExtrinsicFailed::Validity, Error::ApplyExtrinsicFailed, HeaderBackend}; use sp_consensus::{DisableProofRecording, EnableProofRecording, ProofRecording, Proposal}; use sp_core::traits::SpawnNamed; @@ -60,7 +59,7 @@ const DEFAULT_SOFT_DEADLINE_PERCENT: Percent = Percent::from_percent(50); const LOG_TARGET: &'static str = "basic-authorship"; /// [`Proposer`] factory. -pub struct ProposerFactory { +pub struct ProposerFactory { spawn_handle: Box, /// The client instance. client: Arc, @@ -84,11 +83,11 @@ pub struct ProposerFactory { telemetry: Option, /// When estimating the block size, should the proof be included? include_proof_in_block_size_estimation: bool, - /// phantom member to pin the `Backend`/`ProofRecording` type. - _phantom: PhantomData<(B, PR)>, + /// phantom member to pin the `ProofRecording` type. + _phantom: PhantomData, } -impl ProposerFactory { +impl ProposerFactory { /// Create a new proposer factory. /// /// Proof recording will be disabled when using proposers built by this instance to build @@ -114,7 +113,7 @@ impl ProposerFactory { } } -impl ProposerFactory { +impl ProposerFactory { /// Create a new proposer factory with proof recording enabled. /// /// Each proposer created by this instance will record a proof while building a block. @@ -147,7 +146,7 @@ impl ProposerFactory { } } -impl ProposerFactory { +impl ProposerFactory { /// Set the default block size limit in bytes. /// /// The default value for the block size limit is: @@ -176,29 +175,23 @@ impl ProposerFactory { } } -impl ProposerFactory +impl ProposerFactory where A: TransactionPool + 'static, - B: backend::Backend + Send + Sync + 'static, Block: BlockT, - C: BlockBuilderProvider - + HeaderBackend - + ProvideRuntimeApi - + Send - + Sync - + 'static, + C: HeaderBackend + ProvideRuntimeApi + Send + Sync + 'static, C::Api: ApiExt + BlockBuilderApi, { fn init_with_now( &mut self, parent_header: &::Header, now: Box time::Instant + Send + Sync>, - ) -> Proposer { + ) -> Proposer { let parent_hash = parent_header.hash(); info!("πŸ™Œ Starting consensus session on top of parent {:?}", parent_hash); - let proposer = Proposer::<_, _, _, _, PR> { + let proposer = Proposer::<_, _, _, PR> { spawn_handle: self.spawn_handle.clone(), client: self.client.clone(), parent_hash, @@ -217,22 +210,16 @@ where } } -impl sp_consensus::Environment for ProposerFactory +impl sp_consensus::Environment for ProposerFactory where A: TransactionPool + 'static, - B: backend::Backend + Send + Sync + 'static, Block: BlockT, - C: BlockBuilderProvider - + HeaderBackend - + ProvideRuntimeApi - + Send - + Sync - + 'static, + C: HeaderBackend + ProvideRuntimeApi + CallApiAt + Send + Sync + 'static, C::Api: ApiExt + BlockBuilderApi, PR: ProofRecording, { type CreateProposer = future::Ready>; - type Proposer = Proposer; + type Proposer = Proposer; type Error = sp_blockchain::Error; fn init(&mut self, parent_header: &::Header) -> Self::CreateProposer { @@ -241,7 +228,7 @@ where } /// The proposer logic. -pub struct Proposer { +pub struct Proposer { spawn_handle: Box, client: Arc, parent_hash: Block::Hash, @@ -253,20 +240,14 @@ pub struct Proposer { include_proof_in_block_size_estimation: bool, soft_deadline_percent: Percent, telemetry: Option, - _phantom: PhantomData<(B, PR)>, + _phantom: PhantomData, } -impl sp_consensus::Proposer for Proposer +impl sp_consensus::Proposer for Proposer where A: TransactionPool + 'static, - B: backend::Backend + Send + Sync + 'static, Block: BlockT, - C: BlockBuilderProvider - + HeaderBackend - + ProvideRuntimeApi - + Send - + Sync - + 'static, + C: HeaderBackend + ProvideRuntimeApi + CallApiAt + Send + Sync + 'static, C::Api: ApiExt + BlockBuilderApi, PR: ProofRecording, { @@ -313,17 +294,11 @@ where /// It allows us to increase block utilization. const MAX_SKIPPED_TRANSACTIONS: usize = 8; -impl Proposer +impl Proposer where A: TransactionPool, - B: backend::Backend + Send + Sync + 'static, Block: BlockT, - C: BlockBuilderProvider - + HeaderBackend - + ProvideRuntimeApi - + Send - + Sync - + 'static, + C: HeaderBackend + ProvideRuntimeApi + CallApiAt + Send + Sync + 'static, C::Api: ApiExt + BlockBuilderApi, PR: ProofRecording, { @@ -335,8 +310,12 @@ where block_size_limit: Option, ) -> Result, sp_blockchain::Error> { let block_timer = time::Instant::now(); - let mut block_builder = - self.client.new_block_at(self.parent_hash, inherent_digests, PR::ENABLED)?; + let mut block_builder = BlockBuilderBuilder::new(&*self.client) + .on_parent_block(self.parent_hash) + .with_parent_block_number(self.parent_number) + .with_proof_recording(PR::ENABLED) + .with_inherent_digests(inherent_digests) + .build()?; self.apply_inherents(&mut block_builder, inherent_data)?; @@ -358,7 +337,7 @@ where /// Apply all inherents to the block. fn apply_inherents( &self, - block_builder: &mut sc_block_builder::BlockBuilder<'_, Block, C, B>, + block_builder: &mut sc_block_builder::BlockBuilder<'_, Block, C>, inherent_data: InherentData, ) -> Result<(), sp_blockchain::Error> { let create_inherents_start = time::Instant::now(); @@ -402,7 +381,7 @@ where /// Apply as many extrinsics as possible to the block. async fn apply_extrinsics( &self, - block_builder: &mut sc_block_builder::BlockBuilder<'_, Block, C, B>, + block_builder: &mut sc_block_builder::BlockBuilder<'_, Block, C>, deadline: time::Instant, block_size_limit: Option, ) -> Result { @@ -976,8 +955,12 @@ mod tests { // Exact block_limit, which includes: // 99 (header_size) + 718 (proof@initialize_block) + 246 (one Transfer extrinsic) let block_limit = { - let builder = - client.new_block_at(genesis_header.hash(), Default::default(), true).unwrap(); + let builder = BlockBuilderBuilder::new(&*client) + .on_parent_block(genesis_header.hash()) + .with_parent_block_number(0) + .enable_proof_recording() + .build() + .unwrap(); builder.estimate_block_size(true) + extrinsics[0].encoded_size() }; let block = block_on(proposer.propose( diff --git a/substrate/client/block-builder/Cargo.toml b/substrate/client/block-builder/Cargo.toml index ff2f9635b7a2..2492c4101b29 100644 --- a/substrate/client/block-builder/Cargo.toml +++ b/substrate/client/block-builder/Cargo.toml @@ -16,7 +16,6 @@ targets = ["x86_64-unknown-linux-gnu"] codec = { package = "parity-scale-codec", version = "3.6.1", features = [ "derive", ] } -sc-client-api = { path = "../api" } sp-api = { path = "../../primitives/api" } sp-block-builder = { path = "../../primitives/block-builder" } sp-blockchain = { path = "../../primitives/blockchain" } diff --git a/substrate/client/block-builder/src/lib.rs b/substrate/client/block-builder/src/lib.rs index 1878e7627480..f62b941fdb18 100644 --- a/substrate/client/block-builder/src/lib.rs +++ b/substrate/client/block-builder/src/lib.rs @@ -29,56 +29,147 @@ use codec::Encode; use sp_api::{ - ApiExt, ApiRef, Core, ProvideRuntimeApi, StorageChanges, StorageProof, TransactionOutcome, + ApiExt, ApiRef, CallApiAt, Core, ProvideRuntimeApi, StorageChanges, StorageProof, + TransactionOutcome, }; -use sp_blockchain::{ApplyExtrinsicFailed, Error}; +use sp_blockchain::{ApplyExtrinsicFailed, Error, HeaderBackend}; use sp_core::traits::CallContext; use sp_runtime::{ legacy, traits::{Block as BlockT, Hash, HashingFor, Header as HeaderT, NumberFor, One}, Digest, }; +use std::marker::PhantomData; -use sc_client_api::backend; pub use sp_block_builder::BlockBuilder as BlockBuilderApi; -/// Used as parameter to [`BlockBuilderProvider`] to express if proof recording should be enabled. -/// -/// When `RecordProof::Yes` is given, all accessed trie nodes should be saved. These recorded -/// trie nodes can be used by a third party to proof this proposal without having access to the -/// full storage. -#[derive(Copy, Clone, PartialEq)] -pub enum RecordProof { - /// `Yes`, record a proof. - Yes, - /// `No`, don't record any proof. - No, +/// A builder for creating an instance of [`BlockBuilder`]. +pub struct BlockBuilderBuilder<'a, B, C> { + call_api_at: &'a C, + _phantom: PhantomData, } -impl RecordProof { - /// Returns if `Self` == `Yes`. - pub fn yes(&self) -> bool { - matches!(self, Self::Yes) +impl<'a, B, C> BlockBuilderBuilder<'a, B, C> +where + B: BlockT, +{ + /// Create a new instance of the builder. + /// + /// `call_api_at`: Something that implements [`CallApiAt`]. + pub fn new(call_api_at: &'a C) -> Self { + Self { call_api_at, _phantom: PhantomData } } -} -/// Will return [`RecordProof::No`] as default value. -impl Default for RecordProof { - fn default() -> Self { - Self::No + /// Specify the parent block to build on top of. + pub fn on_parent_block(self, parent_block: B::Hash) -> BlockBuilderBuilderStage1<'a, B, C> { + BlockBuilderBuilderStage1 { call_api_at: self.call_api_at, parent_block } } } -impl From for RecordProof { - fn from(val: bool) -> Self { - if val { - Self::Yes - } else { - Self::No +/// The second stage of the [`BlockBuilderBuilder`]. +/// +/// This type can not be instantiated directly. To get an instance of it +/// [`BlockBuilderBuilder::new`] needs to be used. +pub struct BlockBuilderBuilderStage1<'a, B: BlockT, C> { + call_api_at: &'a C, + parent_block: B::Hash, +} + +impl<'a, B, C> BlockBuilderBuilderStage1<'a, B, C> +where + B: BlockT, +{ + /// Fetch the parent block number from the given `header_backend`. + /// + /// The parent block number is used to initialize the block number of the new block. + /// + /// Returns an error if the parent block specified in + /// [`on_parent_block`](BlockBuilderBuilder::on_parent_block) does not exist. + pub fn fetch_parent_block_number>( + self, + header_backend: &H, + ) -> Result, Error> { + let parent_number = header_backend.number(self.parent_block)?.ok_or_else(|| { + Error::Backend(format!( + "Could not fetch block number for block: {:?}", + self.parent_block + )) + })?; + + Ok(BlockBuilderBuilderStage2 { + call_api_at: self.call_api_at, + enable_proof_recording: false, + inherent_digests: Default::default(), + parent_block: self.parent_block, + parent_number, + }) + } + + /// Provide the block number for the parent block directly. + /// + /// The parent block is specified in [`on_parent_block`](BlockBuilderBuilder::on_parent_block). + /// The parent block number is used to initialize the block number of the new block. + pub fn with_parent_block_number( + self, + parent_number: NumberFor, + ) -> BlockBuilderBuilderStage2<'a, B, C> { + BlockBuilderBuilderStage2 { + call_api_at: self.call_api_at, + enable_proof_recording: false, + inherent_digests: Default::default(), + parent_block: self.parent_block, + parent_number, } } } +/// The second stage of the [`BlockBuilderBuilder`]. +/// +/// This type can not be instantiated directly. To get an instance of it +/// [`BlockBuilderBuilder::new`] needs to be used. +pub struct BlockBuilderBuilderStage2<'a, B: BlockT, C> { + call_api_at: &'a C, + enable_proof_recording: bool, + inherent_digests: Digest, + parent_block: B::Hash, + parent_number: NumberFor, +} + +impl<'a, B: BlockT, C> BlockBuilderBuilderStage2<'a, B, C> { + /// Enable proof recording for the block builder. + pub fn enable_proof_recording(mut self) -> Self { + self.enable_proof_recording = true; + self + } + + /// Enable/disable proof recording for the block builder. + pub fn with_proof_recording(mut self, enable: bool) -> Self { + self.enable_proof_recording = enable; + self + } + + /// Build the block with the given inherent digests. + pub fn with_inherent_digests(mut self, inherent_digests: Digest) -> Self { + self.inherent_digests = inherent_digests; + self + } + + /// Create the instance of the [`BlockBuilder`]. + pub fn build(self) -> Result, Error> + where + C: CallApiAt + ProvideRuntimeApi, + C::Api: BlockBuilderApi, + { + BlockBuilder::new( + self.call_api_at, + self.parent_block, + self.parent_number, + self.enable_proof_recording, + self.inherent_digests, + ) + } +} + /// A block that was build by [`BlockBuilder`] plus some additional data. /// /// This additional data includes the `storage_changes`, these changes can be applied to the @@ -101,63 +192,34 @@ impl BuiltBlock { } } -/// Block builder provider -pub trait BlockBuilderProvider -where - Block: BlockT, - B: backend::Backend, - Self: Sized, - RA: ProvideRuntimeApi, -{ - /// Create a new block, built on top of `parent`. - /// - /// When proof recording is enabled, all accessed trie nodes are saved. - /// These recorded trie nodes can be used by a third party to proof the - /// output of this block builder without having access to the full storage. - fn new_block_at>( - &self, - parent: Block::Hash, - inherent_digests: Digest, - record_proof: R, - ) -> sp_blockchain::Result>; - - /// Create a new block, built on the head of the chain. - fn new_block( - &self, - inherent_digests: Digest, - ) -> sp_blockchain::Result>; -} - /// Utility for building new (valid) blocks from a stream of extrinsics. -pub struct BlockBuilder<'a, Block: BlockT, A: ProvideRuntimeApi, B> { +pub struct BlockBuilder<'a, Block: BlockT, C: ProvideRuntimeApi + 'a> { extrinsics: Vec, - api: ApiRef<'a, A::Api>, + api: ApiRef<'a, C::Api>, + call_api_at: &'a C, version: u32, parent_hash: Block::Hash, - backend: &'a B, /// The estimated size of the block header. estimated_header_size: usize, } -impl<'a, Block, A, B> BlockBuilder<'a, Block, A, B> +impl<'a, Block, C> BlockBuilder<'a, Block, C> where Block: BlockT, - A: ProvideRuntimeApi + 'a, - A::Api: BlockBuilderApi + ApiExt, - B: backend::Backend, + C: CallApiAt + ProvideRuntimeApi + 'a, + C::Api: BlockBuilderApi, { /// Create a new instance of builder based on the given `parent_hash` and `parent_number`. /// /// While proof recording is enabled, all accessed trie nodes are saved. /// These recorded trie nodes can be used by a third party to prove the /// output of this block builder without having access to the full storage. - pub fn new( - api: &'a A, + fn new( + call_api_at: &'a C, parent_hash: Block::Hash, parent_number: NumberFor, - record_proof: RecordProof, + record_proof: bool, inherent_digests: Digest, - backend: &'a B, ) -> Result { let header = <::Header as HeaderT>::new( parent_number + One::one(), @@ -169,9 +231,9 @@ where let estimated_header_size = header.encoded_size(); - let mut api = api.runtime_api(); + let mut api = call_api_at.runtime_api(); - if record_proof.yes() { + if record_proof { api.record_proof(); } @@ -188,8 +250,8 @@ where extrinsics: Vec::new(), api, version, - backend, estimated_header_size, + call_api_at, }) } @@ -241,7 +303,7 @@ where let proof = self.api.extract_proof(); - let state = self.backend.state_at(self.parent_hash)?; + let state = self.call_api_at.state_at(self.parent_hash)?; let storage_changes = self .api @@ -300,22 +362,18 @@ mod tests { #[test] fn block_building_storage_proof_does_not_include_runtime_by_default() { let builder = substrate_test_runtime_client::TestClientBuilder::new(); - let backend = builder.backend(); let client = builder.build(); let genesis_hash = client.info().best_hash; - let block = BlockBuilder::new( - &client, - genesis_hash, - client.info().best_number, - RecordProof::Yes, - Default::default(), - &*backend, - ) - .unwrap() - .build() - .unwrap(); + let block = BlockBuilderBuilder::new(&client) + .on_parent_block(genesis_hash) + .with_parent_block_number(0) + .enable_proof_recording() + .build() + .unwrap() + .build() + .unwrap(); let proof = block.proof.expect("Proof is build on request"); let genesis_state_root = client.header(genesis_hash).unwrap().unwrap().state_root; @@ -333,18 +391,15 @@ mod tests { #[test] fn failing_extrinsic_rolls_back_changes_in_storage_proof() { let builder = substrate_test_runtime_client::TestClientBuilder::new(); - let backend = builder.backend(); let client = builder.build(); + let genesis_hash = client.info().best_hash; - let mut block_builder = BlockBuilder::new( - &client, - client.info().best_hash, - client.info().best_number, - RecordProof::Yes, - Default::default(), - &*backend, - ) - .unwrap(); + let mut block_builder = BlockBuilderBuilder::new(&client) + .on_parent_block(genesis_hash) + .with_parent_block_number(0) + .enable_proof_recording() + .build() + .unwrap(); block_builder.push(ExtrinsicBuilder::new_read_and_panic(8).build()).unwrap_err(); @@ -352,15 +407,12 @@ mod tests { let proof_with_panic = block.proof.expect("Proof is build on request").encoded_size(); - let mut block_builder = BlockBuilder::new( - &client, - client.info().best_hash, - client.info().best_number, - RecordProof::Yes, - Default::default(), - &*backend, - ) - .unwrap(); + let mut block_builder = BlockBuilderBuilder::new(&client) + .on_parent_block(genesis_hash) + .with_parent_block_number(0) + .enable_proof_recording() + .build() + .unwrap(); block_builder.push(ExtrinsicBuilder::new_read(8).build()).unwrap(); @@ -368,17 +420,14 @@ mod tests { let proof_without_panic = block.proof.expect("Proof is build on request").encoded_size(); - let block = BlockBuilder::new( - &client, - client.info().best_hash, - client.info().best_number, - RecordProof::Yes, - Default::default(), - &*backend, - ) - .unwrap() - .build() - .unwrap(); + let block = BlockBuilderBuilder::new(&client) + .on_parent_block(genesis_hash) + .with_parent_block_number(0) + .enable_proof_recording() + .build() + .unwrap() + .build() + .unwrap(); let proof_empty_block = block.proof.expect("Proof is build on request").encoded_size(); diff --git a/substrate/client/consensus/aura/src/lib.rs b/substrate/client/consensus/aura/src/lib.rs index 2ed451ef663e..1be7be8eeeaa 100644 --- a/substrate/client/consensus/aura/src/lib.rs +++ b/substrate/client/consensus/aura/src/lib.rs @@ -548,7 +548,7 @@ where mod tests { use super::*; use parking_lot::Mutex; - use sc_block_builder::BlockBuilderProvider; + use sc_block_builder::BlockBuilderBuilder; use sc_client_api::BlockchainEvents; use sc_consensus::BoxJustificationImport; use sc_consensus_slots::{BackoffAuthoringOnFinalizedHeadLagging, SimpleSlotWorker}; @@ -604,7 +604,14 @@ mod tests { _: Duration, _: Option, ) -> Self::Proposal { - let r = self.1.new_block(digests).unwrap().build().map_err(|e| e.into()); + let r = BlockBuilderBuilder::new(&*self.1) + .on_parent_block(self.1.chain_info().best_hash) + .fetch_parent_block_number(&*self.1) + .unwrap() + .with_inherent_digests(digests) + .build() + .unwrap() + .build(); future::ready(r.map(|b| Proposal { block: b.block, diff --git a/substrate/client/consensus/babe/src/tests.rs b/substrate/client/consensus/babe/src/tests.rs index 02882d8baaed..d097f37c3257 100644 --- a/substrate/client/consensus/babe/src/tests.rs +++ b/substrate/client/consensus/babe/src/tests.rs @@ -20,7 +20,7 @@ use super::*; use authorship::claim_slot; -use sc_block_builder::{BlockBuilder, BlockBuilderProvider}; +use sc_block_builder::{BlockBuilder, BlockBuilderBuilder}; use sc_client_api::{BlockchainEvents, Finalizer}; use sc_consensus::{BoxBlockImport, BoxJustificationImport}; use sc_consensus_epochs::{EpochIdentifier, EpochIdentifierPosition}; @@ -98,8 +98,13 @@ impl DummyProposer { &mut self, pre_digests: Digest, ) -> future::Ready, Error>> { - let block_builder = - self.factory.client.new_block_at(self.parent_hash, pre_digests, false).unwrap(); + let block_builder = BlockBuilderBuilder::new(&*self.factory.client) + .on_parent_block(self.parent_hash) + .fetch_parent_block_number(&*self.factory.client) + .unwrap() + .with_inherent_digests(pre_digests) + .build() + .unwrap(); let mut block = match block_builder.build().map_err(|e| e.into()) { Ok(b) => b.block, @@ -297,7 +302,7 @@ impl TestNetFactory for BabeTestNet { async fn rejects_empty_block() { sp_tracing::try_init_simple(); let mut net = BabeTestNet::new(3); - let block_builder = |builder: BlockBuilder<_, _, _>| builder.build().unwrap().block; + let block_builder = |builder: BlockBuilder<_, _>| builder.build().unwrap().block; net.mut_peers(|peer| { peer[0].generate_blocks(1, BlockOrigin::NetworkInitialSync, block_builder); }) diff --git a/substrate/client/consensus/beefy/src/tests.rs b/substrate/client/consensus/beefy/src/tests.rs index 902feca16398..3aaa59cbfa1c 100644 --- a/substrate/client/consensus/beefy/src/tests.rs +++ b/substrate/client/consensus/beefy/src/tests.rs @@ -35,6 +35,7 @@ use crate::{ }; use futures::{future, stream::FuturesUnordered, Future, FutureExt, StreamExt}; use parking_lot::Mutex; +use sc_block_builder::BlockBuilderBuilder; use sc_client_api::{Backend as BackendT, BlockchainEvents, FinalityNotifications, HeaderBackend}; use sc_consensus::{ BlockImport, BlockImportParams, BoxJustificationImport, ForkChoiceStrategy, ImportResult, @@ -741,7 +742,6 @@ async fn correct_beefy_payload() { #[tokio::test] async fn beefy_importing_justifications() { use futures::{future::poll_fn, task::Poll}; - use sc_block_builder::BlockBuilderProvider; use sc_client_api::BlockBackend; sp_tracing::try_init_simple(); @@ -774,8 +774,10 @@ async fn beefy_importing_justifications() { .and_then(|j| j.get(BEEFY_ENGINE_ID).cloned()) }; - let builder = full_client - .new_block_at(full_client.chain_info().genesis_hash, Default::default(), false) + let builder = BlockBuilderBuilder::new(&*full_client) + .on_parent_block(full_client.genesis_hash()) + .with_parent_block_number(0) + .build() .unwrap(); let block = builder.build().unwrap().block; let hashof1 = block.header.hash(); @@ -792,7 +794,11 @@ async fn beefy_importing_justifications() { // Import block 2 with "valid" justification (beefy pallet genesis block not yet reached). let block_num = 2; - let builder = full_client.new_block_at(hashof1, Default::default(), false).unwrap(); + let builder = BlockBuilderBuilder::new(&*full_client) + .on_parent_block(hashof1) + .with_parent_block_number(1) + .build() + .unwrap(); let block = builder.build().unwrap().block; let hashof2 = block.header.hash(); @@ -824,7 +830,11 @@ async fn beefy_importing_justifications() { // Import block 3 with valid justification. let block_num = 3; - let builder = full_client.new_block_at(hashof2, Default::default(), false).unwrap(); + let builder = BlockBuilderBuilder::new(&*full_client) + .on_parent_block(hashof2) + .with_parent_block_number(2) + .build() + .unwrap(); let block = builder.build().unwrap().block; let hashof3 = block.header.hash(); let proof = crate::justification::tests::new_finality_proof(block_num, &good_set, keys); @@ -858,7 +868,11 @@ async fn beefy_importing_justifications() { // Import block 4 with invalid justification (incorrect validator set). let block_num = 4; - let builder = full_client.new_block_at(hashof3, Default::default(), false).unwrap(); + let builder = BlockBuilderBuilder::new(&*full_client) + .on_parent_block(hashof3) + .with_parent_block_number(3) + .build() + .unwrap(); let block = builder.build().unwrap().block; let hashof4 = block.header.hash(); let keys = &[BeefyKeyring::Alice]; diff --git a/substrate/client/consensus/grandpa/rpc/src/lib.rs b/substrate/client/consensus/grandpa/rpc/src/lib.rs index c6298bff969b..a7daefaab8eb 100644 --- a/substrate/client/consensus/grandpa/rpc/src/lib.rs +++ b/substrate/client/consensus/grandpa/rpc/src/lib.rs @@ -142,7 +142,7 @@ mod tests { RpcModule, }; use parity_scale_codec::{Decode, Encode}; - use sc_block_builder::{BlockBuilder, RecordProof}; + use sc_block_builder::BlockBuilderBuilder; use sc_consensus_grandpa::{ report, AuthorityId, FinalityProof, GrandpaJustification, GrandpaJustificationSender, }; @@ -335,21 +335,16 @@ mod tests { let peers = &[Ed25519Keyring::Alice]; let builder = TestClientBuilder::new(); - let backend = builder.backend(); let client = builder.build(); let client = Arc::new(client); - let built_block = BlockBuilder::new( - &*client, - client.info().best_hash, - client.info().best_number, - RecordProof::No, - Default::default(), - &*backend, - ) - .unwrap() - .build() - .unwrap(); + let built_block = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.info().best_hash) + .with_parent_block_number(client.info().best_number) + .build() + .unwrap() + .build() + .unwrap(); let block = built_block.block; let block_hash = block.hash(); diff --git a/substrate/client/consensus/grandpa/src/finality_proof.rs b/substrate/client/consensus/grandpa/src/finality_proof.rs index 8a8a688583e3..80b6249ade86 100644 --- a/substrate/client/consensus/grandpa/src/finality_proof.rs +++ b/substrate/client/consensus/grandpa/src/finality_proof.rs @@ -261,7 +261,7 @@ mod tests { use super::*; use crate::{authorities::AuthoritySetChanges, BlockNumberOps, ClientError, SetId}; use futures::executor::block_on; - use sc_block_builder::BlockBuilderProvider; + use sc_block_builder::BlockBuilderBuilder; use sc_client_api::{apply_aux, LockImportRun}; use sp_consensus::BlockOrigin; use sp_consensus_grandpa::GRANDPA_ENGINE_ID as ID; @@ -323,7 +323,14 @@ mod tests { let mut blocks = Vec::new(); for _ in 0..number_of_blocks { - let block = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().best_hash) + .with_parent_block_number(client.chain_info().best_number) + .build() + .unwrap() + .build() + .unwrap() + .block; block_on(client.import(BlockOrigin::Own, block.clone())).unwrap(); blocks.push(block); } diff --git a/substrate/client/consensus/grandpa/src/tests.rs b/substrate/client/consensus/grandpa/src/tests.rs index 0175f7d1b473..644befe98853 100644 --- a/substrate/client/consensus/grandpa/src/tests.rs +++ b/substrate/client/consensus/grandpa/src/tests.rs @@ -54,7 +54,7 @@ use tokio::runtime::Handle; use authorities::AuthoritySet; use communication::grandpa_protocol_name; -use sc_block_builder::{BlockBuilder, BlockBuilderProvider}; +use sc_block_builder::{BlockBuilder, BlockBuilderBuilder}; use sc_consensus::LongestChain; use sp_application_crypto::key_types::GRANDPA; @@ -897,8 +897,11 @@ async fn allows_reimporting_change_blocks() { let (mut block_import, ..) = net.make_block_import(client.clone()); let full_client = client.as_client(); - let mut builder = full_client - .new_block_at(full_client.chain_info().genesis_hash, Default::default(), false) + let mut builder = BlockBuilderBuilder::new(&*full_client) + .on_parent_block(full_client.chain_info().best_hash) + .fetch_parent_block_number(&*full_client) + .unwrap() + .build() .unwrap(); add_scheduled_change( @@ -942,8 +945,11 @@ async fn test_bad_justification() { let (mut block_import, ..) = net.make_block_import(client.clone()); let full_client = client.as_client(); - let mut builder = full_client - .new_block_at(full_client.chain_info().genesis_hash, Default::default(), false) + let mut builder = BlockBuilderBuilder::new(&*full_client) + .on_parent_block(full_client.chain_info().best_hash) + .fetch_parent_block_number(&*full_client) + .unwrap() + .build() .unwrap(); add_scheduled_change( @@ -1913,7 +1919,12 @@ async fn imports_justification_for_regular_blocks_on_import() { // create a new block (without importing it) let generate_block = |parent| { - let builder = full_client.new_block_at(parent, Default::default(), false).unwrap(); + let builder = BlockBuilderBuilder::new(&*full_client) + .on_parent_block(parent) + .fetch_parent_block_number(&*full_client) + .unwrap() + .build() + .unwrap(); builder.build().unwrap().block }; @@ -2042,8 +2053,7 @@ async fn revert_prunes_authority_changes() { let peers = &[Ed25519Keyring::Alice, Ed25519Keyring::Bob, Ed25519Keyring::Charlie]; - type TestBlockBuilder<'a> = - BlockBuilder<'a, Block, PeersFullClient, substrate_test_runtime_client::Backend>; + type TestBlockBuilder<'a> = BlockBuilder<'a, Block, PeersFullClient>; let edit_block = |mut builder: TestBlockBuilder| { add_scheduled_change( &mut builder, diff --git a/substrate/client/consensus/grandpa/src/voting_rule.rs b/substrate/client/consensus/grandpa/src/voting_rule.rs index 27a91d547837..e09780739c73 100644 --- a/substrate/client/consensus/grandpa/src/voting_rule.rs +++ b/substrate/client/consensus/grandpa/src/voting_rule.rs @@ -330,7 +330,7 @@ where #[cfg(test)] mod tests { use super::*; - use sc_block_builder::BlockBuilderProvider; + use sc_block_builder::BlockBuilderBuilder; use sp_consensus::BlockOrigin; use sp_runtime::traits::Header as _; @@ -371,7 +371,14 @@ mod tests { let mut hashes = Vec::with_capacity(200); for _ in 0..200 { - let block = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().best_hash) + .with_parent_block_number(client.chain_info().best_number) + .build() + .unwrap() + .build() + .unwrap() + .block; hashes.push(block.hash()); futures::executor::block_on(client.import(BlockOrigin::Own, block)).unwrap(); @@ -414,7 +421,14 @@ mod tests { let n = 5; let mut hashes = Vec::with_capacity(n); for _ in 0..n { - let block = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().best_hash) + .with_parent_block_number(client.chain_info().best_number) + .build() + .unwrap() + .build() + .unwrap() + .block; hashes.push(block.hash()); futures::executor::block_on(client.import(BlockOrigin::Own, block)).unwrap(); diff --git a/substrate/client/consensus/grandpa/src/warp_proof.rs b/substrate/client/consensus/grandpa/src/warp_proof.rs index dcd55dcdf3af..a0fae6998f5a 100644 --- a/substrate/client/consensus/grandpa/src/warp_proof.rs +++ b/substrate/client/consensus/grandpa/src/warp_proof.rs @@ -322,7 +322,7 @@ mod tests { use crate::{AuthoritySetChanges, GrandpaJustification}; use parity_scale_codec::Encode; use rand::prelude::*; - use sc_block_builder::BlockBuilderProvider; + use sc_block_builder::BlockBuilderBuilder; use sp_blockchain::HeaderBackend; use sp_consensus::BlockOrigin; use sp_consensus_grandpa::GRANDPA_ENGINE_ID; @@ -348,7 +348,11 @@ mod tests { let mut authority_set_changes = Vec::new(); for n in 1..=100 { - let mut builder = client.new_block(Default::default()).unwrap(); + let mut builder = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().best_hash) + .with_parent_block_number(client.chain_info().best_number) + .build() + .unwrap(); let mut new_authorities = None; // we will trigger an authority set change every 10 blocks diff --git a/substrate/client/merkle-mountain-range/src/test_utils.rs b/substrate/client/merkle-mountain-range/src/test_utils.rs index 010b48bb3d7d..5775b4cfe67c 100644 --- a/substrate/client/merkle-mountain-range/src/test_utils.rs +++ b/substrate/client/merkle-mountain-range/src/test_utils.rs @@ -18,7 +18,7 @@ use crate::MmrGadget; use parking_lot::Mutex; -use sc_block_builder::BlockBuilderProvider; +use sc_block_builder::BlockBuilderBuilder; use sc_client_api::{ Backend as BackendT, BlockchainEvents, FinalityNotifications, ImportNotifications, StorageEventStream, StorageKey, @@ -125,7 +125,12 @@ impl MockClient { let mut client = self.client.lock(); let hash = client.expect_block_hash_from_id(&at).unwrap(); - let mut block_builder = client.new_block_at(hash, Default::default(), false).unwrap(); + let mut block_builder = BlockBuilderBuilder::new(&*client) + .on_parent_block(hash) + .fetch_parent_block_number(&*client) + .unwrap() + .build() + .unwrap(); // Make sure the block has a different hash than its siblings block_builder .push_storage_change(b"name".to_vec(), Some(name.to_vec())) diff --git a/substrate/client/network/bitswap/src/lib.rs b/substrate/client/network/bitswap/src/lib.rs index 7bb8b0030653..9200c2fa7726 100644 --- a/substrate/client/network/bitswap/src/lib.rs +++ b/substrate/client/network/bitswap/src/lib.rs @@ -290,7 +290,7 @@ pub enum BitswapError { mod tests { use super::*; use futures::channel::oneshot; - use sc_block_builder::BlockBuilderProvider; + use sc_block_builder::BlockBuilderBuilder; use schema::bitswap::{ message::{wantlist::Entry, Wantlist}, Message as BitswapMessage, @@ -468,7 +468,11 @@ mod tests { #[tokio::test] async fn transaction_found() { let mut client = TestClientBuilder::with_tx_storage(u32::MAX).build(); - let mut block_builder = client.new_block(Default::default()).unwrap(); + let mut block_builder = BlockBuilderBuilder::new(&client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() + .unwrap(); // encoded extrinsic: [161, .. , 2, 6, 16, 19, 55, 19, 56] let ext = ExtrinsicBuilder::new_indexed_call(vec![0x13, 0x37, 0x13, 0x38]).build(); diff --git a/substrate/client/network/sync/src/chain_sync/test.rs b/substrate/client/network/sync/src/chain_sync/test.rs index 23316eaa1949..de908f0449c8 100644 --- a/substrate/client/network/sync/src/chain_sync/test.rs +++ b/substrate/client/network/sync/src/chain_sync/test.rs @@ -21,7 +21,7 @@ use super::*; use crate::service::network::NetworkServiceProvider; use futures::executor::block_on; -use sc_block_builder::BlockBuilderProvider; +use sc_block_builder::BlockBuilderBuilder; use sc_network_common::sync::message::{BlockAnnounce, BlockData, BlockState, FromBlock}; use sp_blockchain::HeaderBackend; use substrate_test_runtime_client::{ @@ -52,7 +52,14 @@ fn processes_empty_response_on_justification_request_for_unknown_block() { .unwrap(); let (a1_hash, a1_number) = { - let a1 = client.new_block(Default::default()).unwrap().build().unwrap().block; + let a1 = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().best_hash) + .with_parent_block_number(client.chain_info().best_number) + .build() + .unwrap() + .build() + .unwrap() + .block; (a1.hash(), *a1.header.number()) }; @@ -115,7 +122,14 @@ fn restart_doesnt_affect_peers_downloading_finality_data() { let mut new_blocks = |n| { for _ in 0..n { - let block = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().best_hash) + .with_parent_block_number(client.chain_info().best_number) + .build() + .unwrap() + .build() + .unwrap() + .block; block_on(client.import(BlockOrigin::Own, block.clone())).unwrap(); } @@ -233,7 +247,12 @@ fn get_block_request( fn build_block(client: &mut Arc, at: Option, fork: bool) -> Block { let at = at.unwrap_or_else(|| client.info().best_hash); - let mut block_builder = client.new_block_at(at, Default::default(), false).unwrap(); + let mut block_builder = BlockBuilderBuilder::new(&**client) + .on_parent_block(at) + .fetch_parent_block_number(&**client) + .unwrap() + .build() + .unwrap(); if fork { block_builder.push_storage_change(vec![1, 2, 3], Some(vec![4, 5, 6])).unwrap(); @@ -774,7 +793,14 @@ fn sync_restart_removes_block_but_not_justification_requests() { let mut new_blocks = |n| { for _ in 0..n { - let block = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().best_hash) + .with_parent_block_number(client.chain_info().best_number) + .build() + .unwrap() + .build() + .unwrap() + .block; block_on(client.import(BlockOrigin::Own, block.clone())).unwrap(); } diff --git a/substrate/client/network/test/src/block_import.rs b/substrate/client/network/test/src/block_import.rs index 25e3b9bee87f..35795432b37f 100644 --- a/substrate/client/network/test/src/block_import.rs +++ b/substrate/client/network/test/src/block_import.rs @@ -20,7 +20,6 @@ use super::*; use futures::executor::block_on; -use sc_block_builder::BlockBuilderProvider; use sc_consensus::{ import_single_block, BasicQueue, BlockImportError, BlockImportStatus, ImportedAux, IncomingBlock, @@ -34,7 +33,14 @@ use substrate_test_runtime_client::{ fn prepare_good_block() -> (TestClient, Hash, u64, PeerId, IncomingBlock) { let mut client = substrate_test_runtime_client::new(); - let block = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block = BlockBuilderBuilder::new(&client) + .on_parent_block(client.chain_info().best_hash) + .with_parent_block_number(client.chain_info().best_number) + .build() + .unwrap() + .build() + .unwrap() + .block; block_on(client.import(BlockOrigin::File, block)).unwrap(); let (hash, number) = (client.block_hash(1).unwrap().unwrap(), 1); diff --git a/substrate/client/network/test/src/lib.rs b/substrate/client/network/test/src/lib.rs index ad4201f94a20..f869e3a171a3 100644 --- a/substrate/client/network/test/src/lib.rs +++ b/substrate/client/network/test/src/lib.rs @@ -38,7 +38,7 @@ use futures::{channel::oneshot, future::BoxFuture, pin_mut, prelude::*}; use libp2p::{build_multiaddr, PeerId}; use log::trace; use parking_lot::Mutex; -use sc_block_builder::{BlockBuilder, BlockBuilderProvider}; +use sc_block_builder::{BlockBuilder, BlockBuilderBuilder}; use sc_client_api::{ backend::{AuxStore, Backend, Finalizer}, BlockBackend, BlockImportNotification, BlockchainEvents, FinalityNotification, @@ -305,9 +305,7 @@ where edit_block: F, ) -> Vec where - F: FnMut( - BlockBuilder, - ) -> Block, + F: FnMut(BlockBuilder) -> Block, { let best_hash = self.client.info().best_hash; self.generate_blocks_at( @@ -331,9 +329,7 @@ where fork_choice: ForkChoiceStrategy, ) -> Vec where - F: FnMut( - BlockBuilder, - ) -> Block, + F: FnMut(BlockBuilder) -> Block, { let best_hash = self.client.info().best_hash; self.generate_blocks_at( @@ -362,15 +358,18 @@ where fork_choice: ForkChoiceStrategy, ) -> Vec where - F: FnMut( - BlockBuilder, - ) -> Block, + F: FnMut(BlockBuilder) -> Block, { let mut hashes = Vec::with_capacity(count); let full_client = self.client.as_client(); let mut at = full_client.block_hash_from_id(&at).unwrap().unwrap(); for _ in 0..count { - let builder = full_client.new_block_at(at, Default::default(), false).unwrap(); + let builder = BlockBuilderBuilder::new(&*full_client) + .on_parent_block(at) + .fetch_parent_block_number(&*full_client) + .unwrap() + .build() + .unwrap(); let block = edit_block(builder); let hash = block.header.hash(); trace!( diff --git a/substrate/client/offchain/src/lib.rs b/substrate/client/offchain/src/lib.rs index a11ac7d86ecb..756ab77ff94e 100644 --- a/substrate/client/offchain/src/lib.rs +++ b/substrate/client/offchain/src/lib.rs @@ -328,12 +328,13 @@ mod tests { use super::*; use futures::executor::block_on; use libp2p::{Multiaddr, PeerId}; - use sc_block_builder::BlockBuilderProvider as _; + use sc_block_builder::BlockBuilderBuilder; use sc_client_api::Backend as _; use sc_network::{config::MultiaddrWithPeerId, types::ProtocolName, ReputationChange}; use sc_transaction_pool::BasicPool; use sc_transaction_pool_api::{InPoolTransaction, TransactionPool}; use sp_consensus::BlockOrigin; + use sp_runtime::traits::Block as BlockT; use std::{collections::HashSet, sync::Arc}; use substrate_test_runtime_client::{ runtime::{ @@ -470,16 +471,24 @@ mod tests { let key = &b"hello"[..]; let value = &b"world"[..]; - let mut block_builder = client.new_block(Default::default()).unwrap(); + let mut block_builder = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() + .unwrap(); let ext = ExtrinsicBuilder::new_offchain_index_set(key.to_vec(), value.to_vec()).build(); block_builder.push(ext).unwrap(); let block = block_builder.build().unwrap().block; - block_on(client.import(BlockOrigin::Own, block)).unwrap(); + block_on(client.import(BlockOrigin::Own, block.clone())).unwrap(); assert_eq!(value, &offchain_db.get(sp_offchain::STORAGE_PREFIX, &key).unwrap()); - let mut block_builder = client.new_block(Default::default()).unwrap(); + let mut block_builder = BlockBuilderBuilder::new(&*client) + .on_parent_block(block.hash()) + .with_parent_block_number(1) + .build() + .unwrap(); let ext = ExtrinsicBuilder::new_offchain_index_clear(key.to_vec()).nonce(1).build(); block_builder.push(ext).unwrap(); diff --git a/substrate/client/rpc-spec-v2/src/archive/tests.rs b/substrate/client/rpc-spec-v2/src/archive/tests.rs index 36f7716e393c..6b288c2c9542 100644 --- a/substrate/client/rpc-spec-v2/src/archive/tests.rs +++ b/substrate/client/rpc-spec-v2/src/archive/tests.rs @@ -27,10 +27,13 @@ use jsonrpsee::{ types::{error::CallError, EmptyServerParams as EmptyParams}, RpcModule, }; -use sc_block_builder::BlockBuilderProvider; +use sc_block_builder::BlockBuilderBuilder; use sp_blockchain::HeaderBackend; use sp_consensus::BlockOrigin; -use sp_runtime::SaturatedConversion; +use sp_runtime::{ + traits::{Block as BlockT, Header as HeaderT}, + SaturatedConversion, +}; use std::sync::Arc; use substrate_test_runtime::Transfer; use substrate_test_runtime_client::{ @@ -72,7 +75,12 @@ async fn archive_body() { assert!(res.is_none()); // Import a new block with an extrinsic. - let mut builder = client.new_block(Default::default()).unwrap(); + let mut builder = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() + .unwrap(); + builder .push_transfer(runtime::Transfer { from: AccountKeyring::Alice.into(), @@ -101,7 +109,12 @@ async fn archive_header() { assert!(res.is_none()); // Import a new block with an extrinsic. - let mut builder = client.new_block(Default::default()).unwrap(); + let mut builder = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() + .unwrap(); + builder .push_transfer(runtime::Transfer { from: AccountKeyring::Alice.into(), @@ -147,24 +160,57 @@ async fn archive_hash_by_height() { // ^^^ h = N // ^^^ h = N + 1 // ^^^ h = N + 2 - let finalized = client.new_block(Default::default()).unwrap().build().unwrap().block; + let finalized = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() + .unwrap() + .build() + .unwrap() + .block; let finalized_hash = finalized.header.hash(); client.import(BlockOrigin::Own, finalized.clone()).await.unwrap(); client.finalize_block(finalized_hash, None).unwrap(); - let block_1 = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block_1 = BlockBuilderBuilder::new(&*client) + .on_parent_block(finalized.hash()) + .with_parent_block_number(*finalized.header().number()) + .build() + .unwrap() + .build() + .unwrap() + .block; let block_1_hash = block_1.header.hash(); client.import(BlockOrigin::Own, block_1.clone()).await.unwrap(); - let block_2 = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block_2 = BlockBuilderBuilder::new(&*client) + .on_parent_block(block_1.hash()) + .with_parent_block_number(*block_1.header().number()) + .build() + .unwrap() + .build() + .unwrap() + .block; let block_2_hash = block_2.header.hash(); client.import(BlockOrigin::Own, block_2.clone()).await.unwrap(); - let block_3 = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block_3 = BlockBuilderBuilder::new(&*client) + .on_parent_block(block_2.hash()) + .with_parent_block_number(*block_2.header().number()) + .build() + .unwrap() + .build() + .unwrap() + .block; let block_3_hash = block_3.header.hash(); client.import(BlockOrigin::Own, block_3.clone()).await.unwrap(); // Import block 4 fork. - let mut block_builder = client.new_block_at(block_1_hash, Default::default(), false).unwrap(); + let mut block_builder = BlockBuilderBuilder::new(&*client) + .on_parent_block(block_1_hash) + .with_parent_block_number(*block_1.header().number()) + .build() + .unwrap(); + // This push is required as otherwise block 3 has the same hash as block 1 and won't get // imported block_builder @@ -238,7 +284,14 @@ async fn archive_call() { .unwrap(); assert_matches!(result, MethodResult::Err(_)); - let block_1 = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block_1 = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() + .unwrap() + .build() + .unwrap() + .block; let block_1_hash = block_1.header.hash(); client.import(BlockOrigin::Own, block_1.clone()).await.unwrap(); diff --git a/substrate/client/rpc-spec-v2/src/chain_head/subscription/inner.rs b/substrate/client/rpc-spec-v2/src/chain_head/subscription/inner.rs index d6f64acd63f5..8a75029a9943 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/subscription/inner.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/subscription/inner.rs @@ -806,7 +806,7 @@ impl> SubscriptionsInner { #[cfg(test)] mod tests { use super::*; - use sc_block_builder::BlockBuilderProvider; + use sc_block_builder::BlockBuilderBuilder; use sc_service::client::new_in_mem; use sp_consensus::BlockOrigin; use sp_core::{testing::TaskExecutor, H256}; @@ -1004,8 +1004,15 @@ mod tests { fn subscription_check_block() { let (backend, mut client) = init_backend(); - let block = client.new_block(Default::default()).unwrap().build().unwrap().block; - let hash = block.header.hash(); + let block = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() + .unwrap() + .build() + .unwrap() + .block; + let hash = block.hash(); futures::executor::block_on(client.import(BlockOrigin::Own, block.clone())).unwrap(); let mut subs = @@ -1034,7 +1041,14 @@ mod tests { #[test] fn subscription_ref_count() { let (backend, mut client) = init_backend(); - let block = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() + .unwrap() + .build() + .unwrap() + .block; let hash = block.header.hash(); futures::executor::block_on(client.import(BlockOrigin::Own, block.clone())).unwrap(); @@ -1077,13 +1091,34 @@ mod tests { #[test] fn subscription_remove_subscription() { let (backend, mut client) = init_backend(); - let block = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() + .unwrap() + .build() + .unwrap() + .block; let hash_1 = block.header.hash(); futures::executor::block_on(client.import(BlockOrigin::Own, block.clone())).unwrap(); - let block = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block = BlockBuilderBuilder::new(&*client) + .on_parent_block(hash_1) + .with_parent_block_number(1) + .build() + .unwrap() + .build() + .unwrap() + .block; let hash_2 = block.header.hash(); futures::executor::block_on(client.import(BlockOrigin::Own, block.clone())).unwrap(); - let block = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block = BlockBuilderBuilder::new(&*client) + .on_parent_block(hash_2) + .with_parent_block_number(2) + .build() + .unwrap() + .build() + .unwrap() + .block; let hash_3 = block.header.hash(); futures::executor::block_on(client.import(BlockOrigin::Own, block.clone())).unwrap(); @@ -1122,13 +1157,34 @@ mod tests { #[test] fn subscription_check_limits() { let (backend, mut client) = init_backend(); - let block = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() + .unwrap() + .build() + .unwrap() + .block; let hash_1 = block.header.hash(); futures::executor::block_on(client.import(BlockOrigin::Own, block.clone())).unwrap(); - let block = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block = BlockBuilderBuilder::new(&*client) + .on_parent_block(hash_1) + .with_parent_block_number(1) + .build() + .unwrap() + .build() + .unwrap() + .block; let hash_2 = block.header.hash(); futures::executor::block_on(client.import(BlockOrigin::Own, block.clone())).unwrap(); - let block = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block = BlockBuilderBuilder::new(&*client) + .on_parent_block(hash_2) + .with_parent_block_number(2) + .build() + .unwrap() + .build() + .unwrap() + .block; let hash_3 = block.header.hash(); futures::executor::block_on(client.import(BlockOrigin::Own, block.clone())).unwrap(); @@ -1173,13 +1229,34 @@ mod tests { #[test] fn subscription_check_limits_with_duration() { let (backend, mut client) = init_backend(); - let block = client.new_block(Default::default()).unwrap().build().unwrap().block; - let hash_1 = block.header.hash(); + let block = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() + .unwrap() + .build() + .unwrap() + .block; + let hash_1 = block.hash(); futures::executor::block_on(client.import(BlockOrigin::Own, block.clone())).unwrap(); - let block = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block = BlockBuilderBuilder::new(&*client) + .on_parent_block(hash_1) + .with_parent_block_number(1) + .build() + .unwrap() + .build() + .unwrap() + .block; let hash_2 = block.header.hash(); futures::executor::block_on(client.import(BlockOrigin::Own, block.clone())).unwrap(); - let block = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block = BlockBuilderBuilder::new(&*client) + .on_parent_block(hash_2) + .with_parent_block_number(2) + .build() + .unwrap() + .build() + .unwrap() + .block; let hash_3 = block.header.hash(); futures::executor::block_on(client.import(BlockOrigin::Own, block.clone())).unwrap(); diff --git a/substrate/client/rpc-spec-v2/src/chain_head/tests.rs b/substrate/client/rpc-spec-v2/src/chain_head/tests.rs index 8aaeb413cdff..c3f5564ebc4e 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/tests.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/tests.rs @@ -31,7 +31,7 @@ use jsonrpsee::{ types::{error::CallError, EmptyServerParams as EmptyParams}, RpcModule, }; -use sc_block_builder::BlockBuilderProvider; +use sc_block_builder::BlockBuilderBuilder; use sc_client_api::ChildInfo; use sc_service::client::new_in_mem; use sp_api::BlockT; @@ -125,7 +125,14 @@ async fn setup_api() -> ( let sub_id = sub.subscription_id(); let sub_id = serde_json::to_string(&sub_id).unwrap(); - let block = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() + .unwrap() + .build() + .unwrap() + .block; client.import(BlockOrigin::Own, block.clone()).await.unwrap(); // Ensure the imported block is propagated and pinned for this subscription. @@ -177,7 +184,14 @@ async fn follow_subscription_produces_blocks() { }); assert_eq!(event, expected); - let block = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() + .unwrap() + .build() + .unwrap() + .block; let best_hash = block.header.hash(); client.import(BlockOrigin::Own, block.clone()).await.unwrap(); @@ -255,8 +269,15 @@ async fn follow_with_runtime() { // Import a new block without runtime changes. // The runtime field must be None in this case. - let block = client.new_block(Default::default()).unwrap().build().unwrap().block; - let best_hash = block.header.hash(); + let block = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() + .unwrap() + .build() + .unwrap() + .block; + let best_hash = block.hash(); client.import(BlockOrigin::Own, block.clone()).await.unwrap(); let event: FollowEvent = get_next_event(&mut sub).await; @@ -302,7 +323,11 @@ async fn follow_with_runtime() { ) .unwrap(); - let mut builder = client.new_block(Default::default()).unwrap(); + let mut builder = BlockBuilderBuilder::new(&*client) + .on_parent_block(best_hash) + .with_parent_block_number(1) + .build() + .unwrap(); builder.push_storage_change(CODE.to_vec(), Some(wasm)).unwrap(); let block = builder.build().unwrap().block; let best_hash = block.header.hash(); @@ -411,7 +436,11 @@ async fn get_body() { ); // Import a block with extrinsics. - let mut builder = client.new_block(Default::default()).unwrap(); + let mut builder = BlockBuilderBuilder::new(&*client) + .on_parent_block(block.hash()) + .with_parent_block_number(1) + .build() + .unwrap(); builder .push_transfer(runtime::Transfer { from: AccountKeyring::Alice.into(), @@ -554,7 +583,14 @@ async fn call_runtime_without_flag() { let sub_id = sub.subscription_id(); let sub_id = serde_json::to_string(&sub_id).unwrap(); - let block = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() + .unwrap() + .build() + .unwrap() + .block; let block_hash = format!("{:?}", block.header.hash()); client.import(BlockOrigin::Own, block.clone()).await.unwrap(); @@ -648,7 +684,11 @@ async fn get_storage_hash() { ); // Import a new block with storage changes. - let mut builder = client.new_block(Default::default()).unwrap(); + let mut builder = BlockBuilderBuilder::new(&*client) + .on_parent_block(block.hash()) + .with_parent_block_number(1) + .build() + .unwrap(); builder.push_storage_change(KEY.to_vec(), Some(VALUE.to_vec())).unwrap(); let block = builder.build().unwrap().block; let block_hash = format!("{:?}", block.header.hash()); @@ -730,11 +770,15 @@ async fn get_storage_hash() { #[tokio::test] async fn get_storage_multi_query_iter() { - let (mut client, api, mut block_sub, sub_id, _) = setup_api().await; + let (mut client, api, mut block_sub, sub_id, block) = setup_api().await; let key = hex_string(&KEY); // Import a new block with storage changes. - let mut builder = client.new_block(Default::default()).unwrap(); + let mut builder = BlockBuilderBuilder::new(&*client) + .on_parent_block(block.hash()) + .with_parent_block_number(1) + .build() + .unwrap(); builder.push_storage_change(KEY.to_vec(), Some(VALUE.to_vec())).unwrap(); let block = builder.build().unwrap().block; let block_hash = format!("{:?}", block.header.hash()); @@ -851,7 +895,7 @@ async fn get_storage_multi_query_iter() { #[tokio::test] async fn get_storage_value() { let (mut client, api, mut block_sub, sub_id, block) = setup_api().await; - let block_hash = format!("{:?}", block.header.hash()); + let block_hash = format!("{:?}", block.hash()); let invalid_hash = hex_string(&INVALID_HASH); let key = hex_string(&KEY); @@ -908,10 +952,14 @@ async fn get_storage_value() { ); // Import a new block with storage changes. - let mut builder = client.new_block(Default::default()).unwrap(); + let mut builder = BlockBuilderBuilder::new(&*client) + .on_parent_block(block.hash()) + .with_parent_block_number(1) + .build() + .unwrap(); builder.push_storage_change(KEY.to_vec(), Some(VALUE.to_vec())).unwrap(); let block = builder.build().unwrap().block; - let block_hash = format!("{:?}", block.header.hash()); + let block_hash = format!("{:?}", block.hash()); client.import(BlockOrigin::Own, block.clone()).await.unwrap(); // Ensure the imported block is propagated and pinned for this subscription. @@ -1199,7 +1247,14 @@ async fn separate_operation_ids_for_subscriptions() { let sub_id_second = sub_second.subscription_id(); let sub_id_second = serde_json::to_string(&sub_id_second).unwrap(); - let block = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() + .unwrap() + .build() + .unwrap() + .block; client.import(BlockOrigin::Own, block.clone()).await.unwrap(); let block_hash = format!("{:?}", block.header.hash()); @@ -1274,18 +1329,35 @@ async fn follow_generates_initial_blocks() { let finalized_hash = client.info().finalized_hash; // Block tree: - // finalized -> block 1 -> block 2 -> block 4 - // -> block 1 -> block 3 - let block_1 = client.new_block(Default::default()).unwrap().build().unwrap().block; + // finalized -> block 1 -> block 2 -> block 3 + // -> block 1 -> block 2_f + let block_1 = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() + .unwrap() + .build() + .unwrap() + .block; let block_1_hash = block_1.header.hash(); client.import(BlockOrigin::Own, block_1.clone()).await.unwrap(); - let block_2 = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block_2 = BlockBuilderBuilder::new(&*client) + .on_parent_block(block_1_hash) + .with_parent_block_number(1) + .build() + .unwrap() + .build() + .unwrap() + .block; let block_2_hash = block_2.header.hash(); client.import(BlockOrigin::Own, block_2.clone()).await.unwrap(); - let mut block_builder = - client.new_block_at(block_1.header.hash(), Default::default(), false).unwrap(); + let mut block_builder = BlockBuilderBuilder::new(&*client) + .on_parent_block(block_1_hash) + .with_parent_block_number(1) + .build() + .unwrap(); // This push is required as otherwise block 3 has the same hash as block 2 and won't get // imported block_builder @@ -1296,9 +1368,9 @@ async fn follow_generates_initial_blocks() { nonce: 0, }) .unwrap(); - let block_3 = block_builder.build().unwrap().block; - let block_3_hash = block_3.header.hash(); - client.import(BlockOrigin::Own, block_3.clone()).await.unwrap(); + let block_2_f = block_builder.build().unwrap().block; + let block_2_f_hash = block_2_f.header.hash(); + client.import(BlockOrigin::Own, block_2_f.clone()).await.unwrap(); let mut sub = api.subscribe("chainHead_unstable_follow", [false]).await.unwrap(); @@ -1333,7 +1405,7 @@ async fn follow_generates_initial_blocks() { // Check block 3. let event: FollowEvent = get_next_event(&mut sub).await; let expected = FollowEvent::NewBlock(NewBlock { - block_hash: format!("{:?}", block_3_hash), + block_hash: format!("{:?}", block_2_f_hash), parent_block_hash: format!("{:?}", block_1_hash), new_runtime: None, with_runtime: false, @@ -1346,14 +1418,21 @@ async fn follow_generates_initial_blocks() { }); assert_eq!(event, expected); - // Import block 4. - let block_4 = client.new_block(Default::default()).unwrap().build().unwrap().block; - let block_4_hash = block_4.header.hash(); - client.import(BlockOrigin::Own, block_4.clone()).await.unwrap(); + // Import block 3. + let block_3 = BlockBuilderBuilder::new(&*client) + .on_parent_block(block_2_hash) + .with_parent_block_number(2) + .build() + .unwrap() + .build() + .unwrap() + .block; + let block_3_hash = block_3.hash(); + client.import(BlockOrigin::Own, block_3.clone()).await.unwrap(); let event: FollowEvent = get_next_event(&mut sub).await; let expected = FollowEvent::NewBlock(NewBlock { - block_hash: format!("{:?}", block_4_hash), + block_hash: format!("{:?}", block_3_hash), parent_block_hash: format!("{:?}", block_2_hash), new_runtime: None, with_runtime: false, @@ -1362,23 +1441,23 @@ async fn follow_generates_initial_blocks() { let event: FollowEvent = get_next_event(&mut sub).await; let expected = FollowEvent::BestBlockChanged(BestBlockChanged { - best_block_hash: format!("{:?}", block_4_hash), + best_block_hash: format!("{:?}", block_3_hash), }); assert_eq!(event, expected); // Check the finalized event: // - blocks 1, 2, 4 from canonical chain are finalized // - block 3 from the fork is pruned - client.finalize_block(block_4_hash, None).unwrap(); + client.finalize_block(block_3_hash, None).unwrap(); let event: FollowEvent = get_next_event(&mut sub).await; let expected = FollowEvent::Finalized(Finalized { finalized_block_hashes: vec![ format!("{:?}", block_1_hash), format!("{:?}", block_2_hash), - format!("{:?}", block_4_hash), + format!("{:?}", block_3_hash), ], - pruned_block_hashes: vec![format!("{:?}", block_3_hash)], + pruned_block_hashes: vec![format!("{:?}", block_2_f_hash)], }); assert_eq!(event, expected); } @@ -1405,7 +1484,15 @@ async fn follow_exceeding_pinned_blocks() { let mut sub = api.subscribe("chainHead_unstable_follow", [false]).await.unwrap(); - let block = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() + .unwrap() + .build() + .unwrap() + .block; + client.import(BlockOrigin::Own, block.clone()).await.unwrap(); // Ensure the imported block is propagated and pinned for this subscription. @@ -1426,13 +1513,27 @@ async fn follow_exceeding_pinned_blocks() { // finalized_block -> block -> block2 // The first 2 blocks are pinned into the subscription, but the block2 will exceed the limit (2 // blocks). - let block2 = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block2 = BlockBuilderBuilder::new(&*client) + .on_parent_block(block.hash()) + .with_parent_block_number(1) + .build() + .unwrap() + .build() + .unwrap() + .block; client.import(BlockOrigin::Own, block2.clone()).await.unwrap(); assert_matches!(get_next_event::>(&mut sub).await, FollowEvent::Stop); // Subscription will not produce any more event for further blocks. - let block3 = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block3 = BlockBuilderBuilder::new(&*client) + .on_parent_block(block2.hash()) + .with_parent_block_number(2) + .build() + .unwrap() + .build() + .unwrap() + .block; client.import(BlockOrigin::Own, block3.clone()).await.unwrap(); assert!(sub.next::>().await.is_none()); @@ -1462,7 +1563,14 @@ async fn follow_with_unpin() { let sub_id = sub.subscription_id(); let sub_id = serde_json::to_string(&sub_id).unwrap(); - let block = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() + .unwrap() + .build() + .unwrap() + .block; let block_hash = format!("{:?}", block.header.hash()); client.import(BlockOrigin::Own, block.clone()).await.unwrap(); @@ -1503,7 +1611,14 @@ async fn follow_with_unpin() { // Block tree: // finalized_block -> block -> block2 // ^ has been unpinned - let block2 = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block2 = BlockBuilderBuilder::new(&*client) + .on_parent_block(block.hash()) + .with_parent_block_number(1) + .build() + .unwrap() + .build() + .unwrap() + .block; client.import(BlockOrigin::Own, block2.clone()).await.unwrap(); assert_matches!( @@ -1516,7 +1631,14 @@ async fn follow_with_unpin() { FollowEvent::BestBlockChanged(_) ); - let block3 = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block3 = BlockBuilderBuilder::new(&*client) + .on_parent_block(block2.hash()) + .with_parent_block_number(2) + .build() + .unwrap() + .build() + .unwrap() + .block; client.import(BlockOrigin::Own, block3.clone()).await.unwrap(); assert_matches!(get_next_event::>(&mut sub).await, FollowEvent::Stop); @@ -1560,27 +1682,52 @@ async fn follow_prune_best_block() { // finalized -> block 1 -> block 2 // ^^^ best block reported // - // -> block 1 -> block 3 -> block 4 + // -> block 1 -> block 2_f -> block 4 // ^^^ finalized // // The block 4 is needed on the longest chain because we want the // best block 2 to be reported as pruned. Pruning is happening at // height (N - 1), where N is the finalized block number. - let block_1 = client.new_block(Default::default()).unwrap().build().unwrap().block; - let block_1_hash = block_1.header.hash(); + let block_1 = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() + .unwrap() + .build() + .unwrap() + .block; + let block_1_hash = block_1.hash(); client.import(BlockOrigin::Own, block_1.clone()).await.unwrap(); - let block_3 = client.new_block(Default::default()).unwrap().build().unwrap().block; - let block_3_hash = block_3.header.hash(); - client.import(BlockOrigin::Own, block_3.clone()).await.unwrap(); - - let block_4 = client.new_block(Default::default()).unwrap().build().unwrap().block; - let block_4_hash = block_4.header.hash(); + let block_2_f = BlockBuilderBuilder::new(&*client) + .on_parent_block(block_1_hash) + .with_parent_block_number(1) + .build() + .unwrap() + .build() + .unwrap() + .block; + let block_2_f_hash = block_2_f.hash(); + client.import(BlockOrigin::Own, block_2_f.clone()).await.unwrap(); + + let block_4 = BlockBuilderBuilder::new(&*client) + .on_parent_block(block_2_f_hash) + .with_parent_block_number(2) + .build() + .unwrap() + .build() + .unwrap() + .block; + let block_4_hash = block_4.hash(); client.import(BlockOrigin::Own, block_4.clone()).await.unwrap(); // Import block 2 as best on the fork. - let mut block_builder = client.new_block_at(block_1_hash, Default::default(), false).unwrap(); + let mut block_builder = BlockBuilderBuilder::new(&*client) + .on_parent_block(block_1_hash) + .with_parent_block_number(1) + .build() + .unwrap(); // This push is required as otherwise block 3 has the same hash as block 2 and won't get // imported block_builder @@ -1613,7 +1760,7 @@ async fn follow_prune_best_block() { // Check block 3. let event: FollowEvent = get_next_event(&mut sub).await; let expected = FollowEvent::NewBlock(NewBlock { - block_hash: format!("{:?}", block_3_hash), + block_hash: format!("{:?}", block_2_f_hash), parent_block_hash: format!("{:?}", block_1_hash), new_runtime: None, with_runtime: false, @@ -1621,7 +1768,7 @@ async fn follow_prune_best_block() { assert_eq!(event, expected); let event: FollowEvent = get_next_event(&mut sub).await; let expected = FollowEvent::BestBlockChanged(BestBlockChanged { - best_block_hash: format!("{:?}", block_3_hash), + best_block_hash: format!("{:?}", block_2_f_hash), }); assert_eq!(event, expected); @@ -1629,7 +1776,7 @@ async fn follow_prune_best_block() { let event: FollowEvent = get_next_event(&mut sub).await; let expected = FollowEvent::NewBlock(NewBlock { block_hash: format!("{:?}", block_4_hash), - parent_block_hash: format!("{:?}", block_3_hash), + parent_block_hash: format!("{:?}", block_2_f_hash), new_runtime: None, with_runtime: false, }); @@ -1670,7 +1817,7 @@ async fn follow_prune_best_block() { let expected = FollowEvent::Finalized(Finalized { finalized_block_hashes: vec![ format!("{:?}", block_1_hash), - format!("{:?}", block_3_hash), + format!("{:?}", block_2_f_hash), format!("{:?}", block_4_hash), ], pruned_block_hashes: vec![format!("{:?}", block_2_hash)], @@ -1708,22 +1855,46 @@ async fn follow_forks_pruned_block() { // // finalized -> block 1 -> block 2 -> block 3 // ^^^ finalized - // -> block 1 -> block 4 -> block 5 + // -> block 1 -> block 2_f -> block 3_f // - let block_1 = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block_1 = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() + .unwrap() + .build() + .unwrap() + .block; client.import(BlockOrigin::Own, block_1.clone()).await.unwrap(); - let block_2 = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block_2 = BlockBuilderBuilder::new(&*client) + .on_parent_block(block_1.hash()) + .with_parent_block_number(1) + .build() + .unwrap() + .build() + .unwrap() + .block; client.import(BlockOrigin::Own, block_2.clone()).await.unwrap(); - let block_3 = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block_3 = BlockBuilderBuilder::new(&*client) + .on_parent_block(block_2.hash()) + .with_parent_block_number(2) + .build() + .unwrap() + .build() + .unwrap() + .block; let block_3_hash = block_3.header.hash(); client.import(BlockOrigin::Own, block_3.clone()).await.unwrap(); // Block 4 with parent Block 1 is not the best imported. - let mut block_builder = - client.new_block_at(block_1.header.hash(), Default::default(), false).unwrap(); + let mut block_builder = BlockBuilderBuilder::new(&*client) + .on_parent_block(block_1.hash()) + .with_parent_block_number(1) + .build() + .unwrap(); // This push is required as otherwise block 4 has the same hash as block 2 and won't get // imported block_builder @@ -1734,11 +1905,14 @@ async fn follow_forks_pruned_block() { nonce: 0, }) .unwrap(); - let block_4 = block_builder.build().unwrap().block; - client.import(BlockOrigin::Own, block_4.clone()).await.unwrap(); + let block_2_f = block_builder.build().unwrap().block; + client.import(BlockOrigin::Own, block_2_f.clone()).await.unwrap(); - let mut block_builder = - client.new_block_at(block_4.header.hash(), Default::default(), false).unwrap(); + let mut block_builder = BlockBuilderBuilder::new(&*client) + .on_parent_block(block_2_f.hash()) + .with_parent_block_number(2) + .build() + .unwrap(); block_builder .push_transfer(Transfer { from: AccountKeyring::Bob.into(), @@ -1747,10 +1921,10 @@ async fn follow_forks_pruned_block() { nonce: 0, }) .unwrap(); - let block_5 = block_builder.build().unwrap().block; - client.import(BlockOrigin::Own, block_5.clone()).await.unwrap(); + let block_3_f = block_builder.build().unwrap().block; + client.import(BlockOrigin::Own, block_3_f.clone()).await.unwrap(); - // Block 4 and 5 are not pruned, pruning happens at height (N - 1). + // Block 2_f and 3_f are not pruned, pruning happens at height (N - 1). client.finalize_block(block_3_hash, None).unwrap(); let mut sub = api.subscribe("chainHead_unstable_follow", [false]).await.unwrap(); @@ -1766,22 +1940,29 @@ async fn follow_forks_pruned_block() { // Block tree: // - // finalized -> block 1 -> block 2 -> block 3 -> block 6 + // finalized -> block 1 -> block 2 -> block 3 -> block 4 // ^^^ finalized - // -> block 1 -> block 4 -> block 5 + // -> block 1 -> block 2_f -> block 3_f // - // Mark block 6 as finalized to force block 4 and 5 to get pruned. + // Mark block 4 as finalized to force block 2_f and 3_f to get pruned. - let block_6 = client.new_block(Default::default()).unwrap().build().unwrap().block; - let block_6_hash = block_6.header.hash(); - client.import(BlockOrigin::Own, block_6.clone()).await.unwrap(); + let block_4 = BlockBuilderBuilder::new(&*client) + .on_parent_block(block_3.hash()) + .with_parent_block_number(3) + .build() + .unwrap() + .build() + .unwrap() + .block; + let block_4_hash = block_4.hash(); + client.import(BlockOrigin::Own, block_4.clone()).await.unwrap(); - client.finalize_block(block_6_hash, None).unwrap(); + client.finalize_block(block_4_hash, None).unwrap(); // Check block 6. let event: FollowEvent = get_next_event(&mut sub).await; let expected = FollowEvent::NewBlock(NewBlock { - block_hash: format!("{:?}", block_6_hash), + block_hash: format!("{:?}", block_4_hash), parent_block_hash: format!("{:?}", block_3_hash), new_runtime: None, with_runtime: false, @@ -1789,14 +1970,14 @@ async fn follow_forks_pruned_block() { assert_eq!(event, expected); let event: FollowEvent = get_next_event(&mut sub).await; let expected = FollowEvent::BestBlockChanged(BestBlockChanged { - best_block_hash: format!("{:?}", block_6_hash), + best_block_hash: format!("{:?}", block_4_hash), }); assert_eq!(event, expected); // Block 4 and 5 must not be reported as pruned. let event: FollowEvent = get_next_event(&mut sub).await; let expected = FollowEvent::Finalized(Finalized { - finalized_block_hashes: vec![format!("{:?}", block_6_hash)], + finalized_block_hashes: vec![format!("{:?}", block_4_hash)], pruned_block_hashes: vec![], }); assert_eq!(event, expected); @@ -1826,26 +2007,51 @@ async fn follow_report_multiple_pruned_block() { // // finalized -> block 1 -> block 2 -> block 3 // ^^^ finalized after subscription - // -> block 1 -> block 4 -> block 5 + // -> block 1 -> block 2_f -> block 3_f let finalized_hash = client.info().finalized_hash; - let block_1 = client.new_block(Default::default()).unwrap().build().unwrap().block; - let block_1_hash = block_1.header.hash(); + let block_1 = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() + .unwrap() + .build() + .unwrap() + .block; + let block_1_hash = block_1.hash(); client.import(BlockOrigin::Own, block_1.clone()).await.unwrap(); - let block_2 = client.new_block(Default::default()).unwrap().build().unwrap().block; - let block_2_hash = block_2.header.hash(); + let block_2 = BlockBuilderBuilder::new(&*client) + .on_parent_block(block_1.hash()) + .with_parent_block_number(1) + .build() + .unwrap() + .build() + .unwrap() + .block; + let block_2_hash = block_2.hash(); client.import(BlockOrigin::Own, block_2.clone()).await.unwrap(); - let block_3 = client.new_block(Default::default()).unwrap().build().unwrap().block; - let block_3_hash = block_3.header.hash(); + let block_3 = BlockBuilderBuilder::new(&*client) + .on_parent_block(block_2.hash()) + .with_parent_block_number(2) + .build() + .unwrap() + .build() + .unwrap() + .block; + let block_3_hash = block_3.hash(); client.import(BlockOrigin::Own, block_3.clone()).await.unwrap(); - // Block 4 with parent Block 1 is not the best imported. - let mut block_builder = - client.new_block_at(block_1.header.hash(), Default::default(), false).unwrap(); - // This push is required as otherwise block 4 has the same hash as block 2 and won't get + // Block 2_f with parent Block 1 is not the best imported. + let mut block_builder = BlockBuilderBuilder::new(&*client) + .on_parent_block(block_1.hash()) + .with_parent_block_number(1) + .build() + .unwrap(); + + // This push is required as otherwise block 2_f has the same hash as block 2 and won't get // imported block_builder .push_transfer(Transfer { @@ -1855,12 +2061,16 @@ async fn follow_report_multiple_pruned_block() { nonce: 0, }) .unwrap(); - let block_4 = block_builder.build().unwrap().block; - let block_4_hash = block_4.header.hash(); - client.import(BlockOrigin::Own, block_4.clone()).await.unwrap(); + let block_2_f = block_builder.build().unwrap().block; + let block_2_f_hash = block_2_f.hash(); + client.import(BlockOrigin::Own, block_2_f.clone()).await.unwrap(); + + let mut block_builder = BlockBuilderBuilder::new(&*client) + .on_parent_block(block_2_f.hash()) + .with_parent_block_number(2) + .build() + .unwrap(); - let mut block_builder = - client.new_block_at(block_4.header.hash(), Default::default(), false).unwrap(); block_builder .push_transfer(Transfer { from: AccountKeyring::Bob.into(), @@ -1869,9 +2079,9 @@ async fn follow_report_multiple_pruned_block() { nonce: 0, }) .unwrap(); - let block_5 = block_builder.build().unwrap().block; - let block_5_hash = block_5.header.hash(); - client.import(BlockOrigin::Own, block_5.clone()).await.unwrap(); + let block_3_f = block_builder.build().unwrap().block; + let block_3_f_hash = block_3_f.hash(); + client.import(BlockOrigin::Own, block_3_f.clone()).await.unwrap(); let mut sub = api.subscribe("chainHead_unstable_follow", [false]).await.unwrap(); // Initialized must always be reported first. @@ -1913,7 +2123,7 @@ async fn follow_report_multiple_pruned_block() { // The fork must also be reported. let event: FollowEvent = get_next_event(&mut sub).await; let expected = FollowEvent::NewBlock(NewBlock { - block_hash: format!("{:?}", block_4_hash), + block_hash: format!("{:?}", block_2_f_hash), parent_block_hash: format!("{:?}", block_1_hash), new_runtime: None, with_runtime: false, @@ -1922,8 +2132,8 @@ async fn follow_report_multiple_pruned_block() { let event: FollowEvent = get_next_event(&mut sub).await; let expected = FollowEvent::NewBlock(NewBlock { - block_hash: format!("{:?}", block_5_hash), - parent_block_hash: format!("{:?}", block_4_hash), + block_hash: format!("{:?}", block_3_f_hash), + parent_block_hash: format!("{:?}", block_2_f_hash), new_runtime: None, with_runtime: false, }); @@ -1953,22 +2163,30 @@ async fn follow_report_multiple_pruned_block() { // Block tree: // - // finalized -> block 1 -> block 2 -> block 3 -> block 6 + // finalized -> block 1 -> block 2 -> block 3 -> block 4 // ^^^ finalized - // -> block 1 -> block 4 -> block 5 + // -> block 1 -> block 2_f -> block 3_f // - // Mark block 6 as finalized to force block 4 and 5 to get pruned. + // Mark block 4 as finalized to force block 2_f and 3_f to get pruned. - let block_6 = client.new_block(Default::default()).unwrap().build().unwrap().block; - let block_6_hash = block_6.header.hash(); - client.import(BlockOrigin::Own, block_6.clone()).await.unwrap(); + let block_4 = BlockBuilderBuilder::new(&*client) + .on_parent_block(block_3.hash()) + .with_parent_block_number(3) + .build() + .unwrap() + .build() + .unwrap() + .block; - client.finalize_block(block_6_hash, None).unwrap(); + let block_4_hash = block_4.hash(); + client.import(BlockOrigin::Own, block_4.clone()).await.unwrap(); + + client.finalize_block(block_4_hash, None).unwrap(); // Check block 6. let event: FollowEvent = get_next_event(&mut sub).await; let expected = FollowEvent::NewBlock(NewBlock { - block_hash: format!("{:?}", block_6_hash), + block_hash: format!("{:?}", block_4_hash), parent_block_hash: format!("{:?}", block_3_hash), new_runtime: None, with_runtime: false, @@ -1976,15 +2194,15 @@ async fn follow_report_multiple_pruned_block() { assert_eq!(event, expected); let event: FollowEvent = get_next_event(&mut sub).await; let expected = FollowEvent::BestBlockChanged(BestBlockChanged { - best_block_hash: format!("{:?}", block_6_hash), + best_block_hash: format!("{:?}", block_4_hash), }); assert_eq!(event, expected); // Block 4 and 5 be reported as pruned, not just the stale head (block 5). let event: FollowEvent = get_next_event(&mut sub).await; let expected = FollowEvent::Finalized(Finalized { - finalized_block_hashes: vec![format!("{:?}", block_6_hash)], - pruned_block_hashes: vec![format!("{:?}", block_4_hash), format!("{:?}", block_5_hash)], + finalized_block_hashes: vec![format!("{:?}", block_4_hash)], + pruned_block_hashes: vec![format!("{:?}", block_2_f_hash), format!("{:?}", block_3_f_hash)], }); assert_eq!(event, expected); } @@ -2052,7 +2270,14 @@ async fn pin_block_references() { let sub_id = sub.subscription_id(); let sub_id = serde_json::to_string(&sub_id).unwrap(); - let block = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() + .unwrap() + .build() + .unwrap() + .block; let hash = block.header.hash(); let block_hash = format!("{:?}", hash); client.import(BlockOrigin::Own, block.clone()).await.unwrap(); @@ -2089,8 +2314,15 @@ async fn pin_block_references() { // Add another 2 blocks and make sure we drop the subscription with the blocks pinned. let mut hashes = Vec::new(); for _ in 0..2 { - let block = client.new_block(Default::default()).unwrap().build().unwrap().block; - let hash = block.header.hash(); + let block = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().best_hash) + .with_parent_block_number(client.chain_info().best_number) + .build() + .unwrap() + .build() + .unwrap() + .block; + let hash = block.hash(); client.import(BlockOrigin::Own, block.clone()).await.unwrap(); // Ensure the imported block is propagated for this subscription. @@ -2115,7 +2347,14 @@ async fn pin_block_references() { drop(sub); // The `chainHead` detects the subscription was terminated when it tries // to send another block. - let block = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().best_hash) + .with_parent_block_number(client.chain_info().best_number) + .build() + .unwrap() + .build() + .unwrap() + .block; client.import(BlockOrigin::Own, block.clone()).await.unwrap(); for hash in &hashes { @@ -2146,7 +2385,14 @@ async fn follow_finalized_before_new_block() { .into_rpc(); // Make sure the block is imported for it to be pinned. - let block_1 = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block_1 = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() + .unwrap() + .build() + .unwrap() + .block; let block_1_hash = block_1.header.hash(); client.import(BlockOrigin::Own, block_1.clone()).await.unwrap(); @@ -2191,8 +2437,15 @@ async fn follow_finalized_before_new_block() { }); assert_eq!(event, expected); - let block_2 = client.new_block(Default::default()).unwrap().build().unwrap().block; - let block_2_hash = block_2.header.hash(); + let block_2 = BlockBuilderBuilder::new(&*client) + .on_parent_block(block_1.hash()) + .with_parent_block_number(1) + .build() + .unwrap() + .build() + .unwrap() + .block; + let block_2_hash = block_2.hash(); client.import(BlockOrigin::Own, block_2.clone()).await.unwrap(); // Triggering the `BlockImportNotification` notification for block 1 should have no effect @@ -2250,7 +2503,14 @@ async fn ensure_operation_limits_works() { let sub_id = sub.subscription_id(); let sub_id = serde_json::to_string(&sub_id).unwrap(); - let block = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() + .unwrap() + .build() + .unwrap() + .block; client.import(BlockOrigin::Own, block.clone()).await.unwrap(); // Ensure the imported block is propagated and pinned for this subscription. @@ -2349,7 +2609,11 @@ async fn check_continue_operation() { let sub_id = serde_json::to_string(&sub_id).unwrap(); // Import a new block with storage changes. - let mut builder = client.new_block(Default::default()).unwrap(); + let mut builder = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() + .unwrap(); builder.push_storage_change(b":m".to_vec(), Some(b"a".to_vec())).unwrap(); builder.push_storage_change(b":mo".to_vec(), Some(b"ab".to_vec())).unwrap(); builder.push_storage_change(b":moc".to_vec(), Some(b"abc".to_vec())).unwrap(); @@ -2528,7 +2792,11 @@ async fn stop_storage_operation() { let sub_id = serde_json::to_string(&sub_id).unwrap(); // Import a new block with storage changes. - let mut builder = client.new_block(Default::default()).unwrap(); + let mut builder = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() + .unwrap(); builder.push_storage_change(b":m".to_vec(), Some(b"a".to_vec())).unwrap(); builder.push_storage_change(b":mo".to_vec(), Some(b"ab".to_vec())).unwrap(); let block = builder.build().unwrap().block; @@ -2612,7 +2880,7 @@ async fn stop_storage_operation() { #[tokio::test] async fn storage_closest_merkle_value() { - let (mut client, api, mut sub, sub_id, _) = setup_api().await; + let (mut client, api, mut sub, sub_id, block) = setup_api().await; /// The core of this test. /// @@ -2738,7 +3006,11 @@ async fn storage_closest_merkle_value() { } // Import a new block with storage changes. - let mut builder = client.new_block(Default::default()).unwrap(); + let mut builder = BlockBuilderBuilder::new(&*client) + .on_parent_block(block.hash()) + .with_parent_block_number(1) + .build() + .unwrap(); builder.push_storage_change(b":AAAA".to_vec(), Some(vec![1; 64])).unwrap(); builder.push_storage_change(b":AAAB".to_vec(), Some(vec![2; 64])).unwrap(); let block = builder.build().unwrap().block; @@ -2758,7 +3030,11 @@ async fn storage_closest_merkle_value() { let merkle_values_lhs = expect_merkle_request(&api, &mut sub, sub_id.clone(), block_hash).await; // Import a new block with and change AAAB value. - let mut builder = client.new_block(Default::default()).unwrap(); + let mut builder = BlockBuilderBuilder::new(&*client) + .on_parent_block(block.hash()) + .with_parent_block_number(2) + .build() + .unwrap(); builder.push_storage_change(b":AAAA".to_vec(), Some(vec![1; 64])).unwrap(); builder.push_storage_change(b":AAAB".to_vec(), Some(vec![3; 64])).unwrap(); let block = builder.build().unwrap().block; diff --git a/substrate/client/rpc/src/chain/tests.rs b/substrate/client/rpc/src/chain/tests.rs index 75211a43bd9f..cff5bf39811c 100644 --- a/substrate/client/rpc/src/chain/tests.rs +++ b/substrate/client/rpc/src/chain/tests.rs @@ -20,7 +20,7 @@ use super::*; use crate::testing::{test_executor, timeout_secs}; use assert_matches::assert_matches; use jsonrpsee::types::EmptyServerParams as EmptyParams; -use sc_block_builder::BlockBuilderProvider; +use sc_block_builder::BlockBuilderBuilder; use sp_consensus::BlockOrigin; use sp_rpc::list::ListOrValue; use substrate_test_runtime_client::{ @@ -75,7 +75,14 @@ async fn should_return_a_block() { let mut client = Arc::new(substrate_test_runtime_client::new()); let api = new_full(client.clone(), test_executor()).into_rpc(); - let block = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().best_hash) + .with_parent_block_number(client.chain_info().best_number) + .build() + .unwrap() + .build() + .unwrap() + .block; let block_hash = block.hash(); client.import(BlockOrigin::Own, block).await.unwrap(); @@ -152,7 +159,14 @@ async fn should_return_block_hash() { api.call("chain_getBlockHash", [ListOrValue::from(1_u64)]).await.unwrap(); assert_matches!(res, None); - let block = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().best_hash) + .with_parent_block_number(client.chain_info().best_number) + .build() + .unwrap() + .build() + .unwrap() + .block; client.import(BlockOrigin::Own, block.clone()).await.unwrap(); let res: ListOrValue> = @@ -197,7 +211,14 @@ async fn should_return_finalized_hash() { assert_eq!(res, client.genesis_hash()); // import new block - let block = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().best_hash) + .with_parent_block_number(client.chain_info().best_number) + .build() + .unwrap() + .build() + .unwrap() + .block; let block_hash = block.hash(); client.import(BlockOrigin::Own, block).await.unwrap(); @@ -232,7 +253,14 @@ async fn test_head_subscription(method: &str) { let mut sub = { let api = new_full(client.clone(), test_executor()).into_rpc(); let sub = api.subscribe(method, EmptyParams::new()).await.unwrap(); - let block = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().best_hash) + .with_parent_block_number(client.chain_info().best_number) + .build() + .unwrap() + .build() + .unwrap() + .block; let block_hash = block.hash(); client.import(BlockOrigin::Own, block).await.unwrap(); client.finalize_block(block_hash, None).unwrap(); diff --git a/substrate/client/rpc/src/dev/tests.rs b/substrate/client/rpc/src/dev/tests.rs index db6a9a119da0..448d16274f27 100644 --- a/substrate/client/rpc/src/dev/tests.rs +++ b/substrate/client/rpc/src/dev/tests.rs @@ -17,7 +17,7 @@ // along with this program. If not, see . use super::*; -use sc_block_builder::BlockBuilderProvider; +use sc_block_builder::BlockBuilderBuilder; use sp_blockchain::HeaderBackend; use sp_consensus::BlockOrigin; use substrate_test_runtime_client::{prelude::*, runtime::Block}; @@ -27,7 +27,14 @@ async fn block_stats_work() { let mut client = Arc::new(substrate_test_runtime_client::new()); let api = >::new(client.clone(), DenyUnsafe::No).into_rpc(); - let block = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() + .unwrap() + .build() + .unwrap() + .block; let (expected_witness_len, expected_witness_compact_len, expected_block_len) = { let genesis_hash = client.chain_info().genesis_hash; @@ -72,7 +79,14 @@ async fn deny_unsafe_works() { let mut client = Arc::new(substrate_test_runtime_client::new()); let api = >::new(client.clone(), DenyUnsafe::Yes).into_rpc(); - let block = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() + .unwrap() + .build() + .unwrap() + .block; client.import(BlockOrigin::Own, block).await.unwrap(); let best_hash = client.info().best_hash; diff --git a/substrate/client/rpc/src/state/tests.rs b/substrate/client/rpc/src/state/tests.rs index 35352f6d890e..594f51efba68 100644 --- a/substrate/client/rpc/src/state/tests.rs +++ b/substrate/client/rpc/src/state/tests.rs @@ -25,7 +25,7 @@ use jsonrpsee::{ core::Error as RpcError, types::{error::CallError as RpcCallError, EmptyServerParams as EmptyParams, ErrorObject}, }; -use sc_block_builder::BlockBuilderProvider; +use sc_block_builder::BlockBuilderBuilder; use sc_rpc_api::DenyUnsafe; use sp_consensus::BlockOrigin; use sp_core::{hash::H256, storage::ChildInfo}; @@ -218,7 +218,11 @@ async fn should_notify_about_storage_changes() { let sub = api_rpc.subscribe("state_subscribeStorage", EmptyParams::new()).await.unwrap(); // Cause a change: - let mut builder = client.new_block(Default::default()).unwrap(); + let mut builder = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().best_hash) + .with_parent_block_number(client.chain_info().best_number) + .build() + .unwrap(); builder .push_transfer(Transfer { from: AccountKeyring::Alice.into(), @@ -263,7 +267,11 @@ async fn should_send_initial_storage_changes_and_notifications() { .await .unwrap(); - let mut builder = client.new_block(Default::default()).unwrap(); + let mut builder = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().best_hash) + .with_parent_block_number(client.chain_info().best_number) + .build() + .unwrap(); builder .push_transfer(Transfer { from: AccountKeyring::Alice.into(), @@ -291,7 +299,11 @@ async fn should_query_storage() { let (api, _child) = new_full(client.clone(), test_executor(), DenyUnsafe::No); let mut add_block = |index| { - let mut builder = client.new_block(Default::default()).unwrap(); + let mut builder = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().best_hash) + .with_parent_block_number(client.chain_info().best_number) + .build() + .unwrap(); // fake change: None -> None -> None builder .push(ExtrinsicBuilder::new_storage_change(vec![1], None).build()) diff --git a/substrate/client/service/Cargo.toml b/substrate/client/service/Cargo.toml index 2386bebf24d2..e5520eb21c3e 100644 --- a/substrate/client/service/Cargo.toml +++ b/substrate/client/service/Cargo.toml @@ -69,7 +69,6 @@ sp-transaction-storage-proof = { path = "../../primitives/transaction-storage-pr sc-rpc-server = { path = "../rpc-servers" } sc-rpc = { path = "../rpc" } sc-rpc-spec-v2 = { path = "../rpc-spec-v2" } -sc-block-builder = { path = "../block-builder" } sc-informant = { path = "../informant" } sc-telemetry = { path = "../telemetry" } prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../utils/prometheus" } diff --git a/substrate/client/service/src/client/client.rs b/substrate/client/service/src/client/client.rs index da4a4f66e2af..9d51aae55b20 100644 --- a/substrate/client/service/src/client/client.rs +++ b/substrate/client/service/src/client/client.rs @@ -24,7 +24,6 @@ use log::{error, info, trace, warn}; use parking_lot::{Mutex, RwLock}; use prometheus_endpoint::Registry; use rand::Rng; -use sc_block_builder::{BlockBuilderApi, BlockBuilderProvider, RecordProof}; use sc_chain_spec::{resolve_state_version_from_wasm, BuildGenesisBlock}; use sc_client_api::{ backend::{ @@ -70,7 +69,7 @@ use sp_runtime::{ Block as BlockT, BlockIdTo, HashingFor, Header as HeaderT, NumberFor, One, SaturatedConversion, Zero, }, - Digest, Justification, Justifications, StateVersion, + Justification, Justifications, StateVersion, }; use sp_state_machine::{ prove_child_read, prove_range_read_with_child_with_size, prove_read, @@ -1402,46 +1401,6 @@ where } } -impl BlockBuilderProvider for Client -where - B: backend::Backend + Send + Sync + 'static, - E: CallExecutor + Send + Sync + 'static, - Block: BlockT, - Self: ChainHeaderBackend + ProvideRuntimeApi, - >::Api: ApiExt + BlockBuilderApi, -{ - fn new_block_at>( - &self, - parent: Block::Hash, - inherent_digests: Digest, - record_proof: R, - ) -> sp_blockchain::Result> { - sc_block_builder::BlockBuilder::new( - self, - parent, - self.expect_block_number_from_id(&BlockId::Hash(parent))?, - record_proof.into(), - inherent_digests, - &self.backend, - ) - } - - fn new_block( - &self, - inherent_digests: Digest, - ) -> sp_blockchain::Result> { - let info = self.chain_info(); - sc_block_builder::BlockBuilder::new( - self, - info.best_hash, - info.best_number, - RecordProof::No, - inherent_digests, - &self.backend, - ) - } -} - impl ExecutorProvider for Client where B: backend::Backend, diff --git a/substrate/client/service/test/src/client/mod.rs b/substrate/client/service/test/src/client/mod.rs index f82008755874..ba83bec8276e 100644 --- a/substrate/client/service/test/src/client/mod.rs +++ b/substrate/client/service/test/src/client/mod.rs @@ -19,7 +19,7 @@ use async_channel::TryRecvError; use futures::executor::block_on; use parity_scale_codec::{Decode, Encode, Joiner}; -use sc_block_builder::BlockBuilderProvider; +use sc_block_builder::BlockBuilderBuilder; use sc_client_api::{ in_mem, BlockBackend, BlockchainEvents, ExecutorProvider, FinalityNotifications, HeaderBackend, StorageProvider, @@ -241,7 +241,14 @@ fn client_initializes_from_genesis_ok() { fn block_builder_works_with_no_transactions() { let mut client = substrate_test_runtime_client::new(); - let block = client.new_block(Default::default()).unwrap().build().unwrap().block; + let block = BlockBuilderBuilder::new(&client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() + .unwrap() + .build() + .unwrap() + .block; block_on(client.import(BlockOrigin::Own, block)).unwrap(); @@ -252,7 +259,11 @@ fn block_builder_works_with_no_transactions() { fn block_builder_works_with_transactions() { let mut client = substrate_test_runtime_client::new(); - let mut builder = client.new_block(Default::default()).unwrap(); + let mut builder = BlockBuilderBuilder::new(&client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() + .unwrap(); builder .push_transfer(Transfer { @@ -307,7 +318,11 @@ fn block_builder_works_with_transactions() { #[test] fn block_builder_does_not_include_invalid() { let mut client = substrate_test_runtime_client::new(); - let mut builder = client.new_block(Default::default()).unwrap(); + let mut builder = BlockBuilderBuilder::new(&client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() + .unwrap(); builder .push_transfer(Transfer { @@ -379,11 +394,25 @@ fn uncles_with_only_ancestors() { let mut client = substrate_test_runtime_client::new(); // G -> A1 - let a1 = client.new_block(Default::default()).unwrap().build().unwrap().block; + let a1 = BlockBuilderBuilder::new(&client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() + .unwrap() + .build() + .unwrap() + .block; block_on(client.import(BlockOrigin::Own, a1.clone())).unwrap(); // A1 -> A2 - let a2 = client.new_block(Default::default()).unwrap().build().unwrap().block; + let a2 = BlockBuilderBuilder::new(&client) + .on_parent_block(a1.hash()) + .with_parent_block_number(*a1.header().number()) + .build() + .unwrap() + .build() + .unwrap() + .block; block_on(client.import(BlockOrigin::Own, a2.clone())).unwrap(); let v: Vec = Vec::new(); assert_eq!(v, client.uncles(a2.hash(), 3).unwrap()); @@ -399,12 +428,21 @@ fn uncles_with_multiple_forks() { let mut client = substrate_test_runtime_client::new(); // G -> A1 - let a1 = client.new_block(Default::default()).unwrap().build().unwrap().block; + let a1 = BlockBuilderBuilder::new(&client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() + .unwrap() + .build() + .unwrap() + .block; block_on(client.import(BlockOrigin::Own, a1.clone())).unwrap(); // A1 -> A2 - let a2 = client - .new_block_at(a1.hash(), Default::default(), false) + let a2 = BlockBuilderBuilder::new(&client) + .on_parent_block(a1.hash()) + .with_parent_block_number(1) + .build() .unwrap() .build() .unwrap() @@ -412,8 +450,10 @@ fn uncles_with_multiple_forks() { block_on(client.import(BlockOrigin::Own, a2.clone())).unwrap(); // A2 -> A3 - let a3 = client - .new_block_at(a2.hash(), Default::default(), false) + let a3 = BlockBuilderBuilder::new(&client) + .on_parent_block(a2.hash()) + .with_parent_block_number(2) + .build() .unwrap() .build() .unwrap() @@ -421,8 +461,10 @@ fn uncles_with_multiple_forks() { block_on(client.import(BlockOrigin::Own, a3.clone())).unwrap(); // A3 -> A4 - let a4 = client - .new_block_at(a3.hash(), Default::default(), false) + let a4 = BlockBuilderBuilder::new(&client) + .on_parent_block(a3.hash()) + .with_parent_block_number(3) + .build() .unwrap() .build() .unwrap() @@ -430,8 +472,10 @@ fn uncles_with_multiple_forks() { block_on(client.import(BlockOrigin::Own, a4.clone())).unwrap(); // A4 -> A5 - let a5 = client - .new_block_at(a4.hash(), Default::default(), false) + let a5 = BlockBuilderBuilder::new(&client) + .on_parent_block(a4.hash()) + .with_parent_block_number(4) + .build() .unwrap() .build() .unwrap() @@ -439,7 +483,12 @@ fn uncles_with_multiple_forks() { block_on(client.import(BlockOrigin::Own, a5.clone())).unwrap(); // A1 -> B2 - let mut builder = client.new_block_at(a1.hash(), Default::default(), false).unwrap(); + let mut builder = BlockBuilderBuilder::new(&client) + .on_parent_block(a1.hash()) + .with_parent_block_number(1) + .build() + .unwrap(); + // this push is required as otherwise B2 has the same hash as A2 and won't get imported builder .push_transfer(Transfer { @@ -453,8 +502,10 @@ fn uncles_with_multiple_forks() { block_on(client.import(BlockOrigin::Own, b2.clone())).unwrap(); // B2 -> B3 - let b3 = client - .new_block_at(b2.hash(), Default::default(), false) + let b3 = BlockBuilderBuilder::new(&client) + .on_parent_block(b2.hash()) + .with_parent_block_number(2) + .build() .unwrap() .build() .unwrap() @@ -462,8 +513,10 @@ fn uncles_with_multiple_forks() { block_on(client.import(BlockOrigin::Own, b3.clone())).unwrap(); // B3 -> B4 - let b4 = client - .new_block_at(b3.hash(), Default::default(), false) + let b4 = BlockBuilderBuilder::new(&client) + .on_parent_block(b3.hash()) + .with_parent_block_number(3) + .build() .unwrap() .build() .unwrap() @@ -471,7 +524,11 @@ fn uncles_with_multiple_forks() { block_on(client.import(BlockOrigin::Own, b4.clone())).unwrap(); // // B2 -> C3 - let mut builder = client.new_block_at(b2.hash(), Default::default(), false).unwrap(); + let mut builder = BlockBuilderBuilder::new(&client) + .on_parent_block(b2.hash()) + .with_parent_block_number(2) + .build() + .unwrap(); // this push is required as otherwise C3 has the same hash as B3 and won't get imported builder .push_transfer(Transfer { @@ -485,7 +542,11 @@ fn uncles_with_multiple_forks() { block_on(client.import(BlockOrigin::Own, c3.clone())).unwrap(); // A1 -> D2 - let mut builder = client.new_block_at(a1.hash(), Default::default(), false).unwrap(); + let mut builder = BlockBuilderBuilder::new(&client) + .on_parent_block(a1.hash()) + .with_parent_block_number(1) + .build() + .unwrap(); // this push is required as otherwise D2 has the same hash as B2 and won't get imported builder .push_transfer(Transfer { @@ -527,11 +588,25 @@ fn finality_target_on_longest_chain_with_single_chain_3_blocks() { let (mut client, longest_chain_select) = TestClientBuilder::new().build_with_longest_chain(); // G -> A1 - let a1 = client.new_block(Default::default()).unwrap().build().unwrap().block; + let a1 = BlockBuilderBuilder::new(&client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() + .unwrap() + .build() + .unwrap() + .block; block_on(client.import(BlockOrigin::Own, a1.clone())).unwrap(); // A1 -> A2 - let a2 = client.new_block(Default::default()).unwrap().build().unwrap().block; + let a2 = BlockBuilderBuilder::new(&client) + .on_parent_block(a1.hash()) + .with_parent_block_number(*a1.header().number()) + .build() + .unwrap() + .build() + .unwrap() + .block; block_on(client.import(BlockOrigin::Own, a2.clone())).unwrap(); let genesis_hash = client.chain_info().genesis_hash; @@ -554,12 +629,21 @@ fn finality_target_on_longest_chain_with_multiple_forks() { let (mut client, longest_chain_select) = TestClientBuilder::new().build_with_longest_chain(); // G -> A1 - let a1 = client.new_block(Default::default()).unwrap().build().unwrap().block; + let a1 = BlockBuilderBuilder::new(&client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() + .unwrap() + .build() + .unwrap() + .block; block_on(client.import(BlockOrigin::Own, a1.clone())).unwrap(); // A1 -> A2 - let a2 = client - .new_block_at(a1.hash(), Default::default(), false) + let a2 = BlockBuilderBuilder::new(&client) + .on_parent_block(a1.hash()) + .with_parent_block_number(*a1.header().number()) + .build() .unwrap() .build() .unwrap() @@ -567,8 +651,10 @@ fn finality_target_on_longest_chain_with_multiple_forks() { block_on(client.import(BlockOrigin::Own, a2.clone())).unwrap(); // A2 -> A3 - let a3 = client - .new_block_at(a2.hash(), Default::default(), false) + let a3 = BlockBuilderBuilder::new(&client) + .on_parent_block(a2.hash()) + .with_parent_block_number(*a2.header().number()) + .build() .unwrap() .build() .unwrap() @@ -576,8 +662,10 @@ fn finality_target_on_longest_chain_with_multiple_forks() { block_on(client.import(BlockOrigin::Own, a3.clone())).unwrap(); // A3 -> A4 - let a4 = client - .new_block_at(a3.hash(), Default::default(), false) + let a4 = BlockBuilderBuilder::new(&client) + .on_parent_block(a3.hash()) + .with_parent_block_number(*a3.header().number()) + .build() .unwrap() .build() .unwrap() @@ -585,8 +673,10 @@ fn finality_target_on_longest_chain_with_multiple_forks() { block_on(client.import(BlockOrigin::Own, a4.clone())).unwrap(); // A4 -> A5 - let a5 = client - .new_block_at(a4.hash(), Default::default(), false) + let a5 = BlockBuilderBuilder::new(&client) + .on_parent_block(a4.hash()) + .with_parent_block_number(4) + .build() .unwrap() .build() .unwrap() @@ -594,7 +684,11 @@ fn finality_target_on_longest_chain_with_multiple_forks() { block_on(client.import(BlockOrigin::Own, a5.clone())).unwrap(); // A1 -> B2 - let mut builder = client.new_block_at(a1.hash(), Default::default(), false).unwrap(); + let mut builder = BlockBuilderBuilder::new(&client) + .on_parent_block(a1.hash()) + .with_parent_block_number(1) + .build() + .unwrap(); // this push is required as otherwise B2 has the same hash as A2 and won't get imported builder .push_transfer(Transfer { @@ -608,8 +702,10 @@ fn finality_target_on_longest_chain_with_multiple_forks() { block_on(client.import(BlockOrigin::Own, b2.clone())).unwrap(); // B2 -> B3 - let b3 = client - .new_block_at(b2.hash(), Default::default(), false) + let b3 = BlockBuilderBuilder::new(&client) + .on_parent_block(b2.hash()) + .with_parent_block_number(2) + .build() .unwrap() .build() .unwrap() @@ -617,8 +713,10 @@ fn finality_target_on_longest_chain_with_multiple_forks() { block_on(client.import(BlockOrigin::Own, b3.clone())).unwrap(); // B3 -> B4 - let b4 = client - .new_block_at(b3.hash(), Default::default(), false) + let b4 = BlockBuilderBuilder::new(&client) + .on_parent_block(b3.hash()) + .with_parent_block_number(3) + .build() .unwrap() .build() .unwrap() @@ -626,7 +724,12 @@ fn finality_target_on_longest_chain_with_multiple_forks() { block_on(client.import(BlockOrigin::Own, b4.clone())).unwrap(); // B2 -> C3 - let mut builder = client.new_block_at(b2.hash(), Default::default(), false).unwrap(); + let mut builder = BlockBuilderBuilder::new(&client) + .on_parent_block(b2.hash()) + .with_parent_block_number(2) + .build() + .unwrap(); + // this push is required as otherwise C3 has the same hash as B3 and won't get imported builder .push_transfer(Transfer { @@ -640,7 +743,12 @@ fn finality_target_on_longest_chain_with_multiple_forks() { block_on(client.import(BlockOrigin::Own, c3.clone())).unwrap(); // A1 -> D2 - let mut builder = client.new_block_at(a1.hash(), Default::default(), false).unwrap(); + let mut builder = BlockBuilderBuilder::new(&client) + .on_parent_block(a1.hash()) + .with_parent_block_number(1) + .build() + .unwrap(); + // this push is required as otherwise D2 has the same hash as B2 and won't get imported builder .push_transfer(Transfer { @@ -771,13 +879,28 @@ fn finality_target_on_longest_chain_with_max_depth_higher_than_best() { // G -> A1 -> A2 let (mut client, chain_select) = TestClientBuilder::new().build_with_longest_chain(); + let chain = client.chain_info(); // G -> A1 - let a1 = client.new_block(Default::default()).unwrap().build().unwrap().block; + let a1 = BlockBuilderBuilder::new(&client) + .on_parent_block(chain.best_hash) + .with_parent_block_number(chain.best_number) + .build() + .unwrap() + .build() + .unwrap() + .block; block_on(client.import(BlockOrigin::Own, a1.clone())).unwrap(); // A1 -> A2 - let a2 = client.new_block(Default::default()).unwrap().build().unwrap().block; + let a2 = BlockBuilderBuilder::new(&client) + .on_parent_block(a1.hash()) + .with_parent_block_number(1) + .build() + .unwrap() + .build() + .unwrap() + .block; block_on(client.import(BlockOrigin::Own, a2.clone())).unwrap(); let genesis_hash = client.chain_info().genesis_hash; @@ -793,30 +916,70 @@ fn finality_target_with_best_not_on_longest_chain() { // ^best let (mut client, chain_select) = TestClientBuilder::new().build_with_longest_chain(); - let genesis_hash = client.chain_info().genesis_hash; + let chain = client.chain_info(); // G -> A1 - let a1 = client.new_block(Default::default()).unwrap().build().unwrap().block; + let a1 = BlockBuilderBuilder::new(&client) + .on_parent_block(chain.best_hash) + .with_parent_block_number(chain.best_number) + .build() + .unwrap() + .build() + .unwrap() + .block; block_on(client.import(BlockOrigin::Own, a1.clone())).unwrap(); // A1 -> A2 - let a2 = client.new_block(Default::default()).unwrap().build().unwrap().block; + let a2 = BlockBuilderBuilder::new(&client) + .on_parent_block(a1.hash()) + .with_parent_block_number(1) + .build() + .unwrap() + .build() + .unwrap() + .block; block_on(client.import(BlockOrigin::Own, a2.clone())).unwrap(); // A2 -> A3 - let a3 = client.new_block(Default::default()).unwrap().build().unwrap().block; + let a3 = BlockBuilderBuilder::new(&client) + .on_parent_block(a2.hash()) + .with_parent_block_number(2) + .build() + .unwrap() + .build() + .unwrap() + .block; block_on(client.import(BlockOrigin::Own, a3.clone())).unwrap(); // A3 -> A4 - let a4 = client.new_block(Default::default()).unwrap().build().unwrap().block; + let a4 = BlockBuilderBuilder::new(&client) + .on_parent_block(a3.hash()) + .with_parent_block_number(3) + .build() + .unwrap() + .build() + .unwrap() + .block; block_on(client.import(BlockOrigin::Own, a4.clone())).unwrap(); // A3 -> A5 - let a5 = client.new_block(Default::default()).unwrap().build().unwrap().block; + let a5 = BlockBuilderBuilder::new(&client) + .on_parent_block(a4.hash()) + .with_parent_block_number(4) + .build() + .unwrap() + .build() + .unwrap() + .block; block_on(client.import(BlockOrigin::Own, a5.clone())).unwrap(); // A1 -> B2 - let mut builder = client.new_block_at(a1.hash(), Default::default(), false).unwrap(); + let mut builder = BlockBuilderBuilder::new(&client) + .on_parent_block(a1.hash()) + .with_parent_block_number(1) + .build() + .unwrap(); + // this push is required as otherwise B2 has the same hash as A2 and won't get imported builder .push_transfer(Transfer { @@ -829,7 +992,10 @@ fn finality_target_with_best_not_on_longest_chain() { let b2 = builder.build().unwrap().block; block_on(client.import(BlockOrigin::Own, b2.clone())).unwrap(); - assert_eq!(a5.hash(), block_on(chain_select.finality_target(genesis_hash, None)).unwrap()); + assert_eq!( + a5.hash(), + block_on(chain_select.finality_target(client.chain_info().genesis_hash, None)).unwrap() + ); assert_eq!(a5.hash(), block_on(chain_select.finality_target(a1.hash(), None)).unwrap()); assert_eq!(a5.hash(), block_on(chain_select.finality_target(a2.hash(), None)).unwrap()); assert_eq!(a5.hash(), block_on(chain_select.finality_target(a3.hash(), None)).unwrap()); @@ -837,8 +1003,10 @@ fn finality_target_with_best_not_on_longest_chain() { assert_eq!(a5.hash(), block_on(chain_select.finality_target(a5.hash(), None)).unwrap()); // B2 -> B3 - let b3 = client - .new_block_at(b2.hash(), Default::default(), false) + let b3 = BlockBuilderBuilder::new(&client) + .on_parent_block(b2.hash()) + .with_parent_block_number(2) + .build() .unwrap() .build() .unwrap() @@ -846,8 +1014,10 @@ fn finality_target_with_best_not_on_longest_chain() { block_on(client.import_as_best(BlockOrigin::Own, b3.clone())).unwrap(); // B3 -> B4 - let b4 = client - .new_block_at(b3.hash(), Default::default(), false) + let b4 = BlockBuilderBuilder::new(&client) + .on_parent_block(b3.hash()) + .with_parent_block_number(3) + .build() .unwrap() .build() .unwrap() @@ -861,7 +1031,10 @@ fn finality_target_with_best_not_on_longest_chain() { // double check that B3 is still the best... assert_eq!(client.info().best_hash, b3.hash()); - assert_eq!(b4.hash(), block_on(chain_select.finality_target(genesis_hash, None)).unwrap()); + assert_eq!( + b4.hash(), + block_on(chain_select.finality_target(client.chain_info().genesis_hash, None)).unwrap() + ); assert_eq!(b4.hash(), block_on(chain_select.finality_target(a1.hash(), None)).unwrap()); assert!(block_on(chain_select.finality_target(a2.hash(), None)).is_err()); assert_eq!(b4.hash(), block_on(chain_select.finality_target(b2.hash(), None)).unwrap()); @@ -878,12 +1051,21 @@ fn import_with_justification() { let mut finality_notifications = client.finality_notification_stream(); // G -> A1 - let a1 = client.new_block(Default::default()).unwrap().build().unwrap().block; + let a1 = BlockBuilderBuilder::new(&client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() + .unwrap() + .build() + .unwrap() + .block; block_on(client.import(BlockOrigin::Own, a1.clone())).unwrap(); // A1 -> A2 - let a2 = client - .new_block_at(a1.hash(), Default::default(), false) + let a2 = BlockBuilderBuilder::new(&client) + .on_parent_block(a1.hash()) + .with_parent_block_number(1) + .build() .unwrap() .build() .unwrap() @@ -893,8 +1075,10 @@ fn import_with_justification() { // A2 -> A3 let justification = Justifications::from((TEST_ENGINE_ID, vec![1, 2, 3])); - let a3 = client - .new_block_at(a2.hash(), Default::default(), false) + let a3 = BlockBuilderBuilder::new(&client) + .on_parent_block(a2.hash()) + .with_parent_block_number(2) + .build() .unwrap() .build() .unwrap() @@ -924,24 +1108,30 @@ fn importing_diverged_finalized_block_should_trigger_reorg() { let mut finality_notifications = client.finality_notification_stream(); - let a1 = client - .new_block_at(client.chain_info().genesis_hash, Default::default(), false) + let a1 = BlockBuilderBuilder::new(&client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() .unwrap() .build() .unwrap() .block; block_on(client.import(BlockOrigin::Own, a1.clone())).unwrap(); - let a2 = client - .new_block_at(a1.hash(), Default::default(), false) + let a2 = BlockBuilderBuilder::new(&client) + .on_parent_block(a1.hash()) + .with_parent_block_number(1) + .build() .unwrap() .build() .unwrap() .block; block_on(client.import(BlockOrigin::Own, a2.clone())).unwrap(); - let mut b1 = client - .new_block_at(client.chain_info().genesis_hash, Default::default(), false) + let mut b1 = BlockBuilderBuilder::new(&client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() .unwrap(); // needed to make sure B1 gets a different hash from A1 b1.push_transfer(Transfer { @@ -979,24 +1169,30 @@ fn finalizing_diverged_block_should_trigger_reorg() { let mut finality_notifications = client.finality_notification_stream(); - let a1 = client - .new_block_at(client.chain_info().genesis_hash, Default::default(), false) + let a1 = BlockBuilderBuilder::new(&client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() .unwrap() .build() .unwrap() .block; block_on(client.import(BlockOrigin::Own, a1.clone())).unwrap(); - let a2 = client - .new_block_at(a1.hash(), Default::default(), false) + let a2 = BlockBuilderBuilder::new(&client) + .on_parent_block(a1.hash()) + .with_parent_block_number(1) + .build() .unwrap() .build() .unwrap() .block; block_on(client.import(BlockOrigin::Own, a2.clone())).unwrap(); - let mut b1 = client - .new_block_at(client.chain_info().genesis_hash, Default::default(), false) + let mut b1 = BlockBuilderBuilder::new(&client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() .unwrap(); // needed to make sure B1 gets a different hash from A1 b1.push_transfer(Transfer { @@ -1009,8 +1205,10 @@ fn finalizing_diverged_block_should_trigger_reorg() { let b1 = b1.build().unwrap().block; block_on(client.import(BlockOrigin::Own, b1.clone())).unwrap(); - let b2 = client - .new_block_at(b1.hash(), Default::default(), false) + let b2 = BlockBuilderBuilder::new(&client) + .on_parent_block(b1.hash()) + .with_parent_block_number(1) + .build() .unwrap() .build() .unwrap() @@ -1035,8 +1233,10 @@ fn finalizing_diverged_block_should_trigger_reorg() { assert_eq!(block_on(select_chain.best_chain()).unwrap().hash(), b2.hash()); // after we build B3 on top of B2 and import it, it should be the new best block - let b3 = client - .new_block_at(b2.hash(), Default::default(), false) + let b3 = BlockBuilderBuilder::new(&client) + .on_parent_block(b2.hash()) + .with_parent_block_number(2) + .build() .unwrap() .build() .unwrap() @@ -1067,32 +1267,40 @@ fn finality_notifications_content() { let mut finality_notifications = client.finality_notification_stream(); - let a1 = client - .new_block_at(client.chain_info().genesis_hash, Default::default(), false) + let a1 = BlockBuilderBuilder::new(&client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() .unwrap() .build() .unwrap() .block; block_on(client.import(BlockOrigin::Own, a1.clone())).unwrap(); - let a2 = client - .new_block_at(a1.hash(), Default::default(), false) + let a2 = BlockBuilderBuilder::new(&client) + .on_parent_block(a1.hash()) + .with_parent_block_number(1) + .build() .unwrap() .build() .unwrap() .block; block_on(client.import(BlockOrigin::Own, a2.clone())).unwrap(); - let a3 = client - .new_block_at(a2.hash(), Default::default(), false) + let a3 = BlockBuilderBuilder::new(&client) + .on_parent_block(a2.hash()) + .with_parent_block_number(2) + .build() .unwrap() .build() .unwrap() .block; block_on(client.import(BlockOrigin::Own, a3.clone())).unwrap(); - let mut b1 = client - .new_block_at(client.chain_info().genesis_hash, Default::default(), false) + let mut b1 = BlockBuilderBuilder::new(&client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() .unwrap(); // needed to make sure B1 gets a different hash from A1 b1.push_transfer(Transfer { @@ -1105,16 +1313,20 @@ fn finality_notifications_content() { let b1 = b1.build().unwrap().block; block_on(client.import(BlockOrigin::Own, b1.clone())).unwrap(); - let b2 = client - .new_block_at(b1.hash(), Default::default(), false) + let b2 = BlockBuilderBuilder::new(&client) + .on_parent_block(b1.hash()) + .with_parent_block_number(1) + .build() .unwrap() .build() .unwrap() .block; block_on(client.import(BlockOrigin::Own, b2.clone())).unwrap(); - let mut c1 = client - .new_block_at(client.chain_info().genesis_hash, Default::default(), false) + let mut c1 = BlockBuilderBuilder::new(&client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() .unwrap(); // needed to make sure B1 gets a different hash from A1 c1.push_transfer(Transfer { @@ -1127,7 +1339,12 @@ fn finality_notifications_content() { let c1 = c1.build().unwrap().block; block_on(client.import(BlockOrigin::Own, c1.clone())).unwrap(); - let mut d3 = client.new_block_at(a2.hash(), Default::default(), false).unwrap(); + let mut d3 = BlockBuilderBuilder::new(&client) + .on_parent_block(a2.hash()) + .with_parent_block_number(2) + .build() + .unwrap(); + // needed to make sure D3 gets a different hash from A3 d3.push_transfer(Transfer { from: AccountKeyring::Alice.into(), @@ -1139,8 +1356,10 @@ fn finality_notifications_content() { let d3 = d3.build().unwrap().block; block_on(client.import(BlockOrigin::Own, d3.clone())).unwrap(); - let d4 = client - .new_block_at(d3.hash(), Default::default(), false) + let d4 = BlockBuilderBuilder::new(&client) + .on_parent_block(d3.hash()) + .with_parent_block_number(3) + .build() .unwrap() .build() .unwrap() @@ -1200,8 +1419,10 @@ fn state_reverted_on_reorg() { // G -> A1 -> A2 // \ // -> B1 - let mut a1 = client - .new_block_at(client.chain_info().genesis_hash, Default::default(), false) + let mut a1 = BlockBuilderBuilder::new(&client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() .unwrap(); a1.push_transfer(Transfer { from: AccountKeyring::Alice.into(), @@ -1213,8 +1434,10 @@ fn state_reverted_on_reorg() { let a1 = a1.build().unwrap().block; block_on(client.import(BlockOrigin::Own, a1.clone())).unwrap(); - let mut b1 = client - .new_block_at(client.chain_info().genesis_hash, Default::default(), false) + let mut b1 = BlockBuilderBuilder::new(&client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() .unwrap(); b1.push_transfer(Transfer { from: AccountKeyring::Alice.into(), @@ -1228,7 +1451,11 @@ fn state_reverted_on_reorg() { block_on(client.import_as_best(BlockOrigin::Own, b1.clone())).unwrap(); assert_eq!(950 * DOLLARS, current_balance(&client)); - let mut a2 = client.new_block_at(a1.hash(), Default::default(), false).unwrap(); + let mut a2 = BlockBuilderBuilder::new(&client) + .on_parent_block(a1.hash()) + .with_parent_block_number(1) + .build() + .unwrap(); a2.push_transfer(Transfer { from: AccountKeyring::Alice.into(), to: AccountKeyring::Charlie.into(), @@ -1272,24 +1499,30 @@ fn doesnt_import_blocks_that_revert_finality() { // \ // -> B1 -> B2 -> B3 - let a1 = client - .new_block_at(client.chain_info().genesis_hash, Default::default(), false) + let a1 = BlockBuilderBuilder::new(&client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() .unwrap() .build() .unwrap() .block; block_on(client.import(BlockOrigin::Own, a1.clone())).unwrap(); - let a2 = client - .new_block_at(a1.hash(), Default::default(), false) + let a2 = BlockBuilderBuilder::new(&client) + .on_parent_block(a1.hash()) + .with_parent_block_number(1) + .build() .unwrap() .build() .unwrap() .block; block_on(client.import(BlockOrigin::Own, a2.clone())).unwrap(); - let mut b1 = client - .new_block_at(client.chain_info().genesis_hash, Default::default(), false) + let mut b1 = BlockBuilderBuilder::new(&client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() .unwrap(); // needed to make sure B1 gets a different hash from A1 @@ -1303,8 +1536,10 @@ fn doesnt_import_blocks_that_revert_finality() { let b1 = b1.build().unwrap().block; block_on(client.import(BlockOrigin::Own, b1.clone())).unwrap(); - let b2 = client - .new_block_at(b1.hash(), Default::default(), false) + let b2 = BlockBuilderBuilder::new(&client) + .on_parent_block(b1.hash()) + .with_parent_block_number(1) + .build() .unwrap() .build() .unwrap() @@ -1313,8 +1548,10 @@ fn doesnt_import_blocks_that_revert_finality() { // prepare B3 before we finalize A2, because otherwise we won't be able to // read changes trie configuration after A2 is finalized - let b3 = client - .new_block_at(b2.hash(), Default::default(), false) + let b3 = BlockBuilderBuilder::new(&client) + .on_parent_block(b2.hash()) + .with_parent_block_number(2) + .build() .unwrap() .build() .unwrap() @@ -1332,8 +1569,10 @@ fn doesnt_import_blocks_that_revert_finality() { // adding a C1 block which is lower than the last finalized should also // fail (with a cheaper check that doesn't require checking ancestry). - let mut c1 = client - .new_block_at(client.chain_info().genesis_hash, Default::default(), false) + let mut c1 = BlockBuilderBuilder::new(&client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() .unwrap(); // needed to make sure C1 gets a different hash from A1 and B1 @@ -1352,8 +1591,10 @@ fn doesnt_import_blocks_that_revert_finality() { assert_eq!(import_err.to_string(), expected_err.to_string()); - let a3 = client - .new_block_at(a2.hash(), Default::default(), false) + let a3 = BlockBuilderBuilder::new(&client) + .on_parent_block(a2.hash()) + .with_parent_block_number(2) + .build() .unwrap() .build() .unwrap() @@ -1400,8 +1641,10 @@ fn respects_block_rules() { // B'[2] - block not ok, (incorrect fork) // build B[1] - let block_ok = client - .new_block_at(client.chain_info().genesis_hash, Default::default(), false) + let block_ok = BlockBuilderBuilder::new(&client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() .unwrap() .build() .unwrap() @@ -1419,8 +1662,10 @@ fn respects_block_rules() { assert_eq!(block_on(client.check_block(params)).unwrap(), ImportResult::imported(false)); // build B'[1] - let mut block_not_ok = client - .new_block_at(client.chain_info().genesis_hash, Default::default(), false) + let mut block_not_ok = BlockBuilderBuilder::new(&client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() .unwrap(); block_not_ok.push_storage_change(vec![0], Some(vec![1])).unwrap(); let block_not_ok = block_not_ok.build().unwrap().block; @@ -1443,7 +1688,11 @@ fn respects_block_rules() { block_on(client.import_as_final(BlockOrigin::Own, block_ok)).unwrap(); // And check good fork (build B[2]) - let mut block_ok = client.new_block_at(block_ok_1_hash, Default::default(), false).unwrap(); + let mut block_ok = BlockBuilderBuilder::new(&client) + .on_parent_block(block_ok_1_hash) + .with_parent_block_number(1) + .build() + .unwrap(); block_ok.push_storage_change(vec![0], Some(vec![2])).unwrap(); let block_ok = block_ok.build().unwrap().block; assert_eq!(*block_ok.header().number(), 2); @@ -1462,8 +1711,11 @@ fn respects_block_rules() { assert_eq!(block_on(client.check_block(params)).unwrap(), ImportResult::imported(false)); // And now try bad fork (build B'[2]) - let mut block_not_ok = - client.new_block_at(block_ok_1_hash, Default::default(), false).unwrap(); + let mut block_not_ok = BlockBuilderBuilder::new(&client) + .on_parent_block(block_ok_1_hash) + .with_parent_block_number(1) + .build() + .unwrap(); block_not_ok.push_storage_change(vec![0], Some(vec![3])).unwrap(); let block_not_ok = block_not_ok.build().unwrap().block; assert_eq!(*block_not_ok.header().number(), 2); @@ -1518,15 +1770,19 @@ fn returns_status_for_pruned_blocks() { let mut client = TestClientBuilder::with_backend(backend).build(); - let a1 = client - .new_block_at(client.chain_info().genesis_hash, Default::default(), false) + let a1 = BlockBuilderBuilder::new(&client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() .unwrap() .build() .unwrap() .block; - let mut b1 = client - .new_block_at(client.chain_info().genesis_hash, Default::default(), false) + let mut b1 = BlockBuilderBuilder::new(&client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() .unwrap(); // b1 is created, but not imported @@ -1562,8 +1818,10 @@ fn returns_status_for_pruned_blocks() { ); assert_eq!(client.block_status(check_block_a1.hash).unwrap(), BlockStatus::InChainWithState); - let a2 = client - .new_block_at(a1.hash(), Default::default(), false) + let a2 = BlockBuilderBuilder::new(&client) + .on_parent_block(a1.hash()) + .with_parent_block_number(1) + .build() .unwrap() .build() .unwrap() @@ -1590,8 +1848,10 @@ fn returns_status_for_pruned_blocks() { ); assert_eq!(client.block_status(check_block_a2.hash).unwrap(), BlockStatus::InChainWithState); - let a3 = client - .new_block_at(a2.hash(), Default::default(), false) + let a3 = BlockBuilderBuilder::new(&client) + .on_parent_block(a2.hash()) + .with_parent_block_number(2) + .build() .unwrap() .build() .unwrap() @@ -1855,7 +2115,15 @@ fn cleans_up_closed_notification_sinks_on_block_import() { // for some reason I can't seem to use `ClientBlockImportExt` let bake_and_import_block = |client: &mut TestClient, origin| { - let block = client.new_block(Default::default()).unwrap().build().unwrap().block; + let chain = client.chain_info(); + let block = BlockBuilderBuilder::new(&*client) + .on_parent_block(chain.best_hash) + .with_parent_block_number(chain.best_number) + .build() + .unwrap() + .build() + .unwrap() + .block; let (header, extrinsics) = block.deconstruct(); let mut import = BlockImportParams::new(origin, header); @@ -1898,25 +2166,32 @@ fn reorg_triggers_a_notification_even_for_sources_that_should_not_trigger_notifi let mut notification_stream = futures::executor::block_on_stream(client.import_notification_stream()); - let a1 = client - .new_block_at(client.chain_info().genesis_hash, Default::default(), false) + let a1 = BlockBuilderBuilder::new(&client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() .unwrap() .build() .unwrap() .block; block_on(client.import(BlockOrigin::NetworkInitialSync, a1.clone())).unwrap(); - let a2 = client - .new_block_at(a1.hash(), Default::default(), false) + let a2 = BlockBuilderBuilder::new(&client) + .on_parent_block(a1.hash()) + .with_parent_block_number(1) + .build() .unwrap() .build() .unwrap() .block; block_on(client.import(BlockOrigin::NetworkInitialSync, a2.clone())).unwrap(); - let mut b1 = client - .new_block_at(client.chain_info().genesis_hash, Default::default(), false) + let mut b1 = BlockBuilderBuilder::new(&client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() .unwrap(); + // needed to make sure B1 gets a different hash from A1 b1.push_transfer(Transfer { from: AccountKeyring::Alice.into(), @@ -1928,8 +2203,10 @@ fn reorg_triggers_a_notification_even_for_sources_that_should_not_trigger_notifi let b1 = b1.build().unwrap().block; block_on(client.import(BlockOrigin::NetworkInitialSync, b1.clone())).unwrap(); - let b2 = client - .new_block_at(b1.hash(), Default::default(), false) + let b2 = BlockBuilderBuilder::new(&client) + .on_parent_block(b1.hash()) + .with_parent_block_number(1) + .build() .unwrap() .build() .unwrap() @@ -1964,8 +2241,10 @@ fn use_dalek_ext_works() { ), ); - let a1 = client - .new_block_at(client.chain_info().genesis_hash, Default::default(), false) + let a1 = BlockBuilderBuilder::new(&client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() .unwrap() .build() .unwrap() @@ -1988,12 +2267,21 @@ fn finalize_after_best_block_updates_best() { let mut client = substrate_test_runtime_client::new(); // G -> A1 - let a1 = client.new_block(Default::default()).unwrap().build().unwrap().block; + let a1 = BlockBuilderBuilder::new(&client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() + .unwrap() + .build() + .unwrap() + .block; block_on(client.import(BlockOrigin::Own, a1.clone())).unwrap(); // A1 -> A2 - let a2 = client - .new_block_at(a1.hash(), Default::default(), false) + let a2 = BlockBuilderBuilder::new(&client) + .on_parent_block(a1.hash()) + .with_parent_block_number(1) + .build() .unwrap() .build() .unwrap() @@ -2001,8 +2289,10 @@ fn finalize_after_best_block_updates_best() { block_on(client.import(BlockOrigin::Own, a2.clone())).unwrap(); // A2 -> A3 - let a3 = client - .new_block_at(a2.hash(), Default::default(), false) + let a3 = BlockBuilderBuilder::new(&client) + .on_parent_block(a2.hash()) + .with_parent_block_number(2) + .build() .unwrap() .build() .unwrap() diff --git a/substrate/client/transaction-pool/tests/pool.rs b/substrate/client/transaction-pool/tests/pool.rs index e5ab01ffbf09..6b1a197440c1 100644 --- a/substrate/client/transaction-pool/tests/pool.rs +++ b/substrate/client/transaction-pool/tests/pool.rs @@ -24,7 +24,7 @@ use futures::{ prelude::*, task::Poll, }; -use sc_block_builder::BlockBuilderProvider; +use sc_block_builder::BlockBuilderBuilder; use sc_client_api::client::BlockchainEvents; use sc_transaction_pool::*; use sc_transaction_pool_api::{ @@ -1011,7 +1011,11 @@ fn import_notification_to_pool_maintain_works() { let mut import_stream = block_on_stream(client.import_notification_stream()); // Build the block with the transaction included - let mut block_builder = client.new_block(Default::default()).unwrap(); + let mut block_builder = BlockBuilderBuilder::new(&*client) + .on_parent_block(best_hash) + .with_parent_block_number(0) + .build() + .unwrap(); block_builder.push(xt).unwrap(); let block = block_builder.build().unwrap().block; block_on(client.import(BlockOrigin::Own, block)).unwrap(); diff --git a/substrate/primitives/api/src/lib.rs b/substrate/primitives/api/src/lib.rs index c3f80acf09ae..c122a2348b8e 100644 --- a/substrate/primitives/api/src/lib.rs +++ b/substrate/primitives/api/src/lib.rs @@ -665,6 +665,34 @@ pub trait CallApiAt { ) -> Result<(), ApiError>; } +#[cfg(feature = "std")] +impl> CallApiAt for std::sync::Arc { + type StateBackend = T::StateBackend; + + fn call_api_at(&self, params: CallApiAtParams) -> Result, ApiError> { + (**self).call_api_at(params) + } + + fn runtime_version_at( + &self, + at_hash: ::Hash, + ) -> Result { + (**self).runtime_version_at(at_hash) + } + + fn state_at(&self, at: ::Hash) -> Result { + (**self).state_at(at) + } + + fn initialize_extensions( + &self, + at: ::Hash, + extensions: &mut Extensions, + ) -> Result<(), ApiError> { + (**self).initialize_extensions(at, extensions) + } +} + /// Auxiliary wrapper that holds an api instance and binds it to the given lifetime. #[cfg(feature = "std")] pub struct ApiRef<'a, T>(T, std::marker::PhantomData<&'a ()>); diff --git a/substrate/primitives/api/test/tests/runtime_calls.rs b/substrate/primitives/api/test/tests/runtime_calls.rs index 353be73dcccd..e66be7f9bf1a 100644 --- a/substrate/primitives/api/test/tests/runtime_calls.rs +++ b/substrate/primitives/api/test/tests/runtime_calls.rs @@ -17,6 +17,7 @@ use std::panic::UnwindSafe; +use sc_block_builder::BlockBuilderBuilder; use sp_api::{ApiExt, Core, ProvideRuntimeApi}; use sp_runtime::{ traits::{HashingFor, Header as HeaderT}, @@ -31,7 +32,6 @@ use substrate_test_runtime_client::{ }; use codec::Encode; -use sc_block_builder::BlockBuilderProvider; use sp_consensus::SelectChain; use substrate_test_runtime_client::sc_executor::WasmExecutor; @@ -105,9 +105,12 @@ fn record_proof_works() { .into_unchecked_extrinsic(); // Build the block and record proof - let mut builder = client - .new_block_at(client.chain_info().best_hash, Default::default(), true) - .expect("Creates block builder"); + let mut builder = BlockBuilderBuilder::new(&client) + .on_parent_block(client.chain_info().best_hash) + .with_parent_block_number(client.chain_info().best_number) + .enable_proof_recording() + .build() + .unwrap(); builder.push(transaction.clone()).unwrap(); let (block, _, proof) = builder.build().expect("Bake block").into_inner(); diff --git a/substrate/test-utils/runtime/client/src/block_builder_ext.rs b/substrate/test-utils/runtime/client/src/block_builder_ext.rs index 78863209e33e..f7bf76b017e4 100644 --- a/substrate/test-utils/runtime/client/src/block_builder_ext.rs +++ b/substrate/test-utils/runtime/client/src/block_builder_ext.rs @@ -17,10 +17,8 @@ //! Block Builder extensions for tests. -use sc_client_api::backend; -use sp_api::{ApiExt, ProvideRuntimeApi}; - use sc_block_builder::BlockBuilderApi; +use sp_api::{ApiExt, ProvideRuntimeApi}; use substrate_test_runtime::*; /// Extension trait for test block builder. @@ -45,12 +43,12 @@ pub trait BlockBuilderExt { ) -> Result<(), sp_blockchain::Error>; } -impl<'a, A, B> BlockBuilderExt - for sc_block_builder::BlockBuilder<'a, substrate_test_runtime::Block, A, B> +impl<'a, A> BlockBuilderExt for sc_block_builder::BlockBuilder<'a, substrate_test_runtime::Block, A> where - A: ProvideRuntimeApi + 'a, + A: ProvideRuntimeApi + + sp_api::CallApiAt + + 'a, A::Api: BlockBuilderApi + ApiExt, - B: backend::Backend, { fn push_transfer( &mut self, diff --git a/substrate/test-utils/runtime/client/src/trait_tests.rs b/substrate/test-utils/runtime/client/src/trait_tests.rs index 5fce7a2860b7..6f6bb5c36ee4 100644 --- a/substrate/test-utils/runtime/client/src/trait_tests.rs +++ b/substrate/test-utils/runtime/client/src/trait_tests.rs @@ -26,14 +26,14 @@ use crate::{ AccountKeyring, BlockBuilderExt, ClientBlockImportExt, TestClientBuilder, TestClientBuilderExt, }; use futures::executor::block_on; -use sc_block_builder::BlockBuilderProvider; +use sc_block_builder::BlockBuilderBuilder; use sc_client_api::{ backend, blockchain::{Backend as BlockChainBackendT, HeaderBackend}, }; use sp_consensus::BlockOrigin; use sp_runtime::traits::Block as BlockT; -use substrate_test_runtime::{self, Transfer}; +use substrate_test_runtime::Transfer; /// helper to test the `leaves` implementation for various backends pub fn test_leaves_for_backend(backend: Arc) @@ -54,13 +54,24 @@ where assert_eq!(blockchain.leaves().unwrap(), vec![genesis_hash]); // G -> A1 - let a1 = client.new_block(Default::default()).unwrap().build().unwrap().block; + let a1 = BlockBuilderBuilder::new(&client) + .on_parent_block(genesis_hash) + .fetch_parent_block_number(&client) + .unwrap() + .build() + .unwrap() + .build() + .unwrap() + .block; block_on(client.import(BlockOrigin::Own, a1.clone())).unwrap(); assert_eq!(blockchain.leaves().unwrap(), vec![a1.hash()]); // A1 -> A2 - let a2 = client - .new_block_at(a1.hash(), Default::default(), false) + let a2 = BlockBuilderBuilder::new(&client) + .on_parent_block(a1.hash()) + .fetch_parent_block_number(&client) + .unwrap() + .build() .unwrap() .build() .unwrap() @@ -70,8 +81,11 @@ where assert_eq!(blockchain.leaves().unwrap(), vec![a2.hash()]); // A2 -> A3 - let a3 = client - .new_block_at(a2.hash(), Default::default(), false) + let a3 = BlockBuilderBuilder::new(&client) + .on_parent_block(a2.hash()) + .fetch_parent_block_number(&client) + .unwrap() + .build() .unwrap() .build() .unwrap() @@ -81,8 +95,11 @@ where assert_eq!(blockchain.leaves().unwrap(), vec![a3.hash()]); // A3 -> A4 - let a4 = client - .new_block_at(a3.hash(), Default::default(), false) + let a4 = BlockBuilderBuilder::new(&client) + .on_parent_block(a3.hash()) + .fetch_parent_block_number(&client) + .unwrap() + .build() .unwrap() .build() .unwrap() @@ -91,8 +108,11 @@ where assert_eq!(blockchain.leaves().unwrap(), vec![a4.hash()]); // A4 -> A5 - let a5 = client - .new_block_at(a4.hash(), Default::default(), false) + let a5 = BlockBuilderBuilder::new(&client) + .on_parent_block(a4.hash()) + .fetch_parent_block_number(&client) + .unwrap() + .build() .unwrap() .build() .unwrap() @@ -102,7 +122,12 @@ where assert_eq!(blockchain.leaves().unwrap(), vec![a5.hash()]); // A1 -> B2 - let mut builder = client.new_block_at(a1.hash(), Default::default(), false).unwrap(); + let mut builder = BlockBuilderBuilder::new(&client) + .on_parent_block(a1.hash()) + .fetch_parent_block_number(&client) + .unwrap() + .build() + .unwrap(); // this push is required as otherwise B2 has the same hash as A2 and won't get imported builder @@ -118,8 +143,11 @@ where assert_eq!(blockchain.leaves().unwrap(), vec![a5.hash(), b2.hash()]); // B2 -> B3 - let b3 = client - .new_block_at(b2.hash(), Default::default(), false) + let b3 = BlockBuilderBuilder::new(&client) + .on_parent_block(b2.hash()) + .fetch_parent_block_number(&client) + .unwrap() + .build() .unwrap() .build() .unwrap() @@ -129,8 +157,11 @@ where assert_eq!(blockchain.leaves().unwrap(), vec![a5.hash(), b3.hash()]); // B3 -> B4 - let b4 = client - .new_block_at(b3.hash(), Default::default(), false) + let b4 = BlockBuilderBuilder::new(&client) + .on_parent_block(b3.hash()) + .fetch_parent_block_number(&client) + .unwrap() + .build() .unwrap() .build() .unwrap() @@ -139,7 +170,12 @@ where assert_eq!(blockchain.leaves().unwrap(), vec![a5.hash(), b4.hash()]); // // B2 -> C3 - let mut builder = client.new_block_at(b2.hash(), Default::default(), false).unwrap(); + let mut builder = BlockBuilderBuilder::new(&client) + .on_parent_block(b2.hash()) + .fetch_parent_block_number(&client) + .unwrap() + .build() + .unwrap(); // this push is required as otherwise C3 has the same hash as B3 and won't get imported builder .push_transfer(Transfer { @@ -154,7 +190,12 @@ where assert_eq!(blockchain.leaves().unwrap(), vec![a5.hash(), b4.hash(), c3.hash()]); // A1 -> D2 - let mut builder = client.new_block_at(a1.hash(), Default::default(), false).unwrap(); + let mut builder = BlockBuilderBuilder::new(&client) + .on_parent_block(a1.hash()) + .fetch_parent_block_number(&client) + .unwrap() + .build() + .unwrap(); // this push is required as otherwise D2 has the same hash as B2 and won't get imported builder .push_transfer(Transfer { @@ -182,14 +223,26 @@ where let mut client = TestClientBuilder::with_backend(backend.clone()).build(); let blockchain = backend.blockchain(); + let genesis_hash = client.chain_info().genesis_hash; // G -> A1 - let a1 = client.new_block(Default::default()).unwrap().build().unwrap().block; + let a1 = BlockBuilderBuilder::new(&client) + .on_parent_block(genesis_hash) + .fetch_parent_block_number(&client) + .unwrap() + .build() + .unwrap() + .build() + .unwrap() + .block; block_on(client.import(BlockOrigin::Own, a1.clone())).unwrap(); // A1 -> A2 - let a2 = client - .new_block_at(a1.hash(), Default::default(), false) + let a2 = BlockBuilderBuilder::new(&client) + .on_parent_block(a1.hash()) + .fetch_parent_block_number(&client) + .unwrap() + .build() .unwrap() .build() .unwrap() @@ -197,8 +250,11 @@ where block_on(client.import(BlockOrigin::Own, a2.clone())).unwrap(); // A2 -> A3 - let a3 = client - .new_block_at(a2.hash(), Default::default(), false) + let a3 = BlockBuilderBuilder::new(&client) + .on_parent_block(a2.hash()) + .fetch_parent_block_number(&client) + .unwrap() + .build() .unwrap() .build() .unwrap() @@ -206,8 +262,11 @@ where block_on(client.import(BlockOrigin::Own, a3.clone())).unwrap(); // A3 -> A4 - let a4 = client - .new_block_at(a3.hash(), Default::default(), false) + let a4 = BlockBuilderBuilder::new(&client) + .on_parent_block(a3.hash()) + .fetch_parent_block_number(&client) + .unwrap() + .build() .unwrap() .build() .unwrap() @@ -215,8 +274,11 @@ where block_on(client.import(BlockOrigin::Own, a4.clone())).unwrap(); // A4 -> A5 - let a5 = client - .new_block_at(a4.hash(), Default::default(), false) + let a5 = BlockBuilderBuilder::new(&client) + .on_parent_block(a4.hash()) + .fetch_parent_block_number(&client) + .unwrap() + .build() .unwrap() .build() .unwrap() @@ -224,7 +286,12 @@ where block_on(client.import(BlockOrigin::Own, a5.clone())).unwrap(); // A1 -> B2 - let mut builder = client.new_block_at(a1.hash(), Default::default(), false).unwrap(); + let mut builder = BlockBuilderBuilder::new(&client) + .on_parent_block(a1.hash()) + .fetch_parent_block_number(&client) + .unwrap() + .build() + .unwrap(); // this push is required as otherwise B2 has the same hash as A2 and won't get imported builder .push_transfer(Transfer { @@ -238,8 +305,11 @@ where block_on(client.import(BlockOrigin::Own, b2.clone())).unwrap(); // B2 -> B3 - let b3 = client - .new_block_at(b2.hash(), Default::default(), false) + let b3 = BlockBuilderBuilder::new(&client) + .on_parent_block(b2.hash()) + .fetch_parent_block_number(&client) + .unwrap() + .build() .unwrap() .build() .unwrap() @@ -247,8 +317,11 @@ where block_on(client.import(BlockOrigin::Own, b3.clone())).unwrap(); // B3 -> B4 - let b4 = client - .new_block_at(b3.hash(), Default::default(), false) + let b4 = BlockBuilderBuilder::new(&client) + .on_parent_block(b3.hash()) + .fetch_parent_block_number(&client) + .unwrap() + .build() .unwrap() .build() .unwrap() @@ -256,7 +329,12 @@ where block_on(client.import(BlockOrigin::Own, b4)).unwrap(); // // B2 -> C3 - let mut builder = client.new_block_at(b2.hash(), Default::default(), false).unwrap(); + let mut builder = BlockBuilderBuilder::new(&client) + .on_parent_block(b2.hash()) + .fetch_parent_block_number(&client) + .unwrap() + .build() + .unwrap(); // this push is required as otherwise C3 has the same hash as B3 and won't get imported builder .push_transfer(Transfer { @@ -270,7 +348,12 @@ where block_on(client.import(BlockOrigin::Own, c3.clone())).unwrap(); // A1 -> D2 - let mut builder = client.new_block_at(a1.hash(), Default::default(), false).unwrap(); + let mut builder = BlockBuilderBuilder::new(&client) + .on_parent_block(a1.hash()) + .fetch_parent_block_number(&client) + .unwrap() + .build() + .unwrap(); // this push is required as otherwise D2 has the same hash as B2 and won't get imported builder .push_transfer(Transfer { @@ -309,14 +392,26 @@ where // A1 -> D2 let mut client = TestClientBuilder::with_backend(backend.clone()).build(); let blockchain = backend.blockchain(); + let genesis_hash = client.chain_info().genesis_hash; // G -> A1 - let a1 = client.new_block(Default::default()).unwrap().build().unwrap().block; + let a1 = BlockBuilderBuilder::new(&client) + .on_parent_block(genesis_hash) + .fetch_parent_block_number(&client) + .unwrap() + .build() + .unwrap() + .build() + .unwrap() + .block; block_on(client.import(BlockOrigin::Own, a1.clone())).unwrap(); // A1 -> A2 - let a2 = client - .new_block_at(a1.hash(), Default::default(), false) + let a2 = BlockBuilderBuilder::new(&client) + .on_parent_block(a1.hash()) + .fetch_parent_block_number(&client) + .unwrap() + .build() .unwrap() .build() .unwrap() @@ -324,8 +419,11 @@ where block_on(client.import(BlockOrigin::Own, a2.clone())).unwrap(); // A2 -> A3 - let a3 = client - .new_block_at(a2.hash(), Default::default(), false) + let a3 = BlockBuilderBuilder::new(&client) + .on_parent_block(a2.hash()) + .fetch_parent_block_number(&client) + .unwrap() + .build() .unwrap() .build() .unwrap() @@ -333,8 +431,11 @@ where block_on(client.import(BlockOrigin::Own, a3.clone())).unwrap(); // A3 -> A4 - let a4 = client - .new_block_at(a3.hash(), Default::default(), false) + let a4 = BlockBuilderBuilder::new(&client) + .on_parent_block(a3.hash()) + .fetch_parent_block_number(&client) + .unwrap() + .build() .unwrap() .build() .unwrap() @@ -342,8 +443,11 @@ where block_on(client.import(BlockOrigin::Own, a4.clone())).unwrap(); // A4 -> A5 - let a5 = client - .new_block_at(a4.hash(), Default::default(), false) + let a5 = BlockBuilderBuilder::new(&client) + .on_parent_block(a4.hash()) + .fetch_parent_block_number(&client) + .unwrap() + .build() .unwrap() .build() .unwrap() @@ -351,7 +455,12 @@ where block_on(client.import(BlockOrigin::Own, a5.clone())).unwrap(); // A1 -> B2 - let mut builder = client.new_block_at(a1.hash(), Default::default(), false).unwrap(); + let mut builder = BlockBuilderBuilder::new(&client) + .on_parent_block(a1.hash()) + .fetch_parent_block_number(&client) + .unwrap() + .build() + .unwrap(); // this push is required as otherwise B2 has the same hash as A2 and won't get imported builder .push_transfer(Transfer { @@ -365,8 +474,11 @@ where block_on(client.import(BlockOrigin::Own, b2.clone())).unwrap(); // B2 -> B3 - let b3 = client - .new_block_at(b2.hash(), Default::default(), false) + let b3 = BlockBuilderBuilder::new(&client) + .on_parent_block(b2.hash()) + .fetch_parent_block_number(&client) + .unwrap() + .build() .unwrap() .build() .unwrap() @@ -374,8 +486,11 @@ where block_on(client.import(BlockOrigin::Own, b3.clone())).unwrap(); // B3 -> B4 - let b4 = client - .new_block_at(b3.hash(), Default::default(), false) + let b4 = BlockBuilderBuilder::new(&client) + .on_parent_block(b3.hash()) + .fetch_parent_block_number(&client) + .unwrap() + .build() .unwrap() .build() .unwrap() @@ -383,7 +498,12 @@ where block_on(client.import(BlockOrigin::Own, b4)).unwrap(); // // B2 -> C3 - let mut builder = client.new_block_at(b2.hash(), Default::default(), false).unwrap(); + let mut builder = BlockBuilderBuilder::new(&client) + .on_parent_block(b2.hash()) + .fetch_parent_block_number(&client) + .unwrap() + .build() + .unwrap(); // this push is required as otherwise C3 has the same hash as B3 and won't get imported builder .push_transfer(Transfer { @@ -397,7 +517,12 @@ where block_on(client.import(BlockOrigin::Own, c3)).unwrap(); // A1 -> D2 - let mut builder = client.new_block_at(a1.hash(), Default::default(), false).unwrap(); + let mut builder = BlockBuilderBuilder::new(&client) + .on_parent_block(a1.hash()) + .fetch_parent_block_number(&client) + .unwrap() + .build() + .unwrap(); // this push is required as otherwise D2 has the same hash as B2 and won't get imported builder .push_transfer(Transfer { diff --git a/substrate/test-utils/runtime/src/lib.rs b/substrate/test-utils/runtime/src/lib.rs index 845ef2c44568..6b963deb5890 100644 --- a/substrate/test-utils/runtime/src/lib.rs +++ b/substrate/test-utils/runtime/src/lib.rs @@ -1020,7 +1020,7 @@ mod tests { use super::*; use codec::Encode; use frame_support::dispatch::DispatchInfo; - use sc_block_builder::BlockBuilderProvider; + use sc_block_builder::BlockBuilderBuilder; use sp_api::{ApiExt, ProvideRuntimeApi}; use sp_consensus::BlockOrigin; use sp_core::{storage::well_known_keys::HEAP_PAGES, traits::CallContext}; @@ -1052,7 +1052,11 @@ mod tests { // Create a block that sets the `:heap_pages` to 32 pages of memory which corresponds to // ~2048k of heap memory. let (new_at_hash, block) = { - let mut builder = client.new_block(Default::default()).unwrap(); + let mut builder = BlockBuilderBuilder::new(&client) + .on_parent_block(best_hash) + .with_parent_block_number(0) + .build() + .unwrap(); builder.push_storage_change(HEAP_PAGES.to_vec(), Some(32u64.encode())).unwrap(); let block = builder.build().unwrap().block; let hash = block.header.hash(); diff --git a/substrate/utils/frame/benchmarking-cli/src/block/bench.rs b/substrate/utils/frame/benchmarking-cli/src/block/bench.rs index c9a7fb1ad33d..a028c7d438e8 100644 --- a/substrate/utils/frame/benchmarking-cli/src/block/bench.rs +++ b/substrate/utils/frame/benchmarking-cli/src/block/bench.rs @@ -20,7 +20,7 @@ use codec::DecodeAll; use frame_support::weights::constants::WEIGHT_REF_TIME_PER_NANOS; use frame_system::ConsumedWeight; -use sc_block_builder::{BlockBuilderApi, BlockBuilderProvider}; +use sc_block_builder::BlockBuilderApi; use sc_cli::{Error, Result}; use sc_client_api::{ Backend as ClientBackend, BlockBackend, HeaderBackend, StorageProvider, UsageProvider, @@ -71,8 +71,7 @@ impl Benchmark where Block: BlockT, BA: ClientBackend, - C: BlockBuilderProvider - + ProvideRuntimeApi + C: ProvideRuntimeApi + StorageProvider + UsageProvider + BlockBackend diff --git a/substrate/utils/frame/benchmarking-cli/src/block/cmd.rs b/substrate/utils/frame/benchmarking-cli/src/block/cmd.rs index ee12c1c5dac3..e28688caa2dd 100644 --- a/substrate/utils/frame/benchmarking-cli/src/block/cmd.rs +++ b/substrate/utils/frame/benchmarking-cli/src/block/cmd.rs @@ -18,7 +18,7 @@ //! Contains the [`BlockCmd`] as entry point for the CLI to execute //! the *block* benchmark. -use sc_block_builder::{BlockBuilderApi, BlockBuilderProvider}; +use sc_block_builder::BlockBuilderApi; use sc_cli::{CliConfiguration, ImportParams, Result, SharedParams}; use sc_client_api::{Backend as ClientBackend, BlockBackend, StorageProvider, UsageProvider}; use sp_api::{ApiExt, ProvideRuntimeApi}; @@ -84,8 +84,7 @@ impl BlockCmd { where Block: BlockT, BA: ClientBackend, - C: BlockBuilderProvider - + BlockBackend + C: BlockBackend + ProvideRuntimeApi + StorageProvider + UsageProvider diff --git a/substrate/utils/frame/benchmarking-cli/src/extrinsic/bench.rs b/substrate/utils/frame/benchmarking-cli/src/extrinsic/bench.rs index 693b9f99f08e..f0a7436dc729 100644 --- a/substrate/utils/frame/benchmarking-cli/src/extrinsic/bench.rs +++ b/substrate/utils/frame/benchmarking-cli/src/extrinsic/bench.rs @@ -17,10 +17,10 @@ //! Contains the core benchmarking logic. -use sc_block_builder::{BlockBuilderApi, BlockBuilderProvider}; +use sc_block_builder::{BlockBuilderApi, BlockBuilderBuilder}; use sc_cli::{Error, Result}; -use sc_client_api::Backend as ClientBackend; -use sp_api::{ApiExt, Core, ProvideRuntimeApi}; +use sc_client_api::UsageProvider; +use sp_api::{ApiExt, CallApiAt, Core, ProvideRuntimeApi}; use sp_blockchain::{ ApplyExtrinsicFailed::Validity, Error::{ApplyExtrinsicFailed, RuntimeApiError}, @@ -61,20 +61,20 @@ pub struct BenchmarkParams { pub(crate) type BenchRecord = Vec; /// Holds all objects needed to run the *overhead* benchmarks. -pub(crate) struct Benchmark { +pub(crate) struct Benchmark { client: Arc, params: BenchmarkParams, inherent_data: sp_inherents::InherentData, digest_items: Vec, - _p: PhantomData<(Block, BA)>, + _p: PhantomData, } -impl Benchmark +impl Benchmark where Block: BlockT, - BA: ClientBackend, - C: BlockBuilderProvider - + ProvideRuntimeApi + C: ProvideRuntimeApi + + CallApiAt + + UsageProvider + sp_blockchain::HeaderBackend, C::Api: ApiExt + BlockBuilderApi, { @@ -129,7 +129,13 @@ where &self, ext_builder: Option<&dyn ExtrinsicBuilder>, ) -> Result<(Block, Option)> { - let mut builder = self.client.new_block(Digest { logs: self.digest_items.clone() })?; + let chain = self.client.usage_info().chain; + let mut builder = BlockBuilderBuilder::new(&*self.client) + .on_parent_block(chain.best_hash) + .with_parent_block_number(chain.best_number) + .with_inherent_digests(Digest { logs: self.digest_items.clone() }) + .build()?; + // Create and insert the inherents. let inherents = builder.create_inherents(self.inherent_data.clone())?; for inherent in inherents { diff --git a/substrate/utils/frame/benchmarking-cli/src/extrinsic/cmd.rs b/substrate/utils/frame/benchmarking-cli/src/extrinsic/cmd.rs index 4c3a6ed1bcd7..99c0230617cb 100644 --- a/substrate/utils/frame/benchmarking-cli/src/extrinsic/cmd.rs +++ b/substrate/utils/frame/benchmarking-cli/src/extrinsic/cmd.rs @@ -15,10 +15,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -use sc_block_builder::{BlockBuilderApi, BlockBuilderProvider}; +use sc_block_builder::BlockBuilderApi; use sc_cli::{CliConfiguration, ImportParams, Result, SharedParams}; -use sc_client_api::Backend as ClientBackend; -use sp_api::{ApiExt, ProvideRuntimeApi}; +use sc_client_api::UsageProvider; +use sp_api::{ApiExt, CallApiAt, ProvideRuntimeApi}; use sp_runtime::{traits::Block as BlockT, DigestItem, OpaqueExtrinsic}; use clap::{Args, Parser}; @@ -84,7 +84,7 @@ impl ExtrinsicCmd { /// Benchmark the execution time of a specific type of extrinsic. /// /// The output will be printed to console. - pub fn run( + pub fn run( &self, client: Arc, inherent_data: sp_inherents::InherentData, @@ -93,9 +93,9 @@ impl ExtrinsicCmd { ) -> Result<()> where Block: BlockT, - BA: ClientBackend, - C: BlockBuilderProvider - + ProvideRuntimeApi + C: ProvideRuntimeApi + + CallApiAt + + UsageProvider + sp_blockchain::HeaderBackend, C::Api: ApiExt + BlockBuilderApi, { diff --git a/substrate/utils/frame/benchmarking-cli/src/overhead/cmd.rs b/substrate/utils/frame/benchmarking-cli/src/overhead/cmd.rs index 5a4c37b1f6f0..4fa8cecf2f7d 100644 --- a/substrate/utils/frame/benchmarking-cli/src/overhead/cmd.rs +++ b/substrate/utils/frame/benchmarking-cli/src/overhead/cmd.rs @@ -18,11 +18,11 @@ //! Contains the [`OverheadCmd`] as entry point for the CLI to execute //! the *overhead* benchmarks. -use sc_block_builder::{BlockBuilderApi, BlockBuilderProvider}; +use sc_block_builder::BlockBuilderApi; use sc_cli::{CliConfiguration, ImportParams, Result, SharedParams}; -use sc_client_api::Backend as ClientBackend; +use sc_client_api::UsageProvider; use sc_service::Configuration; -use sp_api::{ApiExt, ProvideRuntimeApi}; +use sp_api::{ApiExt, CallApiAt, ProvideRuntimeApi}; use sp_runtime::{traits::Block as BlockT, DigestItem, OpaqueExtrinsic}; use clap::{Args, Parser}; @@ -97,7 +97,7 @@ impl OverheadCmd { /// /// Writes the results to console and into two instances of the /// `weights.hbs` template, one for each benchmark. - pub fn run( + pub fn run( &self, cfg: Configuration, client: Arc, @@ -107,9 +107,9 @@ impl OverheadCmd { ) -> Result<()> where Block: BlockT, - BA: ClientBackend, - C: BlockBuilderProvider - + ProvideRuntimeApi + C: ProvideRuntimeApi + + CallApiAt + + UsageProvider + sp_blockchain::HeaderBackend, C::Api: ApiExt + BlockBuilderApi, { From 21fbc00d04f511df4c374edd3a2f99d73207be50 Mon Sep 17 00:00:00 2001 From: georgepisaltu <52418509+georgepisaltu@users.noreply.github.com> Date: Fri, 3 Nov 2023 21:38:26 +0200 Subject: [PATCH 11/31] Identity pallet improvements (#2048) This PR is a follow up to #1661 - [x] rename the `simple` module to `legacy` - [x] fix benchmarks to disregard the number of additional fields - [x] change the storage deposits to charge per encoded byte of the identity information instance, removing the need for `fn additional(&self) -> usize` in `IdentityInformationProvider` - [x] ~add an extrinsic to rejig deposits to account for the change above~ - [ ] ~ensure through proper configuration that the new byte-based deposit is always lower than whatever is reserved now~ - [x] remove `IdentityFields` from the `set_fields` extrinsic signature, as per [this discussion](https://github.com/paritytech/polkadot-sdk/pull/1661#discussion_r1371703403) > ensure through proper configuration that the new byte-based deposit is always lower than whatever is reserved now Not sure this is needed anymore. If the new deposits are higher than what is currently on chain and users don't have enough funds to reserve what is needed, the extrinisc fails and they're basically grandfathered and frozen until they add more funds and/or make a change to their identity. This behavior seems fine to me. Original idea [here](https://github.com/paritytech/polkadot-sdk/pull/1661#issuecomment-1779606319). > add an extrinsic to rejig deposits to account for the change above This was initially implemented but now removed from this PR in favor of the implementation detailed [here](https://github.com/paritytech/polkadot-sdk/pull/2088). --------- Signed-off-by: georgepisaltu Co-authored-by: joepetrowski --- polkadot/runtime/rococo/src/lib.rs | 7 +- .../rococo/src/weights/pallet_identity.rs | 31 +- polkadot/runtime/westend/src/lib.rs | 7 +- .../westend/src/weights/pallet_identity.rs | 30 +- substrate/bin/node/runtime/src/impls.rs | 5 +- substrate/bin/node/runtime/src/lib.rs | 11 +- substrate/frame/alliance/src/lib.rs | 18 +- substrate/frame/alliance/src/mock.rs | 68 +++-- substrate/frame/alliance/src/tests.rs | 20 +- substrate/frame/identity/src/benchmarking.rs | 44 +-- .../identity/src/{simple.rs => legacy.rs} | 32 ++- substrate/frame/identity/src/lib.rs | 120 +++----- substrate/frame/identity/src/tests.rs | 269 +++++++++++------- substrate/frame/identity/src/types.rs | 84 ++---- substrate/frame/identity/src/weights.rs | 72 ++--- 15 files changed, 356 insertions(+), 462 deletions(-) rename substrate/frame/identity/src/{simple.rs => legacy.rs} (89%) diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index b127eda3ba98..257f364dca01 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -76,7 +76,7 @@ use frame_support::{ }; use frame_system::EnsureRoot; use pallet_grandpa::{fg_primitives, AuthorityId as GrandpaId}; -use pallet_identity::simple::IdentityInfo; +use pallet_identity::legacy::IdentityInfo; use pallet_im_online::sr25519::AuthorityId as ImOnlineId; use pallet_session::historical as session_historical; use pallet_transaction_payment::{CurrencyAdapter, FeeDetails, RuntimeDispatchInfo}; @@ -596,7 +596,7 @@ impl claims::Config for Runtime { parameter_types! { // Minimum 100 bytes/ROC deposited (1 CENT/byte) pub const BasicDeposit: Balance = 1000 * CENTS; // 258 bytes on-chain - pub const FieldDeposit: Balance = 250 * CENTS; // 66 bytes on-chain + pub const ByteDeposit: Balance = deposit(0, 1); pub const SubAccountDeposit: Balance = 200 * CENTS; // 53 bytes on-chain pub const MaxSubAccounts: u32 = 100; pub const MaxAdditionalFields: u32 = 100; @@ -607,10 +607,9 @@ impl pallet_identity::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Currency = Balances; type BasicDeposit = BasicDeposit; - type FieldDeposit = FieldDeposit; + type ByteDeposit = ByteDeposit; type SubAccountDeposit = SubAccountDeposit; type MaxSubAccounts = MaxSubAccounts; - type MaxAdditionalFields = MaxAdditionalFields; type IdentityInformation = IdentityInfo; type MaxRegistrars = MaxRegistrars; type Slashed = Treasury; diff --git a/polkadot/runtime/rococo/src/weights/pallet_identity.rs b/polkadot/runtime/rococo/src/weights/pallet_identity.rs index e10c042dde6a..e8c25269ac37 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_identity.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_identity.rs @@ -65,8 +65,7 @@ impl pallet_identity::WeightInfo for WeightInfo { /// Storage: Identity IdentityOf (r:1 w:1) /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) /// The range of component `r` is `[1, 20]`. - /// The range of component `x` is `[0, 100]`. - fn set_identity(r: u32, x: u32, ) -> Weight { + fn set_identity(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `442 + r * (5 Β±0)` // Estimated: `11003` @@ -75,8 +74,6 @@ impl pallet_identity::WeightInfo for WeightInfo { .saturating_add(Weight::from_parts(0, 11003)) // Standard Error: 2_307 .saturating_add(Weight::from_parts(92_753, 0).saturating_mul(r.into())) - // Standard Error: 450 - .saturating_add(Weight::from_parts(449_529, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -130,8 +127,7 @@ impl pallet_identity::WeightInfo for WeightInfo { /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) /// The range of component `r` is `[1, 20]`. /// The range of component `s` is `[0, 100]`. - /// The range of component `x` is `[0, 100]`. - fn clear_identity(_r: u32, s: u32, x: u32, ) -> Weight { + fn clear_identity(_r: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `469 + r * (5 Β±0) + s * (32 Β±0) + x * (66 Β±0)` // Estimated: `11003` @@ -140,8 +136,6 @@ impl pallet_identity::WeightInfo for WeightInfo { .saturating_add(Weight::from_parts(0, 11003)) // Standard Error: 1_353 .saturating_add(Weight::from_parts(1_074_019, 0).saturating_mul(s.into())) - // Standard Error: 1_353 - .saturating_add(Weight::from_parts(229_947, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -151,8 +145,7 @@ impl pallet_identity::WeightInfo for WeightInfo { /// Storage: Identity IdentityOf (r:1 w:1) /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) /// The range of component `r` is `[1, 20]`. - /// The range of component `x` is `[0, 100]`. - fn request_judgement(r: u32, x: u32, ) -> Weight { + fn request_judgement(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `367 + r * (57 Β±0) + x * (66 Β±0)` // Estimated: `11003` @@ -161,16 +154,14 @@ impl pallet_identity::WeightInfo for WeightInfo { .saturating_add(Weight::from_parts(0, 11003)) // Standard Error: 2_214 .saturating_add(Weight::from_parts(83_822, 0).saturating_mul(r.into())) - // Standard Error: 432 - .saturating_add(Weight::from_parts(458_801, 0).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: Identity IdentityOf (r:1 w:1) /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) /// The range of component `r` is `[1, 20]`. - /// The range of component `x` is `[0, 100]`. - fn cancel_request(r: u32, x: u32, ) -> Weight { + fn cancel_request(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `398 + x * (66 Β±0)` // Estimated: `11003` @@ -179,8 +170,6 @@ impl pallet_identity::WeightInfo for WeightInfo { .saturating_add(Weight::from_parts(0, 11003)) // Standard Error: 2_528 .saturating_add(Weight::from_parts(85_593, 0).saturating_mul(r.into())) - // Standard Error: 493 - .saturating_add(Weight::from_parts(468_140, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -234,8 +223,7 @@ impl pallet_identity::WeightInfo for WeightInfo { /// Storage: Identity IdentityOf (r:1 w:1) /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) /// The range of component `r` is `[1, 19]`. - /// The range of component `x` is `[0, 100]`. - fn provide_judgement(r: u32, x: u32, ) -> Weight { + fn provide_judgement(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `445 + r * (57 Β±0) + x * (66 Β±0)` // Estimated: `11003` @@ -244,8 +232,6 @@ impl pallet_identity::WeightInfo for WeightInfo { .saturating_add(Weight::from_parts(0, 11003)) // Standard Error: 2_881 .saturating_add(Weight::from_parts(109_812, 0).saturating_mul(r.into())) - // Standard Error: 533 - .saturating_add(Weight::from_parts(733_244, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -259,8 +245,7 @@ impl pallet_identity::WeightInfo for WeightInfo { /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) /// The range of component `r` is `[1, 20]`. /// The range of component `s` is `[0, 100]`. - /// The range of component `x` is `[0, 100]`. - fn kill_identity(r: u32, s: u32, x: u32, ) -> Weight { + fn kill_identity(r: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `676 + r * (5 Β±0) + s * (32 Β±0) + x * (66 Β±0)` // Estimated: `11003` @@ -271,8 +256,6 @@ impl pallet_identity::WeightInfo for WeightInfo { .saturating_add(Weight::from_parts(15_486, 0).saturating_mul(r.into())) // Standard Error: 1_275 .saturating_add(Weight::from_parts(1_085_117, 0).saturating_mul(s.into())) - // Standard Error: 1_275 - .saturating_add(Weight::from_parts(228_226, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 9dfc3389d574..ec94973af4f3 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -40,7 +40,7 @@ use frame_support::{ }; use frame_system::EnsureRoot; use pallet_grandpa::{fg_primitives, AuthorityId as GrandpaId}; -use pallet_identity::simple::IdentityInfo; +use pallet_identity::legacy::IdentityInfo; use pallet_im_online::sr25519::AuthorityId as ImOnlineId; use pallet_session::historical as session_historical; use pallet_transaction_payment::{CurrencyAdapter, FeeDetails, RuntimeDispatchInfo}; @@ -863,7 +863,7 @@ where parameter_types! { // Minimum 100 bytes/KSM deposited (1 CENT/byte) pub const BasicDeposit: Balance = 1000 * CENTS; // 258 bytes on-chain - pub const FieldDeposit: Balance = 250 * CENTS; // 66 bytes on-chain + pub const ByteDeposit: Balance = deposit(0, 1); pub const SubAccountDeposit: Balance = 200 * CENTS; // 53 bytes on-chain pub const MaxSubAccounts: u32 = 100; pub const MaxAdditionalFields: u32 = 100; @@ -875,10 +875,9 @@ impl pallet_identity::Config for Runtime { type Currency = Balances; type Slashed = (); type BasicDeposit = BasicDeposit; - type FieldDeposit = FieldDeposit; + type ByteDeposit = ByteDeposit; type SubAccountDeposit = SubAccountDeposit; type MaxSubAccounts = MaxSubAccounts; - type MaxAdditionalFields = MaxAdditionalFields; type IdentityInformation = IdentityInfo; type MaxRegistrars = MaxRegistrars; type ForceOrigin = EitherOf, GeneralAdmin>; diff --git a/polkadot/runtime/westend/src/weights/pallet_identity.rs b/polkadot/runtime/westend/src/weights/pallet_identity.rs index 8c11482ebdc1..dea631b9316b 100644 --- a/polkadot/runtime/westend/src/weights/pallet_identity.rs +++ b/polkadot/runtime/westend/src/weights/pallet_identity.rs @@ -68,8 +68,7 @@ impl pallet_identity::WeightInfo for WeightInfo { /// Storage: Identity IdentityOf (r:1 w:1) /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) /// The range of component `r` is `[1, 20]`. - /// The range of component `x` is `[0, 100]`. - fn set_identity(r: u32, x: u32, ) -> Weight { + fn set_identity(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `442 + r * (5 Β±0)` // Estimated: `11003` @@ -78,8 +77,6 @@ impl pallet_identity::WeightInfo for WeightInfo { .saturating_add(Weight::from_parts(0, 11003)) // Standard Error: 7_269 .saturating_add(Weight::from_parts(250_439, 0).saturating_mul(r.into())) - // Standard Error: 1_418 - .saturating_add(Weight::from_parts(483_981, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -133,8 +130,7 @@ impl pallet_identity::WeightInfo for WeightInfo { /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) /// The range of component `r` is `[1, 20]`. /// The range of component `s` is `[0, 100]`. - /// The range of component `x` is `[0, 100]`. - fn clear_identity(r: u32, s: u32, x: u32, ) -> Weight { + fn clear_identity(r: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `469 + r * (5 Β±0) + s * (32 Β±0) + x * (66 Β±0)` // Estimated: `11003` @@ -145,8 +141,6 @@ impl pallet_identity::WeightInfo for WeightInfo { .saturating_add(Weight::from_parts(475_120, 0).saturating_mul(r.into())) // Standard Error: 4_092 .saturating_add(Weight::from_parts(1_348_869, 0).saturating_mul(s.into())) - // Standard Error: 4_092 - .saturating_add(Weight::from_parts(314_033, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -156,8 +150,7 @@ impl pallet_identity::WeightInfo for WeightInfo { /// Storage: Identity IdentityOf (r:1 w:1) /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) /// The range of component `r` is `[1, 20]`. - /// The range of component `x` is `[0, 100]`. - fn request_judgement(r: u32, x: u32, ) -> Weight { + fn request_judgement(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `367 + r * (57 Β±0) + x * (66 Β±0)` // Estimated: `11003` @@ -166,16 +159,13 @@ impl pallet_identity::WeightInfo for WeightInfo { .saturating_add(Weight::from_parts(0, 11003)) // Standard Error: 7_973 .saturating_add(Weight::from_parts(124_283, 0).saturating_mul(r.into())) - // Standard Error: 1_555 - .saturating_add(Weight::from_parts(512_825, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: Identity IdentityOf (r:1 w:1) /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) /// The range of component `r` is `[1, 20]`. - /// The range of component `x` is `[0, 100]`. - fn cancel_request(r: u32, x: u32, ) -> Weight { + fn cancel_request(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `398 + x * (66 Β±0)` // Estimated: `11003` @@ -184,8 +174,6 @@ impl pallet_identity::WeightInfo for WeightInfo { .saturating_add(Weight::from_parts(0, 11003)) // Standard Error: 5_154 .saturating_add(Weight::from_parts(147_560, 0).saturating_mul(r.into())) - // Standard Error: 1_005 - .saturating_add(Weight::from_parts(490_754, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -239,8 +227,7 @@ impl pallet_identity::WeightInfo for WeightInfo { /// Storage: Identity IdentityOf (r:1 w:1) /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) /// The range of component `r` is `[1, 19]`. - /// The range of component `x` is `[0, 100]`. - fn provide_judgement(r: u32, x: u32, ) -> Weight { + fn provide_judgement(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `445 + r * (57 Β±0) + x * (66 Β±0)` // Estimated: `11003` @@ -249,8 +236,6 @@ impl pallet_identity::WeightInfo for WeightInfo { .saturating_add(Weight::from_parts(0, 11003)) // Standard Error: 10_027 .saturating_add(Weight::from_parts(154_816, 0).saturating_mul(r.into())) - // Standard Error: 1_855 - .saturating_add(Weight::from_parts(803_084, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -264,8 +249,7 @@ impl pallet_identity::WeightInfo for WeightInfo { /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) /// The range of component `r` is `[1, 20]`. /// The range of component `s` is `[0, 100]`. - /// The range of component `x` is `[0, 100]`. - fn kill_identity(r: u32, s: u32, x: u32, ) -> Weight { + fn kill_identity(r: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `676 + r * (5 Β±0) + s * (32 Β±0) + x * (66 Β±0)` // Estimated: `11003` @@ -276,8 +260,6 @@ impl pallet_identity::WeightInfo for WeightInfo { .saturating_add(Weight::from_parts(666_376, 0).saturating_mul(r.into())) // Standard Error: 4_433 .saturating_add(Weight::from_parts(1_396_065, 0).saturating_mul(s.into())) - // Standard Error: 4_433 - .saturating_add(Weight::from_parts(300_762, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) diff --git a/substrate/bin/node/runtime/src/impls.rs b/substrate/bin/node/runtime/src/impls.rs index 430a1ac2824b..717fbeadada4 100644 --- a/substrate/bin/node/runtime/src/impls.rs +++ b/substrate/bin/node/runtime/src/impls.rs @@ -26,6 +26,7 @@ use frame_support::{ }; use pallet_alliance::{IdentityVerifier, ProposalIndex, ProposalProvider}; use pallet_asset_tx_payment::HandleCredit; +use pallet_identity::legacy::IdentityField; use sp_std::prelude::*; use crate::{ @@ -56,8 +57,8 @@ impl HandleCredit for CreditToBlockAuthor { pub struct AllianceIdentityVerifier; impl IdentityVerifier for AllianceIdentityVerifier { - fn has_identity(who: &AccountId, fields: u64) -> bool { - crate::Identity::has_identity(who, fields) + fn has_required_identities(who: &AccountId) -> bool { + crate::Identity::has_identity(who, (IdentityField::Display | IdentityField::Web).bits()) } fn has_good_judgement(who: &AccountId) -> bool { diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index 127faec35629..620e89a65e53 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -60,7 +60,7 @@ use node_primitives::{AccountIndex, Balance, BlockNumber, Hash, Moment, Nonce}; use pallet_asset_conversion::{NativeOrAssetId, NativeOrAssetIdConverter}; use pallet_broker::{CoreAssignment, CoreIndex, CoretimeInterface, PartsOf57600}; use pallet_election_provider_multi_phase::{GeometricDepositBase, SolutionAccuracyOf}; -use pallet_identity::simple::IdentityInfo; +use pallet_identity::legacy::IdentityInfo; use pallet_im_online::sr25519::AuthorityId as ImOnlineId; use pallet_nfts::PalletFeatures; use pallet_nis::WithMaximumOf; @@ -1459,8 +1459,10 @@ impl pallet_grandpa::Config for Runtime { } parameter_types! { - pub const BasicDeposit: Balance = 10 * DOLLARS; // 258 bytes on-chain - pub const FieldDeposit: Balance = 250 * CENTS; // 66 bytes on-chain + // difference of 26 bytes on-chain for the registration and 9 bytes on-chain for the identity + // information, already accounted for by the byte deposit + pub const BasicDeposit: Balance = deposit(1, 17); + pub const ByteDeposit: Balance = deposit(0, 1); pub const SubAccountDeposit: Balance = 2 * DOLLARS; // 53 bytes on-chain pub const MaxSubAccounts: u32 = 100; pub const MaxAdditionalFields: u32 = 100; @@ -1471,10 +1473,9 @@ impl pallet_identity::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Currency = Balances; type BasicDeposit = BasicDeposit; - type FieldDeposit = FieldDeposit; + type ByteDeposit = ByteDeposit; type SubAccountDeposit = SubAccountDeposit; type MaxSubAccounts = MaxSubAccounts; - type MaxAdditionalFields = MaxAdditionalFields; type IdentityInformation = IdentityInfo; type MaxRegistrars = MaxRegistrars; type Slashed = Treasury; diff --git a/substrate/frame/alliance/src/lib.rs b/substrate/frame/alliance/src/lib.rs index f3ff03780f59..d4703db68dbb 100644 --- a/substrate/frame/alliance/src/lib.rs +++ b/substrate/frame/alliance/src/lib.rs @@ -112,7 +112,6 @@ use frame_support::{ }, weights::Weight, }; -use pallet_identity::simple::IdentityField; use scale_info::TypeInfo; pub use pallet::*; @@ -135,9 +134,9 @@ type NegativeImbalanceOf = <>::Currency as Currency< /// Interface required for identity verification. pub trait IdentityVerifier { - /// Function that returns whether an account has an identity registered with the identity - /// provider. - fn has_identity(who: &AccountId, fields: u64) -> bool; + /// Function that returns whether an account has the required identities registered with the + /// identity provider. + fn has_required_identities(who: &AccountId) -> bool; /// Whether an account has been deemed "good" by the provider. fn has_good_judgement(who: &AccountId) -> bool; @@ -149,7 +148,7 @@ pub trait IdentityVerifier { /// The non-provider. Imposes no restrictions on account identity. impl IdentityVerifier for () { - fn has_identity(_who: &AccountId, _fields: u64) -> bool { + fn has_required_identities(_who: &AccountId) -> bool { true } @@ -339,7 +338,7 @@ pub mod pallet { /// Balance is insufficient for the required deposit. InsufficientFunds, /// The account's identity does not have display field and website field. - WithoutIdentityDisplayAndWebsite, + WithoutRequiredIdentityFields, /// The account's identity has no good judgement. WithoutGoodIdentityJudgement, /// The proposal hash is not found. @@ -1082,13 +1081,10 @@ impl, I: 'static> Pallet { } fn has_identity(who: &T::AccountId) -> DispatchResult { - const IDENTITY_FIELD_DISPLAY: u64 = IdentityField::Display as u64; - const IDENTITY_FIELD_WEB: u64 = IdentityField::Web as u64; - let judgement = |who: &T::AccountId| -> DispatchResult { ensure!( - T::IdentityVerifier::has_identity(who, IDENTITY_FIELD_DISPLAY | IDENTITY_FIELD_WEB), - Error::::WithoutIdentityDisplayAndWebsite + T::IdentityVerifier::has_required_identities(who), + Error::::WithoutRequiredIdentityFields ); ensure!( T::IdentityVerifier::has_good_judgement(who), diff --git a/substrate/frame/alliance/src/mock.rs b/substrate/frame/alliance/src/mock.rs index a5970bc7af67..ace5214f145f 100644 --- a/substrate/frame/alliance/src/mock.rs +++ b/substrate/frame/alliance/src/mock.rs @@ -31,7 +31,10 @@ pub use frame_support::{ BoundedVec, }; use frame_system::{EnsureRoot, EnsureSignedBy}; -use pallet_identity::{simple::IdentityInfo, Data, Judgement}; +use pallet_identity::{ + legacy::{IdentityField, IdentityInfo}, + Data, Judgement, +}; pub use crate as pallet_alliance; @@ -96,9 +99,9 @@ impl pallet_collective::Config for Test { } parameter_types! { - pub const BasicDeposit: u64 = 10; - pub const FieldDeposit: u64 = 10; - pub const SubAccountDeposit: u64 = 10; + pub const BasicDeposit: u64 = 100; + pub const ByteDeposit: u64 = 10; + pub const SubAccountDeposit: u64 = 100; pub const MaxSubAccounts: u32 = 2; pub const MaxAdditionalFields: u32 = 2; pub const MaxRegistrars: u32 = 20; @@ -117,10 +120,9 @@ impl pallet_identity::Config for Test { type RuntimeEvent = RuntimeEvent; type Currency = Balances; type BasicDeposit = BasicDeposit; - type FieldDeposit = FieldDeposit; + type ByteDeposit = ByteDeposit; type SubAccountDeposit = SubAccountDeposit; type MaxSubAccounts = MaxSubAccounts; - type MaxAdditionalFields = MaxAdditionalFields; type IdentityInformation = IdentityInfo; type MaxRegistrars = MaxRegistrars; type Slashed = (); @@ -131,8 +133,8 @@ impl pallet_identity::Config for Test { pub struct AllianceIdentityVerifier; impl IdentityVerifier for AllianceIdentityVerifier { - fn has_identity(who: &AccountId, fields: u64) -> bool { - Identity::has_identity(who, fields) + fn has_required_identities(who: &AccountId) -> bool { + Identity::has_identity(who, (IdentityField::Display | IdentityField::Web).bits()) } fn has_good_judgement(who: &AccountId) -> bool { @@ -232,20 +234,40 @@ frame_support::construct_runtime!( } ); +fn test_identity_info() -> IdentityInfo { + IdentityInfo { + additional: BoundedVec::default(), + display: Data::Raw(b"name".to_vec().try_into().unwrap()), + legal: Data::default(), + web: Data::Raw(b"website".to_vec().try_into().unwrap()), + riot: Data::default(), + email: Data::default(), + pgp_fingerprint: None, + image: Data::default(), + twitter: Data::default(), + } +} + +pub(super) fn test_identity_info_deposit() -> ::Balance { + let basic_deposit: u64 = ::BasicDeposit::get(); + let byte_deposit: u64 = ::ByteDeposit::get(); + byte_deposit * test_identity_info().encoded_size() as u64 + basic_deposit +} + pub fn new_test_ext() -> sp_io::TestExternalities { let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); pallet_balances::GenesisConfig:: { balances: vec![ - (1, 50), - (2, 50), - (3, 50), - (4, 50), - (5, 30), - (6, 50), - (7, 50), - (8, 50), - (9, 50), + (1, 1000), + (2, 1000), + (3, 1000), + (4, 1000), + (5, test_identity_info_deposit() + 10), + (6, 1000), + (7, 1000), + (8, 1000), + (9, 1000), ], } .assimilate_storage(&mut t) @@ -263,17 +285,7 @@ pub fn new_test_ext() -> sp_io::TestExternalities { ext.execute_with(|| { assert_ok!(Identity::add_registrar(RuntimeOrigin::signed(1), 1)); - let info = IdentityInfo { - additional: BoundedVec::default(), - display: Data::Raw(b"name".to_vec().try_into().unwrap()), - legal: Data::default(), - web: Data::Raw(b"website".to_vec().try_into().unwrap()), - riot: Data::default(), - email: Data::default(), - pgp_fingerprint: None, - image: Data::default(), - twitter: Data::default(), - }; + let info = test_identity_info(); assert_ok!(Identity::set_identity(RuntimeOrigin::signed(1), Box::new(info.clone()))); assert_ok!(Identity::provide_judgement( RuntimeOrigin::signed(1), diff --git a/substrate/frame/alliance/src/tests.rs b/substrate/frame/alliance/src/tests.rs index 098fd86bbae1..8011627b237a 100644 --- a/substrate/frame/alliance/src/tests.rs +++ b/substrate/frame/alliance/src/tests.rs @@ -105,6 +105,9 @@ fn init_members_works() { #[test] fn disband_works() { new_test_ext().execute_with(|| { + let id_deposit = test_identity_info_deposit(); + let expected_join_deposit = ::AllyDeposit::get(); + assert_eq!(Balances::free_balance(9), 1000 - id_deposit); // ensure alliance is set assert_eq!(Alliance::voting_members(), vec![1, 2, 3]); @@ -113,10 +116,10 @@ fn disband_works() { assert!(Alliance::is_member_of(&2, MemberRole::Retiring)); // join alliance and reserve funds - assert_eq!(Balances::free_balance(9), 40); + assert_eq!(Balances::free_balance(9), 1000 - id_deposit); assert_ok!(Alliance::join_alliance(RuntimeOrigin::signed(9))); - assert_eq!(Alliance::deposit_of(9), Some(25)); - assert_eq!(Balances::free_balance(9), 15); + assert_eq!(Alliance::deposit_of(9), Some(expected_join_deposit)); + assert_eq!(Balances::free_balance(9), 1000 - id_deposit - expected_join_deposit); assert!(Alliance::is_member_of(&9, MemberRole::Ally)); // fails without root @@ -146,7 +149,7 @@ fn disband_works() { // assert a retiring member from the previous Alliance not removed assert!(Alliance::is_member_of(&2, MemberRole::Retiring)); // deposit unreserved - assert_eq!(Balances::free_balance(9), 40); + assert_eq!(Balances::free_balance(9), 1000 - id_deposit); System::assert_last_event(mock::RuntimeEvent::Alliance(crate::Event::AllianceDisbanded { fellow_members: 2, @@ -358,6 +361,9 @@ fn remove_announcement_works() { #[test] fn join_alliance_works() { new_test_ext().execute_with(|| { + let id_deposit = test_identity_info_deposit(); + let join_deposit = ::AllyDeposit::get(); + assert_eq!(Balances::free_balance(9), 1000 - id_deposit); // check already member assert_noop!( Alliance::join_alliance(RuntimeOrigin::signed(1)), @@ -384,8 +390,10 @@ fn join_alliance_works() { Error::::InsufficientFunds ); + assert_eq!(Balances::free_balance(4), 1000 - id_deposit); // success to submit assert_ok!(Alliance::join_alliance(RuntimeOrigin::signed(4))); + assert_eq!(Balances::free_balance(4), 1000 - id_deposit - join_deposit); assert_eq!(Alliance::deposit_of(4), Some(25)); assert_eq!(Alliance::members(MemberRole::Ally), vec![4]); @@ -405,7 +413,7 @@ fn join_alliance_works() { #[cfg(not(feature = "runtime-benchmarks"))] assert_noop!( Alliance::join_alliance(RuntimeOrigin::signed(7)), - Error::::WithoutIdentityDisplayAndWebsite + Error::::WithoutRequiredIdentityFields ); }); } @@ -460,7 +468,7 @@ fn nominate_ally_works() { #[cfg(not(feature = "runtime-benchmarks"))] assert_noop!( Alliance::join_alliance(RuntimeOrigin::signed(7)), - Error::::WithoutIdentityDisplayAndWebsite + Error::::WithoutRequiredIdentityFields ); }); } diff --git a/substrate/frame/identity/src/benchmarking.rs b/substrate/frame/identity/src/benchmarking.rs index 16ce4d8246e6..3d976bd6c881 100644 --- a/substrate/frame/identity/src/benchmarking.rs +++ b/substrate/frame/identity/src/benchmarking.rs @@ -22,7 +22,6 @@ use super::*; use crate::Pallet as Identity; -use enumflags2::BitFlag; use frame_benchmarking::{ account, impl_benchmark_test_suite, v2::*, whitelisted_caller, BenchmarkError, }; @@ -49,9 +48,7 @@ fn add_registrars(r: u32) -> Result<(), &'static str> { .expect("RegistrarOrigin has no successful origin required for the benchmark"); Identity::::add_registrar(registrar_origin, registrar_lookup)?; Identity::::set_fee(RawOrigin::Signed(registrar.clone()).into(), i, 10u32.into())?; - let fields = IdentityFields( - ::IdentityField::all(), - ); + let fields = T::IdentityInformation::all_fields(); Identity::::set_fields(RawOrigin::Signed(registrar.clone()).into(), i, fields)?; } @@ -77,7 +74,7 @@ fn create_sub_accounts( // Set identity so `set_subs` does not fail. if IdentityOf::::get(who).is_none() { let _ = T::Currency::make_free_balance_be(who, BalanceOf::::max_value() / 2u32.into()); - let info = T::IdentityInformation::create_identity_info(1); + let info = T::IdentityInformation::create_identity_info(); Identity::::set_identity(who_origin.into(), Box::new(info))?; } @@ -118,10 +115,7 @@ mod benchmarks { } #[benchmark] - fn set_identity( - r: Linear<1, { T::MaxRegistrars::get() }>, - x: Linear<0, { T::MaxAdditionalFields::get() }>, - ) -> Result<(), BenchmarkError> { + fn set_identity(r: Linear<1, { T::MaxRegistrars::get() }>) -> Result<(), BenchmarkError> { add_registrars::(r)?; let caller: T::AccountId = whitelisted_caller(); @@ -131,7 +125,7 @@ mod benchmarks { let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); // Add an initial identity - let initial_info = T::IdentityInformation::create_identity_info(1); + let initial_info = T::IdentityInformation::create_identity_info(); Identity::::set_identity(caller_origin.clone(), Box::new(initial_info.clone()))?; // User requests judgement from all the registrars, and they approve @@ -154,7 +148,7 @@ mod benchmarks { #[extrinsic_call] _( RawOrigin::Signed(caller.clone()), - Box::new(T::IdentityInformation::create_identity_info(x)), + Box::new(T::IdentityInformation::create_identity_info()), ); assert_last_event::(Event::::IdentitySet { who: caller }.into()); @@ -201,7 +195,6 @@ mod benchmarks { fn clear_identity( r: Linear<1, { T::MaxRegistrars::get() }>, s: Linear<0, { T::MaxSubAccounts::get() }>, - x: Linear<0, { T::MaxAdditionalFields::get() }>, ) -> Result<(), BenchmarkError> { let caller: T::AccountId = whitelisted_caller(); let caller_origin = @@ -216,7 +209,7 @@ mod benchmarks { let _ = add_sub_accounts::(&caller, s)?; // Create their main identity with x additional fields - let info = T::IdentityInformation::create_identity_info(x); + let info = T::IdentityInformation::create_identity_info(); Identity::::set_identity(caller_origin.clone(), Box::new(info.clone()))?; // User requests judgement from all the registrars, and they approve @@ -245,10 +238,7 @@ mod benchmarks { } #[benchmark] - fn request_judgement( - r: Linear<1, { T::MaxRegistrars::get() }>, - x: Linear<0, { T::MaxAdditionalFields::get() }>, - ) -> Result<(), BenchmarkError> { + fn request_judgement(r: Linear<1, { T::MaxRegistrars::get() }>) -> Result<(), BenchmarkError> { let caller: T::AccountId = whitelisted_caller(); let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); @@ -256,7 +246,7 @@ mod benchmarks { add_registrars::(r)?; // Create their main identity with x additional fields - let info = T::IdentityInformation::create_identity_info(x); + let info = T::IdentityInformation::create_identity_info(); let caller_origin = ::RuntimeOrigin::from(RawOrigin::Signed(caller.clone())); Identity::::set_identity(caller_origin.clone(), Box::new(info))?; @@ -272,10 +262,7 @@ mod benchmarks { } #[benchmark] - fn cancel_request( - r: Linear<1, { T::MaxRegistrars::get() }>, - x: Linear<0, { T::MaxAdditionalFields::get() }>, - ) -> Result<(), BenchmarkError> { + fn cancel_request(r: Linear<1, { T::MaxRegistrars::get() }>) -> Result<(), BenchmarkError> { let caller: T::AccountId = whitelisted_caller(); let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); @@ -283,7 +270,7 @@ mod benchmarks { add_registrars::(r)?; // Create their main identity with x additional fields - let info = T::IdentityInformation::create_identity_info(x); + let info = T::IdentityInformation::create_identity_info(); let caller_origin = ::RuntimeOrigin::from(RawOrigin::Signed(caller.clone())); Identity::::set_identity(caller_origin.clone(), Box::new(info))?; @@ -367,15 +354,12 @@ mod benchmarks { .expect("RegistrarOrigin has no successful origin required for the benchmark"); Identity::::add_registrar(registrar_origin, caller_lookup)?; - let fields = IdentityFields( - ::IdentityField::all(), - ); - let registrars = Registrars::::get(); ensure!( registrars[r as usize].as_ref().unwrap().fields == Default::default(), "fields already set." ); + let fields = T::IdentityInformation::all_fields(); #[extrinsic_call] _(RawOrigin::Signed(caller), r, fields); @@ -392,7 +376,6 @@ mod benchmarks { #[benchmark] fn provide_judgement( r: Linear<1, { T::MaxRegistrars::get() - 1 }>, - x: Linear<0, { T::MaxAdditionalFields::get() }>, ) -> Result<(), BenchmarkError> { // The user let user: T::AccountId = account("user", r, SEED); @@ -407,7 +390,7 @@ mod benchmarks { add_registrars::(r)?; - let info = T::IdentityInformation::create_identity_info(x); + let info = T::IdentityInformation::create_identity_info(); let info_hash = T::Hashing::hash_of(&info); Identity::::set_identity(user_origin.clone(), Box::new(info))?; @@ -430,7 +413,6 @@ mod benchmarks { fn kill_identity( r: Linear<1, { T::MaxRegistrars::get() }>, s: Linear<0, { T::MaxSubAccounts::get() }>, - x: Linear<0, { T::MaxAdditionalFields::get() }>, ) -> Result<(), BenchmarkError> { add_registrars::(r)?; @@ -440,7 +422,7 @@ mod benchmarks { let target_lookup = T::Lookup::unlookup(target.clone()); let _ = T::Currency::make_free_balance_be(&target, BalanceOf::::max_value()); - let info = T::IdentityInformation::create_identity_info(x); + let info = T::IdentityInformation::create_identity_info(); Identity::::set_identity(target_origin.clone(), Box::new(info.clone()))?; let _ = add_sub_accounts::(&target, s)?; diff --git a/substrate/frame/identity/src/simple.rs b/substrate/frame/identity/src/legacy.rs similarity index 89% rename from substrate/frame/identity/src/simple.rs rename to substrate/frame/identity/src/legacy.rs index db5ecf3b1c97..a7953f7e1786 100644 --- a/substrate/frame/identity/src/simple.rs +++ b/substrate/frame/identity/src/legacy.rs @@ -16,13 +16,15 @@ // limitations under the License. use codec::{Decode, Encode, MaxEncodedLen}; +#[cfg(feature = "runtime-benchmarks")] +use enumflags2::BitFlag; use enumflags2::{bitflags, BitFlags}; use frame_support::{traits::Get, CloneNoBound, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound}; use scale_info::{build::Variants, Path, Type, TypeInfo}; use sp_runtime::{BoundedVec, RuntimeDebug}; use sp_std::prelude::*; -use crate::types::{Data, IdentityFields, IdentityInformationProvider, U64BitFlag}; +use crate::types::{Data, IdentityInformationProvider}; /// The fields that we use to identify the owner of an account with. Each corresponds to a field /// in the `IdentityInfo` struct. @@ -58,8 +60,6 @@ impl TypeInfo for IdentityField { } } -impl U64BitFlag for IdentityField {} - /// Information concerning the identity of the controller of an account. /// /// NOTE: This should be stored at the end of the storage item to facilitate the addition of extra @@ -124,22 +124,20 @@ pub struct IdentityInfo> { } impl + 'static> IdentityInformationProvider for IdentityInfo { - type IdentityField = IdentityField; - - fn has_identity(&self, fields: u64) -> bool { - self.fields().0.bits() & fields == fields - } + type FieldsIdentifier = u64; - fn additional(&self) -> usize { - self.additional.len() + fn has_identity(&self, fields: Self::FieldsIdentifier) -> bool { + self.fields().bits() & fields == fields } #[cfg(feature = "runtime-benchmarks")] - fn create_identity_info(num_fields: u32) -> Self { + fn create_identity_info() -> Self { let data = Data::Raw(vec![0; 32].try_into().unwrap()); IdentityInfo { - additional: vec![(data.clone(), data.clone()); num_fields as usize].try_into().unwrap(), + additional: vec![(data.clone(), data.clone()); FieldLimit::get().try_into().unwrap()] + .try_into() + .unwrap(), display: data.clone(), legal: data.clone(), web: data.clone(), @@ -150,11 +148,15 @@ impl + 'static> IdentityInformationProvider for IdentityInf twitter: data, } } + + #[cfg(feature = "runtime-benchmarks")] + fn all_fields() -> Self::FieldsIdentifier { + IdentityField::all().bits() + } } impl> IdentityInfo { - #[allow(unused)] - pub(crate) fn fields(&self) -> IdentityFields { + pub(crate) fn fields(&self) -> BitFlags { let mut res = >::empty(); if !self.display.is_none() { res.insert(IdentityField::Display); @@ -180,6 +182,6 @@ impl> IdentityInfo { if !self.twitter.is_none() { res.insert(IdentityField::Twitter); } - IdentityFields(res) + res } } diff --git a/substrate/frame/identity/src/lib.rs b/substrate/frame/identity/src/lib.rs index a341cc6bb9bd..264ea3ddb41f 100644 --- a/substrate/frame/identity/src/lib.rs +++ b/substrate/frame/identity/src/lib.rs @@ -73,21 +73,23 @@ #![cfg_attr(not(feature = "std"), no_std)] mod benchmarking; -pub mod simple; +pub mod legacy; #[cfg(test)] mod tests; mod types; pub mod weights; -use frame_support::traits::{BalanceStatus, Currency, OnUnbalanced, ReservableCurrency}; +use frame_support::{ + pallet_prelude::DispatchResult, + traits::{BalanceStatus, Currency, Get, OnUnbalanced, ReservableCurrency}, +}; use sp_runtime::traits::{AppendZerosInput, Hash, Saturating, StaticLookup, Zero}; use sp_std::prelude::*; pub use weights::WeightInfo; pub use pallet::*; pub use types::{ - Data, IdentityFields, IdentityInformationProvider, Judgement, RegistrarIndex, RegistrarInfo, - Registration, + Data, IdentityInformationProvider, Judgement, RegistrarIndex, RegistrarInfo, Registration, }; type BalanceOf = @@ -115,9 +117,9 @@ pub mod pallet { #[pallet::constant] type BasicDeposit: Get>; - /// The amount held on deposit per additional field for a registered identity. + /// The amount held on deposit per encoded byte for a registered identity. #[pallet::constant] - type FieldDeposit: Get>; + type ByteDeposit: Get>; /// The amount held on deposit for a registered subaccount. This should account for the fact /// that one storage item's value will increase by the size of an account ID, and there will @@ -129,11 +131,6 @@ pub mod pallet { #[pallet::constant] type MaxSubAccounts: Get; - /// Maximum number of additional fields that may be stored in an ID. Needed to bound the I/O - /// required to access an identity, but can be pretty high. - #[pallet::constant] - type MaxAdditionalFields: Get; - /// Structure holding information about an identity. type IdentityInformation: IdentityInformationProvider; @@ -206,7 +203,7 @@ pub mod pallet { RegistrarInfo< BalanceOf, T::AccountId, - ::IdentityField, + ::FieldsIdentifier, >, >, T::MaxRegistrars, @@ -238,8 +235,6 @@ pub mod pallet { InvalidIndex, /// The target is invalid. InvalidTarget, - /// Too many additional fields. - TooManyFields, /// Maximum amount of registrars reached. Cannot add any more. TooManyRegistrars, /// Account ID is already named. @@ -328,19 +323,15 @@ pub mod pallet { /// /// Emits `IdentitySet` if successful. #[pallet::call_index(1)] - #[pallet::weight(T::WeightInfo::set_identity( - T::MaxRegistrars::get(), - T::MaxAdditionalFields::get(), - ))] + #[pallet::weight(T::WeightInfo::set_identity(T::MaxRegistrars::get()))] pub fn set_identity( origin: OriginFor, info: Box, ) -> DispatchResultWithPostInfo { let sender = ensure_signed(origin)?; - #[allow(deprecated)] - let extra_fields = info.additional() as u32; - ensure!(extra_fields <= T::MaxAdditionalFields::get(), Error::::TooManyFields); - let fd = >::from(extra_fields) * T::FieldDeposit::get(); + let encoded_byte_size = info.encoded_size() as u32; + let byte_deposit = + T::ByteDeposit::get().saturating_mul(>::from(encoded_byte_size)); let mut id = match >::get(&sender) { Some(mut id) => { @@ -357,7 +348,7 @@ pub mod pallet { }; let old_deposit = id.deposit; - id.deposit = T::BasicDeposit::get() + fd; + id.deposit = T::BasicDeposit::get().saturating_add(byte_deposit); if id.deposit > old_deposit { T::Currency::reserve(&sender, id.deposit - old_deposit)?; } @@ -370,7 +361,7 @@ pub mod pallet { >::insert(&sender, id); Self::deposit_event(Event::IdentitySet { who: sender }); - Ok(Some(T::WeightInfo::set_identity(judgements as u32, extra_fields)).into()) + Ok(Some(T::WeightInfo::set_identity(judgements as u32)).into()) } /// Set the sub-accounts of the sender. @@ -404,7 +395,8 @@ pub mod pallet { ); let (old_deposit, old_ids) = >::get(&sender); - let new_deposit = T::SubAccountDeposit::get() * >::from(subs.len() as u32); + let new_deposit = + T::SubAccountDeposit::get().saturating_mul(>::from(subs.len() as u32)); let not_other_sub = subs.iter().filter_map(|i| SuperOf::::get(&i.0)).all(|i| i.0 == sender); @@ -454,14 +446,13 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::clear_identity( T::MaxRegistrars::get(), T::MaxSubAccounts::get(), - T::MaxAdditionalFields::get(), ))] pub fn clear_identity(origin: OriginFor) -> DispatchResultWithPostInfo { let sender = ensure_signed(origin)?; let (subs_deposit, sub_ids) = >::take(&sender); let id = >::take(&sender).ok_or(Error::::NotNamed)?; - let deposit = id.total_deposit() + subs_deposit; + let deposit = id.total_deposit().saturating_add(subs_deposit); for sub in sub_ids.iter() { >::remove(sub); } @@ -475,7 +466,6 @@ pub mod pallet { Ok(Some(T::WeightInfo::clear_identity( id.judgements.len() as u32, sub_ids.len() as u32, - id.info.additional() as u32, )) .into()) } @@ -497,10 +487,7 @@ pub mod pallet { /// /// Emits `JudgementRequested` if successful. #[pallet::call_index(4)] - #[pallet::weight(T::WeightInfo::request_judgement( - T::MaxRegistrars::get(), - T::MaxAdditionalFields::get(), - ))] + #[pallet::weight(T::WeightInfo::request_judgement(T::MaxRegistrars::get(),))] pub fn request_judgement( origin: OriginFor, #[pallet::compact] reg_index: RegistrarIndex, @@ -530,8 +517,6 @@ pub mod pallet { T::Currency::reserve(&sender, registrar.fee)?; let judgements = id.judgements.len(); - #[allow(deprecated)] - let extra_fields = id.info.additional(); >::insert(&sender, id); Self::deposit_event(Event::JudgementRequested { @@ -539,8 +524,7 @@ pub mod pallet { registrar_index: reg_index, }); - Ok(Some(T::WeightInfo::request_judgement(judgements as u32, extra_fields as u32)) - .into()) + Ok(Some(T::WeightInfo::request_judgement(judgements as u32)).into()) } /// Cancel a previous request. @@ -554,10 +538,7 @@ pub mod pallet { /// /// Emits `JudgementUnrequested` if successful. #[pallet::call_index(5)] - #[pallet::weight(T::WeightInfo::cancel_request( - T::MaxRegistrars::get(), - T::MaxAdditionalFields::get(), - ))] + #[pallet::weight(T::WeightInfo::cancel_request(T::MaxRegistrars::get()))] pub fn cancel_request( origin: OriginFor, reg_index: RegistrarIndex, @@ -578,8 +559,6 @@ pub mod pallet { let err_amount = T::Currency::unreserve(&sender, fee); debug_assert!(err_amount.is_zero()); let judgements = id.judgements.len(); - #[allow(deprecated)] - let extra_fields = id.info.additional(); >::insert(&sender, id); Self::deposit_event(Event::JudgementUnrequested { @@ -587,7 +566,7 @@ pub mod pallet { registrar_index: reg_index, }); - Ok(Some(T::WeightInfo::cancel_request(judgements as u32, extra_fields as u32)).into()) + Ok(Some(T::WeightInfo::cancel_request(judgements as u32)).into()) } /// Set the fee required for a judgement to be requested from a registrar. @@ -669,26 +648,21 @@ pub mod pallet { pub fn set_fields( origin: OriginFor, #[pallet::compact] index: RegistrarIndex, - fields: IdentityFields< - ::IdentityField, - >, + fields: ::FieldsIdentifier, ) -> DispatchResultWithPostInfo { let who = ensure_signed(origin)?; - let registrars = >::mutate(|rs| -> Result { - rs.get_mut(index as usize) - .and_then(|x| x.as_mut()) - .and_then(|r| { - if r.account == who { - r.fields = fields; - Some(()) - } else { - None - } - }) - .ok_or_else(|| DispatchError::from(Error::::InvalidIndex))?; - Ok(rs.len()) - })?; + let registrars = + >::mutate(|registrars| -> Result { + let registrar = registrars + .get_mut(index as usize) + .and_then(|r| r.as_mut()) + .filter(|r| r.account == who) + .ok_or_else(|| DispatchError::from(Error::::InvalidIndex))?; + registrar.fields = fields; + + Ok(registrars.len()) + })?; Ok(Some(T::WeightInfo::set_fields(registrars as u32)).into()) } @@ -706,10 +680,7 @@ pub mod pallet { /// /// Emits `JudgementGiven` if successful. #[pallet::call_index(9)] - #[pallet::weight(T::WeightInfo::provide_judgement( - T::MaxRegistrars::get(), - T::MaxAdditionalFields::get(), - ))] + #[pallet::weight(T::WeightInfo::provide_judgement(T::MaxRegistrars::get()))] pub fn provide_judgement( origin: OriginFor, #[pallet::compact] reg_index: RegistrarIndex, @@ -752,13 +723,10 @@ pub mod pallet { } let judgements = id.judgements.len(); - #[allow(deprecated)] - let extra_fields = id.info.additional(); >::insert(&target, id); Self::deposit_event(Event::JudgementGiven { target, registrar_index: reg_index }); - Ok(Some(T::WeightInfo::provide_judgement(judgements as u32, extra_fields as u32)) - .into()) + Ok(Some(T::WeightInfo::provide_judgement(judgements as u32)).into()) } /// Remove an account's identity and sub-account information and slash the deposits. @@ -777,7 +745,6 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::kill_identity( T::MaxRegistrars::get(), T::MaxSubAccounts::get(), - T::MaxAdditionalFields::get(), ))] pub fn kill_identity( origin: OriginFor, @@ -790,7 +757,7 @@ pub mod pallet { // Grab their deposit (and check that they have one). let (subs_deposit, sub_ids) = >::take(&target); let id = >::take(&target).ok_or(Error::::NotNamed)?; - let deposit = id.total_deposit() + subs_deposit; + let deposit = id.total_deposit().saturating_add(subs_deposit); for sub in sub_ids.iter() { >::remove(sub); } @@ -800,12 +767,8 @@ pub mod pallet { Self::deposit_event(Event::IdentityKilled { who: target, deposit }); #[allow(deprecated)] - Ok(Some(T::WeightInfo::kill_identity( - id.judgements.len() as u32, - sub_ids.len() as u32, - id.info.additional() as u32, - )) - .into()) + Ok(Some(T::WeightInfo::kill_identity(id.judgements.len() as u32, sub_ids.len() as u32)) + .into()) } /// Add the given account to the sender's subs. @@ -936,7 +899,10 @@ impl Pallet { } /// Check if the account has corresponding identity information by the identity field. - pub fn has_identity(who: &T::AccountId, fields: u64) -> bool { + pub fn has_identity( + who: &T::AccountId, + fields: ::FieldsIdentifier, + ) -> bool { IdentityOf::::get(who) .map_or(false, |registration| (registration.info.has_identity(fields))) } diff --git a/substrate/frame/identity/src/tests.rs b/substrate/frame/identity/src/tests.rs index f0980e9c7cc8..71192ea65a8c 100644 --- a/substrate/frame/identity/src/tests.rs +++ b/substrate/frame/identity/src/tests.rs @@ -20,13 +20,13 @@ use super::*; use crate::{ self as pallet_identity, - simple::{IdentityField as SimpleIdentityField, IdentityInfo}, + legacy::{IdentityField, IdentityInfo}, }; use codec::{Decode, Encode}; use frame_support::{ assert_noop, assert_ok, ord_parameter_types, parameter_types, - traits::{ConstU32, ConstU64, EitherOfDiverse}, + traits::{ConstU32, ConstU64, EitherOfDiverse, Get}, BoundedVec, }; use frame_system::{EnsureRoot, EnsureSignedBy}; @@ -105,11 +105,10 @@ impl pallet_identity::Config for Test { type RuntimeEvent = RuntimeEvent; type Currency = Balances; type Slashed = (); - type BasicDeposit = ConstU64<10>; - type FieldDeposit = ConstU64<10>; - type SubAccountDeposit = ConstU64<10>; + type BasicDeposit = ConstU64<100>; + type ByteDeposit = ConstU64<10>; + type SubAccountDeposit = ConstU64<100>; type MaxSubAccounts = ConstU32<2>; - type MaxAdditionalFields = MaxAdditionalFields; type IdentityInformation = IdentityInfo; type MaxRegistrars = MaxRegistrars; type RegistrarOrigin = EnsureOneOrRoot; @@ -120,7 +119,7 @@ impl pallet_identity::Config for Test { pub fn new_test_ext() -> sp_io::TestExternalities { let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); pallet_balances::GenesisConfig:: { - balances: vec![(1, 10), (2, 10), (3, 10), (10, 100), (20, 100), (30, 100)], + balances: vec![(1, 100), (2, 100), (3, 100), (10, 1000), (20, 1000), (30, 1000)], } .assimilate_storage(&mut t) .unwrap(); @@ -143,39 +142,43 @@ fn twenty() -> IdentityInfo { } } +fn id_deposit(id: &IdentityInfo) -> u64 { + let base_deposit: u64 = <::BasicDeposit as Get>::get(); + let byte_deposit: u64 = <::ByteDeposit as Get>::get() * + TryInto::::try_into(id.encoded_size()).unwrap(); + base_deposit + byte_deposit +} + #[test] fn identity_fields_repr_works() { - // `SimpleIdentityField` sanity checks. - assert_eq!(SimpleIdentityField::Display as u64, 1 << 0); - assert_eq!(SimpleIdentityField::Legal as u64, 1 << 1); - assert_eq!(SimpleIdentityField::Web as u64, 1 << 2); - assert_eq!(SimpleIdentityField::Riot as u64, 1 << 3); - assert_eq!(SimpleIdentityField::Email as u64, 1 << 4); - assert_eq!(SimpleIdentityField::PgpFingerprint as u64, 1 << 5); - assert_eq!(SimpleIdentityField::Image as u64, 1 << 6); - assert_eq!(SimpleIdentityField::Twitter as u64, 1 << 7); - - let fields = IdentityFields( - SimpleIdentityField::Legal | - SimpleIdentityField::Web | - SimpleIdentityField::Riot | - SimpleIdentityField::PgpFingerprint | - SimpleIdentityField::Twitter, - ); - - assert!(!fields.0.contains(SimpleIdentityField::Display)); - assert!(fields.0.contains(SimpleIdentityField::Legal)); - assert!(fields.0.contains(SimpleIdentityField::Web)); - assert!(fields.0.contains(SimpleIdentityField::Riot)); - assert!(!fields.0.contains(SimpleIdentityField::Email)); - assert!(fields.0.contains(SimpleIdentityField::PgpFingerprint)); - assert!(!fields.0.contains(SimpleIdentityField::Image)); - assert!(fields.0.contains(SimpleIdentityField::Twitter)); - - // The `IdentityFields` inner `BitFlags::bits` is used for `Encode`/`Decode`, so we ensure that - // the `u64` representation matches what we expect during encode/decode operations. + // `IdentityField` sanity checks. + assert_eq!(IdentityField::Display as u64, 1 << 0); + assert_eq!(IdentityField::Legal as u64, 1 << 1); + assert_eq!(IdentityField::Web as u64, 1 << 2); + assert_eq!(IdentityField::Riot as u64, 1 << 3); + assert_eq!(IdentityField::Email as u64, 1 << 4); + assert_eq!(IdentityField::PgpFingerprint as u64, 1 << 5); + assert_eq!(IdentityField::Image as u64, 1 << 6); + assert_eq!(IdentityField::Twitter as u64, 1 << 7); + + let fields = IdentityField::Legal | + IdentityField::Web | + IdentityField::Riot | + IdentityField::PgpFingerprint | + IdentityField::Twitter; + + assert!(!fields.contains(IdentityField::Display)); + assert!(fields.contains(IdentityField::Legal)); + assert!(fields.contains(IdentityField::Web)); + assert!(fields.contains(IdentityField::Riot)); + assert!(!fields.contains(IdentityField::Email)); + assert!(fields.contains(IdentityField::PgpFingerprint)); + assert!(!fields.contains(IdentityField::Image)); + assert!(fields.contains(IdentityField::Twitter)); + + // Ensure that the `u64` representation matches what we expect. assert_eq!( - fields.0.bits(), + fields.bits(), 0b00000000_00000000_00000000_00000000_00000000_00000000_00000000_10101110 ); } @@ -190,18 +193,23 @@ fn editing_subaccounts_should_work() { Error::::NoIdentity ); - assert_ok!(Identity::set_identity(RuntimeOrigin::signed(10), Box::new(ten()))); + let ten = ten(); + assert_ok!(Identity::set_identity(RuntimeOrigin::signed(10), Box::new(ten.clone()))); + let id_deposit = id_deposit(&ten); + assert_eq!(Balances::free_balance(10), 1000 - id_deposit); + + let sub_deposit: u64 = <::SubAccountDeposit as Get>::get(); // first sub account assert_ok!(Identity::add_sub(RuntimeOrigin::signed(10), 1, data(1))); assert_eq!(SuperOf::::get(1), Some((10, data(1)))); - assert_eq!(Balances::free_balance(10), 80); + assert_eq!(Balances::free_balance(10), 1000 - id_deposit - sub_deposit); // second sub account assert_ok!(Identity::add_sub(RuntimeOrigin::signed(10), 2, data(2))); assert_eq!(SuperOf::::get(1), Some((10, data(1)))); assert_eq!(SuperOf::::get(2), Some((10, data(2)))); - assert_eq!(Balances::free_balance(10), 70); + assert_eq!(Balances::free_balance(10), 1000 - id_deposit - 2 * sub_deposit); // third sub account is too many assert_noop!( @@ -213,20 +221,20 @@ fn editing_subaccounts_should_work() { assert_ok!(Identity::rename_sub(RuntimeOrigin::signed(10), 1, data(11))); assert_eq!(SuperOf::::get(1), Some((10, data(11)))); assert_eq!(SuperOf::::get(2), Some((10, data(2)))); - assert_eq!(Balances::free_balance(10), 70); + assert_eq!(Balances::free_balance(10), 1000 - id_deposit - 2 * sub_deposit); // remove first sub account assert_ok!(Identity::remove_sub(RuntimeOrigin::signed(10), 1)); assert_eq!(SuperOf::::get(1), None); assert_eq!(SuperOf::::get(2), Some((10, data(2)))); - assert_eq!(Balances::free_balance(10), 80); + assert_eq!(Balances::free_balance(10), 1000 - id_deposit - sub_deposit); // add third sub account assert_ok!(Identity::add_sub(RuntimeOrigin::signed(10), 3, data(3))); assert_eq!(SuperOf::::get(1), None); assert_eq!(SuperOf::::get(2), Some((10, data(2)))); assert_eq!(SuperOf::::get(3), Some((10, data(3)))); - assert_eq!(Balances::free_balance(10), 70); + assert_eq!(Balances::free_balance(10), 1000 - id_deposit - 2 * sub_deposit); }); } @@ -234,15 +242,22 @@ fn editing_subaccounts_should_work() { fn resolving_subaccount_ownership_works() { new_test_ext().execute_with(|| { let data = |x| Data::Raw(vec![x; 1].try_into().unwrap()); + let sub_deposit: u64 = <::SubAccountDeposit as Get>::get(); - assert_ok!(Identity::set_identity(RuntimeOrigin::signed(10), Box::new(ten()))); - assert_ok!(Identity::set_identity(RuntimeOrigin::signed(20), Box::new(twenty()))); + let ten = ten(); + let ten_deposit = id_deposit(&ten); + let twenty = twenty(); + let twenty_deposit = id_deposit(&twenty); + assert_ok!(Identity::set_identity(RuntimeOrigin::signed(10), Box::new(ten))); + assert_eq!(Balances::free_balance(10), 1000 - ten_deposit); + assert_ok!(Identity::set_identity(RuntimeOrigin::signed(20), Box::new(twenty))); + assert_eq!(Balances::free_balance(20), 1000 - twenty_deposit); // 10 claims 1 as a subaccount assert_ok!(Identity::add_sub(RuntimeOrigin::signed(10), 1, data(1))); - assert_eq!(Balances::free_balance(1), 10); - assert_eq!(Balances::free_balance(10), 80); - assert_eq!(Balances::reserved_balance(10), 20); + assert_eq!(Balances::free_balance(1), 100); + assert_eq!(Balances::free_balance(10), 1000 - ten_deposit - sub_deposit); + assert_eq!(Balances::reserved_balance(10), ten_deposit + sub_deposit); // 20 cannot claim 1 now assert_noop!( Identity::add_sub(RuntimeOrigin::signed(20), 1, data(1)), @@ -251,9 +266,9 @@ fn resolving_subaccount_ownership_works() { // 1 wants to be with 20 so it quits from 10 assert_ok!(Identity::quit_sub(RuntimeOrigin::signed(1))); // 1 gets the 10 that 10 paid. - assert_eq!(Balances::free_balance(1), 20); - assert_eq!(Balances::free_balance(10), 80); - assert_eq!(Balances::reserved_balance(10), 10); + assert_eq!(Balances::free_balance(1), 100 + sub_deposit); + assert_eq!(Balances::free_balance(10), 1000 - ten_deposit - sub_deposit); + assert_eq!(Balances::reserved_balance(10), ten_deposit); // 20 can claim 1 now assert_ok!(Identity::add_sub(RuntimeOrigin::signed(20), 1, data(1))); }); @@ -269,16 +284,29 @@ fn trailing_zeros_decodes_into_default_data() { assert_eq!(b, Data::None); } +#[test] +fn adding_registrar_invalid_index() { + new_test_ext().execute_with(|| { + assert_ok!(Identity::add_registrar(RuntimeOrigin::signed(1), 3)); + assert_ok!(Identity::set_fee(RuntimeOrigin::signed(3), 0, 10)); + let fields = IdentityField::Display | IdentityField::Legal; + assert_noop!( + Identity::set_fields(RuntimeOrigin::signed(3), 100, fields.bits()), + Error::::InvalidIndex + ); + }); +} + #[test] fn adding_registrar_should_work() { new_test_ext().execute_with(|| { assert_ok!(Identity::add_registrar(RuntimeOrigin::signed(1), 3)); assert_ok!(Identity::set_fee(RuntimeOrigin::signed(3), 0, 10)); - let fields = IdentityFields(SimpleIdentityField::Display | SimpleIdentityField::Legal); - assert_ok!(Identity::set_fields(RuntimeOrigin::signed(3), 0, fields)); + let fields = IdentityField::Display | IdentityField::Legal; + assert_ok!(Identity::set_fields(RuntimeOrigin::signed(3), 0, fields.bits())); assert_eq!( Identity::registrars(), - vec![Some(RegistrarInfo { account: 3, fee: 10, fields })] + vec![Some(RegistrarInfo { account: 3, fee: 10, fields: fields.bits() })] ); }); } @@ -306,11 +334,13 @@ fn registration_should_work() { three_fields.additional.try_push(Default::default()).unwrap(); three_fields.additional.try_push(Default::default()).unwrap(); assert!(three_fields.additional.try_push(Default::default()).is_err()); - assert_ok!(Identity::set_identity(RuntimeOrigin::signed(10), Box::new(ten()))); - assert_eq!(Identity::identity(10).unwrap().info, ten()); - assert_eq!(Balances::free_balance(10), 90); + let ten = ten(); + let id_deposit = id_deposit(&ten); + assert_ok!(Identity::set_identity(RuntimeOrigin::signed(10), Box::new(ten.clone()))); + assert_eq!(Identity::identity(10).unwrap().info, ten); + assert_eq!(Balances::free_balance(10), 1000 - id_deposit); assert_ok!(Identity::clear_identity(RuntimeOrigin::signed(10))); - assert_eq!(Balances::free_balance(10), 100); + assert_eq!(Balances::free_balance(10), 1000); assert_noop!(Identity::clear_identity(RuntimeOrigin::signed(10)), Error::::NotNamed); }); } @@ -407,11 +437,13 @@ fn clearing_judgement_should_work() { #[test] fn killing_slashing_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Identity::set_identity(RuntimeOrigin::signed(10), Box::new(ten()))); + let ten = ten(); + let id_deposit = id_deposit(&ten); + assert_ok!(Identity::set_identity(RuntimeOrigin::signed(10), Box::new(ten))); assert_noop!(Identity::kill_identity(RuntimeOrigin::signed(1), 10), BadOrigin); assert_ok!(Identity::kill_identity(RuntimeOrigin::signed(2), 10)); assert_eq!(Identity::identity(10), None); - assert_eq!(Balances::free_balance(10), 90); + assert_eq!(Balances::free_balance(10), 1000 - id_deposit); assert_noop!( Identity::kill_identity(RuntimeOrigin::signed(2), 10), Error::::NotNamed @@ -422,38 +454,43 @@ fn killing_slashing_should_work() { #[test] fn setting_subaccounts_should_work() { new_test_ext().execute_with(|| { + let ten = ten(); + let id_deposit = id_deposit(&ten); + let sub_deposit: u64 = <::SubAccountDeposit as Get>::get(); let mut subs = vec![(20, Data::Raw(vec![40; 1].try_into().unwrap()))]; assert_noop!( Identity::set_subs(RuntimeOrigin::signed(10), subs.clone()), Error::::NotFound ); - assert_ok!(Identity::set_identity(RuntimeOrigin::signed(10), Box::new(ten()))); + assert_ok!(Identity::set_identity(RuntimeOrigin::signed(10), Box::new(ten))); + assert_eq!(Balances::free_balance(10), 1000 - id_deposit); assert_ok!(Identity::set_subs(RuntimeOrigin::signed(10), subs.clone())); - assert_eq!(Balances::free_balance(10), 80); - assert_eq!(Identity::subs_of(10), (10, vec![20].try_into().unwrap())); + assert_eq!(Balances::free_balance(10), 1000 - id_deposit - sub_deposit); + assert_eq!(Identity::subs_of(10), (sub_deposit, vec![20].try_into().unwrap())); assert_eq!(Identity::super_of(20), Some((10, Data::Raw(vec![40; 1].try_into().unwrap())))); // push another item and re-set it. subs.push((30, Data::Raw(vec![50; 1].try_into().unwrap()))); assert_ok!(Identity::set_subs(RuntimeOrigin::signed(10), subs.clone())); - assert_eq!(Balances::free_balance(10), 70); - assert_eq!(Identity::subs_of(10), (20, vec![20, 30].try_into().unwrap())); + assert_eq!(Balances::free_balance(10), 1000 - id_deposit - 2 * sub_deposit); + assert_eq!(Identity::subs_of(10), (2 * sub_deposit, vec![20, 30].try_into().unwrap())); assert_eq!(Identity::super_of(20), Some((10, Data::Raw(vec![40; 1].try_into().unwrap())))); assert_eq!(Identity::super_of(30), Some((10, Data::Raw(vec![50; 1].try_into().unwrap())))); // switch out one of the items and re-set. subs[0] = (40, Data::Raw(vec![60; 1].try_into().unwrap())); assert_ok!(Identity::set_subs(RuntimeOrigin::signed(10), subs.clone())); - assert_eq!(Balances::free_balance(10), 70); // no change in the balance - assert_eq!(Identity::subs_of(10), (20, vec![40, 30].try_into().unwrap())); + // no change in the balance + assert_eq!(Balances::free_balance(10), 1000 - id_deposit - 2 * sub_deposit); + assert_eq!(Identity::subs_of(10), (2 * sub_deposit, vec![40, 30].try_into().unwrap())); assert_eq!(Identity::super_of(20), None); assert_eq!(Identity::super_of(30), Some((10, Data::Raw(vec![50; 1].try_into().unwrap())))); assert_eq!(Identity::super_of(40), Some((10, Data::Raw(vec![60; 1].try_into().unwrap())))); // clear assert_ok!(Identity::set_subs(RuntimeOrigin::signed(10), vec![])); - assert_eq!(Balances::free_balance(10), 90); + assert_eq!(Balances::free_balance(10), 1000 - id_deposit); assert_eq!(Identity::subs_of(10), (0, BoundedVec::default())); assert_eq!(Identity::super_of(30), None); assert_eq!(Identity::super_of(40), None); @@ -469,13 +506,15 @@ fn setting_subaccounts_should_work() { #[test] fn clearing_account_should_remove_subaccounts_and_refund() { new_test_ext().execute_with(|| { - assert_ok!(Identity::set_identity(RuntimeOrigin::signed(10), Box::new(ten()))); + let ten = ten(); + assert_ok!(Identity::set_identity(RuntimeOrigin::signed(10), Box::new(ten.clone()))); + assert_eq!(Balances::free_balance(10), 1000 - id_deposit(&ten)); assert_ok!(Identity::set_subs( RuntimeOrigin::signed(10), vec![(20, Data::Raw(vec![40; 1].try_into().unwrap()))] )); assert_ok!(Identity::clear_identity(RuntimeOrigin::signed(10))); - assert_eq!(Balances::free_balance(10), 100); + assert_eq!(Balances::free_balance(10), 1000); assert!(Identity::super_of(20).is_none()); }); } @@ -483,13 +522,18 @@ fn clearing_account_should_remove_subaccounts_and_refund() { #[test] fn killing_account_should_remove_subaccounts_and_not_refund() { new_test_ext().execute_with(|| { - assert_ok!(Identity::set_identity(RuntimeOrigin::signed(10), Box::new(ten()))); + let ten = ten(); + let id_deposit = id_deposit(&ten); + let sub_deposit: u64 = <::SubAccountDeposit as Get>::get(); + assert_ok!(Identity::set_identity(RuntimeOrigin::signed(10), Box::new(ten))); + assert_eq!(Balances::free_balance(10), 1000 - id_deposit); assert_ok!(Identity::set_subs( RuntimeOrigin::signed(10), vec![(20, Data::Raw(vec![40; 1].try_into().unwrap()))] )); + assert_eq!(Balances::free_balance(10), 1000 - id_deposit - sub_deposit); assert_ok!(Identity::kill_identity(RuntimeOrigin::signed(2), 10)); - assert_eq!(Balances::free_balance(10), 80); + assert_eq!(Balances::free_balance(10), 1000 - id_deposit - sub_deposit); assert!(Identity::super_of(20).is_none()); }); } @@ -503,10 +547,12 @@ fn cancelling_requested_judgement_should_work() { Identity::cancel_request(RuntimeOrigin::signed(10), 0), Error::::NoIdentity ); - assert_ok!(Identity::set_identity(RuntimeOrigin::signed(10), Box::new(ten()))); + let ten = ten(); + assert_ok!(Identity::set_identity(RuntimeOrigin::signed(10), Box::new(ten.clone()))); + assert_eq!(Balances::free_balance(10), 1000 - id_deposit(&ten)); assert_ok!(Identity::request_judgement(RuntimeOrigin::signed(10), 0, 10)); assert_ok!(Identity::cancel_request(RuntimeOrigin::signed(10), 0)); - assert_eq!(Balances::free_balance(10), 90); + assert_eq!(Balances::free_balance(10), 1000 - id_deposit(&ten)); assert_noop!( Identity::cancel_request(RuntimeOrigin::signed(10), 0), Error::::NotFound @@ -517,7 +563,7 @@ fn cancelling_requested_judgement_should_work() { 0, 10, Judgement::Reasonable, - BlakeTwo256::hash_of(&ten()) + BlakeTwo256::hash_of(&ten) )); assert_noop!( Identity::cancel_request(RuntimeOrigin::signed(10), 0), @@ -531,14 +577,17 @@ fn requesting_judgement_should_work() { new_test_ext().execute_with(|| { assert_ok!(Identity::add_registrar(RuntimeOrigin::signed(1), 3)); assert_ok!(Identity::set_fee(RuntimeOrigin::signed(3), 0, 10)); - assert_ok!(Identity::set_identity(RuntimeOrigin::signed(10), Box::new(ten()))); + let ten = ten(); + let id_deposit = id_deposit(&ten); + assert_ok!(Identity::set_identity(RuntimeOrigin::signed(10), Box::new(ten.clone()))); + assert_eq!(Balances::free_balance(10), 1000 - id_deposit); assert_noop!( Identity::request_judgement(RuntimeOrigin::signed(10), 0, 9), Error::::FeeChanged ); assert_ok!(Identity::request_judgement(RuntimeOrigin::signed(10), 0, 10)); - // 10 for the judgement request, 10 for the identity. - assert_eq!(Balances::free_balance(10), 80); + // 10 for the judgement request and the deposit for the identity. + assert_eq!(Balances::free_balance(10), 1000 - id_deposit - 10); // Re-requesting won't work as we already paid. assert_noop!( @@ -550,10 +599,11 @@ fn requesting_judgement_should_work() { 0, 10, Judgement::Erroneous, - BlakeTwo256::hash_of(&ten()) + BlakeTwo256::hash_of(&ten) )); // Registrar got their payment now. - assert_eq!(Balances::free_balance(3), 20); + // 100 initial balance and 10 for the judgement. + assert_eq!(Balances::free_balance(3), 100 + 10); // Re-requesting still won't work as it's erroneous. assert_noop!( @@ -571,7 +621,7 @@ fn requesting_judgement_should_work() { 0, 10, Judgement::OutOfDate, - BlakeTwo256::hash_of(&ten()) + BlakeTwo256::hash_of(&ten) )); assert_ok!(Identity::request_judgement(RuntimeOrigin::signed(10), 0, 10)); }); @@ -580,12 +630,14 @@ fn requesting_judgement_should_work() { #[test] fn provide_judgement_should_return_judgement_payment_failed_error() { new_test_ext().execute_with(|| { + let ten = ten(); + let id_deposit = id_deposit(&ten); assert_ok!(Identity::add_registrar(RuntimeOrigin::signed(1), 3)); assert_ok!(Identity::set_fee(RuntimeOrigin::signed(3), 0, 10)); - assert_ok!(Identity::set_identity(RuntimeOrigin::signed(10), Box::new(ten()))); + assert_ok!(Identity::set_identity(RuntimeOrigin::signed(10), Box::new(ten.clone()))); assert_ok!(Identity::request_judgement(RuntimeOrigin::signed(10), 0, 10)); - // 10 for the judgement request, 10 for the identity. - assert_eq!(Balances::free_balance(10), 80); + // 10 for the judgement request and the deposit for the identity. + assert_eq!(Balances::free_balance(10), 1000 - id_deposit - 10); // This forces judgement payment failed error Balances::make_free_balance_be(&3, 0); @@ -595,7 +647,7 @@ fn provide_judgement_should_return_judgement_payment_failed_error() { 0, 10, Judgement::Erroneous, - BlakeTwo256::hash_of(&ten()) + BlakeTwo256::hash_of(&ten) ), Error::::JudgementPaymentFailed ); @@ -607,25 +659,24 @@ fn field_deposit_should_work() { new_test_ext().execute_with(|| { assert_ok!(Identity::add_registrar(RuntimeOrigin::signed(1), 3)); assert_ok!(Identity::set_fee(RuntimeOrigin::signed(3), 0, 10)); - assert_ok!(Identity::set_identity( - RuntimeOrigin::signed(10), - Box::new(IdentityInfo { - additional: vec![ - ( - Data::Raw(b"number".to_vec().try_into().unwrap()), - Data::Raw(10u32.encode().try_into().unwrap()) - ), - ( - Data::Raw(b"text".to_vec().try_into().unwrap()), - Data::Raw(b"10".to_vec().try_into().unwrap()) - ), - ] - .try_into() - .unwrap(), - ..Default::default() - }) - )); - assert_eq!(Balances::free_balance(10), 70); + let id = IdentityInfo { + additional: vec![ + ( + Data::Raw(b"number".to_vec().try_into().unwrap()), + Data::Raw(10u32.encode().try_into().unwrap()), + ), + ( + Data::Raw(b"text".to_vec().try_into().unwrap()), + Data::Raw(b"10".to_vec().try_into().unwrap()), + ), + ] + .try_into() + .unwrap(), + ..Default::default() + }; + let id_deposit = id_deposit(&id); + assert_ok!(Identity::set_identity(RuntimeOrigin::signed(10), Box::new(id))); + assert_eq!(Balances::free_balance(10), 1000 - id_deposit); }); } @@ -649,17 +700,15 @@ fn setting_account_id_should_work() { fn test_has_identity() { new_test_ext().execute_with(|| { assert_ok!(Identity::set_identity(RuntimeOrigin::signed(10), Box::new(ten()))); - assert!(Identity::has_identity(&10, SimpleIdentityField::Display as u64)); - assert!(Identity::has_identity(&10, SimpleIdentityField::Legal as u64)); + assert!(Identity::has_identity(&10, IdentityField::Display as u64)); + assert!(Identity::has_identity(&10, IdentityField::Legal as u64)); assert!(Identity::has_identity( &10, - SimpleIdentityField::Display as u64 | SimpleIdentityField::Legal as u64 + IdentityField::Display as u64 | IdentityField::Legal as u64 )); assert!(!Identity::has_identity( &10, - SimpleIdentityField::Display as u64 | - SimpleIdentityField::Legal as u64 | - SimpleIdentityField::Web as u64 + IdentityField::Display as u64 | IdentityField::Legal as u64 | IdentityField::Web as u64 )); }); } diff --git a/substrate/frame/identity/src/types.rs b/substrate/frame/identity/src/types.rs index 7055f6d80cfb..d3e6bf3973f0 100644 --- a/substrate/frame/identity/src/types.rs +++ b/substrate/frame/identity/src/types.rs @@ -17,23 +17,23 @@ use super::*; use codec::{Decode, Encode, MaxEncodedLen}; -use enumflags2::{BitFlag, BitFlags, _internal::RawBitFlags}; use frame_support::{ traits::{ConstU32, Get}, BoundedVec, CloneNoBound, PartialEqNoBound, RuntimeDebugNoBound, }; use scale_info::{ build::{Fields, Variants}, - meta_type, Path, Type, TypeInfo, TypeParameter, + Path, Type, TypeInfo, +}; +use sp_runtime::{ + traits::{Member, Zero}, + RuntimeDebug, }; -use sp_runtime::{traits::Zero, RuntimeDebug}; use sp_std::{fmt::Debug, iter::once, ops::Add, prelude::*}; /// An identifier for a single name registrar/identity verification service. pub type RegistrarIndex = u32; -pub trait U64BitFlag: BitFlag + RawBitFlags {} - /// Either underlying data blob if it is at most 32 bytes, or a hash of it. If the data is greater /// than 32-bytes then it will be truncated when encoding. /// @@ -234,24 +234,19 @@ impl bool; - - /// Interface for providing the number of additional fields this identity information provider - /// holds, used to charge for additional storage and weight. This interface is present for - /// backwards compatibility reasons only and will be removed as soon as the reference identity - /// provider removes additional fields. - #[deprecated] - fn additional(&self) -> usize { - 0 - } + fn has_identity(&self, fields: Self::FieldsIdentifier) -> bool; + /// Create a basic instance of the identity information. #[cfg(feature = "runtime-benchmarks")] - fn create_identity_info(num_fields: u32) -> Self; + fn create_identity_info() -> Self; + + /// The identity information representation for all identity fields enabled. + #[cfg(feature = "runtime-benchmarks")] + fn all_fields() -> Self::FieldsIdentifier; } /// Information on an identity along with judgements from registrars. @@ -262,7 +257,7 @@ pub trait IdentityInformationProvider: CloneNoBound, Encode, Eq, MaxEncodedLen, PartialEqNoBound, RuntimeDebugNoBound, TypeInfo, )] #[codec(mel_bound())] -#[scale_info(skip_type_params(MaxJudgements, MaxAdditionalFields))] +#[scale_info(skip_type_params(MaxJudgements))] pub struct Registration< Balance: Encode + Decode + MaxEncodedLen + Copy + Clone + Debug + Eq + PartialEq, MaxJudgements: Get, @@ -311,7 +306,7 @@ impl< pub struct RegistrarInfo< Balance: Encode + Decode + Clone + Debug + Eq + PartialEq, AccountId: Encode + Decode + Clone + Debug + Eq + PartialEq, - IdField: Clone + Debug + Eq + PartialEq + TypeInfo + U64BitFlag, + IdField: Encode + Decode + Clone + Debug + Default + Eq + PartialEq + TypeInfo + MaxEncodedLen, > { /// The account of the registrar. pub account: AccountId, @@ -321,52 +316,7 @@ pub struct RegistrarInfo< /// Relevant fields for this registrar. Registrar judgements are limited to attestations on /// these fields. - pub fields: IdentityFields, -} - -/// Wrapper type for `BitFlags` that implements `Codec`. -#[derive(Clone, Copy, PartialEq, RuntimeDebug)] -pub struct IdentityFields(pub BitFlags); - -impl Default for IdentityFields { - fn default() -> Self { - Self(Default::default()) - } -} - -impl MaxEncodedLen for IdentityFields -where - IdentityFields: Encode, -{ - fn max_encoded_len() -> usize { - u64::max_encoded_len() - } -} - -impl Eq for IdentityFields {} -impl Encode for IdentityFields { - fn using_encoded R>(&self, f: F) -> R { - let bits: u64 = self.0.bits(); - bits.using_encoded(f) - } -} -impl Decode for IdentityFields { - fn decode(input: &mut I) -> sp_std::result::Result { - let field = u64::decode(input)?; - Ok(Self(>::from_bits(field).map_err(|_| "invalid value")?)) - } -} -impl TypeInfo - for IdentityFields -{ - type Identity = Self; - - fn type_info() -> Type { - Type::builder() - .path(Path::new("BitFlags", module_path!())) - .type_params(vec![TypeParameter::new("T", Some(meta_type::()))]) - .composite(Fields::unnamed().field(|f| f.ty::().type_name("IdentityField"))) - } + pub fields: IdField, } #[cfg(test)] diff --git a/substrate/frame/identity/src/weights.rs b/substrate/frame/identity/src/weights.rs index 02fcd7db3c95..95898e6c6cdf 100644 --- a/substrate/frame/identity/src/weights.rs +++ b/substrate/frame/identity/src/weights.rs @@ -53,17 +53,17 @@ use core::marker::PhantomData; /// Weight functions needed for pallet_identity. pub trait WeightInfo { fn add_registrar(r: u32, ) -> Weight; - fn set_identity(r: u32, x: u32, ) -> Weight; + fn set_identity(r: u32, ) -> Weight; fn set_subs_new(s: u32, ) -> Weight; fn set_subs_old(p: u32, ) -> Weight; - fn clear_identity(r: u32, s: u32, x: u32, ) -> Weight; - fn request_judgement(r: u32, x: u32, ) -> Weight; - fn cancel_request(r: u32, x: u32, ) -> Weight; + fn clear_identity(r: u32, s: u32, ) -> Weight; + fn request_judgement(r: u32, ) -> Weight; + fn cancel_request(r: u32, ) -> Weight; fn set_fee(r: u32, ) -> Weight; fn set_account_id(r: u32, ) -> Weight; fn set_fields(r: u32, ) -> Weight; - fn provide_judgement(r: u32, x: u32, ) -> Weight; - fn kill_identity(r: u32, s: u32, x: u32, ) -> Weight; + fn provide_judgement(r: u32, ) -> Weight; + fn kill_identity(r: u32, s: u32, ) -> Weight; fn add_sub(s: u32, ) -> Weight; fn rename_sub(s: u32, ) -> Weight; fn remove_sub(s: u32, ) -> Weight; @@ -90,8 +90,7 @@ impl WeightInfo for SubstrateWeight { /// Storage: Identity IdentityOf (r:1 w:1) /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) /// The range of component `r` is `[1, 20]`. - /// The range of component `x` is `[0, 100]`. - fn set_identity(r: u32, x: u32, ) -> Weight { + fn set_identity(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `442 + r * (5 Β±0)` // Estimated: `11003` @@ -99,8 +98,6 @@ impl WeightInfo for SubstrateWeight { Weight::from_parts(31_329_634, 11003) // Standard Error: 4_496 .saturating_add(Weight::from_parts(203_570, 0).saturating_mul(r.into())) - // Standard Error: 877 - .saturating_add(Weight::from_parts(429_346, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -152,8 +149,7 @@ impl WeightInfo for SubstrateWeight { /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) /// The range of component `r` is `[1, 20]`. /// The range of component `s` is `[0, 100]`. - /// The range of component `x` is `[0, 100]`. - fn clear_identity(r: u32, s: u32, x: u32, ) -> Weight { + fn clear_identity(r: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `469 + r * (5 Β±0) + s * (32 Β±0) + x * (66 Β±0)` // Estimated: `11003` @@ -163,8 +159,6 @@ impl WeightInfo for SubstrateWeight { .saturating_add(Weight::from_parts(162_357, 0).saturating_mul(r.into())) // Standard Error: 1_937 .saturating_add(Weight::from_parts(1_427_998, 0).saturating_mul(s.into())) - // Standard Error: 1_937 - .saturating_add(Weight::from_parts(247_578, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -174,8 +168,7 @@ impl WeightInfo for SubstrateWeight { /// Storage: Identity IdentityOf (r:1 w:1) /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) /// The range of component `r` is `[1, 20]`. - /// The range of component `x` is `[0, 100]`. - fn request_judgement(r: u32, x: u32, ) -> Weight { + fn request_judgement(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `367 + r * (57 Β±0) + x * (66 Β±0)` // Estimated: `11003` @@ -183,16 +176,13 @@ impl WeightInfo for SubstrateWeight { Weight::from_parts(32_207_018, 11003) // Standard Error: 5_247 .saturating_add(Weight::from_parts(249_156, 0).saturating_mul(r.into())) - // Standard Error: 1_023 - .saturating_add(Weight::from_parts(458_329, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: Identity IdentityOf (r:1 w:1) /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) /// The range of component `r` is `[1, 20]`. - /// The range of component `x` is `[0, 100]`. - fn cancel_request(r: u32, x: u32, ) -> Weight { + fn cancel_request(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `398 + x * (66 Β±0)` // Estimated: `11003` @@ -200,8 +190,6 @@ impl WeightInfo for SubstrateWeight { Weight::from_parts(31_967_170, 11003) // Standard Error: 5_387 .saturating_add(Weight::from_parts(42_676, 0).saturating_mul(r.into())) - // Standard Error: 1_051 - .saturating_add(Weight::from_parts(446_213, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -252,8 +240,7 @@ impl WeightInfo for SubstrateWeight { /// Storage: Identity IdentityOf (r:1 w:1) /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) /// The range of component `r` is `[1, 19]`. - /// The range of component `x` is `[0, 100]`. - fn provide_judgement(r: u32, x: u32, ) -> Weight { + fn provide_judgement(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `445 + r * (57 Β±0) + x * (66 Β±0)` // Estimated: `11003` @@ -261,8 +248,6 @@ impl WeightInfo for SubstrateWeight { Weight::from_parts(17_817_684, 11003) // Standard Error: 8_612 .saturating_add(Weight::from_parts(406_251, 0).saturating_mul(r.into())) - // Standard Error: 1_593 - .saturating_add(Weight::from_parts(755_225, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -276,8 +261,7 @@ impl WeightInfo for SubstrateWeight { /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) /// The range of component `r` is `[1, 20]`. /// The range of component `s` is `[0, 100]`. - /// The range of component `x` is `[0, 100]`. - fn kill_identity(r: u32, s: u32, x: u32, ) -> Weight { + fn kill_identity(r: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `676 + r * (5 Β±0) + s * (32 Β±0) + x * (66 Β±0)` // Estimated: `11003` @@ -287,8 +271,6 @@ impl WeightInfo for SubstrateWeight { .saturating_add(Weight::from_parts(145_285, 0).saturating_mul(r.into())) // Standard Error: 2_472 .saturating_add(Weight::from_parts(1_421_039, 0).saturating_mul(s.into())) - // Standard Error: 2_472 - .saturating_add(Weight::from_parts(240_907, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -384,8 +366,7 @@ impl WeightInfo for () { /// Storage: Identity IdentityOf (r:1 w:1) /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) /// The range of component `r` is `[1, 20]`. - /// The range of component `x` is `[0, 100]`. - fn set_identity(r: u32, x: u32, ) -> Weight { + fn set_identity(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `442 + r * (5 Β±0)` // Estimated: `11003` @@ -393,8 +374,6 @@ impl WeightInfo for () { Weight::from_parts(31_329_634, 11003) // Standard Error: 4_496 .saturating_add(Weight::from_parts(203_570, 0).saturating_mul(r.into())) - // Standard Error: 877 - .saturating_add(Weight::from_parts(429_346, 0).saturating_mul(x.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -446,8 +425,7 @@ impl WeightInfo for () { /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) /// The range of component `r` is `[1, 20]`. /// The range of component `s` is `[0, 100]`. - /// The range of component `x` is `[0, 100]`. - fn clear_identity(r: u32, s: u32, x: u32, ) -> Weight { + fn clear_identity(r: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `469 + r * (5 Β±0) + s * (32 Β±0) + x * (66 Β±0)` // Estimated: `11003` @@ -457,8 +435,6 @@ impl WeightInfo for () { .saturating_add(Weight::from_parts(162_357, 0).saturating_mul(r.into())) // Standard Error: 1_937 .saturating_add(Weight::from_parts(1_427_998, 0).saturating_mul(s.into())) - // Standard Error: 1_937 - .saturating_add(Weight::from_parts(247_578, 0).saturating_mul(x.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -468,8 +444,7 @@ impl WeightInfo for () { /// Storage: Identity IdentityOf (r:1 w:1) /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) /// The range of component `r` is `[1, 20]`. - /// The range of component `x` is `[0, 100]`. - fn request_judgement(r: u32, x: u32, ) -> Weight { + fn request_judgement(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `367 + r * (57 Β±0) + x * (66 Β±0)` // Estimated: `11003` @@ -477,16 +452,13 @@ impl WeightInfo for () { Weight::from_parts(32_207_018, 11003) // Standard Error: 5_247 .saturating_add(Weight::from_parts(249_156, 0).saturating_mul(r.into())) - // Standard Error: 1_023 - .saturating_add(Weight::from_parts(458_329, 0).saturating_mul(x.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: Identity IdentityOf (r:1 w:1) /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) /// The range of component `r` is `[1, 20]`. - /// The range of component `x` is `[0, 100]`. - fn cancel_request(r: u32, x: u32, ) -> Weight { + fn cancel_request(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `398 + x * (66 Β±0)` // Estimated: `11003` @@ -494,8 +466,6 @@ impl WeightInfo for () { Weight::from_parts(31_967_170, 11003) // Standard Error: 5_387 .saturating_add(Weight::from_parts(42_676, 0).saturating_mul(r.into())) - // Standard Error: 1_051 - .saturating_add(Weight::from_parts(446_213, 0).saturating_mul(x.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -546,8 +516,7 @@ impl WeightInfo for () { /// Storage: Identity IdentityOf (r:1 w:1) /// Proof: Identity IdentityOf (max_values: None, max_size: Some(7538), added: 10013, mode: MaxEncodedLen) /// The range of component `r` is `[1, 19]`. - /// The range of component `x` is `[0, 100]`. - fn provide_judgement(r: u32, x: u32, ) -> Weight { + fn provide_judgement(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `445 + r * (57 Β±0) + x * (66 Β±0)` // Estimated: `11003` @@ -555,8 +524,6 @@ impl WeightInfo for () { Weight::from_parts(17_817_684, 11003) // Standard Error: 8_612 .saturating_add(Weight::from_parts(406_251, 0).saturating_mul(r.into())) - // Standard Error: 1_593 - .saturating_add(Weight::from_parts(755_225, 0).saturating_mul(x.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -570,8 +537,7 @@ impl WeightInfo for () { /// Proof: Identity SuperOf (max_values: None, max_size: Some(114), added: 2589, mode: MaxEncodedLen) /// The range of component `r` is `[1, 20]`. /// The range of component `s` is `[0, 100]`. - /// The range of component `x` is `[0, 100]`. - fn kill_identity(r: u32, s: u32, x: u32, ) -> Weight { + fn kill_identity(r: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `676 + r * (5 Β±0) + s * (32 Β±0) + x * (66 Β±0)` // Estimated: `11003` @@ -581,8 +547,6 @@ impl WeightInfo for () { .saturating_add(Weight::from_parts(145_285, 0).saturating_mul(r.into())) // Standard Error: 2_472 .saturating_add(Weight::from_parts(1_421_039, 0).saturating_mul(s.into())) - // Standard Error: 2_472 - .saturating_add(Weight::from_parts(240_907, 0).saturating_mul(x.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(s.into()))) From 1c0b437330f971d898fc75f0ed148998e9e60625 Mon Sep 17 00:00:00 2001 From: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Date: Sat, 4 Nov 2023 10:25:07 +0100 Subject: [PATCH 12/31] cumulus test runtime: remove `GenesisExt` (#2147) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR removes the `GenesisExt` wrapper over the `GenesisRuntimeConfig` in `cumulus-test-service`. Initialization of values that were performed by `GenesisExt::BuildStorage` was moved into `test_pallet` genesis. --------- Co-authored-by: command-bot <> Co-authored-by: Bastian KΓΆcher --- cumulus/test/client/src/lib.rs | 3 +- cumulus/test/runtime/src/lib.rs | 10 +++--- cumulus/test/runtime/src/test_pallet.rs | 23 ++++++++++++ cumulus/test/service/src/chain_spec.rs | 36 ++++--------------- .../zombienet/tests/0002-pov_recovery.toml | 2 +- .../tests/0005-migrate_solo_to_para.toml | 2 +- 6 files changed, 39 insertions(+), 37 deletions(-) diff --git a/cumulus/test/client/src/lib.rs b/cumulus/test/client/src/lib.rs index 61249bdb066a..635455d50335 100644 --- a/cumulus/test/client/src/lib.rs +++ b/cumulus/test/client/src/lib.rs @@ -90,6 +90,7 @@ impl substrate_test_client::GenesisInit for GenesisParameters { cumulus_test_service::testnet_genesis( cumulus_test_service::get_account_id_from_seed::("Alice"), self.endowed_accounts.clone(), + None, ) .build_storage() .unwrap() @@ -127,7 +128,7 @@ impl DefaultTestClientBuilderExt for TestClientBuilder { } fn genesis_config() -> RuntimeGenesisConfig { - cumulus_test_service::testnet_genesis_with_default_endowed(Default::default()) + cumulus_test_service::testnet_genesis_with_default_endowed(Default::default(), None) } /// Create an unsigned extrinsic from a runtime call. diff --git a/cumulus/test/runtime/src/lib.rs b/cumulus/test/runtime/src/lib.rs index 85a1caa0ce35..381cc64cef82 100644 --- a/cumulus/test/runtime/src/lib.rs +++ b/cumulus/test/runtime/src/lib.rs @@ -76,10 +76,6 @@ impl_opaque_keys! { pub struct SessionKeys {} } -/// Some key that we set in genesis and only read in [`TestOnRuntimeUpgrade`] to ensure that -/// [`OnRuntimeUpgrade`] works as expected. -pub const TEST_RUNTIME_UPGRADE_KEY: &[u8] = b"+test_runtime_upgrade_key+"; - /// The para-id used in this runtime. pub const PARACHAIN_ID: u32 = 100; @@ -293,6 +289,7 @@ impl cumulus_pallet_parachain_system::Config for Runtime { } parameter_types! { + // will be set by test_pallet during genesis init pub storage ParachainId: cumulus_primitives_core::ParaId = PARACHAIN_ID.into(); } @@ -367,7 +364,10 @@ pub struct TestOnRuntimeUpgrade; impl OnRuntimeUpgrade for TestOnRuntimeUpgrade { fn on_runtime_upgrade() -> frame_support::weights::Weight { - assert_eq!(sp_io::storage::get(TEST_RUNTIME_UPGRADE_KEY), Some(vec![1, 2, 3, 4].into())); + assert_eq!( + sp_io::storage::get(test_pallet::TEST_RUNTIME_UPGRADE_KEY), + Some(vec![1, 2, 3, 4].into()) + ); Weight::from_parts(1, 0) } } diff --git a/cumulus/test/runtime/src/test_pallet.rs b/cumulus/test/runtime/src/test_pallet.rs index 0af23797dad6..a0763c9069cd 100644 --- a/cumulus/test/runtime/src/test_pallet.rs +++ b/cumulus/test/runtime/src/test_pallet.rs @@ -17,8 +17,13 @@ /// A special pallet that exposes dispatchables that are only useful for testing. pub use pallet::*; +/// Some key that we set in genesis and only read in [`TestOnRuntimeUpgrade`] to ensure that +/// [`OnRuntimeUpgrade`] works as expected. +pub const TEST_RUNTIME_UPGRADE_KEY: &[u8] = b"+test_runtime_upgrade_key+"; + #[frame_support::pallet(dev_mode)] pub mod pallet { + use crate::test_pallet::TEST_RUNTIME_UPGRADE_KEY; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; @@ -45,4 +50,22 @@ pub mod pallet { Ok(()) } } + + #[derive(frame_support::DefaultNoBound)] + #[pallet::genesis_config] + pub struct GenesisConfig { + pub self_para_id: Option, + #[serde(skip)] + pub _config: sp_std::marker::PhantomData, + } + + #[pallet::genesis_build] + impl BuildGenesisConfig for GenesisConfig { + fn build(&self) { + sp_io::storage::set(TEST_RUNTIME_UPGRADE_KEY, &[1, 2, 3, 4]); + self.self_para_id.map(|para_id| { + crate::ParachainId::set(¶_id); + }); + } + } } diff --git a/cumulus/test/service/src/chain_spec.rs b/cumulus/test/service/src/chain_spec.rs index 7c8c984c2b20..c7dac7082fe8 100644 --- a/cumulus/test/service/src/chain_spec.rs +++ b/cumulus/test/service/src/chain_spec.rs @@ -17,7 +17,7 @@ #![allow(missing_docs)] use cumulus_primitives_core::ParaId; -use cumulus_test_runtime::{AccountId, Signature}; +use cumulus_test_runtime::{AccountId, RuntimeGenesisConfig, Signature}; use sc_chain_spec::{ChainSpecExtension, ChainSpecGroup}; use sc_service::ChainType; use serde::{Deserialize, Serialize}; @@ -25,27 +25,7 @@ use sp_core::{sr25519, Pair, Public}; use sp_runtime::traits::{IdentifyAccount, Verify}; /// Specialized `ChainSpec` for the normal parachain runtime. -pub type ChainSpec = sc_service::GenericChainSpec; - -/// Extension for the genesis config to add custom keys easily. -#[derive(serde::Serialize, serde::Deserialize)] -pub struct GenesisExt { - /// The runtime genesis config. - runtime_genesis_config: cumulus_test_runtime::RuntimeGenesisConfig, - /// The parachain id. - para_id: ParaId, -} - -impl sp_runtime::BuildStorage for GenesisExt { - fn assimilate_storage(&self, storage: &mut sp_core::storage::Storage) -> Result<(), String> { - sp_state_machine::BasicExternalities::execute_with_storage(storage, || { - sp_io::storage::set(cumulus_test_runtime::TEST_RUNTIME_UPGRADE_KEY, &[1, 2, 3, 4]); - cumulus_test_runtime::ParachainId::set(&self.para_id); - }); - - self.runtime_genesis_config.assimilate_storage(storage) - } -} +pub type ChainSpec = sc_service::GenericChainSpec; /// Helper function to generate a crypto pair from seed pub fn get_from_seed(seed: &str) -> ::Public { @@ -90,12 +70,7 @@ pub fn get_chain_spec_with_extra_endowed( "Local Testnet", "local_testnet", ChainType::Local, - move || GenesisExt { - runtime_genesis_config: testnet_genesis_with_default_endowed( - extra_endowed_accounts.clone(), - ), - para_id: id, - }, + move || testnet_genesis_with_default_endowed(extra_endowed_accounts.clone(), Some(id)), Vec::new(), None, None, @@ -113,6 +88,7 @@ pub fn get_chain_spec(id: ParaId) -> ChainSpec { /// Local testnet genesis for testing. pub fn testnet_genesis_with_default_endowed( mut extra_endowed_accounts: Vec, + self_para_id: Option, ) -> cumulus_test_runtime::RuntimeGenesisConfig { let mut endowed = vec![ get_account_id_from_seed::("Alice"), @@ -130,13 +106,14 @@ pub fn testnet_genesis_with_default_endowed( ]; endowed.append(&mut extra_endowed_accounts); - testnet_genesis(get_account_id_from_seed::("Alice"), endowed) + testnet_genesis(get_account_id_from_seed::("Alice"), endowed, self_para_id) } /// Creates a local testnet genesis with endowed accounts. pub fn testnet_genesis( root_key: AccountId, endowed_accounts: Vec, + self_para_id: Option, ) -> cumulus_test_runtime::RuntimeGenesisConfig { cumulus_test_runtime::RuntimeGenesisConfig { system: cumulus_test_runtime::SystemConfig { @@ -152,5 +129,6 @@ pub fn testnet_genesis( }, sudo: cumulus_test_runtime::SudoConfig { key: Some(root_key) }, transaction_payment: Default::default(), + test_pallet: cumulus_test_runtime::TestPalletConfig { self_para_id, ..Default::default() }, } } diff --git a/cumulus/zombienet/tests/0002-pov_recovery.toml b/cumulus/zombienet/tests/0002-pov_recovery.toml index 7c74a74a7502..a7c1fbfd4413 100644 --- a/cumulus/zombienet/tests/0002-pov_recovery.toml +++ b/cumulus/zombienet/tests/0002-pov_recovery.toml @@ -4,7 +4,7 @@ default_command = "polkadot" chain = "rococo-local" -[relaychain.genesis.runtime.runtime_genesis_config.configuration.config] +[relaychain.genesis.runtime.configuration.config] # set parameters such that collators only connect to 1 validator as a backing group max_validators_per_core = 1 group_rotation_frequency = 100 # 10 mins diff --git a/cumulus/zombienet/tests/0005-migrate_solo_to_para.toml b/cumulus/zombienet/tests/0005-migrate_solo_to_para.toml index f98c0e6f2592..a500b51f794c 100644 --- a/cumulus/zombienet/tests/0005-migrate_solo_to_para.toml +++ b/cumulus/zombienet/tests/0005-migrate_solo_to_para.toml @@ -32,7 +32,7 @@ cumulus_based = true add_to_genesis = false register_para = false # Set some random value in the genesis state to create a different genesis hash. -[parachains.genesis.runtime.runtime_genesis_config.sudo] +[parachains.genesis.runtime.sudo] key = "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty" # run the parachain that will be used to return the header of the solo chain. From 8d4ae36276af3f8131f37542ef860a64c5d8cc37 Mon Sep 17 00:00:00 2001 From: Ankan <10196091+Ank4n@users.noreply.github.com> Date: Sat, 4 Nov 2023 16:41:40 +0100 Subject: [PATCH 13/31] Speed up try runtime checks for pallet-bags-list (#2151) closes https://github.com/paritytech/polkadot-sdk/issues/2020. This improves running time for pallet-bags-list try runtime checks on westend from ~90 minutes to 6 seconds on M2 pro. --- substrate/frame/bags-list/src/list/mod.rs | 27 ++++++++++++++--------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/substrate/frame/bags-list/src/list/mod.rs b/substrate/frame/bags-list/src/list/mod.rs index d8626080e252..e90530341a15 100644 --- a/substrate/frame/bags-list/src/list/mod.rs +++ b/substrate/frame/bags-list/src/list/mod.rs @@ -543,7 +543,16 @@ impl, I: 'static> List { thresholds.into_iter().filter_map(|t| Bag::::get(t)) }; - let _ = active_bags.clone().try_for_each(|b| b.do_try_state())?; + // build map of bags and the corresponding nodes to avoid multiple lookups + let mut bags_map = BTreeMap::>::new(); + + let _ = active_bags.clone().try_for_each(|b| { + bags_map.insert( + b.bag_upper, + b.iter().map(|n: Node| n.id().clone()).collect::>(), + ); + b.do_try_state() + })?; let nodes_in_bags_count = active_bags.clone().fold(0u32, |acc, cur| acc + cur.iter().count() as u32); @@ -554,6 +563,13 @@ impl, I: 'static> List { // check that all nodes are sane. We check the `ListNodes` storage item directly in case we // have some "stale" nodes that are not in a bag. for (_id, node) in crate::ListNodes::::iter() { + // check that the node is in the correct bag + let expected_bag = bags_map + .get(&node.bag_upper) + .ok_or("bag not found for the node in active bags")?; + frame_support::ensure!(expected_bag.contains(node.id()), "node not found in the bag"); + + // verify node state node.do_try_state()? } @@ -790,12 +806,6 @@ impl, I: 'static> Bag { pub fn std_iter(&self) -> impl Iterator> { sp_std::iter::successors(self.head(), |prev| prev.next()) } - - /// Check if the bag contains a node with `id`. - #[cfg(any(test, feature = "try-runtime", feature = "fuzz"))] - fn contains(&self, id: &T::AccountId) -> bool { - self.iter().any(|n| n.id() == id) - } } /// A Node is the fundamental element comprising the doubly-linked list described by `Bag`. @@ -900,11 +910,8 @@ impl, I: 'static> Node { #[cfg(any(test, feature = "try-runtime", feature = "fuzz"))] fn do_try_state(&self) -> Result<(), TryRuntimeError> { let expected_bag = Bag::::get(self.bag_upper).ok_or("bag not found for node")?; - let id = self.id(); - frame_support::ensure!(expected_bag.contains(id), "node does not exist in the bag"); - let non_terminal_check = !self.is_terminal() && expected_bag.head.as_ref() != Some(id) && expected_bag.tail.as_ref() != Some(id); From f84b89710b0e6da655a1b40a491a8184780489e7 Mon Sep 17 00:00:00 2001 From: Ankan <10196091+Ank4n@users.noreply.github.com> Date: Sat, 4 Nov 2023 16:41:51 +0100 Subject: [PATCH 14/31] Speed up nominator state checks in staking pallet (#2153) Should help https://github.com/paritytech/polkadot-sdk/issues/234. Related to https://github.com/paritytech/polkadot-sdk/issues/2020 and https://github.com/paritytech/polkadot-sdk/issues/2108. Refactors and improves running time for try runtime checks for staking pallet. Tested on westend on my M2 pro: running time drops from 90 seconds to 7 seconds. --- substrate/frame/staking/src/pallet/impls.rs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index bb16fe56d51e..9c36c94b87b4 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -1864,6 +1864,13 @@ impl Pallet { // a check per nominator to ensure their entire stake is correctly distributed. Will only // kick-in if the nomination was submitted before the current era. let era = Self::active_era().unwrap().index; + + // cache era exposures to avoid too many db reads. + let era_exposures = T::SessionInterface::validators() + .iter() + .map(|v| Self::eras_stakers(era, v)) + .collect::>(); + >::iter() .filter_map( |(nominator, nomination)| { @@ -1878,9 +1885,8 @@ impl Pallet { // must be bonded. Self::ensure_is_stash(&nominator)?; let mut sum = BalanceOf::::zero(); - T::SessionInterface::validators() + era_exposures .iter() - .map(|v| Self::eras_stakers(era, v)) .map(|e| -> Result<(), TryRuntimeError> { let individual = e.others.iter().filter(|e| e.who == nominator).collect::>(); @@ -1896,6 +1902,14 @@ impl Pallet { Ok(()) }) .collect::, _>>()?; + + // We take total instead of active as the nominator might have requested to unbond + // some of their stake that is still exposed in the current era. + if sum <= Self::ledger(Stash(nominator.clone()))?.total { + // This can happen when there is a slash in the current era so we only warn. + log!(warn, "nominator stake exceeds what is bonded."); + } + Ok(()) }) .collect::, _>>()?; From 0c39cf049e47f7f167d6bb4137d1f97afc3aebae Mon Sep 17 00:00:00 2001 From: Bulat Saifullin Date: Sun, 5 Nov 2023 16:36:42 +0400 Subject: [PATCH 15/31] Update bootnode lists (#2150) # Description Update the bootnode of kusama parachains before decommissioning the nodes. This will avoid connecting to non-existing bootnodes. --- .../chain-specs/asset-hub-kusama.json | 4 +--- .../chain-specs/asset-hub-polkadot.json | 8 +------- .../chain-specs/asset-hub-rococo.json | 4 +--- .../chain-specs/asset-hub-westend.json | 4 +--- .../chain-specs/bridge-hub-polkadot.json | 6 +----- .../chain-specs/bridge-hub-rococo.json | 4 +--- .../chain-specs/bridge-hub-westend.json | 4 +--- .../chain-specs/collectives-polkadot.json | 4 ---- .../chain-specs/collectives-westend.json | 6 +----- .../chain-specs/contracts-rococo.json | 6 +----- polkadot/node/service/chain-specs/rococo.json | 20 +------------------ .../node/service/chain-specs/westend.json | 6 ------ 12 files changed, 10 insertions(+), 66 deletions(-) diff --git a/cumulus/parachains/chain-specs/asset-hub-kusama.json b/cumulus/parachains/chain-specs/asset-hub-kusama.json index 91a821efab9c..519811f5ec8c 100644 --- a/cumulus/parachains/chain-specs/asset-hub-kusama.json +++ b/cumulus/parachains/chain-specs/asset-hub-kusama.json @@ -3,8 +3,6 @@ "id": "asset-hub-kusama", "chainType": "Live", "bootNodes": [ - "/ip4/34.77.217.152/tcp/30334/p2p/12D3KooWF63ZxKtZMYs5247WQA8fcTiGJb2osXykc31cmjwNLwem", - "/ip4/34.77.119.77/tcp/30334/p2p/12D3KooWGowDwrXAh9cxkbPHPHuwMouFHrMcJhCVXcFS2B8vc5Ry", "/dns/kusama-asset-hub-connect-0.polkadot.io/tcp/30334/p2p/12D3KooWMzvdGcUXxacLdMQzRVrsP1mJrZHcrz8LtGbhLzve84Qx", "/dns/kusama-asset-hub-connect-0.polkadot.io/tcp/443/wss/p2p/12D3KooWMzvdGcUXxacLdMQzRVrsP1mJrZHcrz8LtGbhLzve84Qx", "/dns/kusama-asset-hub-connect-1.polkadot.io/tcp/30334/p2p/12D3KooWQmGf5z3DU1kKcZoLzMNgdbP31ybjuwxS1VGLKMUjq5ez", @@ -53,4 +51,4 @@ "childrenDefault": {} } } -} \ No newline at end of file +} diff --git a/cumulus/parachains/chain-specs/asset-hub-polkadot.json b/cumulus/parachains/chain-specs/asset-hub-polkadot.json index 46f3e0e4a957..bbf90693ca3a 100644 --- a/cumulus/parachains/chain-specs/asset-hub-polkadot.json +++ b/cumulus/parachains/chain-specs/asset-hub-polkadot.json @@ -3,16 +3,10 @@ "id": "asset-hub-polkadot", "chainType": "Live", "bootNodes": [ - "/ip4/34.65.251.121/tcp/30334/p2p/12D3KooWG3GrM6XKMM4gp3cvemdwUvu96ziYoJmqmetLZBXE8bSa", - "/ip4/34.65.35.228/tcp/30334/p2p/12D3KooWMRyTLrCEPcAQD6c4EnudL3vVzg9zji3whvsMYPUYevpq", "/dns/polkadot-asset-hub-connect-0.polkadot.io/tcp/30334/p2p/12D3KooWLHqbcQtoBygf7GJgVjVa3TaeLuf7VbicNdooaCmQM2JZ", "/dns/polkadot-asset-hub-connect-0.polkadot.io/tcp/443/wss/p2p/12D3KooWLHqbcQtoBygf7GJgVjVa3TaeLuf7VbicNdooaCmQM2JZ", "/dns/polkadot-asset-hub-connect-1.polkadot.io/tcp/30334/p2p/12D3KooWNDrKSayoZXGGE2dRSFW2g1iGPq3fTZE2U39ma9yZGKd3", "/dns/polkadot-asset-hub-connect-1.polkadot.io/tcp/443/wss/p2p/12D3KooWNDrKSayoZXGGE2dRSFW2g1iGPq3fTZE2U39ma9yZGKd3", - "/dns/polkadot-asset-hub-connect-2.polkadot.io/tcp/30334/p2p/12D3KooWApa2JW4rbLtgzuK7fjLMupLS9HZheX9cdkQKyu6AnGrP", - "/dns/polkadot-asset-hub-connect-2.polkadot.io/tcp/443/wss/p2p/12D3KooWApa2JW4rbLtgzuK7fjLMupLS9HZheX9cdkQKyu6AnGrP", - "/dns/polkadot-asset-hub-connect-3.polkadot.io/tcp/30334/p2p/12D3KooWRsVeHqRs2iKmjLiguxp8myL4G2mDAWhtX2jHwyWujseV", - "/dns/polkadot-asset-hub-connect-3.polkadot.io/tcp/443/wss/p2p/12D3KooWRsVeHqRs2iKmjLiguxp8myL4G2mDAWhtX2jHwyWujseV", "/dns/boot.stake.plus/tcp/35333/p2p/12D3KooWFrQjYaPZSSLLxEVmoaHFcrF6VoY4awG4KRSLaqy3JCdQ", "/dns/boot.stake.plus/tcp/35334/wss/p2p/12D3KooWFrQjYaPZSSLLxEVmoaHFcrF6VoY4awG4KRSLaqy3JCdQ", "/dns/boot.metaspan.io/tcp/16052/p2p/12D3KooWLwiJuvqQUB4kYaSjLenFKH9dWZhGZ4qi7pSb3sUYU651", @@ -59,4 +53,4 @@ "childrenDefault": {} } } -} \ No newline at end of file +} diff --git a/cumulus/parachains/chain-specs/asset-hub-rococo.json b/cumulus/parachains/chain-specs/asset-hub-rococo.json index 064a2dfc0db8..900d9f0ffb2c 100644 --- a/cumulus/parachains/chain-specs/asset-hub-rococo.json +++ b/cumulus/parachains/chain-specs/asset-hub-rococo.json @@ -4,9 +4,7 @@ "chainType": "Live", "bootNodes": [ "/dns/rococo-asset-hub-bootnode-0.polkadot.io/tcp/30333/p2p/12D3KooWRrZMndHAopzao34uGsN7srjS3gh9nAjTGKLSyJeU31Lg", - "/dns/rococo-asset-hub-bootnode-1.polkadot.io/tcp/30333/p2p/12D3KooWAewimoNJqMaiiV5pYiowA5hLuh5JS5QiRJCCyWVrrSTS", - "/dns/rococo-asset-hub-bootnode-2.polkadot.io/tcp/30333/p2p/12D3KooWA3cVSDJFrN5HEYbt11cK2W7zJbiPHxR2joJXcgqzVt8K", - "/dns/rococo-asset-hub-bootnode-3.polkadot.io/tcp/30333/p2p/12D3KooWPf3MtBZKJ3G6wYyvCTxFCi9vgzxDdHbjJJRCrFu3FgJb" + "/dns/rococo-asset-hub-bootnode-1.polkadot.io/tcp/30333/p2p/12D3KooWAewimoNJqMaiiV5pYiowA5hLuh5JS5QiRJCCyWVrrSTS" ], "telemetryEndpoints": null, "protocolId": null, diff --git a/cumulus/parachains/chain-specs/asset-hub-westend.json b/cumulus/parachains/chain-specs/asset-hub-westend.json index d168803e1c3f..f6cdccee10a2 100644 --- a/cumulus/parachains/chain-specs/asset-hub-westend.json +++ b/cumulus/parachains/chain-specs/asset-hub-westend.json @@ -5,8 +5,6 @@ "bootNodes": [ "/dns/westend-asset-hub-bootnode-0.polkadot.io/tcp/30333/p2p/12D3KooWJaAfPyiye7ZQBuHengTJJoMrcaz7Jj1UzHiKdNxA1Nkd", "/dns/westend-asset-hub-bootnode-1.polkadot.io/tcp/30333/p2p/12D3KooWGL3hpWycWyeqyL9gHNnmmsL474WkPZdqraBHu4L6fQrW", - "/dns/westend-asset-hub-bootnode-2.polkadot.io/tcp/30333/p2p/12D3KooWBkKDWhHzu6Hhe492adEpVV7wzuaWGxUfEnr6g5JCr7Gr", - "/dns/westend-asset-hub-bootnode-3.polkadot.io/tcp/30333/p2p/12D3KooWMGpzCmhD6np6eKqxL7AAunKn1dN86Dr7a9E2xgZ2rt6G", "/dns/boot.stake.plus/tcp/33333/p2p/12D3KooWNiB27rpXX7EYongoWWUeRKzLQxWGms6MQU2B9LX7Ztzo", "/dns/boot.stake.plus/tcp/33334/wss/p2p/12D3KooWNiB27rpXX7EYongoWWUeRKzLQxWGms6MQU2B9LX7Ztzo", "/dns/boot.metaspan.io/tcp/36052/p2p/12D3KooWBCqfNb6Y39DXTr4UBWXyjuS3hcZM1qTbHhDXxF6HkAJJ", @@ -50,4 +48,4 @@ "childrenDefault": {} } } -} \ No newline at end of file +} diff --git a/cumulus/parachains/chain-specs/bridge-hub-polkadot.json b/cumulus/parachains/chain-specs/bridge-hub-polkadot.json index fd56b61115d5..e44a4dacd48b 100644 --- a/cumulus/parachains/chain-specs/bridge-hub-polkadot.json +++ b/cumulus/parachains/chain-specs/bridge-hub-polkadot.json @@ -5,12 +5,8 @@ "bootNodes": [ "/dns/polkadot-bridge-hub-connect-a-0.polkadot.io/tcp/30334/p2p/12D3KooWAVQMhkXmc5ueSYasdsRWQbKus2YGZ6HDZUB4ViJMCxXy", "/dns/polkadot-bridge-hub-connect-a-1.polkadot.io/tcp/30334/p2p/12D3KooWG4ypDHLKGCv4BZ6PuaGUwQHKAH6p2D6arR2uQ1eiR1T3", - "/dns/polkadot-bridge-hub-connect-b-0.polkadot.io/tcp/30334/p2p/12D3KooWCwGKxjpJXnx1mwXKvaxGQm769EM3b6Pg5vbU33wbhsNw", - "/dns/polkadot-bridge-hub-connect-b-1.polkadot.io/tcp/30334/p2p/12D3KooWLiSEdhriJUPdZKFtAjZrQncxN2ssEoDKVrt5mGM4Qu4J", "/dns/polkadot-bridge-hub-connect-a-0.polkadot.io/tcp/443/wss/p2p/12D3KooWAVQMhkXmc5ueSYasdsRWQbKus2YGZ6HDZUB4ViJMCxXy", "/dns/polkadot-bridge-hub-connect-a-1.polkadot.io/tcp/443/wss/p2p/12D3KooWG4ypDHLKGCv4BZ6PuaGUwQHKAH6p2D6arR2uQ1eiR1T3", - "/dns/polkadot-bridge-hub-connect-b-0.polkadot.io/tcp/443/wss/p2p/12D3KooWCwGKxjpJXnx1mwXKvaxGQm769EM3b6Pg5vbU33wbhsNw", - "/dns/polkadot-bridge-hub-connect-b-1.polkadot.io/tcp/443/wss/p2p/12D3KooWLiSEdhriJUPdZKFtAjZrQncxN2ssEoDKVrt5mGM4Qu4J", "/dns/polkadot-bridge-hub-boot-ng.dwellir.com/tcp/30339/p2p/12D3KooWPZ38PL3PhRVcUVYDNn7nRcZF8MykmWWLBKeDV2yna1vV", "/dns/polkadot-bridge-hub-boot-ng.dwellir.com/tcp/443/wss/p2p/12D3KooWPZ38PL3PhRVcUVYDNn7nRcZF8MykmWWLBKeDV2yna1vV", "/dns/boot-cr.gatotech.network/tcp/33130/p2p/12D3KooWCnFzfEdd7MwUNrrDv66FuS2DM5MGuiaB4y48XS7qNjF6", @@ -87,4 +83,4 @@ "childrenDefault": {} } } -} \ No newline at end of file +} diff --git a/cumulus/parachains/chain-specs/bridge-hub-rococo.json b/cumulus/parachains/chain-specs/bridge-hub-rococo.json index ff20d8fb4825..6b430678a86c 100644 --- a/cumulus/parachains/chain-specs/bridge-hub-rococo.json +++ b/cumulus/parachains/chain-specs/bridge-hub-rococo.json @@ -4,9 +4,7 @@ "chainType": "Live", "bootNodes": [ "/dns/rococo-bridge-hub-collator-node-0.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWJCFBJmFF65xz5xHeZQRSCf35BxfSEB3RHQFoLza28LWU", - "/dns/rococo-bridge-hub-collator-node-1.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWJzLd8skcAgA24EcJey7aJAhYctfUxWGjSP5Usk9wbpPZ", - "/dns/rococo-bridge-hub-collator-node-2.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWPZLWbbDJzEXAHPuAcVssPrjQLyZK4nvvmV2ez6gy2FQ3", - "/dns/rococo-bridge-hub-collator-node-3.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWKWMENpCNH7wBVQoHLwQoWUs6acAEmfdV694v9jCuJwYc" + "/dns/rococo-bridge-hub-collator-node-1.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWJzLd8skcAgA24EcJey7aJAhYctfUxWGjSP5Usk9wbpPZ" ], "telemetryEndpoints": null, "protocolId": null, diff --git a/cumulus/parachains/chain-specs/bridge-hub-westend.json b/cumulus/parachains/chain-specs/bridge-hub-westend.json index fbed89729c79..0a441b3a295b 100644 --- a/cumulus/parachains/chain-specs/bridge-hub-westend.json +++ b/cumulus/parachains/chain-specs/bridge-hub-westend.json @@ -5,8 +5,6 @@ "bootNodes": [ "/dns/westend-bridge-hub-collator-node-0.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWKyEuqkkWvFSrwZWKWBAsHgLV3HGfHj7yH3LNJLAVhmxY", "/dns/westend-bridge-hub-collator-node-1.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWBpvudthz61XC4oP2YYFFJdhWohBeQ1ffn1BMSGWhapjd", - "/dns/westend-bridge-hub-collator-node-2.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWPXqdRRthjKAMPFtaXUK7yBxsvh83QsmzXzALA3inoJfo", - "/dns/westend-bridge-hub-collator-node-3.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWAp2YpVaiNBy7rozEHJGocDpaLFt3VFZsGMBEYh4BoEz7", "/dns/westend-bridge-hub-boot-ng.dwellir.com/tcp/30338/p2p/12D3KooWJWWRYTAwBLqYkh7iMBGDr5ouJ3MHj7M3fZ7zWS4zEk6F", "/dns/westend-bridge-hub-boot-ng.dwellir.com/tcp/443/wss/p2p/12D3KooWJWWRYTAwBLqYkh7iMBGDr5ouJ3MHj7M3fZ7zWS4zEk6F", "/dns/boot-cr.gatotech.network/tcp/33330/p2p/12D3KooWJHG6qznPzTSEbuujHNcvyzBZcR9zNRPFcXWUaoVWZBEw", @@ -82,4 +80,4 @@ "childrenDefault": {} } } -} \ No newline at end of file +} diff --git a/cumulus/parachains/chain-specs/collectives-polkadot.json b/cumulus/parachains/chain-specs/collectives-polkadot.json index e17958f1f683..974be3388579 100644 --- a/cumulus/parachains/chain-specs/collectives-polkadot.json +++ b/cumulus/parachains/chain-specs/collectives-polkadot.json @@ -5,12 +5,8 @@ "bootNodes": [ "/dns/polkadot-collectives-connect-ew6-0.polkadot.io/tcp/30334/p2p/12D3KooWLDZT5gAjMtC8fojiCwiz17SC61oeX2C7GWBCqqf9TwVD", "/dns/polkadot-collectives-connect-ew6-1.polkadot.io/tcp/30334/p2p/12D3KooWC9BwKMDyRUTXsE7teSmoKMgbyxqAp3zi2MTGRJR5nhCL", - "/dns/polkadot-collectives-connect-uw1-0.polkadot.io/tcp/30334/p2p/12D3KooWPrJ9VTn3GEs2e7GQs4zoEFiTFcjXFNbQ2iDxFDQAbstQ", - "/dns/polkadot-collectives-connect-uw1-1.polkadot.io/tcp/30334/p2p/12D3KooWGFYW6hJYn3pkpJawyMk6souXh7sznK8yvPmVQ7ktfHbV", "/dns/polkadot-collectives-connect-ew6-0.polkadot.io/tcp/443/wss/p2p/12D3KooWLDZT5gAjMtC8fojiCwiz17SC61oeX2C7GWBCqqf9TwVD", "/dns/polkadot-collectives-connect-ew6-1.polkadot.io/tcp/443/wss/p2p/12D3KooWC9BwKMDyRUTXsE7teSmoKMgbyxqAp3zi2MTGRJR5nhCL", - "/dns/polkadot-collectives-connect-uw1-0.polkadot.io/tcp/443/wss/p2p/12D3KooWPrJ9VTn3GEs2e7GQs4zoEFiTFcjXFNbQ2iDxFDQAbstQ", - "/dns/polkadot-collectives-connect-uw1-1.polkadot.io/tcp/443/wss/p2p/12D3KooWGFYW6hJYn3pkpJawyMk6souXh7sznK8yvPmVQ7ktfHbV", "/dns/boot.stake.plus/tcp/37333/p2p/12D3KooWRgFfEtwPo3xorKGYALRHRteKNgF37iN9q8xTLPYc34LA", "/dns/boot.stake.plus/tcp/37334/wss/p2p/12D3KooWRgFfEtwPo3xorKGYALRHRteKNgF37iN9q8xTLPYc34LA", "/dns/boot.metaspan.io/tcp/16072/p2p/12D3KooWJWTTu2t2yg5bFRH6tjEpfzKwZir5R9JRRjQpgFPXdDfp", diff --git a/cumulus/parachains/chain-specs/collectives-westend.json b/cumulus/parachains/chain-specs/collectives-westend.json index a40512997daa..e06671faa04e 100644 --- a/cumulus/parachains/chain-specs/collectives-westend.json +++ b/cumulus/parachains/chain-specs/collectives-westend.json @@ -5,12 +5,8 @@ "bootNodes": [ "/dns/westend-collectives-collator-node-0.parity-testnet.parity.io/tcp/30334/p2p/12D3KooWBMAuyzQu3yAf8YXyoyxsSzSsgoaqAepgnNyQcPaPjPXe", "/dns/westend-collectives-collator-node-1.parity-testnet.parity.io/tcp/30334/p2p/12D3KooWAujYtHbCs4MiDD57JNTntTJnYnikfnaPa7JdnMyAUrHB", - "/dns/westend-collectives-collator-node-2.parity-testnet.parity.io/tcp/30334/p2p/12D3KooWP4pJg6pZUpxETd8Rs6GmS9FeRCeNtrBerqZhUyEPCiPp", - "/dns/westend-collectives-collator-node-3.parity-testnet.parity.io/tcp/30334/p2p/12D3KooWBbrBYhXxFXhdio3AiuaqMG26pn91SUnd12gJiVn2Wh8Q", "/dns/westend-collectives-collator-0.polkadot.io/tcp/443/wss/p2p/12D3KooWBMAuyzQu3yAf8YXyoyxsSzSsgoaqAepgnNyQcPaPjPXe", "/dns/westend-collectives-collator-1.polkadot.io/tcp/443/wss/p2p/12D3KooWAujYtHbCs4MiDD57JNTntTJnYnikfnaPa7JdnMyAUrHB", - "/dns/westend-collectives-collator-2.polkadot.io/tcp/443/wss/p2p/12D3KooWP4pJg6pZUpxETd8Rs6GmS9FeRCeNtrBerqZhUyEPCiPp", - "/dns/westend-collectives-collator-3.polkadot.io/tcp/443/wss/p2p/12D3KooWBbrBYhXxFXhdio3AiuaqMG26pn91SUnd12gJiVn2Wh8Q", "/dns/boot.stake.plus/tcp/38333/p2p/12D3KooWQoVsFCfgu21iu6kdtQsU9T6dPn1wsyLn1U34yPerR6zQ", "/dns/boot.stake.plus/tcp/38334/wss/p2p/12D3KooWQoVsFCfgu21iu6kdtQsU9T6dPn1wsyLn1U34yPerR6zQ", "/dns/boot.metaspan.io/tcp/36072/p2p/12D3KooWEf2QXWq5pAbFJLfbnexA7KYtRRDSPkqTP64n1KtdsdV2", @@ -107,4 +103,4 @@ "childrenDefault": {} } } -} \ No newline at end of file +} diff --git a/cumulus/parachains/chain-specs/contracts-rococo.json b/cumulus/parachains/chain-specs/contracts-rococo.json index 09108e9c0995..422268a5efdb 100644 --- a/cumulus/parachains/chain-specs/contracts-rococo.json +++ b/cumulus/parachains/chain-specs/contracts-rococo.json @@ -5,12 +5,8 @@ "bootNodes": [ "/dns/rococo-contracts-collator-node-0.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWKg3Rpxcr9oJ8n6khoxpGKWztCZydtUZk2cojHqnfLrpj", "/dns/rococo-contracts-collator-node-1.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWPEXYrz8tHU3nDtPoPw4V7ou5dzMEWSTuUj7vaWiYVAVh", - "/dns/rococo-contracts-collator-node-2.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWEVU8AFNary4nP4qEnEcwJaRuy59Wefekzdu9pKbnVEhk", - "/dns/rococo-contracts-collator-node-3.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWP6pV3ZmcXzGDjv8ZMgA6nZxfAKDxSz4VNiLx6vVCQgJX", "/dns/rococo-contracts-collator-node-0.polkadot.io/tcp/443/wss/p2p/12D3KooWKg3Rpxcr9oJ8n6khoxpGKWztCZydtUZk2cojHqnfLrpj", - "/dns/rococo-contracts-collator-node-1.polkadot.io/tcp/443/wss/p2p/12D3KooWPEXYrz8tHU3nDtPoPw4V7ou5dzMEWSTuUj7vaWiYVAVh", - "/dns/rococo-contracts-collator-node-2.polkadot.io/tcp/443/wss/p2p/12D3KooWEVU8AFNary4nP4qEnEcwJaRuy59Wefekzdu9pKbnVEhk", - "/dns/rococo-contracts-collator-node-3.polkadot.io/tcp/443/wss/p2p/12D3KooWP6pV3ZmcXzGDjv8ZMgA6nZxfAKDxSz4VNiLx6vVCQgJX" + "/dns/rococo-contracts-collator-node-1.polkadot.io/tcp/443/wss/p2p/12D3KooWPEXYrz8tHU3nDtPoPw4V7ou5dzMEWSTuUj7vaWiYVAVh" ], "telemetryEndpoints": null, diff --git a/polkadot/node/service/chain-specs/rococo.json b/polkadot/node/service/chain-specs/rococo.json index 2648063641c9..e16962981e19 100644 --- a/polkadot/node/service/chain-specs/rococo.json +++ b/polkadot/node/service/chain-specs/rococo.json @@ -5,28 +5,10 @@ "bootNodes": [ "/dns/rococo-bootnode-0.polkadot.io/tcp/30333/p2p/12D3KooWGikJMBmRiG5ofCqn8aijCijgfmZR5H9f53yUF3srm6Nm", "/dns/rococo-bootnode-1.polkadot.io/tcp/30333/p2p/12D3KooWLDfH9mHRCidrd5NfQjp7rRMUcJSEUwSvEKyu7xU2cG3d", - "/dns/rococo-bootnode-2.polkadot.io/tcp/30333/p2p/12D3KooWSikgbrcWjVgSed7r1uXk4TeAieDnHKtrPDVZBu5XkQha", - "/dns/rococo-bootnode-3.polkadot.io/tcp/30333/p2p/12D3KooWPeKuW1BBPv4pNr8xqEv7jqy7rQnS3oq9U7xTCvj9qt2k", - "/dns/rococo-bootnode-4.polkadot.io/tcp/30333/p2p/12D3KooWNy7K8TNaP2Whcp3tsjBVUg2HcKMUvAArsimjvd1g31w4", - "/dns/rococo-bootnode-5.polkadot.io/tcp/30333/p2p/12D3KooWAVV9DZfvJp2brvs5zcQDTBFxNmEFJKy2dsvezWL4Bmy8", - "/dns/rococo-bootnode-6.polkadot.io/tcp/30333/p2p/12D3KooWM3hvXvaShyp7drQCavFHuwobkYdnCp2uHU5iRRAQwsw2", - "/dns/rococo-bootnode-7.polkadot.io/tcp/30333/p2p/12D3KooWSbGtxfWCwn1tdmfZYESbmxzbTG2LKwKUrioDaZBcdMY4", "/dns/rococo-bootnode-0.polkadot.io/tcp/30334/ws/p2p/12D3KooWGikJMBmRiG5ofCqn8aijCijgfmZR5H9f53yUF3srm6Nm", "/dns/rococo-bootnode-1.polkadot.io/tcp/30334/ws/p2p/12D3KooWLDfH9mHRCidrd5NfQjp7rRMUcJSEUwSvEKyu7xU2cG3d", - "/dns/rococo-bootnode-2.polkadot.io/tcp/30334/ws/p2p/12D3KooWSikgbrcWjVgSed7r1uXk4TeAieDnHKtrPDVZBu5XkQha", - "/dns/rococo-bootnode-3.polkadot.io/tcp/30334/ws/p2p/12D3KooWPeKuW1BBPv4pNr8xqEv7jqy7rQnS3oq9U7xTCvj9qt2k", - "/dns/rococo-bootnode-4.polkadot.io/tcp/30334/ws/p2p/12D3KooWNy7K8TNaP2Whcp3tsjBVUg2HcKMUvAArsimjvd1g31w4", - "/dns/rococo-bootnode-5.polkadot.io/tcp/30334/ws/p2p/12D3KooWAVV9DZfvJp2brvs5zcQDTBFxNmEFJKy2dsvezWL4Bmy8", - "/dns/rococo-bootnode-6.polkadot.io/tcp/30334/ws/p2p/12D3KooWM3hvXvaShyp7drQCavFHuwobkYdnCp2uHU5iRRAQwsw2", - "/dns/rococo-bootnode-7.polkadot.io/tcp/30334/ws/p2p/12D3KooWSbGtxfWCwn1tdmfZYESbmxzbTG2LKwKUrioDaZBcdMY4", "/dns/rococo-bootnode-0.polkadot.io/tcp/443/wss/p2p/12D3KooWGikJMBmRiG5ofCqn8aijCijgfmZR5H9f53yUF3srm6Nm", - "/dns/rococo-bootnode-1.polkadot.io/tcp/443/wss/p2p/12D3KooWLDfH9mHRCidrd5NfQjp7rRMUcJSEUwSvEKyu7xU2cG3d", - "/dns/rococo-bootnode-2.polkadot.io/tcp/443/wss/p2p/12D3KooWSikgbrcWjVgSed7r1uXk4TeAieDnHKtrPDVZBu5XkQha", - "/dns/rococo-bootnode-3.polkadot.io/tcp/443/wss/p2p/12D3KooWPeKuW1BBPv4pNr8xqEv7jqy7rQnS3oq9U7xTCvj9qt2k", - "/dns/rococo-bootnode-4.polkadot.io/tcp/443/wss/p2p/12D3KooWNy7K8TNaP2Whcp3tsjBVUg2HcKMUvAArsimjvd1g31w4", - "/dns/rococo-bootnode-5.polkadot.io/tcp/443/wss/p2p/12D3KooWAVV9DZfvJp2brvs5zcQDTBFxNmEFJKy2dsvezWL4Bmy8", - "/dns/rococo-bootnode-6.polkadot.io/tcp/443/wss/p2p/12D3KooWM3hvXvaShyp7drQCavFHuwobkYdnCp2uHU5iRRAQwsw2", - "/dns/rococo-bootnode-7.polkadot.io/tcp/443/wss/p2p/12D3KooWSbGtxfWCwn1tdmfZYESbmxzbTG2LKwKUrioDaZBcdMY4" + "/dns/rococo-bootnode-1.polkadot.io/tcp/443/wss/p2p/12D3KooWLDfH9mHRCidrd5NfQjp7rRMUcJSEUwSvEKyu7xU2cG3d" ], "telemetryEndpoints": [ [ diff --git a/polkadot/node/service/chain-specs/westend.json b/polkadot/node/service/chain-specs/westend.json index fd1f4550127f..b2ffba9304bc 100644 --- a/polkadot/node/service/chain-specs/westend.json +++ b/polkadot/node/service/chain-specs/westend.json @@ -8,12 +8,6 @@ "/dns/westend-bootnode-1.polkadot.io/tcp/30333/p2p/12D3KooWPVPzs42GvRBShdUMtFsk4SvnByrSdWqb6aeAAHvLMSLS", "/dns/westend-bootnode-1.polkadot.io/tcp/30334/ws/p2p/12D3KooWPVPzs42GvRBShdUMtFsk4SvnByrSdWqb6aeAAHvLMSLS", "/dns/westend-bootnode-1.polkadot.io/tcp/443/wss/p2p/12D3KooWPVPzs42GvRBShdUMtFsk4SvnByrSdWqb6aeAAHvLMSLS", - "/dns/westend-bootnode-2.polkadot.io/tcp/30333/p2p/12D3KooWByVpK92hMi9CzTjyFg9cPHDU5ariTM3EPMq9vdh5S5Po", - "/dns/westend-bootnode-2.polkadot.io/tcp/30334/ws/p2p/12D3KooWByVpK92hMi9CzTjyFg9cPHDU5ariTM3EPMq9vdh5S5Po", - "/dns/westend-bootnode-2.polkadot.io/tcp/443/wss/p2p/12D3KooWByVpK92hMi9CzTjyFg9cPHDU5ariTM3EPMq9vdh5S5Po", - "/dns/westend-bootnode-3.polkadot.io/tcp/30333/p2p/12D3KooWGi1tCpKXLMYED9y28QXLnwgD4neYb1Arqq4QpeV1Sv3K", - "/dns/westend-bootnode-3.polkadot.io/tcp/30334/ws/p2p/12D3KooWGi1tCpKXLMYED9y28QXLnwgD4neYb1Arqq4QpeV1Sv3K", - "/dns/westend-bootnode-3.polkadot.io/tcp/443/wss/p2p/12D3KooWGi1tCpKXLMYED9y28QXLnwgD4neYb1Arqq4QpeV1Sv3K", "/dns/boot.stake.plus/tcp/32333/p2p/12D3KooWK8fjVoSvMq5copQYMsdYreSGPGgcMbGMgbMDPfpf3sm7", "/dns/boot.stake.plus/tcp/32334/wss/p2p/12D3KooWK8fjVoSvMq5copQYMsdYreSGPGgcMbGMgbMDPfpf3sm7", "/dns/boot-node.helikon.io/tcp/7080/p2p/12D3KooWRFDPyT8vA8mLzh6dJoyujn4QNjeqi6Ch79eSMz9beKXC", From c46a7dbb6126c368766a0e8a02fc04853c241643 Mon Sep 17 00:00:00 2001 From: s0me0ne-unkn0wn <48632512+s0me0ne-unkn0wn@users.noreply.github.com> Date: Sun, 5 Nov 2023 13:51:36 +0100 Subject: [PATCH 16/31] Tracking allocator: mark `Spinlock::unlock()` as unsafe and provide a safety contract (#2156) --- polkadot/node/tracking-allocator/src/lib.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/polkadot/node/tracking-allocator/src/lib.rs b/polkadot/node/tracking-allocator/src/lib.rs index 8441bd1ffafa..ab8597b5c382 100644 --- a/polkadot/node/tracking-allocator/src/lib.rs +++ b/polkadot/node/tracking-allocator/src/lib.rs @@ -72,8 +72,11 @@ impl Spinlock { } } + // SAFETY: It should be only called from the guard's destructor. Calling it explicitly while + // the guard is alive is undefined behavior, as it breaks the security contract of `Deref` and + // `DerefMut`, which implies that lock is held at the moment of dereferencing. #[inline] - fn unlock(&self) { + unsafe fn unlock(&self) { self.lock.store(false, Ordering::Release); } } @@ -97,7 +100,9 @@ impl DerefMut for SpinlockGuard<'_, T> { impl Drop for SpinlockGuard<'_, T> { fn drop(&mut self) { - self.lock.unlock(); + // SAFETY: Calling `unlock` is only safe when it's guaranteed no guard outlives the + // unlocking point; here, the guard is dropped, so it is safe. + unsafe { self.lock.unlock() } } } From 8ba7a6aba89fe7b2fd22a66f5b7c7b13e75f5e91 Mon Sep 17 00:00:00 2001 From: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Date: Sun, 5 Nov 2023 15:19:23 +0100 Subject: [PATCH 17/31] `chain-spec`: getting ready for native-runtime-free world (#1256) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR prepares chains specs for _native-runtime-free_ world. This PR has following changes: - `substrate`: - adds support for: - JSON based `GenesisConfig` to `ChainSpec` allowing interaction with runtime `GenesisBuilder` API. - interacting with arbitrary runtime wasm blob to[ `chain-spec-builder`](https://github.com/paritytech/substrate/blob/3ef576eaeb3f42610e85daecc464961cf1295570/bin/utils/chain-spec-builder/src/lib.rs#L46) command line util, - removes [`code`](https://github.com/paritytech/substrate/blob/3ef576eaeb3f42610e85daecc464961cf1295570/frame/system/src/lib.rs#L660) from `system_pallet` - adds `code` to the `ChainSpec` - deprecates [`ChainSpec::from_genesis`](https://github.com/paritytech/substrate/blob/3ef576eaeb3f42610e85daecc464961cf1295570/client/chain-spec/src/chain_spec.rs#L263), but also changes the signature of this method extending it with `code` argument. [`ChainSpec::builder()`](https://github.com/paritytech/substrate/blob/20bee680ed098be7239cf7a6b804cd4de267983e/client/chain-spec/src/chain_spec.rs#L507) should be used instead. - `polkadot`: - all references to `RuntimeGenesisConfig` in `node/service` are removed, - all `(kusama|polkadot|versi|rococo|wococo)_(staging|dev)_genesis_config` functions now return the JSON patch for default runtime `GenesisConfig`, - `ChainSpecBuilder` is used, `ChainSpec::from_genesis` is removed, - `cumulus`: - `ChainSpecBuilder` is used, `ChainSpec::from_genesis` is removed, - _JSON_ patch configuration used instead of `RuntimeGenesisConfig struct` in all chain specs. --------- Co-authored-by: command-bot <> Co-authored-by: Javier Viola Co-authored-by: Davide Galassi Co-authored-by: Francisco Aguirre Co-authored-by: Kevin Krone Co-authored-by: Bastian KΓΆcher --- .gitlab-ci.yml | 2 +- Cargo.lock | 29 + cumulus/parachain-template/node/Cargo.toml | 1 + .../parachain-template/node/src/chain_spec.rs | 221 ++-- .../emulated/common/Cargo.toml | 2 + .../emulated/common/src/constants.rs | 895 ++++++++++---- .../assets/asset-hub-rococo/Cargo.toml | 2 + .../assets/asset-hub-rococo/src/lib.rs | 11 + cumulus/polkadot-parachain/Cargo.toml | 1 + .../src/chain_spec/asset_hubs.rs | 1093 ++++++++--------- .../src/chain_spec/bridge_hubs.rs | 548 ++++----- .../src/chain_spec/collectives.rs | 181 ++- .../src/chain_spec/contracts.rs | 346 +++--- .../src/chain_spec/glutton.rs | 125 +- .../src/chain_spec/penpal.rs | 128 +- .../src/chain_spec/rococo_parachain.rs | 136 +- .../src/chain_spec/seedling.rs | 54 +- .../src/chain_spec/shell.rs | 47 +- cumulus/polkadot-parachain/src/command.rs | 39 +- cumulus/test/client/src/lib.rs | 27 +- cumulus/test/runtime/Cargo.toml | 2 + cumulus/test/runtime/src/lib.rs | 11 + cumulus/test/service/Cargo.toml | 1 + cumulus/test/service/src/chain_spec.rs | 49 +- cumulus/test/service/src/cli.rs | 5 +- cumulus/test/service/src/genesis.rs | 2 +- cumulus/test/service/src/lib.rs | 2 +- cumulus/test/service/src/main.rs | 2 +- .../zombienet/tests/0002-pov_recovery.toml | 2 +- .../tests/0005-migrate_solo_to_para.toml | 2 +- polkadot/node/service/src/chain_spec.rs | 536 +++----- polkadot/node/test/client/src/lib.rs | 2 +- polkadot/node/test/service/Cargo.toml | 1 - polkadot/node/test/service/src/chain_spec.rs | 83 +- polkadot/runtime/common/src/lib.rs | 5 +- .../runtime/parachains/src/configuration.rs | 1 + polkadot/runtime/rococo/Cargo.toml | 2 +- polkadot/runtime/rococo/constants/Cargo.toml | 3 + .../functional/0002-parachains-disputes.toml | 2 +- .../0004-parachains-garbage-candidate.toml | 2 +- ...0005-parachains-disputes-past-session.toml | 2 +- .../zombienet_tests/misc/0001-paritydb.toml | 2 +- prdoc/pr_1256.prdoc | 19 + substrate/Cargo.toml | 1 + substrate/bin/minimal/node/Cargo.toml | 1 + substrate/bin/minimal/node/src/chain_spec.rs | 41 +- substrate/bin/minimal/runtime/Cargo.toml | 4 + substrate/bin/minimal/runtime/src/lib.rs | 11 + substrate/bin/node-template/node/Cargo.toml | 1 + .../bin/node-template/node/src/chain_spec.rs | 161 +-- .../bin/node-template/runtime/Cargo.toml | 14 +- substrate/bin/node/cli/Cargo.toml | 1 - substrate/bin/node/cli/src/chain_spec.rs | 246 ++-- substrate/bin/node/executor/Cargo.toml | 7 +- substrate/bin/node/executor/benches/bench.rs | 2 +- substrate/bin/node/executor/tests/basic.rs | 16 + substrate/bin/node/executor/tests/common.rs | 2 +- .../tests/res/default_genesis_config.json | 108 ++ substrate/bin/node/runtime/Cargo.toml | 20 +- substrate/bin/node/testing/src/bench.rs | 22 +- substrate/bin/node/testing/src/client.rs | 7 +- substrate/bin/node/testing/src/genesis.rs | 18 +- .../bin/utils/chain-spec-builder/Cargo.toml | 4 + .../bin/utils/chain-spec-builder/bin/main.rs | 87 +- .../bin/utils/chain-spec-builder/src/lib.rs | 311 +++-- substrate/client/chain-spec/Cargo.toml | 12 + substrate/client/chain-spec/README.md | 90 +- .../client/chain-spec/res/raw_no_code.json | 18 + .../client/chain-spec/res/raw_with_code.json | 19 + .../substrate_test_runtime_from_config.json | 128 ++ ...ubstrate_test_runtime_from_config_raw.json | 53 + .../substrate_test_runtime_from_patch.json | 32 + ...substrate_test_runtime_from_patch_raw.json | 32 + substrate/client/chain-spec/src/chain_spec.rs | 830 ++++++++++++- .../src/{genesis.rs => genesis_block.rs} | 0 .../chain-spec/src/genesis_config_builder.rs | 183 +++ substrate/client/chain-spec/src/json_patch.rs | 191 +++ substrate/client/chain-spec/src/lib.rs | 352 ++++-- .../client/cli/src/commands/insert_key.rs | 20 +- substrate/client/cli/src/runner.rs | 20 +- substrate/frame/system/src/lib.rs | 3 - substrate/src/lib.rs | 8 +- substrate/test-utils/runtime/Cargo.toml | 4 +- .../runtime/{src/test_json => res}/README.md | 0 .../default_genesis_config.json | 4 +- .../default_genesis_config_incomplete.json | 4 +- .../default_genesis_config_invalid.json | 4 +- .../res/default_genesis_config_invalid_2.json | 113 ++ .../test-utils/runtime/src/genesismap.rs | 10 +- substrate/test-utils/runtime/src/lib.rs | 51 +- 90 files changed, 4833 insertions(+), 3059 deletions(-) create mode 100644 prdoc/pr_1256.prdoc create mode 100644 substrate/bin/node/executor/tests/res/default_genesis_config.json create mode 100644 substrate/client/chain-spec/res/raw_no_code.json create mode 100644 substrate/client/chain-spec/res/raw_with_code.json create mode 100644 substrate/client/chain-spec/res/substrate_test_runtime_from_config.json create mode 100644 substrate/client/chain-spec/res/substrate_test_runtime_from_config_raw.json create mode 100644 substrate/client/chain-spec/res/substrate_test_runtime_from_patch.json create mode 100644 substrate/client/chain-spec/res/substrate_test_runtime_from_patch_raw.json rename substrate/client/chain-spec/src/{genesis.rs => genesis_block.rs} (100%) create mode 100644 substrate/client/chain-spec/src/genesis_config_builder.rs create mode 100644 substrate/client/chain-spec/src/json_patch.rs rename substrate/test-utils/runtime/{src/test_json => res}/README.md (100%) rename substrate/test-utils/runtime/{src/test_json => res}/default_genesis_config.json (98%) rename substrate/test-utils/runtime/{src/test_json => res}/default_genesis_config_incomplete.json (98%) rename substrate/test-utils/runtime/{src/test_json => res}/default_genesis_config_invalid.json (98%) create mode 100644 substrate/test-utils/runtime/res/default_genesis_config_invalid_2.json diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 835b668de259..f507afda23e3 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -30,7 +30,7 @@ variables: RUSTY_CACHIER_COMPRESSION_METHOD: zstd NEXTEST_FAILURE_OUTPUT: immediate-final NEXTEST_SUCCESS_OUTPUT: final - ZOMBIENET_IMAGE: "docker.io/paritytech/zombienet:v1.3.75" + ZOMBIENET_IMAGE: "docker.io/paritytech/zombienet:v1.3.79" DOCKER_IMAGES_VERSION: "${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHORT_SHA}" default: diff --git a/Cargo.lock b/Cargo.lock index b4501b5d7512..1b4496368ee8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -965,6 +965,7 @@ dependencies = [ "sp-block-builder", "sp-consensus-aura", "sp-core", + "sp-genesis-builder", "sp-inherents", "sp-offchain", "sp-runtime", @@ -4222,6 +4223,7 @@ dependencies = [ "sp-api", "sp-block-builder", "sp-core", + "sp-genesis-builder", "sp-inherents", "sp-io", "sp-offchain", @@ -4288,6 +4290,7 @@ dependencies = [ "sc-transaction-pool", "sc-transaction-pool-api", "serde", + "serde_json", "sp-api", "sp-arithmetic", "sp-authority-discovery", @@ -6738,7 +6741,9 @@ dependencies = [ "polkadot-service", "rococo-runtime", "rococo-runtime-constants", + "sc-chain-spec", "sc-consensus-grandpa", + "serde_json", "sp-authority-discovery", "sp-consensus-babe", "sp-consensus-beefy", @@ -7159,6 +7164,7 @@ dependencies = [ "parity-scale-codec", "primitive-types", "scale-info", + "serde_json", "sp-api", "sp-authority-discovery", "sp-block-builder", @@ -8199,6 +8205,7 @@ dependencies = [ "sc-telemetry", "sc-transaction-pool", "sc-transaction-pool-api", + "serde_json", "sp-api", "sp-block-builder", "sp-blockchain", @@ -8215,6 +8222,7 @@ name = "minimal-runtime" version = "0.1.0" dependencies = [ "frame", + "frame-support", "pallet-balances", "pallet-sudo", "pallet-timestamp", @@ -8222,6 +8230,7 @@ dependencies = [ "pallet-transaction-payment-rpc-runtime-api", "parity-scale-codec", "scale-info", + "sp-genesis-builder", "substrate-wasm-builder", ] @@ -8679,6 +8688,7 @@ dependencies = [ "sc-telemetry", "sc-transaction-pool", "sc-transaction-pool-api", + "serde_json", "sp-api", "sp-block-builder", "sp-blockchain", @@ -8730,6 +8740,7 @@ dependencies = [ "pallet-transaction-payment-rpc-runtime-api", "parity-scale-codec", "scale-info", + "serde_json", "sp-api", "sp-block-builder", "sp-consensus-aura", @@ -11168,6 +11179,7 @@ dependencies = [ "sc-transaction-pool", "sc-transaction-pool-api", "serde", + "serde_json", "sp-api", "sp-block-builder", "sp-blockchain", @@ -12743,6 +12755,7 @@ dependencies = [ "sp-runtime", "sp-session", "sp-timestamp", + "sp-tracing 10.0.0", "sp-transaction-pool", "staging-xcm", "substrate-build-script-utils", @@ -14783,7 +14796,11 @@ dependencies = [ name = "sc-chain-spec" version = "4.0.0-dev" dependencies = [ + "array-bytes 6.1.0", + "docify", + "log", "memmap2", + "parity-scale-codec", "sc-chain-spec-derive", "sc-client-api", "sc-executor", @@ -14791,10 +14808,16 @@ dependencies = [ "sc-telemetry", "serde", "serde_json", + "sp-application-crypto", "sp-blockchain", + "sp-consensus-babe", "sp-core", + "sp-genesis-builder", + "sp-io", + "sp-keyring", "sp-runtime", "sp-state-machine", + "substrate-test-runtime", ] [[package]] @@ -17986,11 +18009,15 @@ version = "2.0.0" dependencies = [ "ansi_term", "clap 4.4.6", + "kitchensink-runtime", + "log", "rand 0.8.5", "sc-chain-spec", "sc-keystore", + "serde_json", "sp-core", "sp-keystore", + "sp-tracing 10.0.0", "staging-node-cli", ] @@ -18110,6 +18137,7 @@ dependencies = [ "parity-scale-codec", "sc-executor", "scale-info", + "serde_json", "sp-application-crypto", "sp-consensus-babe", "sp-core", @@ -18339,6 +18367,7 @@ name = "substrate" version = "1.0.0" dependencies = [ "frame-support", + "sc-chain-spec", "sc-cli", "sc-consensus-aura", "sc-consensus-babe", diff --git a/cumulus/parachain-template/node/Cargo.toml b/cumulus/parachain-template/node/Cargo.toml index 3507e9bcd326..2a08395008f8 100644 --- a/cumulus/parachain-template/node/Cargo.toml +++ b/cumulus/parachain-template/node/Cargo.toml @@ -17,6 +17,7 @@ codec = { package = "parity-scale-codec", version = "3.0.0" } serde = { version = "1.0.188", features = ["derive"] } jsonrpsee = { version = "0.16.2", features = ["server"] } futures = "0.3.28" +serde_json = "1.0.104" # Local parachain-template-runtime = { path = "../runtime" } diff --git a/cumulus/parachain-template/node/src/chain_spec.rs b/cumulus/parachain-template/node/src/chain_spec.rs index 0ca3c51900f2..a79c78699c07 100644 --- a/cumulus/parachain-template/node/src/chain_spec.rs +++ b/cumulus/parachain-template/node/src/chain_spec.rs @@ -7,8 +7,7 @@ use sp_core::{sr25519, Pair, Public}; use sp_runtime::traits::{IdentifyAccount, Verify}; /// Specialized `ChainSpec` for the normal parachain runtime. -pub type ChainSpec = - sc_service::GenericChainSpec; +pub type ChainSpec = sc_service::GenericChainSpec<(), Extensions>; /// The default XCM version to set in genesis config. const SAFE_XCM_VERSION: u32 = xcm::prelude::XCM_VERSION; @@ -68,53 +67,48 @@ pub fn development_config() -> ChainSpec { properties.insert("tokenDecimals".into(), 12.into()); properties.insert("ss58Format".into(), 42.into()); - ChainSpec::from_genesis( - // Name - "Development", - // ID - "dev", - ChainType::Development, - move || { - testnet_genesis( - // initial collators. - vec![ - ( - get_account_id_from_seed::("Alice"), - get_collator_keys_from_seed("Alice"), - ), - ( - get_account_id_from_seed::("Bob"), - get_collator_keys_from_seed("Bob"), - ), - ], - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Charlie"), - get_account_id_from_seed::("Dave"), - get_account_id_from_seed::("Eve"), - get_account_id_from_seed::("Ferdie"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - get_account_id_from_seed::("Charlie//stash"), - get_account_id_from_seed::("Dave//stash"), - get_account_id_from_seed::("Eve//stash"), - get_account_id_from_seed::("Ferdie//stash"), - ], - get_account_id_from_seed::("Alice"), - 1000.into(), - ) - }, - Vec::new(), - None, - None, - None, - None, + ChainSpec::builder( + parachain_template_runtime::WASM_BINARY + .expect("WASM binary was not built, please build it!"), Extensions { - relay_chain: "rococo-local".into(), // You MUST set this to the correct network! + relay_chain: "rococo-local".into(), + // You MUST set this to the correct network! para_id: 1000, }, ) + .with_name("Development") + .with_id("dev") + .with_chain_type(ChainType::Development) + .with_genesis_config_patch(testnet_genesis( + // initial collators. + vec![ + ( + get_account_id_from_seed::("Alice"), + get_collator_keys_from_seed("Alice"), + ), + ( + get_account_id_from_seed::("Bob"), + get_collator_keys_from_seed("Bob"), + ), + ], + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Charlie"), + get_account_id_from_seed::("Dave"), + get_account_id_from_seed::("Eve"), + get_account_id_from_seed::("Ferdie"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + get_account_id_from_seed::("Charlie//stash"), + get_account_id_from_seed::("Dave//stash"), + get_account_id_from_seed::("Eve//stash"), + get_account_id_from_seed::("Ferdie//stash"), + ], + get_account_id_from_seed::("Alice"), + 1000.into(), + )) + .build() } pub fn local_testnet_config() -> ChainSpec { @@ -124,59 +118,51 @@ pub fn local_testnet_config() -> ChainSpec { properties.insert("tokenDecimals".into(), 12.into()); properties.insert("ss58Format".into(), 42.into()); - ChainSpec::from_genesis( - // Name - "Local Testnet", - // ID - "local_testnet", - ChainType::Local, - move || { - testnet_genesis( - // initial collators. - vec![ - ( - get_account_id_from_seed::("Alice"), - get_collator_keys_from_seed("Alice"), - ), - ( - get_account_id_from_seed::("Bob"), - get_collator_keys_from_seed("Bob"), - ), - ], - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Charlie"), - get_account_id_from_seed::("Dave"), - get_account_id_from_seed::("Eve"), - get_account_id_from_seed::("Ferdie"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - get_account_id_from_seed::("Charlie//stash"), - get_account_id_from_seed::("Dave//stash"), - get_account_id_from_seed::("Eve//stash"), - get_account_id_from_seed::("Ferdie//stash"), - ], - get_account_id_from_seed::("Alice"), - 1000.into(), - ) - }, - // Bootnodes - Vec::new(), - // Telemetry - None, - // Protocol ID - Some("template-local"), - // Fork ID - None, - // Properties - Some(properties), - // Extensions + #[allow(deprecated)] + ChainSpec::builder( + parachain_template_runtime::WASM_BINARY + .expect("WASM binary was not built, please build it!"), Extensions { - relay_chain: "rococo-local".into(), // You MUST set this to the correct network! + relay_chain: "rococo-local".into(), + // You MUST set this to the correct network! para_id: 1000, }, ) + .with_name("Local Testnet") + .with_id("local_testnet") + .with_chain_type(ChainType::Local) + .with_genesis_config_patch(testnet_genesis( + // initial collators. + vec![ + ( + get_account_id_from_seed::("Alice"), + get_collator_keys_from_seed("Alice"), + ), + ( + get_account_id_from_seed::("Bob"), + get_collator_keys_from_seed("Bob"), + ), + ], + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Charlie"), + get_account_id_from_seed::("Dave"), + get_account_id_from_seed::("Eve"), + get_account_id_from_seed::("Ferdie"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + get_account_id_from_seed::("Charlie//stash"), + get_account_id_from_seed::("Dave//stash"), + get_account_id_from_seed::("Eve//stash"), + get_account_id_from_seed::("Ferdie//stash"), + ], + get_account_id_from_seed::("Alice"), + 1000.into(), + )) + .with_protocol_id("template-local") + .with_properties(properties) + .build() } fn testnet_genesis( @@ -184,28 +170,20 @@ fn testnet_genesis( endowed_accounts: Vec, root: AccountId, id: ParaId, -) -> parachain_template_runtime::RuntimeGenesisConfig { - parachain_template_runtime::RuntimeGenesisConfig { - system: parachain_template_runtime::SystemConfig { - code: parachain_template_runtime::WASM_BINARY - .expect("WASM binary was not build, please build it!") - .to_vec(), - ..Default::default() - }, - balances: parachain_template_runtime::BalancesConfig { - balances: endowed_accounts.iter().cloned().map(|k| (k, 1 << 60)).collect(), +) -> serde_json::Value { + serde_json::json!({ + "balances": { + "balances": endowed_accounts.iter().cloned().map(|k| (k, 1u64 << 60)).collect::>(), }, - parachain_info: parachain_template_runtime::ParachainInfoConfig { - parachain_id: id, - ..Default::default() + "parachainInfo": { + "parachainId": id, }, - collator_selection: parachain_template_runtime::CollatorSelectionConfig { - invulnerables: invulnerables.iter().cloned().map(|(acc, _)| acc).collect(), - candidacy_bond: EXISTENTIAL_DEPOSIT * 16, - ..Default::default() + "collatorSelection": { + "invulnerables": invulnerables.iter().cloned().map(|(acc, _)| acc).collect::>(), + "candidacyBond": EXISTENTIAL_DEPOSIT * 16, }, - session: parachain_template_runtime::SessionConfig { - keys: invulnerables + "session": { + "keys": invulnerables .into_iter() .map(|(acc, aura)| { ( @@ -214,18 +192,11 @@ fn testnet_genesis( template_session_keys(aura), // session keys ) }) - .collect(), + .collect::>(), }, - // no need to pass anything to aura, in fact it will panic if we do. Session will take care - // of this. - aura: Default::default(), - aura_ext: Default::default(), - parachain_system: Default::default(), - polkadot_xcm: parachain_template_runtime::PolkadotXcmConfig { - safe_xcm_version: Some(SAFE_XCM_VERSION), - ..Default::default() + "polkadotXcm": { + "safeXcmVersion": Some(SAFE_XCM_VERSION), }, - transaction_payment: Default::default(), - sudo: parachain_template_runtime::SudoConfig { key: Some(root) }, - } + "sudo": { "key": Some(root) } + }) } diff --git a/cumulus/parachains/integration-tests/emulated/common/Cargo.toml b/cumulus/parachains/integration-tests/emulated/common/Cargo.toml index 5845e6940fb5..4182fea52748 100644 --- a/cumulus/parachains/integration-tests/emulated/common/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/common/Cargo.toml @@ -10,6 +10,7 @@ publish = false [dependencies] codec = { package = "parity-scale-codec", version = "3.4.0", default-features = false } paste = "1.0.14" +serde_json = "1.0.104" # Substrate grandpa = { package = "sc-consensus-grandpa", path = "../../../../../substrate/client/consensus/grandpa" } @@ -24,6 +25,7 @@ pallet-staking = { path = "../../../../../substrate/frame/staking", default-feat pallet-message-queue = { path = "../../../../../substrate/frame/message-queue", default-features = false} pallet-im-online = { path = "../../../../../substrate/frame/im-online", default-features = false} beefy-primitives = { package = "sp-consensus-beefy", path = "../../../../../substrate/primitives/consensus/beefy" } +sc-chain-spec = { path = "../../../../../substrate/client/chain-spec", default-features = false } # Polkadot polkadot-core-primitives = { path = "../../../../../polkadot/core-primitives", default-features = false} diff --git a/cumulus/parachains/integration-tests/emulated/common/src/constants.rs b/cumulus/parachains/integration-tests/emulated/common/src/constants.rs index 592269e9aa38..86027229c59a 100644 --- a/cumulus/parachains/integration-tests/emulated/common/src/constants.rs +++ b/cumulus/parachains/integration-tests/emulated/common/src/constants.rs @@ -17,12 +17,15 @@ use beefy_primitives::ecdsa_crypto::AuthorityId as BeefyId; use grandpa::AuthorityId as GrandpaId; use pallet_im_online::sr25519::AuthorityId as ImOnlineId; +use sc_chain_spec::GenesisConfigBuilderRuntimeCaller; use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId; use sp_consensus_babe::AuthorityId as BabeId; use sp_core::{sr25519, storage::Storage, Pair, Public}; +#[cfg(test)] +use sp_runtime::BuildStorage; use sp_runtime::{ traits::{IdentifyAccount, Verify}, - BuildStorage, MultiSignature, Perbill, + MultiSignature, Perbill, }; // Cumulus @@ -58,6 +61,29 @@ where AccountPublic::from(get_from_seed::(seed)).into_account() } +/// Helper function to build the genesis storage using given json patch and code +fn build_genesis_storage(patch: serde_json::Value, code: &[u8]) -> Storage { + let mut storage = GenesisConfigBuilderRuntimeCaller::new(code) + .get_storage_for_patch(patch) + .unwrap(); + storage + .top + .insert(sp_core::storage::well_known_keys::CODE.to_vec(), code.into()); + storage +} + +#[cfg(test)] +/// Helper function used in tests to build the genesis storage using given RuntimeGenesisConfig and +/// code Used in `legacy_vs_json_check` submods to verify storage building with JSON patch against +/// building with RuntimeGenesisConfig struct. +fn build_genesis_storage_legacy(builder: &dyn BuildStorage, code: &[u8]) -> Storage { + let mut storage = builder.build_storage().unwrap(); + storage + .top + .insert(sp_core::storage::well_known_keys::CODE.to_vec(), code.into()); + storage +} + pub mod accounts { use super::*; pub const ALICE: &str = "Alice"; @@ -186,20 +212,16 @@ pub mod westend { } pub fn genesis() -> Storage { - let genesis_config = westend_runtime::RuntimeGenesisConfig { - system: westend_runtime::SystemConfig { - code: westend_runtime::WASM_BINARY.unwrap().to_vec(), - ..Default::default() - }, - balances: westend_runtime::BalancesConfig { - balances: accounts::init_balances() + let genesis_config = serde_json::json!({ + "balances": { + "balances": accounts::init_balances() .iter() .cloned() .map(|k| (k, ENDOWMENT)) - .collect(), + .collect::>(), }, - session: westend_runtime::SessionConfig { - keys: validators::initial_authorities() + "session": { + "keys": validators::initial_authorities() .iter() .map(|x| { ( @@ -218,33 +240,106 @@ pub mod westend { }) .collect::>(), }, - staking: westend_runtime::StakingConfig { - validator_count: validators::initial_authorities().len() as u32, - minimum_validator_count: 1, - stakers: validators::initial_authorities() + "staking": { + "validatorCount": validators::initial_authorities().len() as u32, + "minimumValidatorCount": 1, + "stakers": validators::initial_authorities() .iter() .map(|x| { - (x.0.clone(), x.1.clone(), STASH, westend_runtime::StakerStatus::Validator) + (x.0.clone(), x.1.clone(), STASH, westend_runtime::StakerStatus::::Validator) }) - .collect(), - invulnerables: validators::initial_authorities() + .collect::>(), + "invulnerables": validators::initial_authorities() .iter() .map(|x| x.0.clone()) - .collect(), - force_era: pallet_staking::Forcing::ForceNone, - slash_reward_fraction: Perbill::from_percent(10), - ..Default::default() + .collect::>(), + "forceEra": pallet_staking::Forcing::ForceNone, + "slashRewardFraction": Perbill::from_percent(10), }, - babe: westend_runtime::BabeConfig { - authorities: Default::default(), - epoch_config: Some(westend_runtime::BABE_GENESIS_EPOCH_CONFIG), - ..Default::default() + "babe": { + "epochConfig": Some(westend_runtime::BABE_GENESIS_EPOCH_CONFIG), }, - configuration: westend_runtime::ConfigurationConfig { config: get_host_config() }, - ..Default::default() - }; + "configuration": { "config": get_host_config() }, + }); - genesis_config.build_storage().unwrap() + build_genesis_storage(genesis_config, westend_runtime::WASM_BINARY.unwrap()) + } + + #[cfg(test)] + mod legacy_vs_json_check { + use super::*; + fn genesis() -> Storage { + let genesis_config = westend_runtime::RuntimeGenesisConfig { + system: westend_runtime::SystemConfig::default(), + balances: westend_runtime::BalancesConfig { + balances: accounts::init_balances() + .iter() + .cloned() + .map(|k| (k, ENDOWMENT)) + .collect(), + }, + session: westend_runtime::SessionConfig { + keys: validators::initial_authorities() + .iter() + .map(|x| { + ( + x.0.clone(), + x.0.clone(), + westend::session_keys( + x.2.clone(), + x.3.clone(), + x.4.clone(), + x.5.clone(), + x.6.clone(), + x.7.clone(), + get_from_seed::("Alice"), + ), + ) + }) + .collect::>(), + }, + staking: westend_runtime::StakingConfig { + validator_count: validators::initial_authorities().len() as u32, + minimum_validator_count: 1, + stakers: validators::initial_authorities() + .iter() + .map(|x| { + ( + x.0.clone(), + x.1.clone(), + STASH, + westend_runtime::StakerStatus::Validator, + ) + }) + .collect(), + invulnerables: validators::initial_authorities() + .iter() + .map(|x| x.0.clone()) + .collect(), + force_era: pallet_staking::Forcing::ForceNone, + slash_reward_fraction: Perbill::from_percent(10), + ..Default::default() + }, + babe: westend_runtime::BabeConfig { + authorities: Default::default(), + epoch_config: Some(westend_runtime::BABE_GENESIS_EPOCH_CONFIG), + ..Default::default() + }, + configuration: westend_runtime::ConfigurationConfig { config: get_host_config() }, + ..Default::default() + }; + + build_genesis_storage_legacy(&genesis_config, westend_runtime::WASM_BINARY.unwrap()) + } + + #[test] + fn test_genesis() { + let j1 = super::genesis(); + let j2 = genesis(); + + assert_eq!(j1.top, j2.top); + assert_eq!(j1.children_default, j2.children_default); + } } } @@ -294,20 +389,16 @@ pub mod rococo { } pub fn genesis() -> Storage { - let genesis_config = rococo_runtime::RuntimeGenesisConfig { - system: rococo_runtime::SystemConfig { - code: rococo_runtime::WASM_BINARY.unwrap().to_vec(), - ..Default::default() - }, - balances: rococo_runtime::BalancesConfig { - balances: accounts::init_balances() + let genesis_config = serde_json::json!({ + "balances": { + "balances": accounts::init_balances() .iter() .map(|k| (k.clone(), ENDOWMENT)) - .collect(), + .collect::>(), }, // indices: rococo_runtime::IndicesConfig { indices: vec![] }, - session: rococo_runtime::SessionConfig { - keys: validators::initial_authorities() + "session": { + "keys": validators::initial_authorities() .iter() .map(|x| { ( @@ -326,16 +417,14 @@ pub mod rococo { }) .collect::>(), }, - babe: rococo_runtime::BabeConfig { - authorities: Default::default(), - epoch_config: Some(rococo_runtime::BABE_GENESIS_EPOCH_CONFIG), - ..Default::default() + "babe": { + "epochConfig": Some(rococo_runtime::BABE_GENESIS_EPOCH_CONFIG), }, - sudo: rococo_runtime::SudoConfig { - key: Some(get_account_id_from_seed::("Alice")), + "sudo": { + "key": Some(get_account_id_from_seed::("Alice")), }, - configuration: rococo_runtime::ConfigurationConfig { config: get_host_config() }, - paras: rococo_runtime::ParasConfig { + "configuration": { "config": get_host_config() }, + "paras": rococo_runtime::ParasConfig { paras: vec![ ( asset_hub_rococo::PARA_ID.into(), @@ -370,14 +459,109 @@ pub mod rococo { ], ..Default::default() }, - registrar: rococo_runtime::RegistrarConfig { - next_free_para_id: polkadot_primitives::LOWEST_PUBLIC_ID, - ..Default::default() + "registrar": { + "nextFreeParaId": polkadot_primitives::LOWEST_PUBLIC_ID, }, - ..Default::default() - }; + }); + + build_genesis_storage(genesis_config, rococo_runtime::WASM_BINARY.unwrap()) + } + + #[cfg(test)] + mod legacy_vs_json_check { + use super::*; + fn genesis() -> Storage { + let genesis_config = rococo_runtime::RuntimeGenesisConfig { + system: rococo_runtime::SystemConfig::default(), + balances: rococo_runtime::BalancesConfig { + balances: accounts::init_balances() + .iter() + .map(|k| (k.clone(), ENDOWMENT)) + .collect(), + }, + // indices: rococo_runtime::IndicesConfig { indices: vec![] }, + session: rococo_runtime::SessionConfig { + keys: validators::initial_authorities() + .iter() + .map(|x| { + ( + x.0.clone(), + x.0.clone(), + session_keys( + x.2.clone(), + x.3.clone(), + x.4.clone(), + x.5.clone(), + x.6.clone(), + x.7.clone(), + get_from_seed::("Alice"), + ), + ) + }) + .collect::>(), + }, + babe: rococo_runtime::BabeConfig { + authorities: Default::default(), + epoch_config: Some(rococo_runtime::BABE_GENESIS_EPOCH_CONFIG), + ..Default::default() + }, + sudo: rococo_runtime::SudoConfig { + key: Some(get_account_id_from_seed::("Alice")), + }, + configuration: rococo_runtime::ConfigurationConfig { config: get_host_config() }, + paras: rococo_runtime::ParasConfig { + paras: vec![ + ( + asset_hub_rococo::PARA_ID.into(), + ParaGenesisArgs { + genesis_head: HeadData::default(), + validation_code: ValidationCode( + asset_hub_rococo_runtime::WASM_BINARY.unwrap().to_vec(), + ), + para_kind: ParaKind::Parachain, + }, + ), + ( + penpal::PARA_ID_A.into(), + ParaGenesisArgs { + genesis_head: HeadData::default(), + validation_code: ValidationCode( + penpal_runtime::WASM_BINARY.unwrap().to_vec(), + ), + para_kind: ParaKind::Parachain, + }, + ), + ( + penpal::PARA_ID_B.into(), + ParaGenesisArgs { + genesis_head: HeadData::default(), + validation_code: ValidationCode( + penpal_runtime::WASM_BINARY.unwrap().to_vec(), + ), + para_kind: ParaKind::Parachain, + }, + ), + ], + ..Default::default() + }, + registrar: rococo_runtime::RegistrarConfig { + next_free_para_id: polkadot_primitives::LOWEST_PUBLIC_ID, + ..Default::default() + }, + ..Default::default() + }; - genesis_config.build_storage().unwrap() + build_genesis_storage_legacy(&genesis_config, rococo_runtime::WASM_BINARY.unwrap()) + } + + #[test] + fn test_genesis() { + let j1 = super::genesis(); + let j2 = genesis(); + + assert_eq!(j1.top, j2.top); + assert_eq!(j1.children_default, j2.children_default); + } } } @@ -388,35 +572,27 @@ pub mod asset_hub_westend { pub const ED: Balance = parachains_common::westend::currency::EXISTENTIAL_DEPOSIT; pub fn genesis() -> Storage { - let genesis_config = asset_hub_westend_runtime::RuntimeGenesisConfig { - system: asset_hub_westend_runtime::SystemConfig { - code: asset_hub_westend_runtime::WASM_BINARY - .expect("WASM binary was not build, please build it!") - .to_vec(), - ..Default::default() - }, - balances: asset_hub_westend_runtime::BalancesConfig { - balances: accounts::init_balances() + let genesis_config = serde_json::json!({ + "balances": { + "balances": accounts::init_balances() .iter() .cloned() .map(|k| (k, ED * 4096)) - .collect(), + .collect::>(), }, - parachain_info: asset_hub_westend_runtime::ParachainInfoConfig { - parachain_id: PARA_ID.into(), - ..Default::default() + "parachainInfo": { + "parachainId": cumulus_primitives_core::ParaId::from(PARA_ID), }, - collator_selection: asset_hub_westend_runtime::CollatorSelectionConfig { - invulnerables: collators::invulnerables() + "collatorSelection": { + "invulnerables": collators::invulnerables() .iter() .cloned() .map(|(acc, _)| acc) - .collect(), - candidacy_bond: ED * 16, - ..Default::default() + .collect::>(), + "candidacyBond": ED * 16, }, - session: asset_hub_westend_runtime::SessionConfig { - keys: collators::invulnerables() + "session": { + "keys": collators::invulnerables() .into_iter() .map(|(acc, aura)| { ( @@ -425,16 +601,80 @@ pub mod asset_hub_westend { asset_hub_westend_runtime::SessionKeys { aura }, // session keys ) }) - .collect(), + .collect::>(), }, - polkadot_xcm: asset_hub_westend_runtime::PolkadotXcmConfig { - safe_xcm_version: Some(SAFE_XCM_VERSION), - ..Default::default() + "polkadotXcm": { + "safeXcmVersion": Some(SAFE_XCM_VERSION), }, - ..Default::default() - }; + }); - genesis_config.build_storage().unwrap() + build_genesis_storage( + genesis_config, + asset_hub_westend_runtime::WASM_BINARY + .expect("WASM binary was not built, please build it!"), + ) + } + + #[cfg(test)] + mod legacy_vs_json_check { + use super::*; + fn genesis() -> Storage { + let genesis_config = asset_hub_westend_runtime::RuntimeGenesisConfig { + system: asset_hub_westend_runtime::SystemConfig::default(), + balances: asset_hub_westend_runtime::BalancesConfig { + balances: accounts::init_balances() + .iter() + .cloned() + .map(|k| (k, ED * 4096)) + .collect(), + }, + parachain_info: asset_hub_westend_runtime::ParachainInfoConfig { + parachain_id: PARA_ID.into(), + ..Default::default() + }, + collator_selection: asset_hub_westend_runtime::CollatorSelectionConfig { + invulnerables: collators::invulnerables() + .iter() + .cloned() + .map(|(acc, _)| acc) + .collect(), + candidacy_bond: ED * 16, + ..Default::default() + }, + session: asset_hub_westend_runtime::SessionConfig { + keys: collators::invulnerables() + .into_iter() + .map(|(acc, aura)| { + ( + acc.clone(), // account id + acc, // validator id + asset_hub_westend_runtime::SessionKeys { aura }, // session keys + ) + }) + .collect(), + }, + polkadot_xcm: asset_hub_westend_runtime::PolkadotXcmConfig { + safe_xcm_version: Some(SAFE_XCM_VERSION), + ..Default::default() + }, + ..Default::default() + }; + + build_genesis_storage_legacy( + &genesis_config, + asset_hub_westend_runtime::WASM_BINARY + .expect("WASM binary was not built, please build it!"), + ) + } + + #[test] + fn test_genesis() { + let j1 = super::genesis(); + let j2 = genesis(); + + assert_eq!(j1.top, j2.top); + assert_eq!(j1.children_default, j2.children_default); + } } } @@ -444,109 +684,221 @@ pub mod asset_hub_rococo { pub const ED: Balance = parachains_common::rococo::currency::EXISTENTIAL_DEPOSIT; pub fn genesis() -> Storage { - let genesis_config = asset_hub_rococo_runtime::RuntimeGenesisConfig { - system: asset_hub_rococo_runtime::SystemConfig { - code: asset_hub_rococo_runtime::WASM_BINARY - .expect("WASM binary was not build, please build it!") - .to_vec(), - ..Default::default() - }, - balances: asset_hub_rococo_runtime::BalancesConfig { - balances: accounts::init_balances() + let genesis_config = serde_json::json!({ + "balances": { + "balances": accounts::init_balances() .iter() .cloned() .map(|k| (k, ED * 4096 * 4096)) - .collect(), + .collect::>(), }, - parachain_info: asset_hub_rococo_runtime::ParachainInfoConfig { - parachain_id: PARA_ID.into(), - ..Default::default() + "parachainInfo": { + "parachainId": cumulus_primitives_core::ParaId::from(PARA_ID), }, - collator_selection: asset_hub_rococo_runtime::CollatorSelectionConfig { - invulnerables: collators::invulnerables() + "collatorSelection": { + "invulnerables": collators::invulnerables() .iter() .cloned() .map(|(acc, _)| acc) - .collect(), - candidacy_bond: ED * 16, - ..Default::default() + .collect::>(), + "candidacyBond": ED * 16, }, - session: asset_hub_rococo_runtime::SessionConfig { - keys: collators::invulnerables() + "session": { + "keys": collators::invulnerables() .into_iter() .map(|(acc, aura)| { ( - acc.clone(), // account id - acc, // validator id + acc.clone(), // account id + acc, // validator id asset_hub_rococo_runtime::SessionKeys { aura }, // session keys ) }) - .collect(), + .collect::>(), }, - polkadot_xcm: asset_hub_rococo_runtime::PolkadotXcmConfig { - safe_xcm_version: Some(SAFE_XCM_VERSION), - ..Default::default() + "polkadotXcm": { + "safeXcmVersion": Some(SAFE_XCM_VERSION), }, - ..Default::default() - }; + }); + + build_genesis_storage( + genesis_config, + asset_hub_rococo_runtime::WASM_BINARY + .expect("WASM binary was not built, please build it!"), + ) + } + + #[cfg(test)] + mod legacy_vs_json_check { + use super::*; + pub fn genesis() -> Storage { + let genesis_config = asset_hub_rococo_runtime::RuntimeGenesisConfig { + system: asset_hub_rococo_runtime::SystemConfig::default(), + balances: asset_hub_rococo_runtime::BalancesConfig { + balances: accounts::init_balances() + .iter() + .cloned() + .map(|k| (k, ED * 4096 * 4096)) + .collect(), + }, + parachain_info: asset_hub_rococo_runtime::ParachainInfoConfig { + parachain_id: PARA_ID.into(), + ..Default::default() + }, + collator_selection: asset_hub_rococo_runtime::CollatorSelectionConfig { + invulnerables: collators::invulnerables() + .iter() + .cloned() + .map(|(acc, _)| acc) + .collect(), + candidacy_bond: ED * 16, + ..Default::default() + }, + session: asset_hub_rococo_runtime::SessionConfig { + keys: collators::invulnerables() + .into_iter() + .map(|(acc, aura)| { + ( + acc.clone(), // account id + acc, // validator id + asset_hub_rococo_runtime::SessionKeys { aura }, // session keys + ) + }) + .collect(), + }, + polkadot_xcm: asset_hub_rococo_runtime::PolkadotXcmConfig { + safe_xcm_version: Some(SAFE_XCM_VERSION), + ..Default::default() + }, + ..Default::default() + }; + + build_genesis_storage_legacy( + &genesis_config, + asset_hub_rococo_runtime::WASM_BINARY + .expect("WASM binary was not built, please build it!"), + ) + } - genesis_config.build_storage().unwrap() + #[test] + fn test_genesis() { + let j1 = super::genesis(); + let j2 = genesis(); + + assert_eq!(j1.top, j2.top); + assert_eq!(j1.children_default, j2.children_default); + } } } pub mod asset_hub_wococo { use super::*; pub const PARA_ID: u32 = 1000; - pub const ED: Balance = parachains_common::wococo::currency::EXISTENTIAL_DEPOSIT; + pub const ED: Balance = parachains_common::rococo::currency::EXISTENTIAL_DEPOSIT; pub fn genesis() -> Storage { - let genesis_config = asset_hub_rococo_runtime::RuntimeGenesisConfig { - system: asset_hub_rococo_runtime::SystemConfig { - code: asset_hub_rococo_runtime::WASM_BINARY - .expect("WASM binary was not build, please build it!") - .to_vec(), - ..Default::default() - }, - balances: asset_hub_rococo_runtime::BalancesConfig { - balances: accounts::init_balances() + let genesis_config = serde_json::json!({ + "balances": { + "balances": accounts::init_balances() .iter() .cloned() .map(|k| (k, ED * 4096)) - .collect(), + .collect::>(), }, - parachain_info: asset_hub_rococo_runtime::ParachainInfoConfig { - parachain_id: PARA_ID.into(), - ..Default::default() + "parachainInfo": { + "parachainId": cumulus_primitives_core::ParaId::from(PARA_ID), }, - collator_selection: asset_hub_rococo_runtime::CollatorSelectionConfig { - invulnerables: collators::invulnerables() + "collatorSelection": { + "invulnerables": collators::invulnerables() .iter() .cloned() .map(|(acc, _)| acc) - .collect(), - candidacy_bond: ED * 16, - ..Default::default() + .collect::>(), + "candidacyBond": ED * 16, }, - session: asset_hub_rococo_runtime::SessionConfig { - keys: collators::invulnerables() + "session": { + "keys": collators::invulnerables() .into_iter() .map(|(acc, aura)| { ( - acc.clone(), // account id - acc, // validator id - asset_hub_rococo_runtime::SessionKeys { aura }, // session keys + acc.clone(), // account id + acc, // validator id + asset_hub_westend_runtime::SessionKeys { aura }, // session keys ) }) - .collect(), + .collect::>(), }, - polkadot_xcm: asset_hub_rococo_runtime::PolkadotXcmConfig { - safe_xcm_version: Some(SAFE_XCM_VERSION), - ..Default::default() + "polkadotXcm": { + "safeXcmVersion": Some(SAFE_XCM_VERSION), }, - ..Default::default() - }; + }); + + build_genesis_storage( + genesis_config, + asset_hub_rococo_runtime::WASM_BINARY + .expect("WASM binary was not built, please build it!"), + ) + } + + #[cfg(test)] + mod legacy_vs_json_check { + use super::*; + pub fn genesis() -> Storage { + let genesis_config = asset_hub_rococo_runtime::RuntimeGenesisConfig { + system: asset_hub_rococo_runtime::SystemConfig::default(), + balances: asset_hub_rococo_runtime::BalancesConfig { + balances: accounts::init_balances() + .iter() + .cloned() + .map(|k| (k, ED * 4096)) + .collect(), + }, + parachain_info: asset_hub_rococo_runtime::ParachainInfoConfig { + parachain_id: PARA_ID.into(), + ..Default::default() + }, + collator_selection: asset_hub_rococo_runtime::CollatorSelectionConfig { + invulnerables: collators::invulnerables() + .iter() + .cloned() + .map(|(acc, _)| acc) + .collect(), + candidacy_bond: ED * 16, + ..Default::default() + }, + session: asset_hub_rococo_runtime::SessionConfig { + keys: collators::invulnerables() + .into_iter() + .map(|(acc, aura)| { + ( + acc.clone(), // account id + acc, // validator id + asset_hub_rococo_runtime::SessionKeys { aura }, // session keys + ) + }) + .collect(), + }, + polkadot_xcm: asset_hub_rococo_runtime::PolkadotXcmConfig { + safe_xcm_version: Some(SAFE_XCM_VERSION), + ..Default::default() + }, + ..Default::default() + }; - genesis_config.build_storage().unwrap() + build_genesis_storage_legacy( + &genesis_config, + asset_hub_rococo_runtime::WASM_BINARY + .expect("WASM binary was not built, please build it!"), + ) + } + + #[test] + fn test_genesis() { + let j1 = super::genesis(); + let j2 = genesis(); + + assert_eq!(j1.top, j2.top); + assert_eq!(j1.children_default, j2.children_default); + } } } @@ -558,35 +910,27 @@ pub mod penpal { pub const ED: Balance = penpal_runtime::EXISTENTIAL_DEPOSIT; pub fn genesis(para_id: u32) -> Storage { - let genesis_config = penpal_runtime::RuntimeGenesisConfig { - system: penpal_runtime::SystemConfig { - code: penpal_runtime::WASM_BINARY - .expect("WASM binary was not build, please build it!") - .to_vec(), - ..Default::default() - }, - balances: penpal_runtime::BalancesConfig { - balances: accounts::init_balances() + let genesis_config = serde_json::json!({ + "balances": { + "balances": accounts::init_balances() .iter() .cloned() .map(|k| (k, ED * 4096)) - .collect(), + .collect::>(), }, - parachain_info: penpal_runtime::ParachainInfoConfig { - parachain_id: para_id.into(), - ..Default::default() + "parachainInfo": { + "parachainId": cumulus_primitives_core::ParaId::from(para_id), }, - collator_selection: penpal_runtime::CollatorSelectionConfig { - invulnerables: collators::invulnerables() + "collatorSelection": { + "invulnerables": collators::invulnerables() .iter() .cloned() .map(|(acc, _)| acc) - .collect(), - candidacy_bond: ED * 16, - ..Default::default() + .collect::>(), + "candidacyBond": ED * 16, }, - session: penpal_runtime::SessionConfig { - keys: collators::invulnerables() + "session": { + "keys": collators::invulnerables() .into_iter() .map(|(acc, aura)| { ( @@ -595,19 +939,84 @@ pub mod penpal { penpal_runtime::SessionKeys { aura }, // session keys ) }) - .collect(), + .collect::>(), }, - polkadot_xcm: penpal_runtime::PolkadotXcmConfig { - safe_xcm_version: Some(SAFE_XCM_VERSION), - ..Default::default() + "polkadotXcm": { + "safeXcmVersion": Some(SAFE_XCM_VERSION), }, - sudo: penpal_runtime::SudoConfig { - key: Some(get_account_id_from_seed::("Alice")), + "sudo": { + "key": Some(get_account_id_from_seed::("Alice")), }, - ..Default::default() - }; + }); - genesis_config.build_storage().unwrap() + build_genesis_storage( + genesis_config, + penpal_runtime::WASM_BINARY.expect("WASM binary was not built, please build it!"), + ) + } + + #[cfg(test)] + mod legacy_vs_json_check { + use super::*; + fn genesis(para_id: u32) -> Storage { + let genesis_config = penpal_runtime::RuntimeGenesisConfig { + system: penpal_runtime::SystemConfig::default(), + balances: penpal_runtime::BalancesConfig { + balances: accounts::init_balances() + .iter() + .cloned() + .map(|k| (k, ED * 4096)) + .collect(), + }, + parachain_info: penpal_runtime::ParachainInfoConfig { + parachain_id: para_id.into(), + ..Default::default() + }, + collator_selection: penpal_runtime::CollatorSelectionConfig { + invulnerables: collators::invulnerables() + .iter() + .cloned() + .map(|(acc, _)| acc) + .collect(), + candidacy_bond: ED * 16, + ..Default::default() + }, + session: penpal_runtime::SessionConfig { + keys: collators::invulnerables() + .into_iter() + .map(|(acc, aura)| { + ( + acc.clone(), // account id + acc, // validator id + penpal_runtime::SessionKeys { aura }, // session keys + ) + }) + .collect(), + }, + polkadot_xcm: penpal_runtime::PolkadotXcmConfig { + safe_xcm_version: Some(SAFE_XCM_VERSION), + ..Default::default() + }, + sudo: penpal_runtime::SudoConfig { + key: Some(get_account_id_from_seed::("Alice")), + }, + ..Default::default() + }; + + build_genesis_storage_legacy( + &genesis_config, + penpal_runtime::WASM_BINARY.expect("WASM binary was not built, please build it!"), + ) + } + + #[test] + fn test_genesis() { + let j1 = super::genesis(101); + let j2 = genesis(101); + + assert_eq!(j1.top, j2.top); + assert_eq!(j1.children_default, j2.children_default); + } } } @@ -618,35 +1027,27 @@ pub mod bridge_hub_rococo { pub const ED: Balance = parachains_common::rococo::currency::EXISTENTIAL_DEPOSIT; pub fn genesis() -> Storage { - let genesis_config = bridge_hub_rococo_runtime::RuntimeGenesisConfig { - system: bridge_hub_rococo_runtime::SystemConfig { - code: bridge_hub_rococo_runtime::WASM_BINARY - .expect("WASM binary was not build, please build it!") - .to_vec(), - ..Default::default() - }, - balances: bridge_hub_rococo_runtime::BalancesConfig { - balances: accounts::init_balances() + let genesis_config = serde_json::json!({ + "balances": { + "balances": accounts::init_balances() .iter() .cloned() .map(|k| (k, ED * 4096)) - .collect(), + .collect::>(), }, - parachain_info: bridge_hub_rococo_runtime::ParachainInfoConfig { - parachain_id: PARA_ID.into(), - ..Default::default() + "parachainInfo": { + "parachainId": cumulus_primitives_core::ParaId::from(PARA_ID), }, - collator_selection: bridge_hub_rococo_runtime::CollatorSelectionConfig { - invulnerables: collators::invulnerables() + "collatorSelection": { + "invulnerables": collators::invulnerables() .iter() .cloned() .map(|(acc, _)| acc) - .collect(), - candidacy_bond: ED * 16, - ..Default::default() + .collect::>(), + "candidacyBond": ED * 16, }, - session: bridge_hub_rococo_runtime::SessionConfig { - keys: collators::invulnerables() + "session": { + "keys": collators::invulnerables() .into_iter() .map(|(acc, aura)| { ( @@ -655,31 +1056,107 @@ pub mod bridge_hub_rococo { bridge_hub_rococo_runtime::SessionKeys { aura }, // session keys ) }) - .collect(), + .collect::>(), }, - polkadot_xcm: bridge_hub_rococo_runtime::PolkadotXcmConfig { - safe_xcm_version: Some(SAFE_XCM_VERSION), - ..Default::default() + "polkadotXcm": { + "safeXcmVersion": Some(SAFE_XCM_VERSION), }, - bridge_wococo_grandpa: bridge_hub_rococo_runtime::BridgeWococoGrandpaConfig { - owner: Some(get_account_id_from_seed::(accounts::BOB)), - ..Default::default() + "bridgeWococoGrandpa": { + "owner": Some(get_account_id_from_seed::(accounts::BOB)), }, - bridge_rococo_grandpa: bridge_hub_rococo_runtime::BridgeRococoGrandpaConfig { - owner: Some(get_account_id_from_seed::(accounts::BOB)), - ..Default::default() + "bridgeRococoGrandpa": { + "owner": Some(get_account_id_from_seed::(accounts::BOB)), }, - bridge_rococo_messages: bridge_hub_rococo_runtime::BridgeRococoMessagesConfig { - owner: Some(get_account_id_from_seed::(accounts::BOB)), - ..Default::default() + "bridgeRococoMessages": { + "owner": Some(get_account_id_from_seed::(accounts::BOB)), }, - bridge_wococo_messages: bridge_hub_rococo_runtime::BridgeWococoMessagesConfig { - owner: Some(get_account_id_from_seed::(accounts::BOB)), + "bridgeWococoMessages": { + "owner": Some(get_account_id_from_seed::(accounts::BOB)), + } + }); + + build_genesis_storage( + genesis_config, + bridge_hub_rococo_runtime::WASM_BINARY + .expect("WASM binary was not built, please build it!"), + ) + } + + #[cfg(test)] + mod legacy_vs_json_check { + use super::*; + fn genesis() -> Storage { + let genesis_config = bridge_hub_rococo_runtime::RuntimeGenesisConfig { + system: bridge_hub_rococo_runtime::SystemConfig::default(), + balances: bridge_hub_rococo_runtime::BalancesConfig { + balances: accounts::init_balances() + .iter() + .cloned() + .map(|k| (k, ED * 4096)) + .collect(), + }, + parachain_info: bridge_hub_rococo_runtime::ParachainInfoConfig { + parachain_id: PARA_ID.into(), + ..Default::default() + }, + collator_selection: bridge_hub_rococo_runtime::CollatorSelectionConfig { + invulnerables: collators::invulnerables() + .iter() + .cloned() + .map(|(acc, _)| acc) + .collect(), + candidacy_bond: ED * 16, + ..Default::default() + }, + session: bridge_hub_rococo_runtime::SessionConfig { + keys: collators::invulnerables() + .into_iter() + .map(|(acc, aura)| { + ( + acc.clone(), // account id + acc, // validator id + bridge_hub_rococo_runtime::SessionKeys { aura }, // session keys + ) + }) + .collect(), + }, + polkadot_xcm: bridge_hub_rococo_runtime::PolkadotXcmConfig { + safe_xcm_version: Some(SAFE_XCM_VERSION), + ..Default::default() + }, + bridge_wococo_grandpa: bridge_hub_rococo_runtime::BridgeWococoGrandpaConfig { + owner: Some(get_account_id_from_seed::(accounts::BOB)), + ..Default::default() + }, + bridge_rococo_grandpa: bridge_hub_rococo_runtime::BridgeRococoGrandpaConfig { + owner: Some(get_account_id_from_seed::(accounts::BOB)), + ..Default::default() + }, + bridge_rococo_messages: bridge_hub_rococo_runtime::BridgeRococoMessagesConfig { + owner: Some(get_account_id_from_seed::(accounts::BOB)), + ..Default::default() + }, + bridge_wococo_messages: bridge_hub_rococo_runtime::BridgeWococoMessagesConfig { + owner: Some(get_account_id_from_seed::(accounts::BOB)), + ..Default::default() + }, ..Default::default() - }, - ..Default::default() - }; + }; - genesis_config.build_storage().unwrap() + build_genesis_storage_legacy( + &genesis_config, + bridge_hub_rococo_runtime::WASM_BINARY + .expect("WASM binary was not built, please build it!"), + ) + } + + #[test] + fn test_genesis() { + let j1 = super::genesis(); + let j2 = genesis(); + + assert_eq!(j1.top, j2.top); + assert_eq!(j1.children_default, j2.children_default); + } } } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml b/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml index 7efcf1e1585d..0b163ae1a207 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml @@ -45,6 +45,7 @@ sp-block-builder = { path = "../../../../../substrate/primitives/block-builder", sp-consensus-aura = { path = "../../../../../substrate/primitives/consensus/aura", default-features = false} sp-core = { path = "../../../../../substrate/primitives/core", default-features = false} sp-inherents = { path = "../../../../../substrate/primitives/inherents", default-features = false} +sp-genesis-builder = { path = "../../../../../substrate/primitives/genesis-builder", default-features = false } sp-offchain = { path = "../../../../../substrate/primitives/offchain", default-features = false} sp-runtime = { path = "../../../../../substrate/primitives/runtime", default-features = false} sp-session = { path = "../../../../../substrate/primitives/session", default-features = false} @@ -230,6 +231,7 @@ std = [ "sp-block-builder/std", "sp-consensus-aura/std", "sp-core/std", + "sp-genesis-builder/std", "sp-inherents/std", "sp-offchain/std", "sp-runtime/std", diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs index 2c6c85619c93..f649ebedeff9 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs @@ -60,6 +60,7 @@ use cumulus_primitives_core::ParaId; use frame_support::{ construct_runtime, dispatch::DispatchClass, + genesis_builder_helper::{build_config, create_default_config}, ord_parameter_types, parameter_types, traits::{ AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU32, ConstU64, ConstU8, EitherOfDiverse, @@ -1568,6 +1569,16 @@ impl_runtime_apis! { Ok(batches) } } + + impl sp_genesis_builder::GenesisBuilder for Runtime { + fn create_default_config() -> Vec { + create_default_config::() + } + + fn build_config(config: Vec) -> sp_genesis_builder::Result { + build_config::(config) + } + } } cumulus_pallet_parachain_system::register_validate_block! { diff --git a/cumulus/polkadot-parachain/Cargo.toml b/cumulus/polkadot-parachain/Cargo.toml index 56646efc61a8..c7a79e3e97ab 100644 --- a/cumulus/polkadot-parachain/Cargo.toml +++ b/cumulus/polkadot-parachain/Cargo.toml @@ -48,6 +48,7 @@ sp-io = { path = "../../substrate/primitives/io" } sp-core = { path = "../../substrate/primitives/core" } sp-session = { path = "../../substrate/primitives/session" } sc-consensus = { path = "../../substrate/client/consensus/common" } +sp-tracing = { path = "../../substrate/primitives/tracing" } sc-cli = { path = "../../substrate/client/cli" } sc-client-api = { path = "../../substrate/client/api" } sc-executor = { path = "../../substrate/client/executor" } diff --git a/cumulus/polkadot-parachain/src/chain_spec/asset_hubs.rs b/cumulus/polkadot-parachain/src/chain_spec/asset_hubs.rs index 636488cbfe2c..b4a73ff8aaa8 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/asset_hubs.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/asset_hubs.rs @@ -79,35 +79,30 @@ pub fn asset_hub_polkadot_development_config() -> AssetHubPolkadotChainSpec { properties.insert("tokenSymbol".into(), "DOT".into()); properties.insert("tokenDecimals".into(), 10.into()); - AssetHubPolkadotChainSpec::from_genesis( - // Name - "Polkadot Asset Hub Development", - // ID - "asset-hub-polkadot-dev", - ChainType::Local, - move || { - asset_hub_polkadot_genesis( - // initial collators. - vec![( - get_account_id_from_seed::("Alice"), - get_collator_keys_from_seed::("Alice"), - )], - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - ], - 1000.into(), - ) - }, - Vec::new(), - None, - None, - None, - Some(properties), + AssetHubPolkadotChainSpec::builder( + asset_hub_polkadot_runtime::WASM_BINARY + .expect("WASM binary was not built, please build it!"), Extensions { relay_chain: "polkadot-dev".into(), para_id: 1000 }, ) + .with_name("Polkadot Asset Hub Development") + .with_id("asset-hub-polkadot-dev") + .with_chain_type(ChainType::Local) + .with_genesis_config_patch(asset_hub_polkadot_genesis( + // initial collators. + vec![( + get_account_id_from_seed::("Alice"), + get_collator_keys_from_seed::("Alice"), + )], + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + ], + 1000.into(), + )) + .with_properties(properties) + .build() } pub fn asset_hub_polkadot_local_config() -> AssetHubPolkadotChainSpec { @@ -116,49 +111,45 @@ pub fn asset_hub_polkadot_local_config() -> AssetHubPolkadotChainSpec { properties.insert("tokenSymbol".into(), "DOT".into()); properties.insert("tokenDecimals".into(), 10.into()); - AssetHubPolkadotChainSpec::from_genesis( - // Name - "Polkadot Asset Hub Local", - // ID - "asset-hub-polkadot-local", - ChainType::Local, - move || { - asset_hub_polkadot_genesis( - // initial collators. - vec![ - ( - get_account_id_from_seed::("Alice"), - get_collator_keys_from_seed::("Alice"), - ), - ( - get_account_id_from_seed::("Bob"), - get_collator_keys_from_seed::("Bob"), - ), - ], - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Charlie"), - get_account_id_from_seed::("Dave"), - get_account_id_from_seed::("Eve"), - get_account_id_from_seed::("Ferdie"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - get_account_id_from_seed::("Charlie//stash"), - get_account_id_from_seed::("Dave//stash"), - get_account_id_from_seed::("Eve//stash"), - get_account_id_from_seed::("Ferdie//stash"), - ], - 1000.into(), - ) - }, - Vec::new(), - None, - None, - None, - Some(properties), + AssetHubPolkadotChainSpec::builder( + asset_hub_polkadot_runtime::WASM_BINARY + .expect("WASM binary was not built, please build it!"), Extensions { relay_chain: "polkadot-local".into(), para_id: 1000 }, ) + .with_name("Polkadot Asset Hub Local") + .with_id("asset-hub-polkadot-local") + .with_chain_type(ChainType::Local) + .with_genesis_config_patch(asset_hub_polkadot_genesis( + // initial collators. + vec![ + ( + get_account_id_from_seed::("Alice"), + get_collator_keys_from_seed::("Alice"), + ), + ( + get_account_id_from_seed::("Bob"), + get_collator_keys_from_seed::("Bob"), + ), + ], + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Charlie"), + get_account_id_from_seed::("Dave"), + get_account_id_from_seed::("Eve"), + get_account_id_from_seed::("Ferdie"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + get_account_id_from_seed::("Charlie//stash"), + get_account_id_from_seed::("Dave//stash"), + get_account_id_from_seed::("Eve//stash"), + get_account_id_from_seed::("Ferdie//stash"), + ], + 1000.into(), + )) + .with_boot_nodes(Vec::new()) + .with_properties(properties) + .build() } // Not used for syncing, but just to determine the genesis values set for the upgrade from shell. @@ -168,89 +159,81 @@ pub fn asset_hub_polkadot_config() -> AssetHubPolkadotChainSpec { properties.insert("tokenSymbol".into(), "DOT".into()); properties.insert("tokenDecimals".into(), 10.into()); - AssetHubPolkadotChainSpec::from_genesis( - // Name - "Polkadot Asset Hub", - // ID - "asset-hub-polkadot", - ChainType::Live, - move || { - asset_hub_polkadot_genesis( - // initial collators. - vec![ - ( - hex!("4c3d674d2a01060f0ded218e5dcc6f90c1726f43df79885eb3e22d97a20d5421") - .into(), - hex!("4c3d674d2a01060f0ded218e5dcc6f90c1726f43df79885eb3e22d97a20d5421") - .unchecked_into(), - ), - ( - hex!("c7d7d38d16bc23c6321152c50306212dc22c0efc04a2e52b5cccfc31ab3d7811") - .into(), - hex!("c7d7d38d16bc23c6321152c50306212dc22c0efc04a2e52b5cccfc31ab3d7811") - .unchecked_into(), - ), - ( - hex!("c5c07ba203d7375675f5c1ebe70f0a5bb729ae57b48bcc877fcc2ab21309b762") - .into(), - hex!("c5c07ba203d7375675f5c1ebe70f0a5bb729ae57b48bcc877fcc2ab21309b762") - .unchecked_into(), - ), - ( - hex!("0b2d0013fb974794bd7aa452465b567d48ef70373fe231a637c1fb7c547e85b3") - .into(), - hex!("0b2d0013fb974794bd7aa452465b567d48ef70373fe231a637c1fb7c547e85b3") - .unchecked_into(), - ), - ], - vec![], - 1000u32.into(), - ) - }, - vec![ - "/ip4/34.65.251.121/tcp/30334/p2p/12D3KooWG3GrM6XKMM4gp3cvemdwUvu96ziYoJmqmetLZBXE8bSa".parse().unwrap(), - "/ip4/34.65.35.228/tcp/30334/p2p/12D3KooWMRyTLrCEPcAQD6c4EnudL3vVzg9zji3whvsMYPUYevpq".parse().unwrap(), - "/ip4/34.83.247.146/tcp/30334/p2p/12D3KooWE4jFh5FpJDkWVZhnWtFnbSqRhdjvC7Dp9b8b3FTuubQC".parse().unwrap(), - "/ip4/104.199.117.230/tcp/30334/p2p/12D3KooWG9R8pVXKumVo2rdkeVD4j5PVhRTqmYgLHY3a4yPYgLqM".parse().unwrap(), - ], - None, - None, - None, - Some(properties), + AssetHubPolkadotChainSpec::builder( + asset_hub_polkadot_runtime::WASM_BINARY + .expect("WASM binary was not built, please build it!"), Extensions { relay_chain: "polkadot".into(), para_id: 1000 }, ) + .with_name("Polkadot Asset Hub") + .with_id("asset-hub-polkadot") + .with_chain_type(ChainType::Live) + .with_genesis_config_patch(asset_hub_polkadot_genesis( + // initial collators. + vec![ + ( + hex!("4c3d674d2a01060f0ded218e5dcc6f90c1726f43df79885eb3e22d97a20d5421").into(), + hex!("4c3d674d2a01060f0ded218e5dcc6f90c1726f43df79885eb3e22d97a20d5421") + .unchecked_into(), + ), + ( + hex!("c7d7d38d16bc23c6321152c50306212dc22c0efc04a2e52b5cccfc31ab3d7811").into(), + hex!("c7d7d38d16bc23c6321152c50306212dc22c0efc04a2e52b5cccfc31ab3d7811") + .unchecked_into(), + ), + ( + hex!("c5c07ba203d7375675f5c1ebe70f0a5bb729ae57b48bcc877fcc2ab21309b762").into(), + hex!("c5c07ba203d7375675f5c1ebe70f0a5bb729ae57b48bcc877fcc2ab21309b762") + .unchecked_into(), + ), + ( + hex!("0b2d0013fb974794bd7aa452465b567d48ef70373fe231a637c1fb7c547e85b3").into(), + hex!("0b2d0013fb974794bd7aa452465b567d48ef70373fe231a637c1fb7c547e85b3") + .unchecked_into(), + ), + ], + vec![], + 1000u32.into(), + )) + .with_boot_nodes(vec![ + "/ip4/34.65.251.121/tcp/30334/p2p/12D3KooWG3GrM6XKMM4gp3cvemdwUvu96ziYoJmqmetLZBXE8bSa" + .parse() + .unwrap(), + "/ip4/34.65.35.228/tcp/30334/p2p/12D3KooWMRyTLrCEPcAQD6c4EnudL3vVzg9zji3whvsMYPUYevpq" + .parse() + .unwrap(), + "/ip4/34.83.247.146/tcp/30334/p2p/12D3KooWE4jFh5FpJDkWVZhnWtFnbSqRhdjvC7Dp9b8b3FTuubQC" + .parse() + .unwrap(), + "/ip4/104.199.117.230/tcp/30334/p2p/12D3KooWG9R8pVXKumVo2rdkeVD4j5PVhRTqmYgLHY3a4yPYgLqM" + .parse() + .unwrap(), + ]) + .with_properties(properties) + .build() } fn asset_hub_polkadot_genesis( invulnerables: Vec<(AccountId, AssetHubPolkadotAuraId)>, endowed_accounts: Vec, id: ParaId, -) -> asset_hub_polkadot_runtime::RuntimeGenesisConfig { - asset_hub_polkadot_runtime::RuntimeGenesisConfig { - system: asset_hub_polkadot_runtime::SystemConfig { - code: asset_hub_polkadot_runtime::WASM_BINARY - .expect("WASM binary was not build, please build it!") - .to_vec(), - ..Default::default() - }, - balances: asset_hub_polkadot_runtime::BalancesConfig { - balances: endowed_accounts +) -> serde_json::Value { + serde_json::json!( { + "balances": { + "balances": endowed_accounts .iter() .cloned() .map(|k| (k, ASSET_HUB_POLKADOT_ED * 4096)) - .collect(), + .collect::>(), }, - parachain_info: asset_hub_polkadot_runtime::ParachainInfoConfig { - parachain_id: id, - ..Default::default() + "parachainInfo": { + "parachainId": id, }, - collator_selection: asset_hub_polkadot_runtime::CollatorSelectionConfig { - invulnerables: invulnerables.iter().cloned().map(|(acc, _)| acc).collect(), - candidacy_bond: ASSET_HUB_POLKADOT_ED * 16, - ..Default::default() + "collatorSelection": { + "invulnerables": invulnerables.iter().cloned().map(|(acc, _)| acc).collect::>(), + "candidacyBond": ASSET_HUB_POLKADOT_ED * 16, }, - session: asset_hub_polkadot_runtime::SessionConfig { - keys: invulnerables + "session": { + "keys": invulnerables .into_iter() .map(|(acc, aura)| { ( @@ -259,18 +242,12 @@ fn asset_hub_polkadot_genesis( asset_hub_polkadot_session_keys(aura), // session keys ) }) - .collect(), - }, - // no need to pass anything to aura, in fact it will panic if we do. Session will take care - // of this. - aura: Default::default(), - aura_ext: Default::default(), - parachain_system: Default::default(), - polkadot_xcm: asset_hub_polkadot_runtime::PolkadotXcmConfig { - safe_xcm_version: Some(SAFE_XCM_VERSION), - ..Default::default() + .collect::>(), }, - } + "polkadotXcm": { + "safeXcmVersion": Some(SAFE_XCM_VERSION), + } + }) } pub fn asset_hub_kusama_development_config() -> AssetHubKusamaChainSpec { @@ -279,35 +256,29 @@ pub fn asset_hub_kusama_development_config() -> AssetHubKusamaChainSpec { properties.insert("tokenSymbol".into(), "KSM".into()); properties.insert("tokenDecimals".into(), 12.into()); - AssetHubKusamaChainSpec::from_genesis( - // Name - "Kusama Asset Hub Development", - // ID - "asset-hub-kusama-dev", - ChainType::Local, - move || { - asset_hub_kusama_genesis( - // initial collators. - vec![( - get_account_id_from_seed::("Alice"), - get_collator_keys_from_seed::("Alice"), - )], - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - ], - 1000.into(), - ) - }, - Vec::new(), - None, - None, - None, - Some(properties), + AssetHubKusamaChainSpec::builder( + asset_hub_kusama_runtime::WASM_BINARY.expect("WASM binary was not built, please build it!"), Extensions { relay_chain: "kusama-dev".into(), para_id: 1000 }, ) + .with_name("Kusama Asset Hub Development") + .with_id("asset-hub-kusama-dev") + .with_chain_type(ChainType::Local) + .with_genesis_config_patch(asset_hub_kusama_genesis( + // initial collators. + vec![( + get_account_id_from_seed::("Alice"), + get_collator_keys_from_seed::("Alice"), + )], + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + ], + 1000.into(), + )) + .with_properties(properties) + .build() } pub fn asset_hub_kusama_local_config() -> AssetHubKusamaChainSpec { @@ -316,49 +287,43 @@ pub fn asset_hub_kusama_local_config() -> AssetHubKusamaChainSpec { properties.insert("tokenSymbol".into(), "KSM".into()); properties.insert("tokenDecimals".into(), 12.into()); - AssetHubKusamaChainSpec::from_genesis( - // Name - "Kusama Asset Hub Local", - // ID - "asset-hub-kusama-local", - ChainType::Local, - move || { - asset_hub_kusama_genesis( - // initial collators. - vec![ - ( - get_account_id_from_seed::("Alice"), - get_collator_keys_from_seed::("Alice"), - ), - ( - get_account_id_from_seed::("Bob"), - get_collator_keys_from_seed::("Bob"), - ), - ], - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Charlie"), - get_account_id_from_seed::("Dave"), - get_account_id_from_seed::("Eve"), - get_account_id_from_seed::("Ferdie"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - get_account_id_from_seed::("Charlie//stash"), - get_account_id_from_seed::("Dave//stash"), - get_account_id_from_seed::("Eve//stash"), - get_account_id_from_seed::("Ferdie//stash"), - ], - 1000.into(), - ) - }, - Vec::new(), - None, - None, - None, - Some(properties), + AssetHubKusamaChainSpec::builder( + asset_hub_kusama_runtime::WASM_BINARY.expect("WASM binary was not built, please build it!"), Extensions { relay_chain: "kusama-local".into(), para_id: 1000 }, ) + .with_name("Kusama Asset Hub Local") + .with_id("asset-hub-kusama-local") + .with_chain_type(ChainType::Local) + .with_genesis_config_patch(asset_hub_kusama_genesis( + // initial collators. + vec![ + ( + get_account_id_from_seed::("Alice"), + get_collator_keys_from_seed::("Alice"), + ), + ( + get_account_id_from_seed::("Bob"), + get_collator_keys_from_seed::("Bob"), + ), + ], + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Charlie"), + get_account_id_from_seed::("Dave"), + get_account_id_from_seed::("Eve"), + get_account_id_from_seed::("Ferdie"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + get_account_id_from_seed::("Charlie//stash"), + get_account_id_from_seed::("Dave//stash"), + get_account_id_from_seed::("Eve//stash"), + get_account_id_from_seed::("Ferdie//stash"), + ], + 1000.into(), + )) + .with_properties(properties) + .build() } pub fn asset_hub_kusama_config() -> AssetHubKusamaChainSpec { @@ -367,84 +332,66 @@ pub fn asset_hub_kusama_config() -> AssetHubKusamaChainSpec { properties.insert("tokenSymbol".into(), "KSM".into()); properties.insert("tokenDecimals".into(), 12.into()); - AssetHubKusamaChainSpec::from_genesis( - // Name - "Kusama Asset Hub", - // ID - "asset-hub-kusama", - ChainType::Live, - move || { - asset_hub_kusama_genesis( - // initial collators. - vec![ - ( - hex!("50673d59020488a4ffc9d8c6de3062a65977046e6990915617f85fef6d349730") - .into(), - hex!("50673d59020488a4ffc9d8c6de3062a65977046e6990915617f85fef6d349730") - .unchecked_into(), - ), - ( - hex!("fe8102dbc244e7ea2babd9f53236d67403b046154370da5c3ea99def0bd0747a") - .into(), - hex!("fe8102dbc244e7ea2babd9f53236d67403b046154370da5c3ea99def0bd0747a") - .unchecked_into(), - ), - ( - hex!("38144b5398e5d0da5ec936a3af23f5a96e782f676ab19d45f29075ee92eca76a") - .into(), - hex!("38144b5398e5d0da5ec936a3af23f5a96e782f676ab19d45f29075ee92eca76a") - .unchecked_into(), - ), - ( - hex!("3253947640e309120ae70fa458dcacb915e2ddd78f930f52bd3679ec63fc4415") - .into(), - hex!("3253947640e309120ae70fa458dcacb915e2ddd78f930f52bd3679ec63fc4415") - .unchecked_into(), - ), - ], - Vec::new(), - 1000.into(), - ) - }, - Vec::new(), - None, - None, - None, - Some(properties), + AssetHubKusamaChainSpec::builder( + asset_hub_kusama_runtime::WASM_BINARY.expect("WASM binary was not built, please build it!"), Extensions { relay_chain: "kusama".into(), para_id: 1000 }, ) + .with_name("Kusama Asset Hub") + .with_id("asset-hub-kusama") + .with_chain_type(ChainType::Live) + .with_genesis_config_patch(asset_hub_kusama_genesis( + // initial collators. + vec![ + ( + hex!("50673d59020488a4ffc9d8c6de3062a65977046e6990915617f85fef6d349730").into(), + hex!("50673d59020488a4ffc9d8c6de3062a65977046e6990915617f85fef6d349730") + .unchecked_into(), + ), + ( + hex!("fe8102dbc244e7ea2babd9f53236d67403b046154370da5c3ea99def0bd0747a").into(), + hex!("fe8102dbc244e7ea2babd9f53236d67403b046154370da5c3ea99def0bd0747a") + .unchecked_into(), + ), + ( + hex!("38144b5398e5d0da5ec936a3af23f5a96e782f676ab19d45f29075ee92eca76a").into(), + hex!("38144b5398e5d0da5ec936a3af23f5a96e782f676ab19d45f29075ee92eca76a") + .unchecked_into(), + ), + ( + hex!("3253947640e309120ae70fa458dcacb915e2ddd78f930f52bd3679ec63fc4415").into(), + hex!("3253947640e309120ae70fa458dcacb915e2ddd78f930f52bd3679ec63fc4415") + .unchecked_into(), + ), + ], + Vec::new(), + 1000.into(), + )) + .with_properties(properties) + .build() } fn asset_hub_kusama_genesis( invulnerables: Vec<(AccountId, AuraId)>, endowed_accounts: Vec, id: ParaId, -) -> asset_hub_kusama_runtime::RuntimeGenesisConfig { - asset_hub_kusama_runtime::RuntimeGenesisConfig { - system: asset_hub_kusama_runtime::SystemConfig { - code: asset_hub_kusama_runtime::WASM_BINARY - .expect("WASM binary was not build, please build it!") - .to_vec(), - ..Default::default() - }, - balances: asset_hub_kusama_runtime::BalancesConfig { - balances: endowed_accounts +) -> serde_json::Value { + serde_json::json!( { + "balances": { + "balances": endowed_accounts .iter() .cloned() .map(|k| (k, ASSET_HUB_KUSAMA_ED * 524_288)) - .collect(), + .collect::>(), }, - parachain_info: asset_hub_kusama_runtime::ParachainInfoConfig { - parachain_id: id, - ..Default::default() + "parachainInfo": { + "parachainId": id, }, - collator_selection: asset_hub_kusama_runtime::CollatorSelectionConfig { - invulnerables: invulnerables.iter().cloned().map(|(acc, _)| acc).collect(), - candidacy_bond: ASSET_HUB_KUSAMA_ED * 16, - ..Default::default() + "collatorSelection": { + "invulnerables": invulnerables.iter().cloned().map(|(acc, _)| acc).collect::>(), + "candidacyBond": ASSET_HUB_KUSAMA_ED * 16, }, - session: asset_hub_kusama_runtime::SessionConfig { - keys: invulnerables + "session": { + "keys": invulnerables .into_iter() .map(|(acc, aura)| { ( @@ -453,16 +400,12 @@ fn asset_hub_kusama_genesis( asset_hub_kusama_session_keys(aura), // session keys ) }) - .collect(), + .collect::>(), }, - aura: Default::default(), - aura_ext: Default::default(), - parachain_system: Default::default(), - polkadot_xcm: asset_hub_kusama_runtime::PolkadotXcmConfig { - safe_xcm_version: Some(SAFE_XCM_VERSION), - ..Default::default() + "polkadotXcm": { + "safeXcmVersion": Some(SAFE_XCM_VERSION), }, - } + }) } pub fn asset_hub_westend_development_config() -> AssetHubWestendChainSpec { @@ -470,35 +413,30 @@ pub fn asset_hub_westend_development_config() -> AssetHubWestendChainSpec { properties.insert("tokenSymbol".into(), "WND".into()); properties.insert("tokenDecimals".into(), 12.into()); - AssetHubWestendChainSpec::from_genesis( - // Name - "Westend Asset Hub Development", - // ID - "asset-hub-westend-dev", - ChainType::Local, - move || { - asset_hub_westend_genesis( - // initial collators. - vec![( - get_account_id_from_seed::("Alice"), - get_collator_keys_from_seed::("Alice"), - )], - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - ], - 1000.into(), - ) - }, - Vec::new(), - None, - None, - None, - Some(properties), + AssetHubWestendChainSpec::builder( + asset_hub_westend_runtime::WASM_BINARY + .expect("WASM binary was not built, please build it!"), Extensions { relay_chain: "westend".into(), para_id: 1000 }, ) + .with_name("Westend Asset Hub Development") + .with_id("asset-hub-westend-dev") + .with_chain_type(ChainType::Local) + .with_genesis_config_patch(asset_hub_westend_genesis( + // initial collators. + vec![( + get_account_id_from_seed::("Alice"), + get_collator_keys_from_seed::("Alice"), + )], + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + ], + 1000.into(), + )) + .with_properties(properties) + .build() } pub fn asset_hub_westend_local_config() -> AssetHubWestendChainSpec { @@ -506,49 +444,44 @@ pub fn asset_hub_westend_local_config() -> AssetHubWestendChainSpec { properties.insert("tokenSymbol".into(), "WND".into()); properties.insert("tokenDecimals".into(), 12.into()); - AssetHubWestendChainSpec::from_genesis( - // Name - "Westend Asset Hub Local", - // ID - "asset-hub-westend-local", - ChainType::Local, - move || { - asset_hub_westend_genesis( - // initial collators. - vec![ - ( - get_account_id_from_seed::("Alice"), - get_collator_keys_from_seed::("Alice"), - ), - ( - get_account_id_from_seed::("Bob"), - get_collator_keys_from_seed::("Bob"), - ), - ], - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Charlie"), - get_account_id_from_seed::("Dave"), - get_account_id_from_seed::("Eve"), - get_account_id_from_seed::("Ferdie"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - get_account_id_from_seed::("Charlie//stash"), - get_account_id_from_seed::("Dave//stash"), - get_account_id_from_seed::("Eve//stash"), - get_account_id_from_seed::("Ferdie//stash"), - ], - 1000.into(), - ) - }, - Vec::new(), - None, - None, - None, - Some(properties), + AssetHubWestendChainSpec::builder( + asset_hub_westend_runtime::WASM_BINARY + .expect("WASM binary was not built, please build it!"), Extensions { relay_chain: "westend-local".into(), para_id: 1000 }, ) + .with_name("Westend Asset Hub Local") + .with_id("asset-hub-westend-local") + .with_chain_type(ChainType::Local) + .with_genesis_config_patch(asset_hub_westend_genesis( + // initial collators. + vec![ + ( + get_account_id_from_seed::("Alice"), + get_collator_keys_from_seed::("Alice"), + ), + ( + get_account_id_from_seed::("Bob"), + get_collator_keys_from_seed::("Bob"), + ), + ], + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Charlie"), + get_account_id_from_seed::("Dave"), + get_account_id_from_seed::("Eve"), + get_account_id_from_seed::("Ferdie"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + get_account_id_from_seed::("Charlie//stash"), + get_account_id_from_seed::("Dave//stash"), + get_account_id_from_seed::("Eve//stash"), + get_account_id_from_seed::("Ferdie//stash"), + ], + 1000.into(), + )) + .with_properties(properties) + .build() } pub fn asset_hub_westend_config() -> AssetHubWestendChainSpec { @@ -556,84 +489,67 @@ pub fn asset_hub_westend_config() -> AssetHubWestendChainSpec { properties.insert("tokenSymbol".into(), "WND".into()); properties.insert("tokenDecimals".into(), 12.into()); - AssetHubWestendChainSpec::from_genesis( - // Name - "Westend Asset Hub", - // ID - "asset-hub-westend", - ChainType::Live, - move || { - asset_hub_westend_genesis( - // initial collators. - vec![ - ( - hex!("9cfd429fa002114f33c1d3e211501d62830c9868228eb3b4b8ae15a83de04325") - .into(), - hex!("9cfd429fa002114f33c1d3e211501d62830c9868228eb3b4b8ae15a83de04325") - .unchecked_into(), - ), - ( - hex!("12a03fb4e7bda6c9a07ec0a11d03c24746943e054ff0bb04938970104c783876") - .into(), - hex!("12a03fb4e7bda6c9a07ec0a11d03c24746943e054ff0bb04938970104c783876") - .unchecked_into(), - ), - ( - hex!("1256436307dfde969324e95b8c62cb9101f520a39435e6af0f7ac07b34e1931f") - .into(), - hex!("1256436307dfde969324e95b8c62cb9101f520a39435e6af0f7ac07b34e1931f") - .unchecked_into(), - ), - ( - hex!("98102b7bca3f070f9aa19f58feed2c0a4e107d203396028ec17a47e1ed80e322") - .into(), - hex!("98102b7bca3f070f9aa19f58feed2c0a4e107d203396028ec17a47e1ed80e322") - .unchecked_into(), - ), - ], - Vec::new(), - 1000.into(), - ) - }, - Vec::new(), - None, - None, - None, - Some(properties), + AssetHubWestendChainSpec::builder( + asset_hub_westend_runtime::WASM_BINARY + .expect("WASM binary was not built, please build it!"), Extensions { relay_chain: "westend".into(), para_id: 1000 }, ) + .with_name("Westend Asset Hub") + .with_id("asset-hub-westend") + .with_chain_type(ChainType::Live) + .with_genesis_config_patch(asset_hub_westend_genesis( + // initial collators. + vec![ + ( + hex!("9cfd429fa002114f33c1d3e211501d62830c9868228eb3b4b8ae15a83de04325").into(), + hex!("9cfd429fa002114f33c1d3e211501d62830c9868228eb3b4b8ae15a83de04325") + .unchecked_into(), + ), + ( + hex!("12a03fb4e7bda6c9a07ec0a11d03c24746943e054ff0bb04938970104c783876").into(), + hex!("12a03fb4e7bda6c9a07ec0a11d03c24746943e054ff0bb04938970104c783876") + .unchecked_into(), + ), + ( + hex!("1256436307dfde969324e95b8c62cb9101f520a39435e6af0f7ac07b34e1931f").into(), + hex!("1256436307dfde969324e95b8c62cb9101f520a39435e6af0f7ac07b34e1931f") + .unchecked_into(), + ), + ( + hex!("98102b7bca3f070f9aa19f58feed2c0a4e107d203396028ec17a47e1ed80e322").into(), + hex!("98102b7bca3f070f9aa19f58feed2c0a4e107d203396028ec17a47e1ed80e322") + .unchecked_into(), + ), + ], + Vec::new(), + 1000.into(), + )) + .with_properties(properties) + .build() } fn asset_hub_westend_genesis( invulnerables: Vec<(AccountId, AuraId)>, endowed_accounts: Vec, id: ParaId, -) -> asset_hub_westend_runtime::RuntimeGenesisConfig { - asset_hub_westend_runtime::RuntimeGenesisConfig { - system: asset_hub_westend_runtime::SystemConfig { - code: asset_hub_westend_runtime::WASM_BINARY - .expect("WASM binary was not build, please build it!") - .to_vec(), - ..Default::default() - }, - balances: asset_hub_westend_runtime::BalancesConfig { - balances: endowed_accounts +) -> serde_json::Value { + serde_json::json!({ + "balances": { + "balances": endowed_accounts .iter() .cloned() .map(|k| (k, ASSET_HUB_WESTEND_ED * 4096)) - .collect(), + .collect::>(), }, - parachain_info: asset_hub_westend_runtime::ParachainInfoConfig { - parachain_id: id, - ..Default::default() + "parachainInfo": { + "parachainId": id, }, - collator_selection: asset_hub_westend_runtime::CollatorSelectionConfig { - invulnerables: invulnerables.iter().cloned().map(|(acc, _)| acc).collect(), - candidacy_bond: ASSET_HUB_WESTEND_ED * 16, - ..Default::default() + "collatorSelection": { + "invulnerables": invulnerables.iter().cloned().map(|(acc, _)| acc).collect::>(), + "candidacyBond": ASSET_HUB_WESTEND_ED * 16, }, - session: asset_hub_westend_runtime::SessionConfig { - keys: invulnerables + "session": { + "keys": invulnerables .into_iter() .map(|(acc, aura)| { ( @@ -642,18 +558,12 @@ fn asset_hub_westend_genesis( asset_hub_westend_session_keys(aura), // session keys ) }) - .collect(), + .collect::>(), }, - // no need to pass anything to aura, in fact it will panic if we do. Session will take care - // of this. - aura: Default::default(), - aura_ext: Default::default(), - parachain_system: Default::default(), - polkadot_xcm: asset_hub_westend_runtime::PolkadotXcmConfig { - safe_xcm_version: Some(SAFE_XCM_VERSION), - ..Default::default() + "polkadotXcm": { + "safeXcmVersion": Some(SAFE_XCM_VERSION), }, - } + }) } pub fn asset_hub_rococo_development_config() -> AssetHubRococoChainSpec { @@ -688,35 +598,29 @@ fn asset_hub_rococo_like_development_config( chain_id: &str, para_id: u32, ) -> AssetHubRococoChainSpec { - AssetHubRococoChainSpec::from_genesis( - // Name - name, - // ID - chain_id, - ChainType::Local, - move || { - asset_hub_rococo_genesis( - // initial collators. - vec![( - get_account_id_from_seed::("Alice"), - get_collator_keys_from_seed::("Alice"), - )], - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - ], - para_id.into(), - ) - }, - Vec::new(), - None, - None, - None, - Some(properties), + AssetHubRococoChainSpec::builder( + asset_hub_rococo_runtime::WASM_BINARY.expect("WASM binary was not built, please build it!"), Extensions { relay_chain: "rococo-dev".into(), para_id }, ) + .with_name(name) + .with_id(chain_id) + .with_chain_type(ChainType::Local) + .with_genesis_config_patch(asset_hub_rococo_genesis( + // initial collators. + vec![( + get_account_id_from_seed::("Alice"), + get_collator_keys_from_seed::("Alice"), + )], + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + ], + para_id.into(), + )) + .with_properties(properties) + .build() } pub fn asset_hub_rococo_local_config() -> AssetHubRococoChainSpec { @@ -751,49 +655,43 @@ fn asset_hub_rococo_like_local_config( chain_id: &str, para_id: u32, ) -> AssetHubRococoChainSpec { - AssetHubRococoChainSpec::from_genesis( - // Name - name, - // ID - chain_id, - ChainType::Local, - move || { - asset_hub_rococo_genesis( - // initial collators. - vec![ - ( - get_account_id_from_seed::("Alice"), - get_collator_keys_from_seed::("Alice"), - ), - ( - get_account_id_from_seed::("Bob"), - get_collator_keys_from_seed::("Bob"), - ), - ], - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Charlie"), - get_account_id_from_seed::("Dave"), - get_account_id_from_seed::("Eve"), - get_account_id_from_seed::("Ferdie"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - get_account_id_from_seed::("Charlie//stash"), - get_account_id_from_seed::("Dave//stash"), - get_account_id_from_seed::("Eve//stash"), - get_account_id_from_seed::("Ferdie//stash"), - ], - para_id.into(), - ) - }, - Vec::new(), - None, - None, - None, - Some(properties), + AssetHubRococoChainSpec::builder( + asset_hub_rococo_runtime::WASM_BINARY.expect("WASM binary was not built, please build it!"), Extensions { relay_chain: "rococo-local".into(), para_id }, ) + .with_name(name) + .with_id(chain_id) + .with_chain_type(ChainType::Local) + .with_genesis_config_patch(asset_hub_rococo_genesis( + // initial collators. + vec![ + ( + get_account_id_from_seed::("Alice"), + get_collator_keys_from_seed::("Alice"), + ), + ( + get_account_id_from_seed::("Bob"), + get_collator_keys_from_seed::("Bob"), + ), + ], + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Charlie"), + get_account_id_from_seed::("Dave"), + get_account_id_from_seed::("Eve"), + get_account_id_from_seed::("Ferdie"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + get_account_id_from_seed::("Charlie//stash"), + get_account_id_from_seed::("Dave//stash"), + get_account_id_from_seed::("Eve//stash"), + get_account_id_from_seed::("Ferdie//stash"), + ], + para_id.into(), + )) + .with_properties(properties) + .build() } pub fn asset_hub_rococo_genesis_config() -> AssetHubRococoChainSpec { @@ -801,56 +699,46 @@ pub fn asset_hub_rococo_genesis_config() -> AssetHubRococoChainSpec { properties.insert("tokenSymbol".into(), "ROC".into()); properties.insert("tokenDecimals".into(), 12.into()); let para_id = 1000; - AssetHubRococoChainSpec::from_genesis( - // Name - "Rococo Asset Hub", - // ID - "asset-hub-rococo", - ChainType::Live, - move || { - asset_hub_rococo_genesis( - // initial collators. - vec![ - // E8XC6rTJRsioKCp6KMy6zd24ykj4gWsusZ3AkSeyavpVBAG - ( - hex!("44cb62d1d6cdd2fff2a5ef3bb7ef827be5b3e117a394ecaa634d8dd9809d5608") - .into(), - hex!("44cb62d1d6cdd2fff2a5ef3bb7ef827be5b3e117a394ecaa634d8dd9809d5608") - .unchecked_into(), - ), - // G28iWEybndgGRbhfx83t7Q42YhMPByHpyqWDUgeyoGF94ri - ( - hex!("9864b85e23aa4506643db9879c3dbbeabaa94d269693a4447f537dd6b5893944") - .into(), - hex!("9864b85e23aa4506643db9879c3dbbeabaa94d269693a4447f537dd6b5893944") - .unchecked_into(), - ), - // G839e2eMiq7UXbConsY6DS1XDAYG2XnQxAmLuRLGGQ3Px9c - ( - hex!("9ce5741ee2f1ac3bdedbde9f3339048f4da2cb88ddf33a0977fa0b4cf86e2948") - .into(), - hex!("9ce5741ee2f1ac3bdedbde9f3339048f4da2cb88ddf33a0977fa0b4cf86e2948") - .unchecked_into(), - ), - // GLao4ukFUW6qhexuZowdFrKa2NLCfnEjZMftSXXfvGv1vvt - ( - hex!("a676ed15f5a325eab49ed8d5f8c00f3f814b19bb58cda14ad10894c078dd337f") - .into(), - hex!("a676ed15f5a325eab49ed8d5f8c00f3f814b19bb58cda14ad10894c078dd337f") - .unchecked_into(), - ), - ], - Vec::new(), - para_id.into(), - ) - }, - Vec::new(), - None, - None, - None, - Some(properties), + AssetHubRococoChainSpec::builder( + asset_hub_rococo_runtime::WASM_BINARY.expect("WASM binary was not built, please build it!"), Extensions { relay_chain: "rococo".into(), para_id }, ) + .with_name("Rococo Asset Hub") + .with_id("asset-hub-rococo") + .with_chain_type(ChainType::Live) + .with_genesis_config_patch(asset_hub_rococo_genesis( + // initial collators. + vec![ + // E8XC6rTJRsioKCp6KMy6zd24ykj4gWsusZ3AkSeyavpVBAG + ( + hex!("44cb62d1d6cdd2fff2a5ef3bb7ef827be5b3e117a394ecaa634d8dd9809d5608").into(), + hex!("44cb62d1d6cdd2fff2a5ef3bb7ef827be5b3e117a394ecaa634d8dd9809d5608") + .unchecked_into(), + ), + // G28iWEybndgGRbhfx83t7Q42YhMPByHpyqWDUgeyoGF94ri + ( + hex!("9864b85e23aa4506643db9879c3dbbeabaa94d269693a4447f537dd6b5893944").into(), + hex!("9864b85e23aa4506643db9879c3dbbeabaa94d269693a4447f537dd6b5893944") + .unchecked_into(), + ), + // G839e2eMiq7UXbConsY6DS1XDAYG2XnQxAmLuRLGGQ3Px9c + ( + hex!("9ce5741ee2f1ac3bdedbde9f3339048f4da2cb88ddf33a0977fa0b4cf86e2948").into(), + hex!("9ce5741ee2f1ac3bdedbde9f3339048f4da2cb88ddf33a0977fa0b4cf86e2948") + .unchecked_into(), + ), + // GLao4ukFUW6qhexuZowdFrKa2NLCfnEjZMftSXXfvGv1vvt + ( + hex!("a676ed15f5a325eab49ed8d5f8c00f3f814b19bb58cda14ad10894c078dd337f").into(), + hex!("a676ed15f5a325eab49ed8d5f8c00f3f814b19bb58cda14ad10894c078dd337f") + .unchecked_into(), + ), + ], + Vec::new(), + para_id.into(), + )) + .with_properties(properties) + .build() } pub fn asset_hub_wococo_genesis_config() -> AssetHubWococoChainSpec { @@ -859,87 +747,71 @@ pub fn asset_hub_wococo_genesis_config() -> AssetHubWococoChainSpec { properties.insert("tokenSymbol".into(), "WOC".into()); properties.insert("tokenDecimals".into(), 12.into()); let para_id = 1000; - AssetHubWococoChainSpec::from_genesis( - // Name - "Wococo Asset Hub", - // ID - "asset-hub-wococo", - ChainType::Live, - move || { - asset_hub_rococo_genesis( - // initial collators. - vec![ - // 5C8RGkS8t5K93fB2hkgKbvSYs5iG6AknJMuQmbBDeazon9Lj - ( - hex!("02d526f43cf27e94f478f9db785dc86052a77c695e7c855211839d3fde3ce534") - .into(), - hex!("02d526f43cf27e94f478f9db785dc86052a77c695e7c855211839d3fde3ce534") - .unchecked_into(), - ), - // 5GePeDZQeBagXH7kH5QPKnQKi39Z5hoYFB5FmUtEvc4yxKej - ( - hex!("caa1f623ca183296c4521b56cc29c484ca017830f8cb538f30f2d4664d631814") - .into(), - hex!("caa1f623ca183296c4521b56cc29c484ca017830f8cb538f30f2d4664d631814") - .unchecked_into(), - ), - // 5CfnTTb9NMJDNKDntA83mHKoedZ7wjDC8ypLCTDd4NwUx3zv - ( - hex!("1ac112d635db2bd34e79ae2b99486cf7c0b71a928668e4feb3dc4633d368f965") - .into(), - hex!("1ac112d635db2bd34e79ae2b99486cf7c0b71a928668e4feb3dc4633d368f965") - .unchecked_into(), - ), - // 5EqheiwiG22gvGpN7cvrbeaQzhg7rzsYYVkYK4yj5vRrTQRQ - ( - hex!("7ac9d11be07334cd27e9eb849f5fc7677a10ad36b6ab38b377d3c8b2c0b08b66") - .into(), - hex!("7ac9d11be07334cd27e9eb849f5fc7677a10ad36b6ab38b377d3c8b2c0b08b66") - .unchecked_into(), - ), - ], - Vec::new(), - para_id.into(), - ) - }, - Vec::new(), - None, - None, - None, - Some(properties), + AssetHubRococoChainSpec::builder( + asset_hub_rococo_runtime::WASM_BINARY.expect("WASM binary was not built, please build it!"), Extensions { relay_chain: "wococo".into(), para_id }, ) + .with_name("Wococo Asset Hub") + .with_id("asset-hub-wococo") + .with_chain_type(ChainType::Live) + .with_genesis_config_patch(asset_hub_rococo_genesis( + // initial collators. + vec![ + // 5C8RGkS8t5K93fB2hkgKbvSYs5iG6AknJMuQmbBDeazon9Lj + ( + hex!("02d526f43cf27e94f478f9db785dc86052a77c695e7c855211839d3fde3ce534").into(), + hex!("02d526f43cf27e94f478f9db785dc86052a77c695e7c855211839d3fde3ce534") + .unchecked_into(), + ), + // 5GePeDZQeBagXH7kH5QPKnQKi39Z5hoYFB5FmUtEvc4yxKej + ( + hex!("caa1f623ca183296c4521b56cc29c484ca017830f8cb538f30f2d4664d631814").into(), + hex!("caa1f623ca183296c4521b56cc29c484ca017830f8cb538f30f2d4664d631814") + .unchecked_into(), + ), + // 5CfnTTb9NMJDNKDntA83mHKoedZ7wjDC8ypLCTDd4NwUx3zv + ( + hex!("1ac112d635db2bd34e79ae2b99486cf7c0b71a928668e4feb3dc4633d368f965").into(), + hex!("1ac112d635db2bd34e79ae2b99486cf7c0b71a928668e4feb3dc4633d368f965") + .unchecked_into(), + ), + // 5EqheiwiG22gvGpN7cvrbeaQzhg7rzsYYVkYK4yj5vRrTQRQ + ( + hex!("7ac9d11be07334cd27e9eb849f5fc7677a10ad36b6ab38b377d3c8b2c0b08b66").into(), + hex!("7ac9d11be07334cd27e9eb849f5fc7677a10ad36b6ab38b377d3c8b2c0b08b66") + .unchecked_into(), + ), + ], + Vec::new(), + para_id.into(), + )) + .with_properties(properties) + .build() } fn asset_hub_rococo_genesis( invulnerables: Vec<(AccountId, AuraId)>, endowed_accounts: Vec, id: ParaId, -) -> asset_hub_rococo_runtime::RuntimeGenesisConfig { - asset_hub_rococo_runtime::RuntimeGenesisConfig { - system: asset_hub_rococo_runtime::SystemConfig { - code: asset_hub_rococo_runtime::WASM_BINARY - .expect("WASM binary was not build, please build it!") - .to_vec(), - ..Default::default() - }, - balances: asset_hub_rococo_runtime::BalancesConfig { +) -> serde_json::Value { + serde_json::json!({ + "balances": asset_hub_rococo_runtime::BalancesConfig { balances: endowed_accounts .iter() .cloned() .map(|k| (k, ASSET_HUB_ROCOCO_ED * 524_288)) .collect(), }, - parachain_info: asset_hub_rococo_runtime::ParachainInfoConfig { + "parachainInfo": asset_hub_rococo_runtime::ParachainInfoConfig { parachain_id: id, ..Default::default() }, - collator_selection: asset_hub_rococo_runtime::CollatorSelectionConfig { + "collatorSelection": asset_hub_rococo_runtime::CollatorSelectionConfig { invulnerables: invulnerables.iter().cloned().map(|(acc, _)| acc).collect(), candidacy_bond: ASSET_HUB_ROCOCO_ED * 16, ..Default::default() }, - session: asset_hub_rococo_runtime::SessionConfig { + "session": asset_hub_rococo_runtime::SessionConfig { keys: invulnerables .into_iter() .map(|(acc, aura)| { @@ -951,12 +823,9 @@ fn asset_hub_rococo_genesis( }) .collect(), }, - aura: Default::default(), - aura_ext: Default::default(), - parachain_system: Default::default(), - polkadot_xcm: asset_hub_rococo_runtime::PolkadotXcmConfig { + "polkadotXcm": asset_hub_rococo_runtime::PolkadotXcmConfig { safe_xcm_version: Some(SAFE_XCM_VERSION), ..Default::default() - }, - } + } + }) } diff --git a/cumulus/polkadot-parachain/src/chain_spec/bridge_hubs.rs b/cumulus/polkadot-parachain/src/chain_spec/bridge_hubs.rs index 2910d81ee296..71fb9e5b1404 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/bridge_hubs.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/bridge_hubs.rs @@ -215,8 +215,7 @@ pub mod rococo { parachains_common::rococo::currency::EXISTENTIAL_DEPOSIT; /// Specialized `ChainSpec` for the normal parachain runtime. - pub type BridgeHubChainSpec = - sc_service::GenericChainSpec; + pub type BridgeHubChainSpec = sc_service::GenericChainSpec<(), Extensions>; pub type RuntimeApi = bridge_hub_rococo_runtime::RuntimeApi; @@ -235,52 +234,47 @@ pub mod rococo { properties.insert("tokenDecimals".into(), 12.into()); modify_props(&mut properties); - BridgeHubChainSpec::from_genesis( - // Name - chain_name, - // ID - super::ensure_id(id).expect("invalid id"), - ChainType::Local, - move || { - genesis( - // initial collators. - vec![ - ( - get_account_id_from_seed::("Alice"), - get_collator_keys_from_seed::("Alice"), - ), - ( - get_account_id_from_seed::("Bob"), - get_collator_keys_from_seed::("Bob"), - ), - ], - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Charlie"), - get_account_id_from_seed::("Dave"), - get_account_id_from_seed::("Eve"), - get_account_id_from_seed::("Ferdie"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - get_account_id_from_seed::("Charlie//stash"), - get_account_id_from_seed::("Dave//stash"), - get_account_id_from_seed::("Eve//stash"), - get_account_id_from_seed::("Ferdie//stash"), - ], - para_id, - bridges_pallet_owner_seed - .as_ref() - .map(|seed| get_account_id_from_seed::(seed)), - ) - }, - Vec::new(), - None, - None, - None, - Some(properties), + BridgeHubChainSpec::builder( + bridge_hub_rococo_runtime::WASM_BINARY + .expect("WASM binary was not built, please build it!"), Extensions { relay_chain: relay_chain.to_string(), para_id: para_id.into() }, ) + .with_name(chain_name) + .with_id(super::ensure_id(id).expect("invalid id")) + .with_chain_type(ChainType::Local) + .with_genesis_config_patch(genesis( + // initial collators. + vec![ + ( + get_account_id_from_seed::("Alice"), + get_collator_keys_from_seed::("Alice"), + ), + ( + get_account_id_from_seed::("Bob"), + get_collator_keys_from_seed::("Bob"), + ), + ], + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Charlie"), + get_account_id_from_seed::("Dave"), + get_account_id_from_seed::("Eve"), + get_account_id_from_seed::("Ferdie"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + get_account_id_from_seed::("Charlie//stash"), + get_account_id_from_seed::("Dave//stash"), + get_account_id_from_seed::("Eve//stash"), + get_account_id_from_seed::("Ferdie//stash"), + ], + para_id, + bridges_pallet_owner_seed + .as_ref() + .map(|seed| get_account_id_from_seed::(seed)), + )) + .with_properties(properties) + .build() } fn genesis( @@ -288,28 +282,20 @@ pub mod rococo { endowed_accounts: Vec, id: ParaId, bridges_pallet_owner: Option, - ) -> bridge_hub_rococo_runtime::RuntimeGenesisConfig { - bridge_hub_rococo_runtime::RuntimeGenesisConfig { - system: bridge_hub_rococo_runtime::SystemConfig { - code: bridge_hub_rococo_runtime::WASM_BINARY - .expect("WASM binary was not build, please build it!") - .to_vec(), - ..Default::default() - }, - balances: bridge_hub_rococo_runtime::BalancesConfig { - balances: endowed_accounts.iter().cloned().map(|k| (k, 1 << 60)).collect(), + ) -> serde_json::Value { + serde_json::json!({ + "balances": { + "balances": endowed_accounts.iter().cloned().map(|k| (k, 1u64 << 60)).collect::>(), }, - parachain_info: bridge_hub_rococo_runtime::ParachainInfoConfig { - parachain_id: id, - ..Default::default() + "parachainInfo": { + "parachainId": id, }, - collator_selection: bridge_hub_rococo_runtime::CollatorSelectionConfig { - invulnerables: invulnerables.iter().cloned().map(|(acc, _)| acc).collect(), - candidacy_bond: BRIDGE_HUB_ROCOCO_ED * 16, - ..Default::default() + "collatorSelection": { + "invulnerables": invulnerables.iter().cloned().map(|(acc, _)| acc).collect::>(), + "candidacyBond": BRIDGE_HUB_ROCOCO_ED * 16, }, - session: bridge_hub_rococo_runtime::SessionConfig { - keys: invulnerables + "session": { + "keys": invulnerables .into_iter() .map(|(acc, aura)| { ( @@ -318,40 +304,31 @@ pub mod rococo { bridge_hub_rococo_runtime::SessionKeys { aura }, // session keys ) }) - .collect(), + .collect::>(), }, - aura: Default::default(), - aura_ext: Default::default(), - parachain_system: Default::default(), - polkadot_xcm: bridge_hub_rococo_runtime::PolkadotXcmConfig { - safe_xcm_version: Some(SAFE_XCM_VERSION), - ..Default::default() + "polkadotXcm": { + "safeXcmVersion": Some(SAFE_XCM_VERSION), }, - bridge_wococo_grandpa: bridge_hub_rococo_runtime::BridgeWococoGrandpaConfig { - owner: bridges_pallet_owner.clone(), - ..Default::default() + + "bridgeWococoGrandpa": { + "owner": bridges_pallet_owner.clone(), }, - bridge_westend_grandpa: bridge_hub_rococo_runtime::BridgeWestendGrandpaConfig { - owner: bridges_pallet_owner.clone(), - ..Default::default() + "bridgeWestendGrandpa": { + "owner": bridges_pallet_owner.clone(), }, - bridge_rococo_grandpa: bridge_hub_rococo_runtime::BridgeRococoGrandpaConfig { - owner: bridges_pallet_owner.clone(), - ..Default::default() + "bridgeRococoGrandpa": { + "owner": bridges_pallet_owner.clone(), }, - bridge_rococo_messages: bridge_hub_rococo_runtime::BridgeRococoMessagesConfig { - owner: bridges_pallet_owner.clone(), - ..Default::default() + "bridgeRococoMessages": { + "owner": bridges_pallet_owner.clone(), }, - bridge_wococo_messages: bridge_hub_rococo_runtime::BridgeWococoMessagesConfig { - owner: bridges_pallet_owner.clone(), - ..Default::default() + "bridgeWococoMessages": { + "owner": bridges_pallet_owner.clone(), }, - bridge_westend_messages: bridge_hub_rococo_runtime::BridgeWestendMessagesConfig { - owner: bridges_pallet_owner.clone(), - ..Default::default() + "bridgeWestendMessages": { + "owner": bridges_pallet_owner.clone(), }, - } + }) } } @@ -403,8 +380,7 @@ pub mod kusama { parachains_common::kusama::currency::EXISTENTIAL_DEPOSIT; /// Specialized `ChainSpec` for the normal parachain runtime. - pub type BridgeHubChainSpec = - sc_service::GenericChainSpec; + pub type BridgeHubChainSpec = sc_service::GenericChainSpec<(), Extensions>; pub type RuntimeApi = bridge_hub_kusama_runtime::RuntimeApi; pub fn local_config( @@ -418,81 +394,68 @@ pub mod kusama { properties.insert("tokenSymbol".into(), "KSM".into()); properties.insert("tokenDecimals".into(), 12.into()); - BridgeHubChainSpec::from_genesis( - // Name - chain_name, - // ID - super::ensure_id(id).expect("invalid id"), - ChainType::Local, - move || { - genesis( - // initial collators. - vec![ - ( - get_account_id_from_seed::("Alice"), - get_collator_keys_from_seed::("Alice"), - ), - ( - get_account_id_from_seed::("Bob"), - get_collator_keys_from_seed::("Bob"), - ), - ], - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Charlie"), - get_account_id_from_seed::("Dave"), - get_account_id_from_seed::("Eve"), - get_account_id_from_seed::("Ferdie"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - get_account_id_from_seed::("Charlie//stash"), - get_account_id_from_seed::("Dave//stash"), - get_account_id_from_seed::("Eve//stash"), - get_account_id_from_seed::("Ferdie//stash"), - ], - para_id, - ) - }, - Vec::new(), - None, - None, - None, - Some(properties), + BridgeHubChainSpec::builder( + bridge_hub_kusama_runtime::WASM_BINARY + .expect("WASM binary was not built, please build it!"), Extensions { relay_chain: relay_chain.to_string(), para_id: para_id.into() }, ) + .with_name(chain_name) + .with_id(super::ensure_id(id).expect("invalid id")) + .with_chain_type(ChainType::Local) + .with_genesis_config_patch(genesis( + // initial collators. + vec![ + ( + get_account_id_from_seed::("Alice"), + get_collator_keys_from_seed::("Alice"), + ), + ( + get_account_id_from_seed::("Bob"), + get_collator_keys_from_seed::("Bob"), + ), + ], + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Charlie"), + get_account_id_from_seed::("Dave"), + get_account_id_from_seed::("Eve"), + get_account_id_from_seed::("Ferdie"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + get_account_id_from_seed::("Charlie//stash"), + get_account_id_from_seed::("Dave//stash"), + get_account_id_from_seed::("Eve//stash"), + get_account_id_from_seed::("Ferdie//stash"), + ], + para_id, + )) + .with_properties(properties) + .build() } fn genesis( invulnerables: Vec<(AccountId, AuraId)>, endowed_accounts: Vec, id: ParaId, - ) -> bridge_hub_kusama_runtime::RuntimeGenesisConfig { - bridge_hub_kusama_runtime::RuntimeGenesisConfig { - system: bridge_hub_kusama_runtime::SystemConfig { - code: bridge_hub_kusama_runtime::WASM_BINARY - .expect("WASM binary was not build, please build it!") - .to_vec(), - ..Default::default() - }, - balances: bridge_hub_kusama_runtime::BalancesConfig { - balances: endowed_accounts + ) -> serde_json::Value { + serde_json::json!({ + "balances": { + "balances": endowed_accounts .iter() .cloned() .map(|k| (k, BRIDGE_HUB_KUSAMA_ED * 524_288)) - .collect(), + .collect::>(), }, - parachain_info: bridge_hub_kusama_runtime::ParachainInfoConfig { - parachain_id: id, - ..Default::default() + "parachainInfo": { + "parachainId": id, }, - collator_selection: bridge_hub_kusama_runtime::CollatorSelectionConfig { - invulnerables: invulnerables.iter().cloned().map(|(acc, _)| acc).collect(), - candidacy_bond: BRIDGE_HUB_KUSAMA_ED * 16, - ..Default::default() + "collatorSelection": { + "invulnerables": invulnerables.iter().cloned().map(|(acc, _)| acc).collect::>(), + "candidacyBond": BRIDGE_HUB_KUSAMA_ED * 16, }, - session: bridge_hub_kusama_runtime::SessionConfig { - keys: invulnerables + "session": { + "keys": invulnerables .into_iter() .map(|(acc, aura)| { ( @@ -501,16 +464,12 @@ pub mod kusama { bridge_hub_kusama_runtime::SessionKeys { aura }, // session keys ) }) - .collect(), + .collect::>(), }, - aura: Default::default(), - aura_ext: Default::default(), - parachain_system: Default::default(), - polkadot_xcm: bridge_hub_kusama_runtime::PolkadotXcmConfig { - safe_xcm_version: Some(SAFE_XCM_VERSION), - ..Default::default() - }, - } + "polkadotXcm": { + "safeXcmVersion": Some(SAFE_XCM_VERSION), + } + }) } } @@ -545,52 +504,47 @@ pub mod westend { properties.insert("tokenSymbol".into(), "WND".into()); properties.insert("tokenDecimals".into(), 12.into()); - BridgeHubChainSpec::from_genesis( - // Name - chain_name, - // ID - super::ensure_id(id).expect("invalid id"), - ChainType::Local, - move || { - genesis( - // initial collators. - vec![ - ( - get_account_id_from_seed::("Alice"), - get_collator_keys_from_seed::("Alice"), - ), - ( - get_account_id_from_seed::("Bob"), - get_collator_keys_from_seed::("Bob"), - ), - ], - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Charlie"), - get_account_id_from_seed::("Dave"), - get_account_id_from_seed::("Eve"), - get_account_id_from_seed::("Ferdie"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - get_account_id_from_seed::("Charlie//stash"), - get_account_id_from_seed::("Dave//stash"), - get_account_id_from_seed::("Eve//stash"), - get_account_id_from_seed::("Ferdie//stash"), - ], - para_id, - bridges_pallet_owner_seed - .as_ref() - .map(|seed| get_account_id_from_seed::(seed)), - ) - }, - Vec::new(), - None, - None, - None, - Some(properties), + BridgeHubChainSpec::builder( + bridge_hub_westend_runtime::WASM_BINARY + .expect("WASM binary was not build, please build it!"), Extensions { relay_chain: relay_chain.to_string(), para_id: para_id.into() }, ) + .with_name(chain_name) + .with_id(super::ensure_id(id).expect("invalid id")) + .with_chain_type(ChainType::Local) + .with_genesis_config_patch(genesis( + // initial collators. + vec![ + ( + get_account_id_from_seed::("Alice"), + get_collator_keys_from_seed::("Alice"), + ), + ( + get_account_id_from_seed::("Bob"), + get_collator_keys_from_seed::("Bob"), + ), + ], + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Charlie"), + get_account_id_from_seed::("Dave"), + get_account_id_from_seed::("Eve"), + get_account_id_from_seed::("Ferdie"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + get_account_id_from_seed::("Charlie//stash"), + get_account_id_from_seed::("Dave//stash"), + get_account_id_from_seed::("Eve//stash"), + get_account_id_from_seed::("Ferdie//stash"), + ], + para_id, + bridges_pallet_owner_seed + .as_ref() + .map(|seed| get_account_id_from_seed::(seed)), + )) + .with_properties(properties) + .build() } fn genesis( @@ -598,28 +552,20 @@ pub mod westend { endowed_accounts: Vec, id: ParaId, bridges_pallet_owner: Option, - ) -> bridge_hub_westend_runtime::RuntimeGenesisConfig { - bridge_hub_westend_runtime::RuntimeGenesisConfig { - system: bridge_hub_westend_runtime::SystemConfig { - code: bridge_hub_westend_runtime::WASM_BINARY - .expect("WASM binary was not build, please build it!") - .to_vec(), - ..Default::default() - }, - balances: bridge_hub_westend_runtime::BalancesConfig { - balances: endowed_accounts.iter().cloned().map(|k| (k, 1 << 60)).collect(), + ) -> serde_json::Value { + serde_json::json!({ + "balances": { + "balances": endowed_accounts.iter().cloned().map(|k| (k, 1u64 << 60)).collect::>(), }, - parachain_info: bridge_hub_westend_runtime::ParachainInfoConfig { - parachain_id: id, - ..Default::default() + "parachainInfo": { + "parachainId": id, }, - collator_selection: bridge_hub_westend_runtime::CollatorSelectionConfig { - invulnerables: invulnerables.iter().cloned().map(|(acc, _)| acc).collect(), - candidacy_bond: BRIDGE_HUB_WESTEND_ED * 16, - ..Default::default() + "collatorSelection": { + "invulnerables": invulnerables.iter().cloned().map(|(acc, _)| acc).collect::>(), + "candidacyBond": BRIDGE_HUB_WESTEND_ED * 16, }, - session: bridge_hub_westend_runtime::SessionConfig { - keys: invulnerables + "session": { + "keys": invulnerables .into_iter() .map(|(acc, aura)| { ( @@ -628,24 +574,18 @@ pub mod westend { bridge_hub_westend_runtime::SessionKeys { aura }, // session keys ) }) - .collect(), - }, - aura: Default::default(), - aura_ext: Default::default(), - parachain_system: Default::default(), - polkadot_xcm: bridge_hub_westend_runtime::PolkadotXcmConfig { - safe_xcm_version: Some(SAFE_XCM_VERSION), - ..Default::default() + .collect::>(), }, - bridge_rococo_grandpa: bridge_hub_westend_runtime::BridgeRococoGrandpaConfig { - owner: bridges_pallet_owner.clone(), - ..Default::default() + "polkadotXcm": { + "safeXcmVersion": Some(SAFE_XCM_VERSION), }, - bridge_rococo_messages: bridge_hub_westend_runtime::BridgeRococoMessagesConfig { - owner: bridges_pallet_owner.clone(), - ..Default::default() + "bridgeRococoGrandpa": { + "owner": bridges_pallet_owner.clone(), }, - } + "bridgeRococoMessages": { + "owner": bridges_pallet_owner.clone(), + } + }) } } @@ -666,8 +606,7 @@ pub mod polkadot { parachains_common::polkadot::currency::EXISTENTIAL_DEPOSIT; /// Specialized `ChainSpec` for the normal parachain runtime. - pub type BridgeHubChainSpec = - sc_service::GenericChainSpec; + pub type BridgeHubChainSpec = sc_service::GenericChainSpec<(), Extensions>; pub type RuntimeApi = bridge_hub_polkadot_runtime::RuntimeApi; pub fn local_config( @@ -681,81 +620,68 @@ pub mod polkadot { properties.insert("tokenSymbol".into(), "DOT".into()); properties.insert("tokenDecimals".into(), 10.into()); - BridgeHubChainSpec::from_genesis( - // Name - chain_name, - // ID - super::ensure_id(id).expect("invalid id"), - ChainType::Local, - move || { - genesis( - // initial collators. - vec![ - ( - get_account_id_from_seed::("Alice"), - get_collator_keys_from_seed::("Alice"), - ), - ( - get_account_id_from_seed::("Bob"), - get_collator_keys_from_seed::("Bob"), - ), - ], - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Charlie"), - get_account_id_from_seed::("Dave"), - get_account_id_from_seed::("Eve"), - get_account_id_from_seed::("Ferdie"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - get_account_id_from_seed::("Charlie//stash"), - get_account_id_from_seed::("Dave//stash"), - get_account_id_from_seed::("Eve//stash"), - get_account_id_from_seed::("Ferdie//stash"), - ], - para_id, - ) - }, - Vec::new(), - None, - None, - None, - Some(properties), + BridgeHubChainSpec::builder( + bridge_hub_polkadot_runtime::WASM_BINARY + .expect("WASM binary was not built, please build it!"), Extensions { relay_chain: relay_chain.to_string(), para_id: para_id.into() }, ) + .with_name(chain_name) + .with_id(super::ensure_id(id).expect("invalid id")) + .with_chain_type(ChainType::Local) + .with_genesis_config_patch(genesis( + // initial collators. + vec![ + ( + get_account_id_from_seed::("Alice"), + get_collator_keys_from_seed::("Alice"), + ), + ( + get_account_id_from_seed::("Bob"), + get_collator_keys_from_seed::("Bob"), + ), + ], + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Charlie"), + get_account_id_from_seed::("Dave"), + get_account_id_from_seed::("Eve"), + get_account_id_from_seed::("Ferdie"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + get_account_id_from_seed::("Charlie//stash"), + get_account_id_from_seed::("Dave//stash"), + get_account_id_from_seed::("Eve//stash"), + get_account_id_from_seed::("Ferdie//stash"), + ], + para_id, + )) + .with_properties(properties) + .build() } fn genesis( invulnerables: Vec<(AccountId, AuraId)>, endowed_accounts: Vec, id: ParaId, - ) -> bridge_hub_polkadot_runtime::RuntimeGenesisConfig { - bridge_hub_polkadot_runtime::RuntimeGenesisConfig { - system: bridge_hub_polkadot_runtime::SystemConfig { - code: bridge_hub_polkadot_runtime::WASM_BINARY - .expect("WASM binary was not build, please build it!") - .to_vec(), - ..Default::default() - }, - balances: bridge_hub_polkadot_runtime::BalancesConfig { - balances: endowed_accounts + ) -> serde_json::Value { + serde_json::json!({ + "balances": { + "balances": endowed_accounts .iter() .cloned() .map(|k| (k, BRIDGE_HUB_POLKADOT_ED * 4096)) - .collect(), + .collect::>(), }, - parachain_info: bridge_hub_polkadot_runtime::ParachainInfoConfig { - parachain_id: id, - ..Default::default() + "parachainInfo": { + "parachainId": id, }, - collator_selection: bridge_hub_polkadot_runtime::CollatorSelectionConfig { - invulnerables: invulnerables.iter().cloned().map(|(acc, _)| acc).collect(), - candidacy_bond: BRIDGE_HUB_POLKADOT_ED * 16, - ..Default::default() + "collatorSelection": { + "invulnerables": invulnerables.iter().cloned().map(|(acc, _)| acc).collect::>(), + "candidacyBond": BRIDGE_HUB_POLKADOT_ED * 16, }, - session: bridge_hub_polkadot_runtime::SessionConfig { - keys: invulnerables + "session": { + "keys": invulnerables .into_iter() .map(|(acc, aura)| { ( @@ -764,15 +690,11 @@ pub mod polkadot { bridge_hub_polkadot_runtime::SessionKeys { aura }, // session keys ) }) - .collect(), + .collect::>(), }, - aura: Default::default(), - aura_ext: Default::default(), - parachain_system: Default::default(), - polkadot_xcm: bridge_hub_polkadot_runtime::PolkadotXcmConfig { - safe_xcm_version: Some(SAFE_XCM_VERSION), - ..Default::default() - }, - } + "polkadotXcm": { + "safeXcmVersion": Some(SAFE_XCM_VERSION), + } + }) } } diff --git a/cumulus/polkadot-parachain/src/chain_spec/collectives.rs b/cumulus/polkadot-parachain/src/chain_spec/collectives.rs index fbf49b9535a4..0a8064f50ca5 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/collectives.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/collectives.rs @@ -22,8 +22,7 @@ use parachains_common::{AccountId, AuraId, Balance as CollectivesBalance}; use sc_service::ChainType; use sp_core::sr25519; -pub type CollectivesPolkadotChainSpec = - sc_service::GenericChainSpec; +pub type CollectivesPolkadotChainSpec = sc_service::GenericChainSpec<(), Extensions>; const COLLECTIVES_POLKADOT_ED: CollectivesBalance = parachains_common::polkadot::currency::EXISTENTIAL_DEPOSIT; @@ -43,37 +42,33 @@ pub fn collectives_polkadot_development_config() -> CollectivesPolkadotChainSpec properties.insert("tokenSymbol".into(), "DOT".into()); properties.insert("tokenDecimals".into(), 10.into()); - CollectivesPolkadotChainSpec::from_genesis( - // Name - "Polkadot Collectives Development", - // ID - "collectives_polkadot_dev", - ChainType::Local, - move || { - collectives_polkadot_genesis( - // initial collators. - vec![( - get_account_id_from_seed::("Alice"), - get_collator_keys_from_seed::("Alice"), - )], - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - ], - // 1002 avoids a potential collision with Kusama-1001 (Encointer) should there ever - // be a collective para on Kusama. - 1002.into(), - ) - }, - Vec::new(), - None, - None, - None, - Some(properties), + CollectivesPolkadotChainSpec::builder( + collectives_polkadot_runtime::WASM_BINARY + .expect("WASM binary was not built, please build it!"), Extensions { relay_chain: "polkadot-dev".into(), para_id: 1002 }, ) + .with_name("Polkadot Collectives Development") + .with_id("collectives_polkadot_dev") + .with_chain_type(ChainType::Local) + .with_genesis_config_patch(collectives_polkadot_genesis( + // initial collators. + vec![( + get_account_id_from_seed::("Alice"), + get_collator_keys_from_seed::("Alice"), + )], + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + ], + // 1002 avoids a potential collision with Kusama-1001 (Encointer) should there ever + // be a collective para on Kusama. + 1002.into(), + )) + .with_boot_nodes(Vec::new()) + .with_properties(properties) + .build() } /// Collectives Polkadot Local Config. @@ -83,81 +78,69 @@ pub fn collectives_polkadot_local_config() -> CollectivesPolkadotChainSpec { properties.insert("tokenSymbol".into(), "DOT".into()); properties.insert("tokenDecimals".into(), 10.into()); - CollectivesPolkadotChainSpec::from_genesis( - // Name - "Polkadot Collectives Local", - // ID - "collectives_polkadot_local", - ChainType::Local, - move || { - collectives_polkadot_genesis( - // initial collators. - vec![ - ( - get_account_id_from_seed::("Alice"), - get_collator_keys_from_seed::("Alice"), - ), - ( - get_account_id_from_seed::("Bob"), - get_collator_keys_from_seed::("Bob"), - ), - ], - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Charlie"), - get_account_id_from_seed::("Dave"), - get_account_id_from_seed::("Eve"), - get_account_id_from_seed::("Ferdie"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - get_account_id_from_seed::("Charlie//stash"), - get_account_id_from_seed::("Dave//stash"), - get_account_id_from_seed::("Eve//stash"), - get_account_id_from_seed::("Ferdie//stash"), - ], - 1002.into(), - ) - }, - Vec::new(), - None, - None, - None, - Some(properties), + CollectivesPolkadotChainSpec::builder( + collectives_polkadot_runtime::WASM_BINARY + .expect("WASM binary was not built, please build it!"), Extensions { relay_chain: "polkadot-local".into(), para_id: 1002 }, ) + .with_name("Polkadot Collectives Local") + .with_id("collectives_polkadot_local") + .with_chain_type(ChainType::Local) + .with_genesis_config_patch(collectives_polkadot_genesis( + // initial collators. + vec![ + ( + get_account_id_from_seed::("Alice"), + get_collator_keys_from_seed::("Alice"), + ), + ( + get_account_id_from_seed::("Bob"), + get_collator_keys_from_seed::("Bob"), + ), + ], + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Charlie"), + get_account_id_from_seed::("Dave"), + get_account_id_from_seed::("Eve"), + get_account_id_from_seed::("Ferdie"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + get_account_id_from_seed::("Charlie//stash"), + get_account_id_from_seed::("Dave//stash"), + get_account_id_from_seed::("Eve//stash"), + get_account_id_from_seed::("Ferdie//stash"), + ], + 1002.into(), + )) + .with_boot_nodes(Vec::new()) + .with_properties(properties) + .build() } fn collectives_polkadot_genesis( invulnerables: Vec<(AccountId, AuraId)>, endowed_accounts: Vec, id: ParaId, -) -> collectives_polkadot_runtime::RuntimeGenesisConfig { - collectives_polkadot_runtime::RuntimeGenesisConfig { - system: collectives_polkadot_runtime::SystemConfig { - code: collectives_polkadot_runtime::WASM_BINARY - .expect("WASM binary was not build, please build it!") - .to_vec(), - ..Default::default() - }, - balances: collectives_polkadot_runtime::BalancesConfig { - balances: endowed_accounts +) -> serde_json::Value { + serde_json::json!( { + "balances": { + "balances": endowed_accounts .iter() .cloned() .map(|k| (k, COLLECTIVES_POLKADOT_ED * 4096)) - .collect(), + .collect::>(), }, - parachain_info: collectives_polkadot_runtime::ParachainInfoConfig { - parachain_id: id, - ..Default::default() + "parachainInfo": { + "parachainId": id, }, - collator_selection: collectives_polkadot_runtime::CollatorSelectionConfig { - invulnerables: invulnerables.iter().cloned().map(|(acc, _)| acc).collect(), - candidacy_bond: COLLECTIVES_POLKADOT_ED * 16, - ..Default::default() + "collatorSelection": { + "invulnerables": invulnerables.iter().cloned().map(|(acc, _)| acc).collect::>(), + "candidacyBond": COLLECTIVES_POLKADOT_ED * 16, }, - session: collectives_polkadot_runtime::SessionConfig { - keys: invulnerables + "session": { + "keys": invulnerables .into_iter() .map(|(acc, aura)| { ( @@ -166,18 +149,12 @@ fn collectives_polkadot_genesis( collectives_polkadot_session_keys(aura), // session keys ) }) - .collect(), + .collect::>(), }, // no need to pass anything to aura, in fact it will panic if we do. Session will take care // of this. - aura: Default::default(), - aura_ext: Default::default(), - parachain_system: Default::default(), - polkadot_xcm: collectives_polkadot_runtime::PolkadotXcmConfig { - safe_xcm_version: Some(SAFE_XCM_VERSION), - ..Default::default() + "polkadotXcm": { + "safeXcmVersion": Some(SAFE_XCM_VERSION), }, - alliance: Default::default(), - alliance_motion: Default::default(), - } + }) } diff --git a/cumulus/polkadot-parachain/src/chain_spec/contracts.rs b/cumulus/polkadot-parachain/src/chain_spec/contracts.rs index 0d5012858edc..7ca66354fbfd 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/contracts.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/contracts.rs @@ -23,8 +23,7 @@ use parachains_common::{AccountId, AuraId}; use sc_service::ChainType; use sp_core::{crypto::UncheckedInto, sr25519}; -pub type ContractsRococoChainSpec = - sc_service::GenericChainSpec; +pub type ContractsRococoChainSpec = sc_service::GenericChainSpec<(), Extensions>; /// No relay chain suffix because the id is the same over all relay chains. const CONTRACTS_PARACHAIN_ID: u32 = 1002; @@ -38,52 +37,46 @@ pub fn contracts_rococo_development_config() -> ContractsRococoChainSpec { properties.insert("tokenSymbol".into(), "ROC".into()); properties.insert("tokenDecimals".into(), 12.into()); - ContractsRococoChainSpec::from_genesis( - // Name - "Contracts on Rococo Development", - // ID - "contracts-rococo-dev", - ChainType::Development, - move || { - contracts_rococo_genesis( - // initial collators. - vec![ - ( - get_account_id_from_seed::("Alice"), - get_collator_keys_from_seed::("Alice"), - ), - ( - get_account_id_from_seed::("Bob"), - get_collator_keys_from_seed::("Bob"), - ), - ], - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Charlie"), - get_account_id_from_seed::("Dave"), - get_account_id_from_seed::("Eve"), - get_account_id_from_seed::("Ferdie"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - get_account_id_from_seed::("Charlie//stash"), - get_account_id_from_seed::("Dave//stash"), - get_account_id_from_seed::("Eve//stash"), - get_account_id_from_seed::("Ferdie//stash"), - ], - CONTRACTS_PARACHAIN_ID.into(), - ) - }, - Vec::new(), - None, - None, - None, - None, + ContractsRococoChainSpec::builder( + contracts_rococo_runtime::WASM_BINARY.expect("WASM binary was not built, please build it!"), Extensions { relay_chain: "rococo-local".into(), // You MUST set this to the correct network! para_id: CONTRACTS_PARACHAIN_ID, }, ) + .with_name("Contracts on Rococo Development") + .with_id("contracts-rococo-dev") + .with_chain_type(ChainType::Development) + .with_genesis_config_patch(contracts_rococo_genesis( + // initial collators. + vec![ + ( + get_account_id_from_seed::("Alice"), + get_collator_keys_from_seed::("Alice"), + ), + ( + get_account_id_from_seed::("Bob"), + get_collator_keys_from_seed::("Bob"), + ), + ], + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Charlie"), + get_account_id_from_seed::("Dave"), + get_account_id_from_seed::("Eve"), + get_account_id_from_seed::("Ferdie"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + get_account_id_from_seed::("Charlie//stash"), + get_account_id_from_seed::("Dave//stash"), + get_account_id_from_seed::("Eve//stash"), + get_account_id_from_seed::("Ferdie//stash"), + ], + CONTRACTS_PARACHAIN_ID.into(), + )) + .with_boot_nodes(Vec::new()) + .build() } pub fn contracts_rococo_local_config() -> ContractsRococoChainSpec { @@ -91,58 +84,46 @@ pub fn contracts_rococo_local_config() -> ContractsRococoChainSpec { properties.insert("tokenSymbol".into(), "ROC".into()); properties.insert("tokenDecimals".into(), 12.into()); - ContractsRococoChainSpec::from_genesis( - // Name - "Contracts on Rococo", - // ID - "contracts-rococo-local", - ChainType::Local, - move || { - contracts_rococo_genesis( - // initial collators. - vec![ - ( - get_account_id_from_seed::("Alice"), - get_collator_keys_from_seed::("Alice"), - ), - ( - get_account_id_from_seed::("Bob"), - get_collator_keys_from_seed::("Bob"), - ), - ], - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Charlie"), - get_account_id_from_seed::("Dave"), - get_account_id_from_seed::("Eve"), - get_account_id_from_seed::("Ferdie"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - get_account_id_from_seed::("Charlie//stash"), - get_account_id_from_seed::("Dave//stash"), - get_account_id_from_seed::("Eve//stash"), - get_account_id_from_seed::("Ferdie//stash"), - ], - CONTRACTS_PARACHAIN_ID.into(), - ) - }, - // Bootnodes - Vec::new(), - // Telemetry - None, - // Protocol ID - None, - // Fork ID - None, - // Properties - Some(properties), - // Extensions + ContractsRococoChainSpec::builder( + contracts_rococo_runtime::WASM_BINARY.expect("WASM binary was not built, please build it!"), Extensions { relay_chain: "rococo-local".into(), // You MUST set this to the correct network! para_id: CONTRACTS_PARACHAIN_ID, }, ) + .with_name("Contracts on Rococo") + .with_id("contracts-rococo-local") + .with_chain_type(ChainType::Local) + .with_genesis_config_patch(contracts_rococo_genesis( + // initial collators. + vec![ + ( + get_account_id_from_seed::("Alice"), + get_collator_keys_from_seed::("Alice"), + ), + ( + get_account_id_from_seed::("Bob"), + get_collator_keys_from_seed::("Bob"), + ), + ], + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Charlie"), + get_account_id_from_seed::("Dave"), + get_account_id_from_seed::("Eve"), + get_account_id_from_seed::("Ferdie"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + get_account_id_from_seed::("Charlie//stash"), + get_account_id_from_seed::("Dave//stash"), + get_account_id_from_seed::("Eve//stash"), + get_account_id_from_seed::("Ferdie//stash"), + ], + CONTRACTS_PARACHAIN_ID.into(), + )) + .with_properties(properties) + .build() } pub fn contracts_rococo_config() -> ContractsRococoChainSpec { @@ -151,111 +132,92 @@ pub fn contracts_rococo_config() -> ContractsRococoChainSpec { properties.insert("tokenSymbol".into(), "ROC".into()); properties.insert("tokenDecimals".into(), 12.into()); - ContractsRococoChainSpec::from_genesis( - // Name - "Contracts on Rococo", - // ID - "contracts-rococo", - ChainType::Live, - move || { - contracts_rococo_genesis( - vec![ - // 5GKFbTTgrVS4Vz1UWWHPqMZQNFWZtqo7H2KpCDyYhEL3aS26 - ( - hex!["bc09354c12c054c8f6b3da208485eacec4ac648bad348895273b37bab5a0937c"] - .into(), - hex!["bc09354c12c054c8f6b3da208485eacec4ac648bad348895273b37bab5a0937c"] - .unchecked_into(), - ), - // 5EPRJHm2GpABVWcwnAujcrhnrjFZyDGd5TwKFzkBoGgdRyv2 - ( - hex!["66be63b7bcbfb91040e5248e2d1ceb822cf219c57848c5924ffa3a1f8e67ba72"] - .into(), - hex!["66be63b7bcbfb91040e5248e2d1ceb822cf219c57848c5924ffa3a1f8e67ba72"] - .unchecked_into(), - ), - // 5GH62vrJrVZxLREcHzm2PR5uTLAT5RQMJitoztCGyaP4o3uM - ( - hex!["ba62886472a0a9f66b5e39f1469ce1c5b3d8cad6be39078daf16f111e89d1e44"] - .into(), - hex!["ba62886472a0a9f66b5e39f1469ce1c5b3d8cad6be39078daf16f111e89d1e44"] - .unchecked_into(), - ), - // 5FHfoJDLdjRYX5KXLRqMDYBbWrwHLMtti21uK4QByUoUAbJF - ( - hex!["8e97f65cda001976311df9bed39e8d0c956089093e94a75ef76fe9347a0eda7b"] - .into(), - hex!["8e97f65cda001976311df9bed39e8d0c956089093e94a75ef76fe9347a0eda7b"] - .unchecked_into(), - ), - ], - // Warning: The configuration for a production chain should not contain - // any endowed accounts here, otherwise it'll be minting extra native tokens - // from the relay chain on the parachain. - vec![ - // NOTE: Remove endowed accounts if deployed on other relay chains. - // Endowed accounts - hex!["baa78c7154c7f82d6d377177e20bcab65d327eca0086513f9964f5a0f6bdad56"].into(), - // AccountId of an account which `ink-waterfall` uses for automated testing - hex!["0e47e2344d523c3cc5c34394b0d58b9a4200e813a038e6c5a6163cc07d70b069"].into(), - ], - CONTRACTS_PARACHAIN_ID.into(), - ) - }, - // Bootnodes - vec![ + ContractsRococoChainSpec::builder( + contracts_rococo_runtime::WASM_BINARY.expect("WASM binary was not built, please build it!"), + Extensions { relay_chain: "rococo".into(), para_id: CONTRACTS_PARACHAIN_ID } + ) + .with_name("Contracts on Rococo") + .with_id("contracts-rococo") + .with_chain_type(ChainType::Live) + .with_genesis_config_patch(contracts_rococo_genesis( + vec![ + // 5GKFbTTgrVS4Vz1UWWHPqMZQNFWZtqo7H2KpCDyYhEL3aS26 + ( + hex!["bc09354c12c054c8f6b3da208485eacec4ac648bad348895273b37bab5a0937c"] + .into(), + hex!["bc09354c12c054c8f6b3da208485eacec4ac648bad348895273b37bab5a0937c"] + .unchecked_into(), + ), + // 5EPRJHm2GpABVWcwnAujcrhnrjFZyDGd5TwKFzkBoGgdRyv2 + ( + hex!["66be63b7bcbfb91040e5248e2d1ceb822cf219c57848c5924ffa3a1f8e67ba72"] + .into(), + hex!["66be63b7bcbfb91040e5248e2d1ceb822cf219c57848c5924ffa3a1f8e67ba72"] + .unchecked_into(), + ), + // 5GH62vrJrVZxLREcHzm2PR5uTLAT5RQMJitoztCGyaP4o3uM + ( + hex!["ba62886472a0a9f66b5e39f1469ce1c5b3d8cad6be39078daf16f111e89d1e44"] + .into(), + hex!["ba62886472a0a9f66b5e39f1469ce1c5b3d8cad6be39078daf16f111e89d1e44"] + .unchecked_into(), + ), + // 5FHfoJDLdjRYX5KXLRqMDYBbWrwHLMtti21uK4QByUoUAbJF + ( + hex!["8e97f65cda001976311df9bed39e8d0c956089093e94a75ef76fe9347a0eda7b"] + .into(), + hex!["8e97f65cda001976311df9bed39e8d0c956089093e94a75ef76fe9347a0eda7b"] + .unchecked_into(), + ), + ], + // Warning: The configuration for a production chain should not contain + // any endowed accounts here, otherwise it'll be minting extra native tokens + // from the relay chain on the parachain. + vec![ + // NOTE: Remove endowed accounts if deployed on other relay chains. + // Endowed accounts + hex!["baa78c7154c7f82d6d377177e20bcab65d327eca0086513f9964f5a0f6bdad56"].into(), + // AccountId of an account which `ink-waterfall` uses for automated testing + hex!["0e47e2344d523c3cc5c34394b0d58b9a4200e813a038e6c5a6163cc07d70b069"].into(), + ], + CONTRACTS_PARACHAIN_ID.into(), + )) + .with_boot_nodes(vec![ "/dns/contracts-collator-0.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWKg3Rpxcr9oJ8n6khoxpGKWztCZydtUZk2cojHqnfLrpj" - .parse() - .expect("MultiaddrWithPeerId"), + .parse() + .expect("MultiaddrWithPeerId"), "/dns/contracts-collator-1.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWPEXYrz8tHU3nDtPoPw4V7ou5dzMEWSTuUj7vaWiYVAVh" - .parse() - .expect("MultiaddrWithPeerId"), + .parse() + .expect("MultiaddrWithPeerId"), "/dns/contracts-collator-2.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWEVU8AFNary4nP4qEnEcwJaRuy59Wefekzdu9pKbnVEhk" - .parse() - .expect("MultiaddrWithPeerId"), + .parse() + .expect("MultiaddrWithPeerId"), "/dns/contracts-collator-3.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWP6pV3ZmcXzGDjv8ZMgA6nZxfAKDxSz4VNiLx6vVCQgJX" - .parse() - .expect("MultiaddrWithPeerId"), - ], - // Telemetry - None, - // Protocol ID - None, - // Fork ID - None, - // Properties - Some(properties), - // Extensions - Extensions { relay_chain: "rococo".into(), para_id: CONTRACTS_PARACHAIN_ID }, - ) + .parse() + .expect("MultiaddrWithPeerId"), + ]) + .with_properties(properties) + .build() } fn contracts_rococo_genesis( invulnerables: Vec<(AccountId, AuraId)>, endowed_accounts: Vec, id: ParaId, -) -> contracts_rococo_runtime::RuntimeGenesisConfig { - contracts_rococo_runtime::RuntimeGenesisConfig { - system: contracts_rococo_runtime::SystemConfig { - code: contracts_rococo_runtime::WASM_BINARY - .expect("WASM binary was not build, please build it!") - .to_vec(), - ..Default::default() - }, - balances: contracts_rococo_runtime::BalancesConfig { - balances: endowed_accounts.iter().cloned().map(|k| (k, 1 << 60)).collect(), +) -> serde_json::Value { + serde_json::json!( { + "balances": { + "balances": endowed_accounts.iter().cloned().map(|k| (k, 1u64 << 60)).collect::>(), }, - parachain_info: contracts_rococo_runtime::ParachainInfoConfig { - parachain_id: id, - ..Default::default() + "parachainInfo": { + "parachainId": id, }, - collator_selection: contracts_rococo_runtime::CollatorSelectionConfig { - invulnerables: invulnerables.iter().cloned().map(|(acc, _)| acc).collect(), - candidacy_bond: CONTRACTS_ROCOCO_ED * 16, - ..Default::default() + "collatorSelection": { + "invulnerables": invulnerables.iter().cloned().map(|(acc, _)| acc).collect::>(), + "candidacyBond": CONTRACTS_ROCOCO_ED * 16, }, - session: contracts_rococo_runtime::SessionConfig { - keys: invulnerables + "session": { + "keys": invulnerables .into_iter() .map(|(acc, aura)| { ( @@ -264,21 +226,17 @@ fn contracts_rococo_genesis( contracts_rococo_runtime::SessionKeys { aura }, // session keys ) }) - .collect(), + .collect::>(), }, // no need to pass anything to aura, in fact it will panic if we do. Session will take care // of this. - aura: Default::default(), - aura_ext: Default::default(), - parachain_system: Default::default(), - polkadot_xcm: contracts_rococo_runtime::PolkadotXcmConfig { - safe_xcm_version: Some(SAFE_XCM_VERSION), - ..Default::default() + "polkadotXcm": { + "safeXcmVersion": Some(SAFE_XCM_VERSION), }, - sudo: contracts_rococo_runtime::SudoConfig { - key: Some( - hex!["2681a28014e7d3a5bfb32a003b3571f53c408acbc28d351d6bf58f5028c4ef14"].into(), - ), + "sudo": { + "key": Some(sp_runtime::AccountId32::from(hex![ + "2681a28014e7d3a5bfb32a003b3571f53c408acbc28d351d6bf58f5028c4ef14" + ])), }, - } + }) } diff --git a/cumulus/polkadot-parachain/src/chain_spec/glutton.rs b/cumulus/polkadot-parachain/src/chain_spec/glutton.rs index 881fae398827..1a0a06404c58 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/glutton.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/glutton.rs @@ -23,103 +23,72 @@ use sp_core::sr25519; use super::get_collator_keys_from_seed; /// Specialized `ChainSpec` for the Glutton parachain runtime. -pub type GluttonChainSpec = - sc_service::GenericChainSpec; +pub type GluttonChainSpec = sc_service::GenericChainSpec<(), Extensions>; pub fn glutton_development_config(para_id: ParaId) -> GluttonChainSpec { - GluttonChainSpec::from_genesis( - // Name - "Glutton Development", - // ID - "glutton_dev", - ChainType::Local, - move || glutton_genesis(para_id, vec![get_collator_keys_from_seed::("Alice")]), - Vec::new(), - None, - None, - None, - None, + GluttonChainSpec::builder( + glutton_runtime::WASM_BINARY.expect("WASM binary was not built, please build it!"), Extensions { relay_chain: "kusama-dev".into(), para_id: para_id.into() }, ) + .with_name("Glutton Development") + .with_id("glutton_dev") + .with_chain_type(ChainType::Local) + .with_genesis_config_patch(glutton_genesis( + para_id, + vec![get_collator_keys_from_seed::("Alice")], + )) + .build() } pub fn glutton_local_config(para_id: ParaId) -> GluttonChainSpec { - GluttonChainSpec::from_genesis( - // Name - "Glutton Local", - // ID - "glutton_local", - ChainType::Local, - move || { - glutton_genesis( - para_id, - vec![ - get_collator_keys_from_seed::("Alice"), - get_collator_keys_from_seed::("Bob"), - ], - ) - }, - Vec::new(), - None, - None, - None, - None, + GluttonChainSpec::builder( + glutton_runtime::WASM_BINARY.expect("WASM binary was not built, please build it!"), Extensions { relay_chain: "kusama-local".into(), para_id: para_id.into() }, ) + .with_name("Glutton Local") + .with_id("glutton_local") + .with_chain_type(ChainType::Local) + .with_genesis_config_patch(glutton_genesis( + para_id, + vec![ + get_collator_keys_from_seed::("Alice"), + get_collator_keys_from_seed::("Bob"), + ], + )) + .build() } pub fn glutton_config(para_id: ParaId) -> GluttonChainSpec { let mut properties = sc_chain_spec::Properties::new(); properties.insert("ss58Format".into(), 2.into()); - GluttonChainSpec::from_genesis( - // Name - format!("Glutton {}", para_id).as_str(), - // ID - format!("glutton-kusama-{}", para_id).as_str(), - ChainType::Live, - move || { - glutton_genesis( - para_id, - vec![ - get_collator_keys_from_seed::("Alice"), - get_collator_keys_from_seed::("Bob"), - ], - ) - }, - Vec::new(), - None, - // Protocol ID - Some(format!("glutton-kusama-{}", para_id).as_str()), - None, - Some(properties), + GluttonChainSpec::builder( + glutton_runtime::WASM_BINARY.expect("WASM binary was not built, please build it!"), Extensions { relay_chain: "kusama".into(), para_id: para_id.into() }, ) + .with_name(format!("Glutton {}", para_id).as_str()) + .with_id(format!("glutton-kusama-{}", para_id).as_str()) + .with_chain_type(ChainType::Live) + .with_genesis_config_patch(glutton_genesis( + para_id, + vec![ + get_collator_keys_from_seed::("Alice"), + get_collator_keys_from_seed::("Bob"), + ], + )) + .with_protocol_id(format!("glutton-kusama-{}", para_id).as_str()) + .with_properties(properties) + .build() } -fn glutton_genesis( - parachain_id: ParaId, - collators: Vec, -) -> glutton_runtime::RuntimeGenesisConfig { - glutton_runtime::RuntimeGenesisConfig { - system: glutton_runtime::SystemConfig { - code: glutton_runtime::WASM_BINARY - .expect("WASM binary was not build, please build it!") - .to_vec(), - ..Default::default() - }, - parachain_info: glutton_runtime::ParachainInfoConfig { parachain_id, ..Default::default() }, - parachain_system: Default::default(), - glutton: glutton_runtime::GluttonConfig { - compute: Default::default(), - storage: Default::default(), - trash_data_count: Default::default(), - ..Default::default() +fn glutton_genesis(parachain_id: ParaId, collators: Vec) -> serde_json::Value { + serde_json::json!( { + "parachainInfo": { + "parachainId": parachain_id }, - aura: glutton_runtime::AuraConfig { authorities: collators }, - aura_ext: Default::default(), - sudo: glutton_runtime::SudoConfig { - key: Some(get_account_id_from_seed::("Alice")), + "sudo": { + "key": Some(get_account_id_from_seed::("Alice")), }, - } + "aura": { "authorities": collators }, + }) } diff --git a/cumulus/polkadot-parachain/src/chain_spec/penpal.rs b/cumulus/polkadot-parachain/src/chain_spec/penpal.rs index 479d1701e379..2e35ee231dff 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/penpal.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/penpal.rs @@ -22,8 +22,7 @@ use parachains_common::{AccountId, AuraId}; use sc_service::ChainType; use sp_core::sr25519; /// Specialized `ChainSpec` for the normal parachain runtime. -pub type PenpalChainSpec = - sc_service::GenericChainSpec; +pub type PenpalChainSpec = sc_service::GenericChainSpec<(), Extensions>; pub fn get_penpal_chain_spec(id: ParaId, relay_chain: &str) -> PenpalChainSpec { // Give your base currency a unit name and decimal places @@ -32,84 +31,69 @@ pub fn get_penpal_chain_spec(id: ParaId, relay_chain: &str) -> PenpalChainSpec { properties.insert("tokenDecimals".into(), 12u32.into()); properties.insert("ss58Format".into(), 42u32.into()); - PenpalChainSpec::from_genesis( - // Name - "Penpal Parachain", - // ID - &format!("penpal-{}", relay_chain.replace("-local", "")), - ChainType::Development, - move || { - penpal_testnet_genesis( - // initial collators. - vec![ - ( - get_account_id_from_seed::("Alice"), - get_collator_keys_from_seed::("Alice"), - ), - ( - get_account_id_from_seed::("Bob"), - get_collator_keys_from_seed::("Bob"), - ), - ], - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Charlie"), - get_account_id_from_seed::("Dave"), - get_account_id_from_seed::("Eve"), - get_account_id_from_seed::("Ferdie"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - get_account_id_from_seed::("Charlie//stash"), - get_account_id_from_seed::("Dave//stash"), - get_account_id_from_seed::("Eve//stash"), - get_account_id_from_seed::("Ferdie//stash"), - ], - id, - ) - }, - Vec::new(), - None, - None, - None, - None, + PenpalChainSpec::builder( + penpal_runtime::WASM_BINARY.expect("WASM binary was not built, please build it!"), Extensions { relay_chain: relay_chain.into(), // You MUST set this to the correct network! para_id: id.into(), }, ) + .with_name("Penpal Parachain") + .with_id(&format!("penpal-{}", relay_chain.replace("-local", ""))) + .with_chain_type(ChainType::Development) + .with_genesis_config_patch(penpal_testnet_genesis( + // initial collators. + vec![ + ( + get_account_id_from_seed::("Alice"), + get_collator_keys_from_seed::("Alice"), + ), + ( + get_account_id_from_seed::("Bob"), + get_collator_keys_from_seed::("Bob"), + ), + ], + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Charlie"), + get_account_id_from_seed::("Dave"), + get_account_id_from_seed::("Eve"), + get_account_id_from_seed::("Ferdie"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + get_account_id_from_seed::("Charlie//stash"), + get_account_id_from_seed::("Dave//stash"), + get_account_id_from_seed::("Eve//stash"), + get_account_id_from_seed::("Ferdie//stash"), + ], + id, + )) + .build() } fn penpal_testnet_genesis( invulnerables: Vec<(AccountId, AuraId)>, endowed_accounts: Vec, id: ParaId, -) -> penpal_runtime::RuntimeGenesisConfig { - penpal_runtime::RuntimeGenesisConfig { - system: penpal_runtime::SystemConfig { - code: penpal_runtime::WASM_BINARY - .expect("WASM binary was not build, please build it!") - .to_vec(), - ..Default::default() - }, - balances: penpal_runtime::BalancesConfig { - balances: endowed_accounts +) -> serde_json::Value { + serde_json::json!({ + "balances": { + "balances": endowed_accounts .iter() .cloned() .map(|k| (k, penpal_runtime::EXISTENTIAL_DEPOSIT * 4096)) - .collect(), + .collect::>(), }, - parachain_info: penpal_runtime::ParachainInfoConfig { - parachain_id: id, - ..Default::default() + "parachainInfo": { + "parachainId": id, }, - collator_selection: penpal_runtime::CollatorSelectionConfig { - invulnerables: invulnerables.iter().cloned().map(|(acc, _)| acc).collect(), - candidacy_bond: penpal_runtime::EXISTENTIAL_DEPOSIT * 16, - ..Default::default() + "collatorSelection": { + "invulnerables": invulnerables.iter().cloned().map(|(acc, _)| acc).collect::>(), + "candidacyBond": penpal_runtime::EXISTENTIAL_DEPOSIT * 16, }, - session: penpal_runtime::SessionConfig { - keys: invulnerables + "session": { + "keys": invulnerables .into_iter() .map(|(acc, aura)| { ( @@ -118,21 +102,15 @@ fn penpal_testnet_genesis( penpal_session_keys(aura), // session keys ) }) - .collect(), + .collect::>(), }, - // no need to pass anything to aura, in fact it will panic if we do. Session will take care - // of this. - aura: Default::default(), - aura_ext: Default::default(), - parachain_system: Default::default(), - polkadot_xcm: penpal_runtime::PolkadotXcmConfig { - safe_xcm_version: Some(SAFE_XCM_VERSION), - ..Default::default() + "polkadotXcm": { + "safeXcmVersion": Some(SAFE_XCM_VERSION), }, - sudo: penpal_runtime::SudoConfig { - key: Some(get_account_id_from_seed::("Alice")), + "sudo": { + "key": Some(get_account_id_from_seed::("Alice")), }, - } + }) } /// Generate the session keys from individual elements. diff --git a/cumulus/polkadot-parachain/src/chain_spec/rococo_parachain.rs b/cumulus/polkadot-parachain/src/chain_spec/rococo_parachain.rs index fb66efbf9ae1..c2ba44314568 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/rococo_parachain.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/rococo_parachain.rs @@ -25,73 +25,61 @@ use rococo_parachain_runtime::AuraId; use sc_chain_spec::ChainType; use sp_core::{crypto::UncheckedInto, sr25519}; -pub type RococoParachainChainSpec = - sc_service::GenericChainSpec; +pub type RococoParachainChainSpec = sc_service::GenericChainSpec<(), Extensions>; pub fn rococo_parachain_local_config() -> RococoParachainChainSpec { - RococoParachainChainSpec::from_genesis( - "Rococo Parachain Local", - "local_testnet", - ChainType::Local, - move || { - testnet_genesis( - get_account_id_from_seed::("Alice"), - vec![get_from_seed::("Alice"), get_from_seed::("Bob")], - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Charlie"), - get_account_id_from_seed::("Dave"), - get_account_id_from_seed::("Eve"), - get_account_id_from_seed::("Ferdie"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - get_account_id_from_seed::("Charlie//stash"), - get_account_id_from_seed::("Dave//stash"), - get_account_id_from_seed::("Eve//stash"), - get_account_id_from_seed::("Ferdie//stash"), - ], - 1000.into(), - ) - }, - Vec::new(), - None, - None, - None, - None, + RococoParachainChainSpec::builder( + rococo_parachain_runtime::WASM_BINARY.expect("WASM binary was not built, please build it!"), Extensions { relay_chain: "rococo-local".into(), para_id: 1000 }, ) + .with_name("Rococo Parachain Local") + .with_id("local_testnet") + .with_chain_type(ChainType::Local) + .with_genesis_config_patch(testnet_genesis( + get_account_id_from_seed::("Alice"), + vec![get_from_seed::("Alice"), get_from_seed::("Bob")], + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Charlie"), + get_account_id_from_seed::("Dave"), + get_account_id_from_seed::("Eve"), + get_account_id_from_seed::("Ferdie"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + get_account_id_from_seed::("Charlie//stash"), + get_account_id_from_seed::("Dave//stash"), + get_account_id_from_seed::("Eve//stash"), + get_account_id_from_seed::("Ferdie//stash"), + ], + 1000.into(), + )) + .build() } pub fn staging_rococo_parachain_local_config() -> RococoParachainChainSpec { - RococoParachainChainSpec::from_genesis( - "Staging Rococo Parachain Local", - "staging_testnet", - ChainType::Live, - move || { - testnet_genesis( - hex!["9ed7705e3c7da027ba0583a22a3212042f7e715d3c168ba14f1424e2bc111d00"].into(), - vec![ - // $secret//one - hex!["aad9fa2249f87a210a0f93400b7f90e47b810c6d65caa0ca3f5af982904c2a33"] - .unchecked_into(), - // $secret//two - hex!["d47753f0cca9dd8da00c70e82ec4fc5501a69c49a5952a643d18802837c88212"] - .unchecked_into(), - ], - vec![ - hex!["9ed7705e3c7da027ba0583a22a3212042f7e715d3c168ba14f1424e2bc111d00"].into() - ], - 1000.into(), - ) - }, - Vec::new(), - None, - None, - None, - None, + #[allow(deprecated)] + RococoParachainChainSpec::builder( + rococo_parachain_runtime::WASM_BINARY.expect("WASM binary was not built, please build it!"), Extensions { relay_chain: "rococo-local".into(), para_id: 1000 }, ) + .with_name("Staging Rococo Parachain Local") + .with_id("staging_testnet") + .with_chain_type(ChainType::Live) + .with_genesis_config_patch(testnet_genesis( + hex!["9ed7705e3c7da027ba0583a22a3212042f7e715d3c168ba14f1424e2bc111d00"].into(), + vec![ + // $secret//one + hex!["aad9fa2249f87a210a0f93400b7f90e47b810c6d65caa0ca3f5af982904c2a33"] + .unchecked_into(), + // $secret//two + hex!["d47753f0cca9dd8da00c70e82ec4fc5501a69c49a5952a643d18802837c88212"] + .unchecked_into(), + ], + vec![hex!["9ed7705e3c7da027ba0583a22a3212042f7e715d3c168ba14f1424e2bc111d00"].into()], + 1000.into(), + )) + .build() } pub(crate) fn testnet_genesis( @@ -99,28 +87,18 @@ pub(crate) fn testnet_genesis( initial_authorities: Vec, endowed_accounts: Vec, id: ParaId, -) -> rococo_parachain_runtime::RuntimeGenesisConfig { - rococo_parachain_runtime::RuntimeGenesisConfig { - system: rococo_parachain_runtime::SystemConfig { - code: rococo_parachain_runtime::WASM_BINARY - .expect("WASM binary was not build, please build it!") - .to_vec(), - ..Default::default() - }, - balances: rococo_parachain_runtime::BalancesConfig { - balances: endowed_accounts.iter().cloned().map(|k| (k, 1 << 60)).collect(), +) -> serde_json::Value { + serde_json::json!({ + "balances": { + "balances": endowed_accounts.iter().cloned().map(|k| (k, 1u64 << 60)).collect::>(), }, - sudo: rococo_parachain_runtime::SudoConfig { key: Some(root_key) }, - parachain_info: rococo_parachain_runtime::ParachainInfoConfig { - parachain_id: id, - ..Default::default() + "sudo": { "key": Some(root_key) }, + "parachainInfo": { + "parachainId": id, }, - aura: rococo_parachain_runtime::AuraConfig { authorities: initial_authorities }, - aura_ext: Default::default(), - parachain_system: Default::default(), - polkadot_xcm: rococo_parachain_runtime::PolkadotXcmConfig { - safe_xcm_version: Some(SAFE_XCM_VERSION), - ..Default::default() + "aura": { "authorities": initial_authorities }, + "polkadotXcm": { + "safeXcmVersion": Some(SAFE_XCM_VERSION), }, - } + }) } diff --git a/cumulus/polkadot-parachain/src/chain_spec/seedling.rs b/cumulus/polkadot-parachain/src/chain_spec/seedling.rs index 6a5842320976..b034588e14c0 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/seedling.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/seedling.rs @@ -23,49 +23,35 @@ use sp_core::sr25519; use super::get_collator_keys_from_seed; /// Specialized `ChainSpec` for the seedling parachain runtime. -pub type SeedlingChainSpec = - sc_service::GenericChainSpec; +pub type SeedlingChainSpec = sc_service::GenericChainSpec<(), Extensions>; pub fn get_seedling_chain_spec() -> SeedlingChainSpec { - SeedlingChainSpec::from_genesis( - "Seedling Local Testnet", - "seedling_local_testnet", - ChainType::Local, - move || { - seedling_testnet_genesis( - get_account_id_from_seed::("Alice"), - 2000.into(), - vec![get_collator_keys_from_seed::("Alice")], - ) - }, - Vec::new(), - None, - None, - None, - None, + SeedlingChainSpec::builder( + seedling_runtime::WASM_BINARY.expect("WASM binary was not built, please build it!"), Extensions { relay_chain: "westend".into(), para_id: 2000 }, ) + .with_name("Seedling Local Testnet") + .with_id("seedling_local_testnet") + .with_chain_type(ChainType::Local) + .with_genesis_config_patch(seedling_testnet_genesis( + get_account_id_from_seed::("Alice"), + 2000.into(), + vec![get_collator_keys_from_seed::("Alice")], + )) + .with_boot_nodes(Vec::new()) + .build() } fn seedling_testnet_genesis( root_key: AccountId, parachain_id: ParaId, collators: Vec, -) -> seedling_runtime::RuntimeGenesisConfig { - seedling_runtime::RuntimeGenesisConfig { - system: seedling_runtime::SystemConfig { - code: seedling_runtime::WASM_BINARY - .expect("WASM binary was not build, please build it!") - .to_vec(), - ..Default::default() - }, - sudo: seedling_runtime::SudoConfig { key: Some(root_key) }, - parachain_info: seedling_runtime::ParachainInfoConfig { - parachain_id, - ..Default::default() +) -> serde_json::Value { + serde_json::json!({ + "sudo": { "key": Some(root_key) }, + "parachainInfo": { + "parachainId": parachain_id, }, - parachain_system: Default::default(), - aura: seedling_runtime::AuraConfig { authorities: collators }, - aura_ext: Default::default(), - } + "aura": { "authorities": collators }, + }) } diff --git a/cumulus/polkadot-parachain/src/chain_spec/shell.rs b/cumulus/polkadot-parachain/src/chain_spec/shell.rs index 0899c1af3b4d..02c65e809a6c 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/shell.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/shell.rs @@ -22,40 +22,27 @@ use sc_service::ChainType; use super::get_collator_keys_from_seed; /// Specialized `ChainSpec` for the shell parachain runtime. -pub type ShellChainSpec = - sc_service::GenericChainSpec; +pub type ShellChainSpec = sc_service::GenericChainSpec<(), Extensions>; pub fn get_shell_chain_spec() -> ShellChainSpec { - ShellChainSpec::from_genesis( - "Shell Local Testnet", - "shell_local_testnet", - ChainType::Local, - move || { - shell_testnet_genesis(1000.into(), vec![get_collator_keys_from_seed::("Alice")]) - }, - Vec::new(), - None, - None, - None, - None, + ShellChainSpec::builder( + shell_runtime::WASM_BINARY.expect("WASM binary was not built, please build it!"), Extensions { relay_chain: "westend".into(), para_id: 1000 }, ) + .with_name("Shell Local Testnet") + .with_id("shell_local_testnet") + .with_chain_type(ChainType::Local) + .with_genesis_config_patch(shell_testnet_genesis( + 1000.into(), + vec![get_collator_keys_from_seed::("Alice")], + )) + .with_boot_nodes(Vec::new()) + .build() } -fn shell_testnet_genesis( - parachain_id: ParaId, - collators: Vec, -) -> shell_runtime::RuntimeGenesisConfig { - shell_runtime::RuntimeGenesisConfig { - system: shell_runtime::SystemConfig { - code: shell_runtime::WASM_BINARY - .expect("WASM binary was not build, please build it!") - .to_vec(), - ..Default::default() - }, - parachain_info: shell_runtime::ParachainInfoConfig { parachain_id, ..Default::default() }, - parachain_system: Default::default(), - aura: shell_runtime::AuraConfig { authorities: collators }, - aura_ext: Default::default(), - } +fn shell_testnet_genesis(parachain_id: ParaId, collators: Vec) -> serde_json::Value { + serde_json::json!({ + "parachainInfo": { "parachainId": parachain_id}, + "aura": { "authorities": collators }, + }) } diff --git a/cumulus/polkadot-parachain/src/command.rs b/cumulus/polkadot-parachain/src/command.rs index 9787465fb0a2..3f93c90558a3 100644 --- a/cumulus/polkadot-parachain/src/command.rs +++ b/cumulus/polkadot-parachain/src/command.rs @@ -1200,35 +1200,30 @@ mod tests { cfg_file_path } - pub type DummyChainSpec = - sc_service::GenericChainSpec; + pub type DummyChainSpec = sc_service::GenericChainSpec<(), E>; pub fn create_default_with_extensions( id: &str, extension: E, ) -> DummyChainSpec { - DummyChainSpec::from_genesis( - "Dummy local testnet", - id, - ChainType::Local, - move || { - crate::chain_spec::rococo_parachain::testnet_genesis( - get_account_id_from_seed::("Alice"), - vec![ - get_from_seed::("Alice"), - get_from_seed::("Bob"), - ], - vec![get_account_id_from_seed::("Alice")], - 1000.into(), - ) - }, - Vec::new(), - None, - None, - None, - None, + DummyChainSpec::builder( + rococo_parachain_runtime::WASM_BINARY + .expect("WASM binary was not built, please build it!"), extension, ) + .with_name("Dummy local testnet") + .with_id(id) + .with_chain_type(ChainType::Local) + .with_genesis_config_patch(crate::chain_spec::rococo_parachain::testnet_genesis( + get_account_id_from_seed::("Alice"), + vec![ + get_from_seed::("Alice"), + get_from_seed::("Bob"), + ], + vec![get_account_id_from_seed::("Alice")], + 1000.into(), + )) + .build() } #[test] diff --git a/cumulus/test/client/src/lib.rs b/cumulus/test/client/src/lib.rs index 635455d50335..a3c79158f492 100644 --- a/cumulus/test/client/src/lib.rs +++ b/cumulus/test/client/src/lib.rs @@ -19,13 +19,13 @@ mod block_builder; use codec::{Decode, Encode}; use runtime::{ - Balance, Block, BlockHashCount, Runtime, RuntimeCall, RuntimeGenesisConfig, Signature, - SignedExtra, SignedPayload, UncheckedExtrinsic, VERSION, + Balance, Block, BlockHashCount, Runtime, RuntimeCall, Signature, SignedExtra, SignedPayload, + UncheckedExtrinsic, VERSION, }; use sc_executor::HeapAllocStrategy; use sc_executor_common::runtime_blob::RuntimeBlob; use sp_blockchain::HeaderBackend; -use sp_core::{sr25519, Pair}; +use sp_core::Pair; use sp_io::TestExternalities; use sp_runtime::{generic::Era, BuildStorage, SaturatedConversion}; @@ -84,17 +84,12 @@ pub struct GenesisParameters { impl substrate_test_client::GenesisInit for GenesisParameters { fn genesis_storage(&self) -> Storage { - if self.endowed_accounts.is_empty() { - genesis_config().build_storage().unwrap() - } else { - cumulus_test_service::testnet_genesis( - cumulus_test_service::get_account_id_from_seed::("Alice"), - self.endowed_accounts.clone(), - None, - ) - .build_storage() - .unwrap() - } + cumulus_test_service::chain_spec::get_chain_spec_with_extra_endowed( + None, + self.endowed_accounts.clone(), + ) + .build_storage() + .expect("Builds test runtime genesis storage") } } @@ -127,10 +122,6 @@ impl DefaultTestClientBuilderExt for TestClientBuilder { } } -fn genesis_config() -> RuntimeGenesisConfig { - cumulus_test_service::testnet_genesis_with_default_endowed(Default::default(), None) -} - /// Create an unsigned extrinsic from a runtime call. pub fn generate_unsigned(function: impl Into) -> UncheckedExtrinsic { UncheckedExtrinsic::new_unsigned(function.into()) diff --git a/cumulus/test/runtime/Cargo.toml b/cumulus/test/runtime/Cargo.toml index e1e723449ea1..3ea51c1973f4 100644 --- a/cumulus/test/runtime/Cargo.toml +++ b/cumulus/test/runtime/Cargo.toml @@ -23,6 +23,7 @@ pallet-transaction-payment = { path = "../../../substrate/frame/transaction-paym sp-api = { path = "../../../substrate/primitives/api", default-features = false} sp-block-builder = { path = "../../../substrate/primitives/block-builder", default-features = false} sp-core = { path = "../../../substrate/primitives/core", default-features = false} +sp-genesis-builder = { path = "../../../substrate/primitives/genesis-builder", default-features = false} sp-inherents = { path = "../../../substrate/primitives/inherents", default-features = false} sp-io = { path = "../../../substrate/primitives/io", default-features = false} sp-offchain = { path = "../../../substrate/primitives/offchain", default-features = false} @@ -59,6 +60,7 @@ std = [ "sp-api/std", "sp-block-builder/std", "sp-core/std", + "sp-genesis-builder/std", "sp-inherents/std", "sp-io/std", "sp-offchain/std", diff --git a/cumulus/test/runtime/src/lib.rs b/cumulus/test/runtime/src/lib.rs index 381cc64cef82..19fd6d5f02db 100644 --- a/cumulus/test/runtime/src/lib.rs +++ b/cumulus/test/runtime/src/lib.rs @@ -47,6 +47,7 @@ use sp_version::RuntimeVersion; pub use frame_support::{ construct_runtime, dispatch::DispatchClass, + genesis_builder_helper::{build_config, create_default_config}, parameter_types, traits::{ConstU8, Randomness}, weights::{ @@ -473,6 +474,16 @@ impl_runtime_apis! { ParachainSystem::collect_collation_info(header) } } + + impl sp_genesis_builder::GenesisBuilder for Runtime { + fn create_default_config() -> Vec { + create_default_config::() + } + + fn build_config(config: Vec) -> sp_genesis_builder::Result { + build_config::(config) + } + } } cumulus_pallet_parachain_system::register_validate_block! { diff --git a/cumulus/test/service/Cargo.toml b/cumulus/test/service/Cargo.toml index 14e45a4c7e67..5d73ba83d523 100644 --- a/cumulus/test/service/Cargo.toml +++ b/cumulus/test/service/Cargo.toml @@ -17,6 +17,7 @@ criterion = { version = "0.5.1", features = [ "async_tokio" ] } jsonrpsee = { version = "0.16.2", features = ["server"] } rand = "0.8.5" serde = { version = "1.0.188", features = ["derive"] } +serde_json = "1.0.107" tokio = { version = "1.32.0", features = ["macros"] } tracing = "0.1.37" url = "2.4.0" diff --git a/cumulus/test/service/src/chain_spec.rs b/cumulus/test/service/src/chain_spec.rs index c7dac7082fe8..61bbf755d890 100644 --- a/cumulus/test/service/src/chain_spec.rs +++ b/cumulus/test/service/src/chain_spec.rs @@ -63,25 +63,25 @@ where /// The given accounts are initialized with funds in addition /// to the default known accounts. pub fn get_chain_spec_with_extra_endowed( - id: ParaId, + id: Option, extra_endowed_accounts: Vec, ) -> ChainSpec { - ChainSpec::from_genesis( - "Local Testnet", - "local_testnet", - ChainType::Local, - move || testnet_genesis_with_default_endowed(extra_endowed_accounts.clone(), Some(id)), - Vec::new(), - None, - None, - None, - None, - Extensions { para_id: id.into() }, + ChainSpec::builder( + cumulus_test_runtime::WASM_BINARY.expect("WASM binary was not built, please build it!"), + Extensions { para_id: id.unwrap_or(cumulus_test_runtime::PARACHAIN_ID.into()).into() }, ) + .with_name("Local Testnet") + .with_id("local_testnet") + .with_chain_type(ChainType::Local) + .with_genesis_config_patch(testnet_genesis_with_default_endowed( + extra_endowed_accounts.clone(), + id, + )) + .build() } /// Get the chain spec for a specific parachain ID. -pub fn get_chain_spec(id: ParaId) -> ChainSpec { +pub fn get_chain_spec(id: Option) -> ChainSpec { get_chain_spec_with_extra_endowed(id, Default::default()) } @@ -89,7 +89,7 @@ pub fn get_chain_spec(id: ParaId) -> ChainSpec { pub fn testnet_genesis_with_default_endowed( mut extra_endowed_accounts: Vec, self_para_id: Option, -) -> cumulus_test_runtime::RuntimeGenesisConfig { +) -> serde_json::Value { let mut endowed = vec![ get_account_id_from_seed::("Alice"), get_account_id_from_seed::("Bob"), @@ -114,21 +114,12 @@ pub fn testnet_genesis( root_key: AccountId, endowed_accounts: Vec, self_para_id: Option, -) -> cumulus_test_runtime::RuntimeGenesisConfig { - cumulus_test_runtime::RuntimeGenesisConfig { - system: cumulus_test_runtime::SystemConfig { - code: cumulus_test_runtime::WASM_BINARY - .expect("WASM binary was not build, please build it!") - .to_vec(), - ..Default::default() - }, - glutton: Default::default(), - parachain_system: Default::default(), - balances: cumulus_test_runtime::BalancesConfig { +) -> serde_json::Value { + serde_json::json!({ + "balances": cumulus_test_runtime::BalancesConfig { balances: endowed_accounts.iter().cloned().map(|k| (k, 1 << 60)).collect(), }, - sudo: cumulus_test_runtime::SudoConfig { key: Some(root_key) }, - transaction_payment: Default::default(), - test_pallet: cumulus_test_runtime::TestPalletConfig { self_para_id, ..Default::default() }, - } + "sudo": cumulus_test_runtime::SudoConfig { key: Some(root_key) }, + "testPallet": cumulus_test_runtime::TestPalletConfig { self_para_id, ..Default::default() } + }) } diff --git a/cumulus/test/service/src/cli.rs b/cumulus/test/service/src/cli.rs index 9235a0c45c58..ef1159a3c1f8 100644 --- a/cumulus/test/service/src/cli.rs +++ b/cumulus/test/service/src/cli.rs @@ -287,8 +287,9 @@ impl SubstrateCli for TestCollatorCli { fn load_spec(&self, id: &str) -> std::result::Result, String> { Ok(match id { - "" => Box::new(cumulus_test_service::get_chain_spec(ParaId::from(self.parachain_id))) - as Box<_>, + "" => Box::new(cumulus_test_service::get_chain_spec(Some(ParaId::from( + self.parachain_id, + )))) as Box<_>, path => { let chain_spec = cumulus_test_service::chain_spec::ChainSpec::from_json_file(path.into())?; diff --git a/cumulus/test/service/src/genesis.rs b/cumulus/test/service/src/genesis.rs index 9dba57ba9fbc..d4a9a2256264 100644 --- a/cumulus/test/service/src/genesis.rs +++ b/cumulus/test/service/src/genesis.rs @@ -23,7 +23,7 @@ use sp_runtime::traits::Block as BlockT; /// Returns the initial head data for a parachain ID. pub fn initial_head_data(para_id: ParaId) -> HeadData { - let spec = crate::chain_spec::get_chain_spec(para_id); + let spec = crate::chain_spec::get_chain_spec(Some(para_id)); let block: Block = generate_genesis_block(&spec, sp_runtime::StateVersion::V1).unwrap(); let genesis_state = block.header().encode(); genesis_state.into() diff --git a/cumulus/test/service/src/lib.rs b/cumulus/test/service/src/lib.rs index a721645546af..6fd3e4d43d75 100644 --- a/cumulus/test/service/src/lib.rs +++ b/cumulus/test/service/src/lib.rs @@ -719,7 +719,7 @@ pub fn node_config( let role = if is_collator { Role::Authority } else { Role::Full }; let key_seed = key.to_seed(); let mut spec = - Box::new(chain_spec::get_chain_spec_with_extra_endowed(para_id, endowed_accounts)); + Box::new(chain_spec::get_chain_spec_with_extra_endowed(Some(para_id), endowed_accounts)); let mut storage = spec.as_storage_builder().build_storage().expect("could not build storage"); diff --git a/cumulus/test/service/src/main.rs b/cumulus/test/service/src/main.rs index 5946e9cc3506..16b68796bd39 100644 --- a/cumulus/test/service/src/main.rs +++ b/cumulus/test/service/src/main.rs @@ -73,7 +73,7 @@ fn main() -> Result<(), sc_cli::Error> { let runner = cli.create_runner(cmd)?; runner.sync_run(|_config| { let parachain_id = ParaId::from(cmd.parachain_id); - let spec = cumulus_test_service::get_chain_spec(parachain_id); + let spec = cumulus_test_service::get_chain_spec(Some(parachain_id)); cmd.base.run(&spec) }) }, diff --git a/cumulus/zombienet/tests/0002-pov_recovery.toml b/cumulus/zombienet/tests/0002-pov_recovery.toml index a7c1fbfd4413..34cacbc2a9ba 100644 --- a/cumulus/zombienet/tests/0002-pov_recovery.toml +++ b/cumulus/zombienet/tests/0002-pov_recovery.toml @@ -4,7 +4,7 @@ default_command = "polkadot" chain = "rococo-local" -[relaychain.genesis.runtime.configuration.config] +[relaychain.genesis.runtimeGenesis.patch.configuration.config] # set parameters such that collators only connect to 1 validator as a backing group max_validators_per_core = 1 group_rotation_frequency = 100 # 10 mins diff --git a/cumulus/zombienet/tests/0005-migrate_solo_to_para.toml b/cumulus/zombienet/tests/0005-migrate_solo_to_para.toml index a500b51f794c..180ed8bf7b99 100644 --- a/cumulus/zombienet/tests/0005-migrate_solo_to_para.toml +++ b/cumulus/zombienet/tests/0005-migrate_solo_to_para.toml @@ -32,7 +32,7 @@ cumulus_based = true add_to_genesis = false register_para = false # Set some random value in the genesis state to create a different genesis hash. -[parachains.genesis.runtime.sudo] +[parachains.genesis.runtimeGenesis.patch.sudo] key = "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty" # run the parachain that will be used to return the header of the solo chain. diff --git a/polkadot/node/service/src/chain_spec.rs b/polkadot/node/service/src/chain_spec.rs index ce25c08b8773..04e142230b4c 100644 --- a/polkadot/node/service/src/chain_spec.rs +++ b/polkadot/node/service/src/chain_spec.rs @@ -75,7 +75,7 @@ pub type GenericChainSpec = service::GenericChainSpec<(), Extensions>; /// The `ChainSpec` parameterized for the westend runtime. #[cfg(feature = "westend-native")] -pub type WestendChainSpec = service::GenericChainSpec; +pub type WestendChainSpec = service::GenericChainSpec<(), Extensions>; /// The `ChainSpec` parameterized for the westend runtime. // Dummy chain spec, but that is fine when we don't have the native runtime. @@ -84,12 +84,7 @@ pub type WestendChainSpec = GenericChainSpec; /// The `ChainSpec` parameterized for the rococo runtime. #[cfg(feature = "rococo-native")] -pub type RococoChainSpec = service::GenericChainSpec; - -/// The `ChainSpec` parameterized for the `versi` runtime. -/// -/// As of now `Versi` will just be a clone of `Rococo`, until we need it to differ. -pub type VersiChainSpec = RococoChainSpec; +pub type RococoChainSpec = service::GenericChainSpec<(), Extensions>; /// The `ChainSpec` parameterized for the rococo runtime. // Dummy chain spec, but that is fine when we don't have the native runtime. @@ -206,7 +201,7 @@ fn rococo_session_keys( } #[cfg(feature = "westend-native")] -fn westend_staging_testnet_config_genesis(wasm_binary: &[u8]) -> westend::RuntimeGenesisConfig { +fn westend_staging_testnet_config_genesis() -> serde_json::Value { use hex_literal::hex; use sp_core::crypto::UncheckedInto; @@ -344,19 +339,16 @@ fn westend_staging_testnet_config_genesis(wasm_binary: &[u8]) -> westend::Runtim const ENDOWMENT: u128 = 1_000_000 * WND; const STASH: u128 = 100 * WND; - westend::RuntimeGenesisConfig { - system: westend::SystemConfig { code: wasm_binary.to_vec(), ..Default::default() }, - balances: westend::BalancesConfig { - balances: endowed_accounts + serde_json::json!({ + "balances": { + "balances": endowed_accounts .iter() .map(|k: &AccountId| (k.clone(), ENDOWMENT)) .chain(initial_authorities.iter().map(|x| (x.0.clone(), STASH))) - .collect(), + .collect::>(), }, - beefy: Default::default(), - indices: westend::IndicesConfig { indices: vec![] }, - session: westend::SessionConfig { - keys: initial_authorities + "session": { + "keys": initial_authorities .iter() .map(|x| { ( @@ -375,51 +367,32 @@ fn westend_staging_testnet_config_genesis(wasm_binary: &[u8]) -> westend::Runtim }) .collect::>(), }, - staking: westend::StakingConfig { - validator_count: 50, - minimum_validator_count: 4, - stakers: initial_authorities + "staking": { + "validatorCount": 50, + "minimumValidatorCount": 4, + "stakers": initial_authorities .iter() - .map(|x| (x.0.clone(), x.0.clone(), STASH, westend::StakerStatus::Validator)) - .collect(), - invulnerables: initial_authorities.iter().map(|x| x.0.clone()).collect(), - force_era: Forcing::ForceNone, - slash_reward_fraction: Perbill::from_percent(10), - ..Default::default() - }, - babe: westend::BabeConfig { - authorities: Default::default(), - epoch_config: Some(westend::BABE_GENESIS_EPOCH_CONFIG), - ..Default::default() + .map(|x| (x.0.clone(), x.0.clone(), STASH, westend::StakerStatus::::Validator)) + .collect::>(), + "invulnerables": initial_authorities.iter().map(|x| x.0.clone()).collect::>(), + "forceEra": Forcing::ForceNone, + "slashRewardFraction": Perbill::from_percent(10), }, - grandpa: Default::default(), - im_online: Default::default(), - authority_discovery: westend::AuthorityDiscoveryConfig { - keys: vec![], - ..Default::default() + "babe": { + "epochConfig": Some(westend::BABE_GENESIS_EPOCH_CONFIG), }, - vesting: westend::VestingConfig { vesting: vec![] }, - sudo: westend::SudoConfig { key: Some(endowed_accounts[0].clone()) }, - hrmp: Default::default(), - treasury: Default::default(), - configuration: westend::ConfigurationConfig { - config: default_parachains_host_configuration(), + "sudo": { "key": Some(endowed_accounts[0].clone()) }, + "configuration": { + "config": default_parachains_host_configuration(), }, - paras: Default::default(), - registrar: westend_runtime::RegistrarConfig { - next_free_para_id: polkadot_primitives::LOWEST_PUBLIC_ID, - ..Default::default() + "registrar": { + "nextFreeParaId": polkadot_primitives::LOWEST_PUBLIC_ID, }, - xcm_pallet: Default::default(), - nomination_pools: Default::default(), - assigned_slots: Default::default(), - } + }) } #[cfg(feature = "rococo-native")] -fn rococo_staging_testnet_config_genesis( - wasm_binary: &[u8], -) -> rococo_runtime::RuntimeGenesisConfig { +fn rococo_staging_testnet_config_genesis() -> serde_json::Value { use hex_literal::hex; use sp_core::crypto::UncheckedInto; @@ -662,19 +635,16 @@ fn rococo_staging_testnet_config_genesis( const ENDOWMENT: u128 = 1_000_000 * ROC; const STASH: u128 = 100 * ROC; - rococo_runtime::RuntimeGenesisConfig { - system: rococo_runtime::SystemConfig { code: wasm_binary.to_vec(), ..Default::default() }, - balances: rococo_runtime::BalancesConfig { - balances: endowed_accounts + serde_json::json!({ + "balances": { + "balances": endowed_accounts .iter() .map(|k: &AccountId| (k.clone(), ENDOWMENT)) .chain(initial_authorities.iter().map(|x| (x.0.clone(), STASH))) - .collect(), + .collect::>(), }, - beefy: Default::default(), - indices: rococo_runtime::IndicesConfig { indices: vec![] }, - session: rococo_runtime::SessionConfig { - keys: initial_authorities + "session": { + "keys": initial_authorities .iter() .map(|x| { ( @@ -693,80 +663,55 @@ fn rococo_staging_testnet_config_genesis( }) .collect::>(), }, - babe: rococo_runtime::BabeConfig { - authorities: Default::default(), - epoch_config: Some(rococo_runtime::BABE_GENESIS_EPOCH_CONFIG), - ..Default::default() + "babe": { + "epochConfig": Some(rococo_runtime::BABE_GENESIS_EPOCH_CONFIG), }, - grandpa: Default::default(), - im_online: Default::default(), - treasury: Default::default(), - authority_discovery: rococo_runtime::AuthorityDiscoveryConfig { - keys: vec![], - ..Default::default() + "sudo": { "key": Some(endowed_accounts[0].clone()) }, + "configuration": { + "config": default_parachains_host_configuration(), }, - claims: rococo::ClaimsConfig { claims: vec![], vesting: vec![] }, - vesting: rococo::VestingConfig { vesting: vec![] }, - sudo: rococo_runtime::SudoConfig { key: Some(endowed_accounts[0].clone()) }, - paras: rococo_runtime::ParasConfig { paras: vec![], ..Default::default() }, - hrmp: Default::default(), - configuration: rococo_runtime::ConfigurationConfig { - config: default_parachains_host_configuration(), + "registrar": { + "nextFreeParaId": polkadot_primitives::LOWEST_PUBLIC_ID, }, - registrar: rococo_runtime::RegistrarConfig { - next_free_para_id: polkadot_primitives::LOWEST_PUBLIC_ID, - ..Default::default() - }, - xcm_pallet: Default::default(), - nis_counterpart_balances: Default::default(), - assigned_slots: Default::default(), - } + }) } /// Westend staging testnet config. #[cfg(feature = "westend-native")] pub fn westend_staging_testnet_config() -> Result { - let wasm_binary = westend::WASM_BINARY.ok_or("Westend development wasm not available")?; - let boot_nodes = vec![]; - - Ok(WestendChainSpec::from_genesis( - "Westend Staging Testnet", - "westend_staging_testnet", - ChainType::Live, - move || westend_staging_testnet_config_genesis(wasm_binary), - boot_nodes, - Some( - TelemetryEndpoints::new(vec![(WESTEND_STAGING_TELEMETRY_URL.to_string(), 0)]) - .expect("Westend Staging telemetry url is valid; qed"), - ), - Some(DEFAULT_PROTOCOL_ID), - None, - None, + Ok(WestendChainSpec::builder( + westend::WASM_BINARY.ok_or("Westend development wasm not available")?, Default::default(), - )) + ) + .with_name("Westend Staging Testnet") + .with_id("westend_staging_testnet") + .with_chain_type(ChainType::Live) + .with_genesis_config_patch(westend_staging_testnet_config_genesis()) + .with_telemetry_endpoints( + TelemetryEndpoints::new(vec![(WESTEND_STAGING_TELEMETRY_URL.to_string(), 0)]) + .expect("Westend Staging telemetry url is valid; qed"), + ) + .with_protocol_id(DEFAULT_PROTOCOL_ID) + .build()) } /// Rococo staging testnet config. #[cfg(feature = "rococo-native")] pub fn rococo_staging_testnet_config() -> Result { - let wasm_binary = rococo::WASM_BINARY.ok_or("Rococo development wasm not available")?; - let boot_nodes = vec![]; - - Ok(RococoChainSpec::from_genesis( - "Rococo Staging Testnet", - "rococo_staging_testnet", - ChainType::Live, - move || rococo_staging_testnet_config_genesis(wasm_binary), - boot_nodes, - Some( - TelemetryEndpoints::new(vec![(ROCOCO_STAGING_TELEMETRY_URL.to_string(), 0)]) - .expect("Rococo Staging telemetry url is valid; qed"), - ), - Some(DEFAULT_PROTOCOL_ID), - None, - None, + Ok(RococoChainSpec::builder( + rococo::WASM_BINARY.ok_or("Rococo development wasm not available")?, Default::default(), - )) + ) + .with_name("Rococo Staging Testnet") + .with_id("rococo_staging_testnet") + .with_chain_type(ChainType::Live) + .with_genesis_config_patch(rococo_staging_testnet_config_genesis()) + .with_telemetry_endpoints( + TelemetryEndpoints::new(vec![(ROCOCO_STAGING_TELEMETRY_URL.to_string(), 0)]) + .expect("Rococo Staging telemetry url is valid; qed"), + ) + .with_protocol_id(DEFAULT_PROTOCOL_ID) + .build()) } pub fn versi_chain_spec_properties() -> serde_json::map::Map { @@ -783,24 +728,21 @@ pub fn versi_chain_spec_properties() -> serde_json::map::Map Result { - let wasm_binary = rococo::WASM_BINARY.ok_or("Versi development wasm not available")?; - let boot_nodes = vec![]; - - Ok(RococoChainSpec::from_genesis( - "Versi Staging Testnet", - "versi_staging_testnet", - ChainType::Live, - move || rococo_staging_testnet_config_genesis(wasm_binary), - boot_nodes, - Some( - TelemetryEndpoints::new(vec![(VERSI_STAGING_TELEMETRY_URL.to_string(), 0)]) - .expect("Versi Staging telemetry url is valid; qed"), - ), - Some("versi"), - None, - Some(versi_chain_spec_properties()), + Ok(RococoChainSpec::builder( + rococo::WASM_BINARY.ok_or("Versi development wasm not available")?, Default::default(), - )) + ) + .with_name("Versi Staging Testnet") + .with_id("versi_staging_testnet") + .with_chain_type(ChainType::Live) + .with_genesis_config_patch(rococo_staging_testnet_config_genesis()) + .with_telemetry_endpoints( + TelemetryEndpoints::new(vec![(VERSI_STAGING_TELEMETRY_URL.to_string(), 0)]) + .expect("Versi Staging telemetry url is valid; qed"), + ) + .with_protocol_id("versi") + .with_properties(versi_chain_spec_properties()) + .build()) } /// Helper function to generate a crypto pair from seed @@ -879,10 +821,9 @@ fn testnet_accounts() -> Vec { ] } -/// Helper function to create westend `RuntimeGenesisConfig` for testing +/// Helper function to create westend runtime `GenesisConfig` patch for testing #[cfg(feature = "westend-native")] pub fn westend_testnet_genesis( - wasm_binary: &[u8], initial_authorities: Vec<( AccountId, AccountId, @@ -896,21 +837,18 @@ pub fn westend_testnet_genesis( )>, root_key: AccountId, endowed_accounts: Option>, -) -> westend::RuntimeGenesisConfig { +) -> serde_json::Value { let endowed_accounts: Vec = endowed_accounts.unwrap_or_else(testnet_accounts); const ENDOWMENT: u128 = 1_000_000 * WND; const STASH: u128 = 100 * WND; - westend::RuntimeGenesisConfig { - system: westend::SystemConfig { code: wasm_binary.to_vec(), ..Default::default() }, - indices: westend::IndicesConfig { indices: vec![] }, - balances: westend::BalancesConfig { - balances: endowed_accounts.iter().map(|k| (k.clone(), ENDOWMENT)).collect(), + serde_json::json!({ + "balances": { + "balances": endowed_accounts.iter().map(|k| (k.clone(), ENDOWMENT)).collect::>(), }, - beefy: Default::default(), - session: westend::SessionConfig { - keys: initial_authorities + "session": { + "keys": initial_authorities .iter() .map(|x| { ( @@ -929,51 +867,33 @@ pub fn westend_testnet_genesis( }) .collect::>(), }, - staking: westend::StakingConfig { - minimum_validator_count: 1, - validator_count: initial_authorities.len() as u32, - stakers: initial_authorities + "staking": { + "minimumValidatorCount": 1, + "validatorCount": initial_authorities.len() as u32, + "stakers": initial_authorities .iter() - .map(|x| (x.0.clone(), x.0.clone(), STASH, westend::StakerStatus::Validator)) - .collect(), - invulnerables: initial_authorities.iter().map(|x| x.0.clone()).collect(), - force_era: Forcing::NotForcing, - slash_reward_fraction: Perbill::from_percent(10), - ..Default::default() - }, - babe: westend::BabeConfig { - authorities: Default::default(), - epoch_config: Some(westend::BABE_GENESIS_EPOCH_CONFIG), - ..Default::default() + .map(|x| (x.0.clone(), x.0.clone(), STASH, westend::StakerStatus::::Validator)) + .collect::>(), + "invulnerables": initial_authorities.iter().map(|x| x.0.clone()).collect::>(), + "forceEra": Forcing::NotForcing, + "slashRewardFraction": Perbill::from_percent(10), }, - grandpa: Default::default(), - im_online: Default::default(), - authority_discovery: westend::AuthorityDiscoveryConfig { - keys: vec![], - ..Default::default() + "babe": { + "epochConfig": Some(westend::BABE_GENESIS_EPOCH_CONFIG), }, - vesting: westend::VestingConfig { vesting: vec![] }, - sudo: westend::SudoConfig { key: Some(root_key) }, - hrmp: Default::default(), - treasury: Default::default(), - configuration: westend::ConfigurationConfig { - config: default_parachains_host_configuration(), + "sudo": { "key": Some(root_key) }, + "configuration": { + "config": default_parachains_host_configuration(), }, - paras: Default::default(), - registrar: westend_runtime::RegistrarConfig { - next_free_para_id: polkadot_primitives::LOWEST_PUBLIC_ID, - ..Default::default() + "registrar": { + "nextFreeParaId": polkadot_primitives::LOWEST_PUBLIC_ID, }, - xcm_pallet: Default::default(), - nomination_pools: Default::default(), - assigned_slots: Default::default(), - } + }) } -/// Helper function to create rococo `RuntimeGenesisConfig` for testing +/// Helper function to create rococo runtime `GenesisConfig` patch for testing #[cfg(feature = "rococo-native")] pub fn rococo_testnet_genesis( - wasm_binary: &[u8], initial_authorities: Vec<( AccountId, AccountId, @@ -987,20 +907,17 @@ pub fn rococo_testnet_genesis( )>, root_key: AccountId, endowed_accounts: Option>, -) -> rococo_runtime::RuntimeGenesisConfig { +) -> serde_json::Value { let endowed_accounts: Vec = endowed_accounts.unwrap_or_else(testnet_accounts); const ENDOWMENT: u128 = 1_000_000 * ROC; - rococo_runtime::RuntimeGenesisConfig { - system: rococo_runtime::SystemConfig { code: wasm_binary.to_vec(), ..Default::default() }, - beefy: Default::default(), - indices: rococo_runtime::IndicesConfig { indices: vec![] }, - balances: rococo_runtime::BalancesConfig { - balances: endowed_accounts.iter().map(|k| (k.clone(), ENDOWMENT)).collect(), + serde_json::json!({ + "balances": { + "balances": endowed_accounts.iter().map(|k| (k.clone(), ENDOWMENT)).collect::>(), }, - session: rococo_runtime::SessionConfig { - keys: initial_authorities + "session": { + "keys": initial_authorities .iter() .map(|x| { ( @@ -1019,43 +936,25 @@ pub fn rococo_testnet_genesis( }) .collect::>(), }, - babe: rococo_runtime::BabeConfig { - authorities: Default::default(), - epoch_config: Some(rococo_runtime::BABE_GENESIS_EPOCH_CONFIG), - ..Default::default() - }, - grandpa: Default::default(), - im_online: Default::default(), - treasury: Default::default(), - claims: rococo::ClaimsConfig { claims: vec![], vesting: vec![] }, - vesting: rococo::VestingConfig { vesting: vec![] }, - authority_discovery: rococo_runtime::AuthorityDiscoveryConfig { - keys: vec![], - ..Default::default() + "babe": { + "epochConfig": Some(rococo_runtime::BABE_GENESIS_EPOCH_CONFIG), }, - sudo: rococo_runtime::SudoConfig { key: Some(root_key.clone()) }, - hrmp: Default::default(), - configuration: rococo_runtime::ConfigurationConfig { - config: polkadot_runtime_parachains::configuration::HostConfiguration { + "sudo": { "key": Some(root_key.clone()) }, + "configuration": { + "config": polkadot_runtime_parachains::configuration::HostConfiguration { max_validators_per_core: Some(1), ..default_parachains_host_configuration() }, }, - paras: rococo_runtime::ParasConfig { paras: vec![], ..Default::default() }, - registrar: rococo_runtime::RegistrarConfig { - next_free_para_id: polkadot_primitives::LOWEST_PUBLIC_ID, - ..Default::default() - }, - xcm_pallet: Default::default(), - nis_counterpart_balances: Default::default(), - assigned_slots: Default::default(), - } + "registrar": { + "nextFreeParaId": polkadot_primitives::LOWEST_PUBLIC_ID, + } + }) } #[cfg(feature = "westend-native")] -fn westend_development_config_genesis(wasm_binary: &[u8]) -> westend::RuntimeGenesisConfig { +fn westend_development_config_genesis() -> serde_json::Value { westend_testnet_genesis( - wasm_binary, vec![get_authority_keys_from_seed("Alice")], get_account_id_from_seed::("Alice"), None, @@ -1063,9 +962,8 @@ fn westend_development_config_genesis(wasm_binary: &[u8]) -> westend::RuntimeGen } #[cfg(feature = "rococo-native")] -fn rococo_development_config_genesis(wasm_binary: &[u8]) -> rococo_runtime::RuntimeGenesisConfig { +fn rococo_development_config_genesis() -> serde_json::Value { rococo_testnet_genesis( - wasm_binary, vec![get_authority_keys_from_seed("Alice")], get_account_id_from_seed::("Alice"), None, @@ -1075,84 +973,67 @@ fn rococo_development_config_genesis(wasm_binary: &[u8]) -> rococo_runtime::Runt /// Westend development config (single validator Alice) #[cfg(feature = "westend-native")] pub fn westend_development_config() -> Result { - let wasm_binary = westend::WASM_BINARY.ok_or("Westend development wasm not available")?; - - Ok(WestendChainSpec::from_genesis( - "Development", - "westend_dev", - ChainType::Development, - move || westend_development_config_genesis(wasm_binary), - vec![], - None, - Some(DEFAULT_PROTOCOL_ID), - None, - None, + Ok(WestendChainSpec::builder( + westend::WASM_BINARY.ok_or("Westend development wasm not available")?, Default::default(), - )) + ) + .with_name("Development") + .with_id("westend_dev") + .with_chain_type(ChainType::Development) + .with_genesis_config_patch(westend_development_config_genesis()) + .with_protocol_id(DEFAULT_PROTOCOL_ID) + .build()) } /// Rococo development config (single validator Alice) #[cfg(feature = "rococo-native")] pub fn rococo_development_config() -> Result { - let wasm_binary = rococo::WASM_BINARY.ok_or("Rococo development wasm not available")?; - - Ok(RococoChainSpec::from_genesis( - "Development", - "rococo_dev", - ChainType::Development, - move || rococo_development_config_genesis(wasm_binary), - vec![], - None, - Some(DEFAULT_PROTOCOL_ID), - None, - None, + Ok(RococoChainSpec::builder( + rococo::WASM_BINARY.ok_or("Rococo development wasm not available")?, Default::default(), - )) + ) + .with_name("Development") + .with_id("rococo_dev") + .with_chain_type(ChainType::Development) + .with_genesis_config_patch(rococo_development_config_genesis()) + .with_protocol_id(DEFAULT_PROTOCOL_ID) + .build()) } /// `Versi` development config (single validator Alice) #[cfg(feature = "rococo-native")] pub fn versi_development_config() -> Result { - let wasm_binary = rococo::WASM_BINARY.ok_or("Versi development wasm not available")?; - - Ok(RococoChainSpec::from_genesis( - "Development", - "versi_dev", - ChainType::Development, - move || rococo_development_config_genesis(wasm_binary), - vec![], - None, - Some("versi"), - None, - None, + Ok(RococoChainSpec::builder( + rococo::WASM_BINARY.ok_or("Versi development wasm not available")?, Default::default(), - )) + ) + .with_name("Development") + .with_id("versi_dev") + .with_chain_type(ChainType::Development) + .with_genesis_config_patch(rococo_development_config_genesis()) + .with_protocol_id("versi") + .build()) } /// Wococo development config (single validator Alice) #[cfg(feature = "rococo-native")] pub fn wococo_development_config() -> Result { const WOCOCO_DEV_PROTOCOL_ID: &str = "woco"; - let wasm_binary = rococo::WASM_BINARY.ok_or("Wococo development wasm not available")?; - - Ok(RococoChainSpec::from_genesis( - "Development", - "wococo_dev", - ChainType::Development, - move || rococo_development_config_genesis(wasm_binary), - vec![], - None, - Some(WOCOCO_DEV_PROTOCOL_ID), - None, - None, + Ok(RococoChainSpec::builder( + rococo::WASM_BINARY.ok_or("Wococo development wasm not available")?, Default::default(), - )) + ) + .with_name("Development") + .with_id("wococo_dev") + .with_chain_type(ChainType::Development) + .with_genesis_config_patch(rococo_development_config_genesis()) + .with_protocol_id(WOCOCO_DEV_PROTOCOL_ID) + .build()) } #[cfg(feature = "westend-native")] -fn westend_local_testnet_genesis(wasm_binary: &[u8]) -> westend::RuntimeGenesisConfig { +fn westend_local_testnet_genesis() -> serde_json::Value { westend_testnet_genesis( - wasm_binary, vec![get_authority_keys_from_seed("Alice"), get_authority_keys_from_seed("Bob")], get_account_id_from_seed::("Alice"), None, @@ -1162,26 +1043,21 @@ fn westend_local_testnet_genesis(wasm_binary: &[u8]) -> westend::RuntimeGenesisC /// Westend local testnet config (multivalidator Alice + Bob) #[cfg(feature = "westend-native")] pub fn westend_local_testnet_config() -> Result { - let wasm_binary = westend::WASM_BINARY.ok_or("Westend development wasm not available")?; - - Ok(WestendChainSpec::from_genesis( - "Westend Local Testnet", - "westend_local_testnet", - ChainType::Local, - move || westend_local_testnet_genesis(wasm_binary), - vec![], - None, - Some(DEFAULT_PROTOCOL_ID), - None, - None, + Ok(WestendChainSpec::builder( + westend::WASM_BINARY.ok_or("Westend development wasm not available")?, Default::default(), - )) + ) + .with_name("Westend Local Testnet") + .with_id("westend_local_testnet") + .with_chain_type(ChainType::Local) + .with_genesis_config_patch(westend_local_testnet_genesis()) + .with_protocol_id(DEFAULT_PROTOCOL_ID) + .build()) } #[cfg(feature = "rococo-native")] -fn rococo_local_testnet_genesis(wasm_binary: &[u8]) -> rococo_runtime::RuntimeGenesisConfig { +fn rococo_local_testnet_genesis() -> serde_json::Value { rococo_testnet_genesis( - wasm_binary, vec![get_authority_keys_from_seed("Alice"), get_authority_keys_from_seed("Bob")], get_account_id_from_seed::("Alice"), None, @@ -1191,27 +1067,22 @@ fn rococo_local_testnet_genesis(wasm_binary: &[u8]) -> rococo_runtime::RuntimeGe /// Rococo local testnet config (multivalidator Alice + Bob) #[cfg(feature = "rococo-native")] pub fn rococo_local_testnet_config() -> Result { - let wasm_binary = rococo::WASM_BINARY.ok_or("Rococo development wasm not available")?; - - Ok(RococoChainSpec::from_genesis( - "Rococo Local Testnet", - "rococo_local_testnet", - ChainType::Local, - move || rococo_local_testnet_genesis(wasm_binary), - vec![], - None, - Some(DEFAULT_PROTOCOL_ID), - None, - None, + Ok(RococoChainSpec::builder( + rococo::WASM_BINARY.ok_or("Rococo development wasm not available")?, Default::default(), - )) + ) + .with_name("Rococo Local Testnet") + .with_id("rococo_local_testnet") + .with_chain_type(ChainType::Local) + .with_genesis_config_patch(rococo_local_testnet_genesis()) + .with_protocol_id(DEFAULT_PROTOCOL_ID) + .build()) } /// Wococo is a temporary testnet that uses almost the same runtime as rococo. #[cfg(feature = "rococo-native")] -fn wococo_local_testnet_genesis(wasm_binary: &[u8]) -> rococo_runtime::RuntimeGenesisConfig { +fn wococo_local_testnet_genesis() -> serde_json::Value { rococo_testnet_genesis( - wasm_binary, vec![ get_authority_keys_from_seed("Alice"), get_authority_keys_from_seed("Bob"), @@ -1226,27 +1097,22 @@ fn wococo_local_testnet_genesis(wasm_binary: &[u8]) -> rococo_runtime::RuntimeGe /// Wococo local testnet config (multivalidator Alice + Bob + Charlie + Dave) #[cfg(feature = "rococo-native")] pub fn wococo_local_testnet_config() -> Result { - let wasm_binary = rococo::WASM_BINARY.ok_or("Wococo development wasm not available")?; - - Ok(RococoChainSpec::from_genesis( - "Wococo Local Testnet", - "wococo_local_testnet", - ChainType::Local, - move || wococo_local_testnet_genesis(wasm_binary), - vec![], - None, - Some(DEFAULT_PROTOCOL_ID), - None, - None, + Ok(RococoChainSpec::builder( + rococo::WASM_BINARY.ok_or("Rococo development wasm (used for wococo) not available")?, Default::default(), - )) + ) + .with_name("Wococo Local Testnet") + .with_id("wococo_local_testnet") + .with_chain_type(ChainType::Local) + .with_genesis_config_patch(wococo_local_testnet_genesis()) + .with_protocol_id(DEFAULT_PROTOCOL_ID) + .build()) } /// `Versi` is a temporary testnet that uses the same runtime as rococo. #[cfg(feature = "rococo-native")] -fn versi_local_testnet_genesis(wasm_binary: &[u8]) -> rococo_runtime::RuntimeGenesisConfig { +fn versi_local_testnet_genesis() -> serde_json::Value { rococo_testnet_genesis( - wasm_binary, vec![ get_authority_keys_from_seed("Alice"), get_authority_keys_from_seed("Bob"), @@ -1261,18 +1127,14 @@ fn versi_local_testnet_genesis(wasm_binary: &[u8]) -> rococo_runtime::RuntimeGen /// `Versi` local testnet config (multivalidator Alice + Bob + Charlie + Dave) #[cfg(feature = "rococo-native")] pub fn versi_local_testnet_config() -> Result { - let wasm_binary = rococo::WASM_BINARY.ok_or("Versi development wasm not available")?; - - Ok(RococoChainSpec::from_genesis( - "Versi Local Testnet", - "versi_local_testnet", - ChainType::Local, - move || versi_local_testnet_genesis(wasm_binary), - vec![], - None, - Some("versi"), - None, - None, + Ok(RococoChainSpec::builder( + rococo::WASM_BINARY.ok_or("Rococo development wasm (used for versi) not available")?, Default::default(), - )) + ) + .with_name("Versi Local Testnet") + .with_id("versi_local_testnet") + .with_chain_type(ChainType::Local) + .with_genesis_config_patch(versi_local_testnet_genesis()) + .with_protocol_id("versi") + .build()) } diff --git a/polkadot/node/test/client/src/lib.rs b/polkadot/node/test/client/src/lib.rs index 5d97ffcdf1da..6b205c09f2f3 100644 --- a/polkadot/node/test/client/src/lib.rs +++ b/polkadot/node/test/client/src/lib.rs @@ -51,7 +51,7 @@ pub struct GenesisParameters; impl substrate_test_client::GenesisInit for GenesisParameters { fn genesis_storage(&self) -> Storage { - polkadot_test_service::chain_spec::polkadot_local_testnet_genesis() + polkadot_test_service::chain_spec::polkadot_local_testnet_config() .build_storage() .expect("Builds test runtime genesis storage") } diff --git a/polkadot/node/test/service/Cargo.toml b/polkadot/node/test/service/Cargo.toml index 1924418a4856..6c13e45a5667 100644 --- a/polkadot/node/test/service/Cargo.toml +++ b/polkadot/node/test/service/Cargo.toml @@ -59,7 +59,6 @@ substrate-test-client = { path = "../../../../substrate/test-utils/client" } [dev-dependencies] pallet-balances = { path = "../../../../substrate/frame/balances", default-features = false } -serde_json = "1.0.107" substrate-test-utils = { path = "../../../../substrate/test-utils" } tokio = { version = "1.24.2", features = ["macros"] } diff --git a/polkadot/node/test/service/src/chain_spec.rs b/polkadot/node/test/service/src/chain_spec.rs index bedb1250b2a9..0295090b9521 100644 --- a/polkadot/node/test/service/src/chain_spec.rs +++ b/polkadot/node/test/service/src/chain_spec.rs @@ -31,8 +31,7 @@ use test_runtime_constants::currency::DOTS; const DEFAULT_PROTOCOL_ID: &str = "dot"; /// The `ChainSpec` parameterized for polkadot test runtime. -pub type PolkadotChainSpec = - sc_service::GenericChainSpec; +pub type PolkadotChainSpec = sc_service::GenericChainSpec<(), Extensions>; /// Returns the properties for the [`PolkadotChainSpec`]. pub fn polkadot_chain_spec_properties() -> serde_json::map::Map { @@ -46,22 +45,21 @@ pub fn polkadot_chain_spec_properties() -> serde_json::map::Map PolkadotChainSpec { - PolkadotChainSpec::from_genesis( - "Local Testnet", - "local_testnet", - ChainType::Local, - || polkadot_local_testnet_genesis(), - vec![], - None, - Some(DEFAULT_PROTOCOL_ID), - None, - Some(polkadot_chain_spec_properties()), + PolkadotChainSpec::builder( + polkadot_test_runtime::WASM_BINARY.expect("Wasm binary must be built for testing"), Default::default(), ) + .with_name("Local Testnet") + .with_id("local_testnet") + .with_chain_type(ChainType::Local) + .with_genesis_config_patch(polkadot_local_testnet_genesis()) + .with_protocol_id(DEFAULT_PROTOCOL_ID) + .with_properties(polkadot_chain_spec_properties()) + .build() } /// Local testnet genesis config (multivalidator Alice + Bob) -pub fn polkadot_local_testnet_genesis() -> polkadot_test_runtime::RuntimeGenesisConfig { +pub fn polkadot_local_testnet_genesis() -> serde_json::Value { polkadot_testnet_genesis( vec![get_authority_keys_from_seed("Alice"), get_authority_keys_from_seed("Bob")], get_account_id_from_seed::("Alice"), @@ -114,7 +112,7 @@ fn polkadot_testnet_genesis( )>, root_key: AccountId, endowed_accounts: Option>, -) -> polkadot_test_runtime::RuntimeGenesisConfig { +) -> serde_json::Value { use polkadot_test_runtime as runtime; let endowed_accounts: Vec = endowed_accounts.unwrap_or_else(testnet_accounts); @@ -122,17 +120,12 @@ fn polkadot_testnet_genesis( const ENDOWMENT: u128 = 1_000_000 * DOTS; const STASH: u128 = 100 * DOTS; - runtime::RuntimeGenesisConfig { - system: runtime::SystemConfig { - code: runtime::WASM_BINARY.expect("Wasm binary must be built for testing").to_vec(), - ..Default::default() - }, - indices: runtime::IndicesConfig { indices: vec![] }, - balances: runtime::BalancesConfig { - balances: endowed_accounts.iter().map(|k| (k.clone(), ENDOWMENT)).collect(), + serde_json::json!({ + "balances": { + "balances": endowed_accounts.iter().map(|k| (k.clone(), ENDOWMENT)).collect::>(), }, - session: runtime::SessionConfig { - keys: initial_authorities + "session": { + "keys": initial_authorities .iter() .map(|x| { ( @@ -149,33 +142,23 @@ fn polkadot_testnet_genesis( }) .collect::>(), }, - staking: runtime::StakingConfig { - minimum_validator_count: 1, - validator_count: 2, - stakers: initial_authorities + "staking": { + "minimumValidatorCount": 1, + "validatorCount": 2, + "stakers": initial_authorities .iter() - .map(|x| (x.0.clone(), x.0.clone(), STASH, runtime::StakerStatus::Validator)) - .collect(), - invulnerables: initial_authorities.iter().map(|x| x.0.clone()).collect(), - force_era: Forcing::NotForcing, - slash_reward_fraction: Perbill::from_percent(10), - ..Default::default() - }, - babe: runtime::BabeConfig { - authorities: vec![], - epoch_config: Some(BABE_GENESIS_EPOCH_CONFIG), - ..Default::default() + .map(|x| (x.0.clone(), x.0.clone(), STASH, runtime::StakerStatus::::Validator)) + .collect::>(), + "invulnerables": initial_authorities.iter().map(|x| x.0.clone()).collect::>(), + "forceEra": Forcing::NotForcing, + "slashRewardFraction": Perbill::from_percent(10), }, - grandpa: Default::default(), - authority_discovery: runtime::AuthorityDiscoveryConfig { - keys: vec![], - ..Default::default() + "babe": { + "epochConfig": Some(BABE_GENESIS_EPOCH_CONFIG), }, - claims: runtime::ClaimsConfig { claims: vec![], vesting: vec![] }, - vesting: runtime::VestingConfig { vesting: vec![] }, - sudo: runtime::SudoConfig { key: Some(root_key) }, - configuration: runtime::ConfigurationConfig { - config: polkadot_runtime_parachains::configuration::HostConfiguration { + "sudo": { "key": Some(root_key) }, + "configuration": { + "config": polkadot_runtime_parachains::configuration::HostConfiguration { validation_upgrade_cooldown: 10u32, validation_upgrade_delay: 5, code_retention_period: 1200, @@ -188,8 +171,8 @@ fn polkadot_testnet_genesis( minimum_validation_upgrade_delay: 5, ..Default::default() }, - }, - } + } + }) } /// Can be called for a `Configuration` to check if it is a configuration for the `Test` network. diff --git a/polkadot/runtime/common/src/lib.rs b/polkadot/runtime/common/src/lib.rs index 46a9f7d12cd4..70722d509887 100644 --- a/polkadot/runtime/common/src/lib.rs +++ b/polkadot/runtime/common/src/lib.rs @@ -246,7 +246,8 @@ impl sp_runtime::traits::Convert for U256ToBalance { } /// Macro to set a value (e.g. when using the `parameter_types` macro) to either a production value -/// or to an environment variable or testing value (in case the `fast-runtime` feature is selected). +/// or to an environment variable or testing value (in case the `fast-runtime` feature is selected) +/// or one of two testing values depending on feature. /// Note that the environment variable is evaluated _at compile time_. /// /// Usage: @@ -255,6 +256,8 @@ impl sp_runtime::traits::Convert for U256ToBalance { /// // Note that the env variable version parameter cannot be const. /// pub LaunchPeriod: BlockNumber = prod_or_fast!(7 * DAYS, 1, "KSM_LAUNCH_PERIOD"); /// pub const VotingPeriod: BlockNumber = prod_or_fast!(7 * DAYS, 1 * MINUTES); +/// pub const EpochDuration: BlockNumber = +/// prod_or_fast!(1 * HOURS, "fast-runtime", 1 * MINUTES, "fast-runtime-10m", 10 * MINUTES); /// } /// ``` #[macro_export] diff --git a/polkadot/runtime/parachains/src/configuration.rs b/polkadot/runtime/parachains/src/configuration.rs index a65fda370488..d85c267496f4 100644 --- a/polkadot/runtime/parachains/src/configuration.rs +++ b/polkadot/runtime/parachains/src/configuration.rs @@ -56,6 +56,7 @@ const LOG_TARGET: &str = "runtime::configuration"; serde::Serialize, serde::Deserialize, )] +#[serde(deny_unknown_fields)] pub struct HostConfiguration { // NOTE: This structure is used by parachains via merkle proofs. Therefore, this struct // requires special treatment. diff --git a/polkadot/runtime/rococo/Cargo.toml b/polkadot/runtime/rococo/Cargo.toml index 5b55d3fec503..135e5e940569 100644 --- a/polkadot/runtime/rococo/Cargo.toml +++ b/polkadot/runtime/rococo/Cargo.toml @@ -313,7 +313,7 @@ try-runtime = [ ] # Set timing constants (e.g. session period) to faster versions to speed up testing. -fast-runtime = [] +fast-runtime = [ "rococo-runtime-constants/fast-runtime" ] runtime-metrics = [ "runtime-parachains/runtime-metrics", "sp-io/with-tracing" ] diff --git a/polkadot/runtime/rococo/constants/Cargo.toml b/polkadot/runtime/rococo/constants/Cargo.toml index f99dbd123367..8ff6d57ea5b5 100644 --- a/polkadot/runtime/rococo/constants/Cargo.toml +++ b/polkadot/runtime/rococo/constants/Cargo.toml @@ -29,3 +29,6 @@ std = [ "sp-weights/std", "xcm/std", ] + +# Set timing constants (e.g. session period) to faster versions to speed up testing. +fast-runtime = [] diff --git a/polkadot/zombienet_tests/functional/0002-parachains-disputes.toml b/polkadot/zombienet_tests/functional/0002-parachains-disputes.toml index c63d3e021708..e70322e13e6b 100644 --- a/polkadot/zombienet_tests/functional/0002-parachains-disputes.toml +++ b/polkadot/zombienet_tests/functional/0002-parachains-disputes.toml @@ -1,7 +1,7 @@ [settings] timeout = 1000 -[relaychain.genesis.runtime.configuration.config] +[relaychain.genesis.runtimeGenesis.patch.configuration.config] max_validators_per_core = 5 needed_approvals = 8 diff --git a/polkadot/zombienet_tests/functional/0004-parachains-garbage-candidate.toml b/polkadot/zombienet_tests/functional/0004-parachains-garbage-candidate.toml index 94eb6bb5bb1d..5d6f299d4613 100644 --- a/polkadot/zombienet_tests/functional/0004-parachains-garbage-candidate.toml +++ b/polkadot/zombienet_tests/functional/0004-parachains-garbage-candidate.toml @@ -2,7 +2,7 @@ timeout = 1000 bootnode = true -[relaychain.genesis.runtime.configuration.config] +[relaychain.genesis.runtimeGenesis.patch.configuration.config] max_validators_per_core = 1 needed_approvals = 2 diff --git a/polkadot/zombienet_tests/functional/0005-parachains-disputes-past-session.toml b/polkadot/zombienet_tests/functional/0005-parachains-disputes-past-session.toml index 25d922bb6827..e2fbec079b1a 100644 --- a/polkadot/zombienet_tests/functional/0005-parachains-disputes-past-session.toml +++ b/polkadot/zombienet_tests/functional/0005-parachains-disputes-past-session.toml @@ -2,7 +2,7 @@ timeout = 1000 bootnode = true -[relaychain.genesis.runtime.configuration.config] +[relaychain.genesis.runtimeGenesis.patch.configuration.config] max_validators_per_core = 1 needed_approvals = 2 group_rotation_frequency = 2 diff --git a/polkadot/zombienet_tests/misc/0001-paritydb.toml b/polkadot/zombienet_tests/misc/0001-paritydb.toml index e2130963f0ec..399f848d3ac4 100644 --- a/polkadot/zombienet_tests/misc/0001-paritydb.toml +++ b/polkadot/zombienet_tests/misc/0001-paritydb.toml @@ -2,7 +2,7 @@ timeout = 1000 bootnode = true -[relaychain.genesis.runtime.configuration.config] +[relaychain.genesis.runtimeGenesis.patch.configuration.config] max_validators_per_core = 1 needed_approvals = 3 diff --git a/prdoc/pr_1256.prdoc b/prdoc/pr_1256.prdoc new file mode 100644 index 000000000000..f486786fec35 --- /dev/null +++ b/prdoc/pr_1256.prdoc @@ -0,0 +1,19 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "`chain-spec`: getting ready for native-runtime-free world" + +doc: + - audience: Node Dev + description: | + - [`ChainSpec::from_genesis`](https://github.com/paritytech/polkadot-sdk/blob/3df6b4d00eb310900de6f4858114baf68239412c/substrate/client/chain-spec/src/chain_spec.rs#L525) becomes deprecated in favor of [`ChainSpec::builder()`](https://github.com/paritytech/polkadot-sdk/blob/3df6b4d00eb310900de6f4858114baf68239412c/substrate/client/chain-spec/src/chain_spec.rs#L432), + - The signature of [`ChainSpec::from_genesis`] method was changed by extending it with `code` argument. + +migrations: + db: [] + + runtime: [] + +crates: [] + +host_functions: [] diff --git a/substrate/Cargo.toml b/substrate/Cargo.toml index 47cde0ca70c5..8fb1be5821ba 100644 --- a/substrate/Cargo.toml +++ b/substrate/Cargo.toml @@ -17,6 +17,7 @@ subkey = { path = "bin/utils/subkey" } chain-spec-builder = { package = "staging-chain-spec-builder", path = "bin/utils/chain-spec-builder" } sc-service = { path = "client/service" } +sc-chain-spec = { path = "client/chain-spec" } sc-cli = { path = "client/cli" } sc-consensus-aura = { path = "client/consensus/aura" } sc-consensus-babe = { path = "client/consensus/babe" } diff --git a/substrate/bin/minimal/node/Cargo.toml b/substrate/bin/minimal/node/Cargo.toml index 11ce98eec0dd..39d39d7e3122 100644 --- a/substrate/bin/minimal/node/Cargo.toml +++ b/substrate/bin/minimal/node/Cargo.toml @@ -21,6 +21,7 @@ clap = { version = "4.0.9", features = ["derive"] } futures = { version = "0.3.21", features = ["thread-pool"] } futures-timer = "3.0.1" jsonrpsee = { version = "0.16.2", features = ["server"] } +serde_json = "1.0.107" sc-cli = { path = "../../../client/cli" } sc-executor = { path = "../../../client/executor" } diff --git a/substrate/bin/minimal/node/src/chain_spec.rs b/substrate/bin/minimal/node/src/chain_spec.rs index 91fa9ef45202..6b721deb6d1d 100644 --- a/substrate/bin/minimal/node/src/chain_spec.rs +++ b/substrate/bin/minimal/node/src/chain_spec.rs @@ -15,12 +15,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -use runtime::{BalancesConfig, RuntimeGenesisConfig, SudoConfig, SystemConfig, WASM_BINARY}; +use runtime::{BalancesConfig, SudoConfig, WASM_BINARY}; use sc_service::{ChainType, Properties}; +use serde_json::{json, Value}; use sp_keyring::AccountKeyring; /// This is a specialization of the general Substrate ChainSpec type. -pub type ChainSpec = sc_service::GenericChainSpec; +pub type ChainSpec = sc_service::GenericChainSpec<()>; fn props() -> Properties { let mut properties = Properties::new(); @@ -30,37 +31,25 @@ fn props() -> Properties { } pub fn development_config() -> Result { - let wasm_binary = WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?; - Ok(ChainSpec::from_genesis( - "Development", - "dev", - ChainType::Development, - move || testnet_genesis(wasm_binary), - vec![], - None, - None, - None, - Some(props()), - None, - )) + Ok(ChainSpec::builder(WASM_BINARY.expect("Development wasm not available"), Default::default()) + .with_name("Development") + .with_id("dev") + .with_chain_type(ChainType::Development) + .with_genesis_config_patch(testnet_genesis()) + .with_properties(props()) + .build()) } /// Configure initial storage state for FRAME pallets. -fn testnet_genesis(wasm_binary: &[u8]) -> RuntimeGenesisConfig { +fn testnet_genesis() -> Value { use frame::traits::Get; use runtime::interface::{Balance, MinimumBalance}; let endowment = >::get().max(1) * 1000; let balances = AccountKeyring::iter() .map(|a| (a.to_account_id(), endowment)) .collect::>(); - RuntimeGenesisConfig { - system: SystemConfig { - // Add Wasm runtime to storage. - code: wasm_binary.to_vec(), - _config: Default::default(), - }, - balances: BalancesConfig { balances }, - sudo: SudoConfig { key: Some(AccountKeyring::Alice.to_account_id()) }, - ..Default::default() - } + json!({ + "balances": BalancesConfig { balances }, + "sudo": SudoConfig { key: Some(AccountKeyring::Alice.to_account_id()) }, + }) } diff --git a/substrate/bin/minimal/runtime/Cargo.toml b/substrate/bin/minimal/runtime/Cargo.toml index 1f9b49da7bc3..08feb445879f 100644 --- a/substrate/bin/minimal/runtime/Cargo.toml +++ b/substrate/bin/minimal/runtime/Cargo.toml @@ -9,6 +9,7 @@ scale-info = { version = "2.6.0", default-features = false } # this is a frame-based runtime, thus importing `frame` with runtime feature enabled. frame = { path = "../../../frame", default-features = false, features = ["runtime", "experimental"] } +frame-support = { path = "../../../frame/support", default-features = false} # pallets that we want to use pallet-balances = { path = "../../../frame/balances", default-features = false } @@ -17,6 +18,9 @@ pallet-timestamp = { path = "../../../frame/timestamp", default-features = false pallet-transaction-payment = { path = "../../../frame/transaction-payment", default-features = false } pallet-transaction-payment-rpc-runtime-api = { path = "../../../frame/transaction-payment/rpc/runtime-api", default-features = false } +# genesis builder that allows us to interacto with runtime genesis config +sp-genesis-builder = { path = "../../../primitives/genesis-builder", default-features = false} + [build-dependencies] substrate-wasm-builder = { path = "../../../utils/wasm-builder", optional = true } diff --git a/substrate/bin/minimal/runtime/src/lib.rs b/substrate/bin/minimal/runtime/src/lib.rs index 6c59592554c5..efee400c3f59 100644 --- a/substrate/bin/minimal/runtime/src/lib.rs +++ b/substrate/bin/minimal/runtime/src/lib.rs @@ -31,6 +31,7 @@ use frame::{ prelude::*, }, }; +use frame_support::genesis_builder_helper::{build_config, create_default_config}; #[runtime_version] pub const VERSION: RuntimeVersion = RuntimeVersion { @@ -210,6 +211,16 @@ impl_runtime_apis! { TransactionPayment::length_to_fee(length) } } + + impl sp_genesis_builder::GenesisBuilder for Runtime { + fn create_default_config() -> Vec { + create_default_config::() + } + + fn build_config(config: Vec) -> sp_genesis_builder::Result { + build_config::(config) + } + } } /// Some re-exports that the node side code needs to know. Some are useful in this context as well. diff --git a/substrate/bin/node-template/node/Cargo.toml b/substrate/bin/node-template/node/Cargo.toml index 23840cce2229..4e87675514d5 100644 --- a/substrate/bin/node-template/node/Cargo.toml +++ b/substrate/bin/node-template/node/Cargo.toml @@ -19,6 +19,7 @@ name = "node-template" [dependencies] clap = { version = "4.4.6", features = ["derive"] } futures = { version = "0.3.21", features = ["thread-pool"]} +serde_json = "1.0.85" sc-cli = { path = "../../../client/cli" } sp-core = { path = "../../../primitives/core" } diff --git a/substrate/bin/node-template/node/src/chain_spec.rs b/substrate/bin/node-template/node/src/chain_spec.rs index 2cd2d0729302..6e0d78f647a5 100644 --- a/substrate/bin/node-template/node/src/chain_spec.rs +++ b/substrate/bin/node-template/node/src/chain_spec.rs @@ -1,7 +1,4 @@ -use node_template_runtime::{ - AccountId, AuraConfig, BalancesConfig, GrandpaConfig, RuntimeGenesisConfig, Signature, - SudoConfig, SystemConfig, WASM_BINARY, -}; +use node_template_runtime::{AccountId, RuntimeGenesisConfig, Signature, WASM_BINARY}; use sc_service::ChainType; use sp_consensus_aura::sr25519::AuthorityId as AuraId; use sp_consensus_grandpa::AuthorityId as GrandpaId; @@ -37,122 +34,84 @@ pub fn authority_keys_from_seed(s: &str) -> (AuraId, GrandpaId) { } pub fn development_config() -> Result { - let wasm_binary = WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?; - - Ok(ChainSpec::from_genesis( - // Name - "Development", - // ID - "dev", - ChainType::Development, - move || { - testnet_genesis( - wasm_binary, - // Initial PoA authorities - vec![authority_keys_from_seed("Alice")], - // Sudo account - get_account_id_from_seed::("Alice"), - // Pre-funded accounts - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - ], - true, - ) - }, - // Bootnodes - vec![], - // Telemetry - None, - // Protocol ID - None, - None, - // Properties - None, - // Extensions + Ok(ChainSpec::builder( + WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?, None, + ) + .with_name("Development") + .with_id("dev") + .with_chain_type(ChainType::Development) + .with_genesis_config_patch(testnet_genesis( + // Initial PoA authorities + vec![authority_keys_from_seed("Alice")], + // Sudo account + get_account_id_from_seed::("Alice"), + // Pre-funded accounts + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + ], + true, )) + .build()) } pub fn local_testnet_config() -> Result { - let wasm_binary = WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?; - - Ok(ChainSpec::from_genesis( - // Name - "Local Testnet", - // ID - "local_testnet", - ChainType::Local, - move || { - testnet_genesis( - wasm_binary, - // Initial PoA authorities - vec![authority_keys_from_seed("Alice"), authority_keys_from_seed("Bob")], - // Sudo account - get_account_id_from_seed::("Alice"), - // Pre-funded accounts - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Charlie"), - get_account_id_from_seed::("Dave"), - get_account_id_from_seed::("Eve"), - get_account_id_from_seed::("Ferdie"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - get_account_id_from_seed::("Charlie//stash"), - get_account_id_from_seed::("Dave//stash"), - get_account_id_from_seed::("Eve//stash"), - get_account_id_from_seed::("Ferdie//stash"), - ], - true, - ) - }, - // Bootnodes - vec![], - // Telemetry - None, - // Protocol ID - None, - // Properties - None, - None, - // Extensions + Ok(ChainSpec::builder( + WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?, None, + ) + .with_name("Local Testnet") + .with_id("local_testnet") + .with_chain_type(ChainType::Local) + .with_genesis_config_patch(testnet_genesis( + // Initial PoA authorities + vec![authority_keys_from_seed("Alice"), authority_keys_from_seed("Bob")], + // Sudo account + get_account_id_from_seed::("Alice"), + // Pre-funded accounts + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Charlie"), + get_account_id_from_seed::("Dave"), + get_account_id_from_seed::("Eve"), + get_account_id_from_seed::("Ferdie"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + get_account_id_from_seed::("Charlie//stash"), + get_account_id_from_seed::("Dave//stash"), + get_account_id_from_seed::("Eve//stash"), + get_account_id_from_seed::("Ferdie//stash"), + ], + true, )) + .build()) } /// Configure initial storage state for FRAME modules. fn testnet_genesis( - wasm_binary: &[u8], initial_authorities: Vec<(AuraId, GrandpaId)>, root_key: AccountId, endowed_accounts: Vec, _enable_println: bool, -) -> RuntimeGenesisConfig { - RuntimeGenesisConfig { - system: SystemConfig { - // Add Wasm runtime to storage. - code: wasm_binary.to_vec(), - ..Default::default() - }, - balances: BalancesConfig { +) -> serde_json::Value { + serde_json::json!({ + "balances": { // Configure endowed accounts with initial balance of 1 << 60. - balances: endowed_accounts.iter().cloned().map(|k| (k, 1 << 60)).collect(), + "balances": endowed_accounts.iter().cloned().map(|k| (k, 1u64 << 60)).collect::>(), }, - aura: AuraConfig { - authorities: initial_authorities.iter().map(|x| (x.0.clone())).collect(), + "aura": { + "authorities": initial_authorities.iter().map(|x| (x.0.clone())).collect::>(), }, - grandpa: GrandpaConfig { - authorities: initial_authorities.iter().map(|x| (x.1.clone(), 1)).collect(), - ..Default::default() + "grandpa": { + "authorities": initial_authorities.iter().map(|x| (x.1.clone(), 1)).collect::>(), }, - sudo: SudoConfig { + "sudo": { // Assign network admin rights. - key: Some(root_key), + "key": Some(root_key), }, - transaction_payment: Default::default(), - } + }) } diff --git a/substrate/bin/node-template/runtime/Cargo.toml b/substrate/bin/node-template/runtime/Cargo.toml index c6d751484237..0008c82a49d4 100644 --- a/substrate/bin/node-template/runtime/Cargo.toml +++ b/substrate/bin/node-template/runtime/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } -scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.10.0", default-features = false, features = ["derive", "serde"] } pallet-aura = { path = "../../../frame/aura", default-features = false} pallet-balances = { path = "../../../frame/balances", default-features = false} @@ -28,17 +28,18 @@ pallet-transaction-payment = { path = "../../../frame/transaction-payment", defa frame-executive = { path = "../../../frame/executive", default-features = false} sp-api = { path = "../../../primitives/api", default-features = false} sp-block-builder = { path = "../../../primitives/block-builder", default-features = false} -sp-consensus-aura = { path = "../../../primitives/consensus/aura", default-features = false} -sp-consensus-grandpa = { path = "../../../primitives/consensus/grandpa", default-features = false} -sp-core = { path = "../../../primitives/core", default-features = false} +sp-consensus-aura = { path = "../../../primitives/consensus/aura", default-features = false, features = ["serde"] } +sp-consensus-grandpa = { path = "../../../primitives/consensus/grandpa", default-features = false, features = ["serde"] } +sp-core = { path = "../../../primitives/core", default-features = false, features = ["serde"]} sp-inherents = { path = "../../../primitives/inherents", default-features = false} sp-offchain = { path = "../../../primitives/offchain", default-features = false} -sp-runtime = { path = "../../../primitives/runtime", default-features = false} +sp-runtime = { path = "../../../primitives/runtime", default-features = false, features = ["serde"] } sp-session = { path = "../../../primitives/session", default-features = false} sp-std = { path = "../../../primitives/std", default-features = false} sp-storage = { path = "../../../primitives/storage", default-features = false} sp-transaction-pool = { path = "../../../primitives/transaction-pool", default-features = false} -sp-version = { path = "../../../primitives/version", default-features = false} +sp-version = { path = "../../../primitives/version", default-features = false, features = ["serde"] } +serde_json = { version = "1.0.85", default-features = false, features = ["alloc"] } sp-genesis-builder = { default-features = false, path = "../../../primitives/genesis-builder" } # Used for the node template's RPCs @@ -75,6 +76,7 @@ std = [ "pallet-transaction-payment-rpc-runtime-api/std", "pallet-transaction-payment/std", "scale-info/std", + "serde_json/std", "sp-api/std", "sp-block-builder/std", "sp-consensus-aura/std", diff --git a/substrate/bin/node/cli/Cargo.toml b/substrate/bin/node/cli/Cargo.toml index 34cca4495da4..01c532ad20dc 100644 --- a/substrate/bin/node/cli/Cargo.toml +++ b/substrate/bin/node/cli/Cargo.toml @@ -124,7 +124,6 @@ futures = "0.3.21" tempfile = "3.1.0" assert_cmd = "2.0.2" nix = { version = "0.26.1", features = ["signal"] } -serde_json = "1.0" regex = "1.6.0" platforms = "3.0" soketto = "0.7.1" diff --git a/substrate/bin/node/cli/src/chain_spec.rs b/substrate/bin/node/cli/src/chain_spec.rs index 52b480925aa9..3559348d188c 100644 --- a/substrate/bin/node/cli/src/chain_spec.rs +++ b/substrate/bin/node/cli/src/chain_spec.rs @@ -20,10 +20,7 @@ use grandpa_primitives::AuthorityId as GrandpaId; use kitchensink_runtime::{ - constants::currency::*, wasm_binary_unwrap, BabeConfig, BalancesConfig, Block, CouncilConfig, - DemocracyConfig, ElectionsConfig, ImOnlineConfig, IndicesConfig, MaxNominations, - NominationPoolsConfig, SessionConfig, SessionKeys, SocietyConfig, StakerStatus, StakingConfig, - SudoConfig, SystemConfig, TechnicalCommitteeConfig, + constants::currency::*, wasm_binary_unwrap, Block, MaxNominations, SessionKeys, StakerStatus, }; use pallet_im_online::sr25519::AuthorityId as ImOnlineId; use sc_chain_spec::ChainSpecExtension; @@ -45,6 +42,8 @@ pub use node_primitives::{AccountId, Balance, Signature}; type AccountPublic = ::Signer; const STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/"; +const ENDOWMENT: Balance = 10_000_000 * DOLLARS; +const STASH: Balance = ENDOWMENT / 1000; /// Node `ChainSpec` extensions. /// @@ -78,7 +77,11 @@ fn session_keys( SessionKeys { grandpa, babe, im_online, authority_discovery, mixnet } } -fn staging_testnet_config_genesis() -> RuntimeGenesisConfig { +fn configure_accounts_for_staging_testnet() -> ( + Vec<(AccountId, AccountId, GrandpaId, BabeId, ImOnlineId, AuthorityDiscoveryId, MixnetId)>, + AccountId, + Vec, +) { #[rustfmt::skip] // stash, controller, session-key // generated with secret: @@ -190,28 +193,27 @@ fn staging_testnet_config_genesis() -> RuntimeGenesisConfig { ); let endowed_accounts: Vec = vec![root_key.clone()]; + (initial_authorities, root_key, endowed_accounts) +} +fn staging_testnet_config_genesis() -> serde_json::Value { + let (initial_authorities, root_key, endowed_accounts) = + configure_accounts_for_staging_testnet(); testnet_genesis(initial_authorities, vec![], root_key, Some(endowed_accounts)) } /// Staging testnet config. pub fn staging_testnet_config() -> ChainSpec { - let boot_nodes = vec![]; - ChainSpec::from_genesis( - "Staging Testnet", - "staging_testnet", - ChainType::Live, - staging_testnet_config_genesis, - boot_nodes, - Some( + ChainSpec::builder(wasm_binary_unwrap(), Default::default()) + .with_name("Staging Testnet") + .with_id("staging_testnet") + .with_chain_type(ChainType::Live) + .with_genesis_config_patch(staging_testnet_config_genesis()) + .with_telemetry_endpoints( TelemetryEndpoints::new(vec![(STAGING_TELEMETRY_URL.to_string(), 0)]) .expect("Staging telemetry url is valid; qed"), - ), - None, - None, - None, - Default::default(), - ) + ) + .build() } /// Helper function to generate a crypto pair from seed. @@ -244,8 +246,7 @@ pub fn authority_keys_from_seed( ) } -/// Helper function to create RuntimeGenesisConfig for testing. -pub fn testnet_genesis( +fn configure_accounts( initial_authorities: Vec<( AccountId, AccountId, @@ -256,9 +257,14 @@ pub fn testnet_genesis( MixnetId, )>, initial_nominators: Vec, - root_key: AccountId, endowed_accounts: Option>, -) -> RuntimeGenesisConfig { + stash: Balance, +) -> ( + Vec<(AccountId, AccountId, GrandpaId, BabeId, ImOnlineId, AuthorityDiscoveryId, MixnetId)>, + Vec, + usize, + Vec<(AccountId, AccountId, Balance, StakerStatus)>, +) { let mut endowed_accounts: Vec = endowed_accounts.unwrap_or_else(|| { vec![ get_account_id_from_seed::("Alice"), @@ -290,7 +296,7 @@ pub fn testnet_genesis( let mut rng = rand::thread_rng(); let stakers = initial_authorities .iter() - .map(|x| (x.0.clone(), x.0.clone(), STASH, StakerStatus::Validator)) + .map(|x| (x.0.clone(), x.0.clone(), stash, StakerStatus::Validator)) .chain(initial_nominators.iter().map(|x| { use rand::{seq::SliceRandom, Rng}; let limit = (MaxNominations::get() as usize).min(initial_authorities.len()); @@ -301,23 +307,39 @@ pub fn testnet_genesis( .into_iter() .map(|choice| choice.0.clone()) .collect::>(); - (x.clone(), x.clone(), STASH, StakerStatus::Nominator(nominations)) + (x.clone(), x.clone(), stash, StakerStatus::Nominator(nominations)) })) .collect::>(); let num_endowed_accounts = endowed_accounts.len(); - const ENDOWMENT: Balance = 10_000_000 * DOLLARS; - const STASH: Balance = ENDOWMENT / 1000; + (initial_authorities, endowed_accounts, num_endowed_accounts, stakers) +} + +/// Helper function to create RuntimeGenesisConfig json patch for testing. +pub fn testnet_genesis( + initial_authorities: Vec<( + AccountId, + AccountId, + GrandpaId, + BabeId, + ImOnlineId, + AuthorityDiscoveryId, + MixnetId, + )>, + initial_nominators: Vec, + root_key: AccountId, + endowed_accounts: Option>, +) -> serde_json::Value { + let (initial_authorities, endowed_accounts, num_endowed_accounts, stakers) = + configure_accounts(initial_authorities, initial_nominators, endowed_accounts, STASH); - RuntimeGenesisConfig { - system: SystemConfig { code: wasm_binary_unwrap().to_vec(), ..Default::default() }, - balances: BalancesConfig { - balances: endowed_accounts.iter().cloned().map(|x| (x, ENDOWMENT)).collect(), + serde_json::json!({ + "balances": { + "balances": endowed_accounts.iter().cloned().map(|x| (x, ENDOWMENT)).collect::>(), }, - indices: IndicesConfig { indices: vec![] }, - session: SessionConfig { - keys: initial_authorities + "session": { + "keys": initial_authorities .iter() .map(|x| { ( @@ -334,67 +356,45 @@ pub fn testnet_genesis( }) .collect::>(), }, - staking: StakingConfig { - validator_count: initial_authorities.len() as u32, - minimum_validator_count: initial_authorities.len() as u32, - invulnerables: initial_authorities.iter().map(|x| x.0.clone()).collect(), - slash_reward_fraction: Perbill::from_percent(10), - stakers, - ..Default::default() + "staking": { + "validatorCount": initial_authorities.len() as u32, + "minimumValidatorCount": initial_authorities.len() as u32, + "invulnerables": initial_authorities.iter().map(|x| x.0.clone()).collect::>(), + "slashRewardFraction": Perbill::from_percent(10), + "stakers": stakers.clone(), }, - democracy: DemocracyConfig::default(), - elections: ElectionsConfig { - members: endowed_accounts + "elections": { + "members": endowed_accounts .iter() .take((num_endowed_accounts + 1) / 2) .cloned() .map(|member| (member, STASH)) - .collect(), + .collect::>(), }, - council: CouncilConfig::default(), - technical_committee: TechnicalCommitteeConfig { - members: endowed_accounts + "technicalCommittee": { + "members": endowed_accounts .iter() .take((num_endowed_accounts + 1) / 2) .cloned() - .collect(), - phantom: Default::default(), + .collect::>(), }, - sudo: SudoConfig { key: Some(root_key) }, - babe: BabeConfig { - epoch_config: Some(kitchensink_runtime::BABE_GENESIS_EPOCH_CONFIG), - ..Default::default() + "sudo": { "key": Some(root_key.clone()) }, + "babe": { + "epochConfig": Some(kitchensink_runtime::BABE_GENESIS_EPOCH_CONFIG), }, - im_online: ImOnlineConfig { keys: vec![] }, - authority_discovery: Default::default(), - grandpa: Default::default(), - technical_membership: Default::default(), - treasury: Default::default(), - society: SocietyConfig { pot: 0 }, - vesting: Default::default(), - assets: pallet_assets::GenesisConfig { + "society": { "pot": 0 }, + "assets": { // This asset is used by the NIS pallet as counterpart currency. - assets: vec![(9, get_account_id_from_seed::("Alice"), true, 1)], - ..Default::default() + "assets": vec![(9, get_account_id_from_seed::("Alice"), true, 1)], }, - pool_assets: Default::default(), - transaction_storage: Default::default(), - transaction_payment: Default::default(), - alliance: Default::default(), - safe_mode: Default::default(), - tx_pause: Default::default(), - alliance_motion: Default::default(), - nomination_pools: NominationPoolsConfig { - min_create_bond: 10 * DOLLARS, - min_join_bond: 1 * DOLLARS, - ..Default::default() + "nominationPools": { + "minCreateBond": 10 * DOLLARS, + "minJoinBond": 1 * DOLLARS, }, - glutton: Default::default(), - mixnet: Default::default(), - } + }) } -fn development_config_genesis() -> RuntimeGenesisConfig { +fn development_config_genesis_json() -> serde_json::Value { testnet_genesis( vec![authority_keys_from_seed("Alice")], vec![], @@ -405,21 +405,15 @@ fn development_config_genesis() -> RuntimeGenesisConfig { /// Development config (single validator Alice). pub fn development_config() -> ChainSpec { - ChainSpec::from_genesis( - "Development", - "dev", - ChainType::Development, - development_config_genesis, - vec![], - None, - None, - None, - None, - Default::default(), - ) + ChainSpec::builder(wasm_binary_unwrap(), Default::default()) + .with_name("Development") + .with_id("dev") + .with_chain_type(ChainType::Development) + .with_genesis_config_patch(development_config_genesis_json()) + .build() } -fn local_testnet_genesis() -> RuntimeGenesisConfig { +fn local_testnet_genesis() -> serde_json::Value { testnet_genesis( vec![authority_keys_from_seed("Alice"), authority_keys_from_seed("Bob")], vec![], @@ -430,18 +424,12 @@ fn local_testnet_genesis() -> RuntimeGenesisConfig { /// Local testnet config (multivalidator Alice + Bob). pub fn local_testnet_config() -> ChainSpec { - ChainSpec::from_genesis( - "Local Testnet", - "local_testnet", - ChainType::Local, - local_testnet_genesis, - vec![], - None, - None, - None, - None, - Default::default(), - ) + ChainSpec::builder(wasm_binary_unwrap(), Default::default()) + .with_name("Local Testnet") + .with_id("local_testnet") + .with_chain_type(ChainType::Local) + .with_genesis_config_patch(local_testnet_genesis()) + .build() } #[cfg(test)] @@ -451,45 +439,29 @@ pub(crate) mod tests { use sc_service_test; use sp_runtime::BuildStorage; - fn local_testnet_genesis_instant_single() -> RuntimeGenesisConfig { - testnet_genesis( - vec![authority_keys_from_seed("Alice")], - vec![], - get_account_id_from_seed::("Alice"), - None, - ) - } - /// Local testnet config (single validator - Alice). pub fn integration_test_config_with_single_authority() -> ChainSpec { - ChainSpec::from_genesis( - "Integration Test", - "test", - ChainType::Development, - local_testnet_genesis_instant_single, - vec![], - None, - None, - None, - None, - Default::default(), - ) + ChainSpec::builder(wasm_binary_unwrap(), Default::default()) + .with_name("Integration Test") + .with_id("test") + .with_chain_type(ChainType::Development) + .with_genesis_config_patch(testnet_genesis( + vec![authority_keys_from_seed("Alice")], + vec![], + get_account_id_from_seed::("Alice"), + None, + )) + .build() } /// Local testnet config (multivalidator Alice + Bob). pub fn integration_test_config_with_two_authorities() -> ChainSpec { - ChainSpec::from_genesis( - "Integration Test", - "test", - ChainType::Development, - local_testnet_genesis, - vec![], - None, - None, - None, - None, - Default::default(), - ) + ChainSpec::builder(wasm_binary_unwrap(), Default::default()) + .with_name("Integration Test") + .with_id("test") + .with_chain_type(ChainType::Development) + .with_genesis_config_patch(local_testnet_genesis()) + .build() } #[test] diff --git a/substrate/bin/node/executor/Cargo.toml b/substrate/bin/node/executor/Cargo.toml index 5f43b5839e61..12280a9ba1d7 100644 --- a/substrate/bin/node/executor/Cargo.toml +++ b/substrate/bin/node/executor/Cargo.toml @@ -14,17 +14,17 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1" } -scale-info = { version = "2.10.0", features = ["derive"] } +scale-info = { version = "2.10.0", features = ["derive", "serde"] } frame-benchmarking = { path = "../../../frame/benchmarking" } node-primitives = { path = "../primitives" } kitchensink-runtime = { path = "../runtime" } sc-executor = { path = "../../../client/executor" } -sp-core = { path = "../../../primitives/core" } +sp-core = { path = "../../../primitives/core", features=["serde"] } sp-keystore = { path = "../../../primitives/keystore" } sp-state-machine = { path = "../../../primitives/state-machine" } sp-tracing = { path = "../../../primitives/tracing" } sp-trie = { path = "../../../primitives/trie" } -sp-statement-store = { path = "../../../primitives/statement-store" } +sp-statement-store = { path = "../../../primitives/statement-store", features=["serde"] } [dev-dependencies] criterion = "0.4.0" @@ -47,6 +47,7 @@ sp-consensus-babe = { path = "../../../primitives/consensus/babe" } sp-externalities = { path = "../../../primitives/externalities" } sp-keyring = { path = "../../../primitives/keyring" } sp-runtime = { path = "../../../primitives/runtime" } +serde_json = "1.0.85" [features] stress-test = [] diff --git a/substrate/bin/node/executor/benches/bench.rs b/substrate/bin/node/executor/benches/bench.rs index 95c8afd55066..587e76af867c 100644 --- a/substrate/bin/node/executor/benches/bench.rs +++ b/substrate/bin/node/executor/benches/bench.rs @@ -190,7 +190,7 @@ fn bench_execute_block(c: &mut Criterion) { for strategy in execution_methods { group.bench_function(format!("{:?}", strategy), |b| { - let genesis_config = node_testing::genesis::config(Some(compact_code_unwrap())); + let genesis_config = node_testing::genesis::config(); let use_native = match strategy { ExecutionMethod::Native => true, ExecutionMethod::Wasm(..) => false, diff --git a/substrate/bin/node/executor/tests/basic.rs b/substrate/bin/node/executor/tests/basic.rs index a2f46e9fdbe9..cbceac04e8ea 100644 --- a/substrate/bin/node/executor/tests/basic.rs +++ b/substrate/bin/node/executor/tests/basic.rs @@ -857,3 +857,19 @@ fn should_import_block_with_test_client() { futures::executor::block_on(client.import(BlockOrigin::Own, block)).unwrap(); } + +#[test] +fn default_config_as_json_works() { + let mut t = new_test_ext(compact_code_unwrap()); + let r = executor_call(&mut t, "GenesisBuilder_create_default_config", &vec![], false) + .0 + .unwrap(); + let r = Vec::::decode(&mut &r[..]).unwrap(); + let json = String::from_utf8(r.into()).expect("returned value is json. qed."); + let expected = include_str!("res/default_genesis_config.json").to_string(); + + assert_eq!( + serde_json::from_str::(&expected).unwrap(), + serde_json::from_str::(&json).unwrap() + ); +} diff --git a/substrate/bin/node/executor/tests/common.rs b/substrate/bin/node/executor/tests/common.rs index 5f88ba85adf9..2d68c88db925 100644 --- a/substrate/bin/node/executor/tests/common.rs +++ b/substrate/bin/node/executor/tests/common.rs @@ -124,7 +124,7 @@ pub fn executor_call( pub fn new_test_ext(code: &[u8]) -> TestExternalities { let ext = TestExternalities::new_with_code( code, - node_testing::genesis::config(Some(code)).build_storage().unwrap(), + node_testing::genesis::config().build_storage().unwrap(), ); ext } diff --git a/substrate/bin/node/executor/tests/res/default_genesis_config.json b/substrate/bin/node/executor/tests/res/default_genesis_config.json new file mode 100644 index 000000000000..caf12a443d36 --- /dev/null +++ b/substrate/bin/node/executor/tests/res/default_genesis_config.json @@ -0,0 +1,108 @@ +{ + "system": {}, + "babe": { + "authorities": [], + "epochConfig": null + }, + "indices": { + "indices": [] + }, + "balances": { + "balances": [] + }, + "transactionPayment": { + "multiplier": "1000000000000000000" + }, + "staking": { + "validatorCount": 0, + "minimumValidatorCount": 0, + "invulnerables": [], + "forceEra": "NotForcing", + "slashRewardFraction": 0, + "canceledPayout": 0, + "stakers": [], + "minNominatorBond": 0, + "minValidatorBond": 0, + "maxValidatorCount": null, + "maxNominatorCount": null + }, + "session": { + "keys": [] + }, + "democracy": {}, + "council": { + "members": [] + }, + "technicalCommittee": { + "members": [] + }, + "elections": { + "members": [] + }, + "technicalMembership": { + "members": [] + }, + "grandpa": { + "authorities": [] + }, + "treasury": {}, + "sudo": { + "key": null + }, + "imOnline": { + "keys": [] + }, + "authorityDiscovery": { + "keys": [] + }, + "society": { + "pot": 0 + }, + "vesting": { + "vesting": [] + }, + "glutton": { + "compute": "0", + "storage": "0", + "trashDataCount": 0 + }, + "assets": { + "assets": [], + "metadata": [], + "accounts": [] + }, + "poolAssets": { + "assets": [], + "metadata": [], + "accounts": [] + }, + "transactionStorage": { + "byteFee": 10, + "entryFee": 1000, + "storagePeriod": 100800 + }, + "allianceMotion": { + "members": [] + }, + "alliance": { + "fellows": [], + "allies": [] + }, + "mixnet": { + "mixnodes": [] + }, + "nominationPools": { + "minJoinBond": 0, + "minCreateBond": 0, + "maxPools": 16, + "maxMembersPerPool": 32, + "maxMembers": 512, + "globalMaxCommission": null + }, + "txPause": { + "paused": [] + }, + "safeMode": { + "enteredUntil": null + } +} diff --git a/substrate/bin/node/runtime/Cargo.toml b/substrate/bin/node/runtime/Cargo.toml index a9d685523f6c..85d3971e4921 100644 --- a/substrate/bin/node/runtime/Cargo.toml +++ b/substrate/bin/node/runtime/Cargo.toml @@ -20,33 +20,34 @@ codec = { package = "parity-scale-codec", version = "3.6.1", default-features = "derive", "max-encoded-len", ] } -scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.10.0", default-features = false, features = ["derive", "serde"] } static_assertions = "1.1.0" log = { version = "0.4.17", default-features = false } +serde_json = { version = "1.0.85", default-features = false, features = ["alloc", "arbitrary_precision"] } # pallet-asset-conversion: turn on "num-traits" feature primitive-types = { version = "0.12.0", default-features = false, features = ["codec", "scale-info", "num-traits"] } # primitives -sp-authority-discovery = { path = "../../../primitives/authority-discovery", default-features = false} -sp-consensus-babe = { path = "../../../primitives/consensus/babe", default-features = false} -sp-consensus-grandpa = { path = "../../../primitives/consensus/grandpa", default-features = false} +sp-authority-discovery = { path = "../../../primitives/authority-discovery", default-features = false, features=["serde"] } +sp-consensus-babe = { path = "../../../primitives/consensus/babe", default-features = false, features=["serde"] } +sp-consensus-grandpa = { path = "../../../primitives/consensus/grandpa", default-features = false, features=["serde"] } sp-block-builder = { path = "../../../primitives/block-builder", default-features = false} sp-genesis-builder = { default-features = false, path = "../../../primitives/genesis-builder" } sp-inherents = { path = "../../../primitives/inherents", default-features = false} node-primitives = { path = "../primitives", default-features = false} sp-mixnet = { path = "../../../primitives/mixnet", default-features = false } sp-offchain = { path = "../../../primitives/offchain", default-features = false} -sp-core = { path = "../../../primitives/core", default-features = false} +sp-core = { path = "../../../primitives/core", default-features = false, features=["serde"] } sp-std = { path = "../../../primitives/std", default-features = false} sp-api = { path = "../../../primitives/api", default-features = false} -sp-runtime = { path = "../../../primitives/runtime", default-features = false} -sp-staking = { path = "../../../primitives/staking", default-features = false} +sp-runtime = { path = "../../../primitives/runtime", default-features = false, features=["serde"] } +sp-staking = { path = "../../../primitives/staking", default-features = false, features=["serde"] } sp-storage = { path = "../../../primitives/storage", default-features = false} sp-session = { path = "../../../primitives/session", default-features = false} sp-transaction-pool = { path = "../../../primitives/transaction-pool", default-features = false} -sp-statement-store = { path = "../../../primitives/statement-store", default-features = false} -sp-version = { path = "../../../primitives/version", default-features = false} +sp-statement-store = { path = "../../../primitives/statement-store", default-features = false, features=["serde"] } +sp-version = { path = "../../../primitives/version", default-features = false, features=["serde"] } sp-io = { path = "../../../primitives/io", default-features = false} # frame dependencies @@ -230,6 +231,7 @@ std = [ "pallet-whitelist/std", "primitive-types/std", "scale-info/std", + "serde_json/std", "sp-api/std", "sp-authority-discovery/std", "sp-block-builder/std", diff --git a/substrate/bin/node/testing/src/bench.rs b/substrate/bin/node/testing/src/bench.rs index af3701f218ac..89b96c0191ce 100644 --- a/substrate/bin/node/testing/src/bench.rs +++ b/substrate/bin/node/testing/src/bench.rs @@ -398,7 +398,7 @@ impl BenchDb { let client_config = sc_service::ClientConfig::default(); let genesis_block_builder = sc_service::GenesisBlockBuilder::new( - &keyring.generate_genesis(), + keyring.as_storage_builder(), !client_config.no_genesis, backend.clone(), executor.clone(), @@ -590,12 +590,20 @@ impl BenchKeyring { } } - /// Generate genesis with accounts from this keyring endowed with some balance. - pub fn generate_genesis(&self) -> kitchensink_runtime::RuntimeGenesisConfig { - crate::genesis::config_endowed( - Some(kitchensink_runtime::wasm_binary_unwrap()), - self.collect_account_ids(), - ) + /// Generate genesis with accounts from this keyring endowed with some balance and + /// kitchensink_runtime code blob. + pub fn as_storage_builder(&self) -> &dyn sp_runtime::BuildStorage { + self + } +} + +impl sp_runtime::BuildStorage for BenchKeyring { + fn assimilate_storage(&self, storage: &mut sp_core::storage::Storage) -> Result<(), String> { + storage.top.insert( + sp_core::storage::well_known_keys::CODE.to_vec(), + kitchensink_runtime::wasm_binary_unwrap().into(), + ); + crate::genesis::config_endowed(self.collect_account_ids()).assimilate_storage(storage) } } diff --git a/substrate/bin/node/testing/src/client.rs b/substrate/bin/node/testing/src/client.rs index c55867360bd6..22276833fb66 100644 --- a/substrate/bin/node/testing/src/client.rs +++ b/substrate/bin/node/testing/src/client.rs @@ -42,7 +42,12 @@ pub struct GenesisParameters; impl substrate_test_client::GenesisInit for GenesisParameters { fn genesis_storage(&self) -> Storage { - crate::genesis::config(None).build_storage().unwrap() + let mut storage = crate::genesis::config().build_storage().unwrap(); + storage.top.insert( + sp_core::storage::well_known_keys::CODE.to_vec(), + kitchensink_runtime::wasm_binary_unwrap().into(), + ); + storage } } diff --git a/substrate/bin/node/testing/src/genesis.rs b/substrate/bin/node/testing/src/genesis.rs index ab5311751a55..eecbf64775b6 100644 --- a/substrate/bin/node/testing/src/genesis.rs +++ b/substrate/bin/node/testing/src/genesis.rs @@ -20,22 +20,21 @@ use crate::keyring::*; use kitchensink_runtime::{ - constants::currency::*, wasm_binary_unwrap, AccountId, AssetsConfig, BabeConfig, - BalancesConfig, GluttonConfig, GrandpaConfig, IndicesConfig, RuntimeGenesisConfig, - SessionConfig, SocietyConfig, StakerStatus, StakingConfig, SystemConfig, - BABE_GENESIS_EPOCH_CONFIG, + constants::currency::*, AccountId, AssetsConfig, BabeConfig, BalancesConfig, GluttonConfig, + GrandpaConfig, IndicesConfig, RuntimeGenesisConfig, SessionConfig, SocietyConfig, StakerStatus, + StakingConfig, BABE_GENESIS_EPOCH_CONFIG, }; use sp_keyring::{Ed25519Keyring, Sr25519Keyring}; use sp_runtime::Perbill; /// Create genesis runtime configuration for tests. -pub fn config(code: Option<&[u8]>) -> RuntimeGenesisConfig { - config_endowed(code, Default::default()) +pub fn config() -> RuntimeGenesisConfig { + config_endowed(Default::default()) } /// Create genesis runtime configuration for tests with some extra /// endowed accounts. -pub fn config_endowed(code: Option<&[u8]>, extra_endowed: Vec) -> RuntimeGenesisConfig { +pub fn config_endowed(extra_endowed: Vec) -> RuntimeGenesisConfig { let mut endowed = vec![ (alice(), 111 * DOLLARS), (bob(), 100 * DOLLARS), @@ -48,10 +47,7 @@ pub fn config_endowed(code: Option<&[u8]>, extra_endowed: Vec) -> Run endowed.extend(extra_endowed.into_iter().map(|endowed| (endowed, 100 * DOLLARS))); RuntimeGenesisConfig { - system: SystemConfig { - code: code.map(|x| x.to_vec()).unwrap_or_else(|| wasm_binary_unwrap().to_vec()), - ..Default::default() - }, + system: Default::default(), indices: IndicesConfig { indices: vec![] }, balances: BalancesConfig { balances: endowed }, session: SessionConfig { diff --git a/substrate/bin/utils/chain-spec-builder/Cargo.toml b/substrate/bin/utils/chain-spec-builder/Cargo.toml index f25358e52c2c..5c7e21abeab9 100644 --- a/substrate/bin/utils/chain-spec-builder/Cargo.toml +++ b/substrate/bin/utils/chain-spec-builder/Cargo.toml @@ -23,8 +23,12 @@ crate-type = ["rlib"] ansi_term = "0.12.1" clap = { version = "4.4.6", features = ["derive"] } rand = "0.8" +kitchensink-runtime = { version = "3.0.0-dev", path = "../../node/runtime" } +log = "0.4.17" node-cli = { package = "staging-node-cli", path = "../../node/cli" } sc-chain-spec = { path = "../../../client/chain-spec" } sc-keystore = { path = "../../../client/keystore" } +serde_json = "1.0.100" sp-core = { path = "../../../primitives/core" } sp-keystore = { path = "../../../primitives/keystore" } +sp-tracing = { version = "10.0.0", path = "../../../primitives/tracing" } diff --git a/substrate/bin/utils/chain-spec-builder/bin/main.rs b/substrate/bin/utils/chain-spec-builder/bin/main.rs index a002f6dc0e7b..83892afd6ace 100644 --- a/substrate/bin/utils/chain-spec-builder/bin/main.rs +++ b/substrate/bin/utils/chain-spec-builder/bin/main.rs @@ -17,28 +17,40 @@ // along with this program. If not, see . use chain_spec_builder::{ - generate_authority_keys_and_store, generate_chain_spec, print_seeds, ChainSpecBuilder, + generate_authority_keys_and_store, generate_chain_spec, generate_chain_spec_for_runtime, + print_seeds, ChainSpecBuilder, ChainSpecBuilderCmd, EditCmd, GenerateCmd, NewCmd, VerifyCmd, }; use clap::Parser; use node_cli::chain_spec; use rand::{distributions::Alphanumeric, rngs::OsRng, Rng}; +use sc_chain_spec::{update_code_in_json_chain_spec, GenericChainSpec}; use sp_core::{crypto::Ss58Codec, sr25519}; use staging_chain_spec_builder as chain_spec_builder; use std::fs; fn main() -> Result<(), String> { + sp_tracing::try_init_simple(); + + let builder = ChainSpecBuilder::parse(); #[cfg(build_type = "debug")] - println!( - "The chain spec builder builds a chain specification that includes a Substrate runtime \ + if matches!(builder.command, ChainSpecBuilderCmd::Generate(_) | ChainSpecBuilderCmd::New(_)) { + println!( + "The chain spec builder builds a chain specification that includes a Substrate runtime \ compiled as WASM. To ensure proper functioning of the included runtime compile (or run) \ the chain spec builder binary in `--release` mode.\n", - ); - - let builder = ChainSpecBuilder::parse(); - let chain_spec_path = builder.chain_spec_path().to_path_buf(); - - let (authority_seeds, nominator_accounts, endowed_accounts, sudo_account) = match builder { - ChainSpecBuilder::Generate { authorities, nominators, endowed, keystore_path, .. } => { + ); + } + + let chain_spec_path = builder.chain_spec_path.to_path_buf(); + let mut write_chain_spec = true; + + let chain_spec_json = match builder.command { + ChainSpecBuilderCmd::Generate(GenerateCmd { + authorities, + nominators, + endowed, + keystore_path, + }) => { let authorities = authorities.max(1); let rand_str = || -> String { OsRng.sample_iter(&Alphanumeric).take(32).map(char::from).collect() @@ -72,19 +84,58 @@ fn main() -> Result<(), String> { let sudo_account = chain_spec::get_account_id_from_seed::(&sudo_seed).to_ss58check(); - (authority_seeds, nominator_accounts, endowed_accounts, sudo_account) + generate_chain_spec(authority_seeds, nominator_accounts, endowed_accounts, sudo_account) }, - ChainSpecBuilder::New { + ChainSpecBuilderCmd::New(NewCmd { authority_seeds, nominator_accounts, endowed_accounts, sudo_account, - .. - } => (authority_seeds, nominator_accounts, endowed_accounts, sudo_account), - }; + }) => + generate_chain_spec(authority_seeds, nominator_accounts, endowed_accounts, sudo_account), + ChainSpecBuilderCmd::Runtime(cmd) => generate_chain_spec_for_runtime(&cmd), + ChainSpecBuilderCmd::Edit(EditCmd { + ref input_chain_spec, + ref runtime_wasm_path, + convert_to_raw, + }) => { + let chain_spec = GenericChainSpec::<()>::from_json_file(input_chain_spec.clone())?; + + let mut chain_spec_json = + serde_json::from_str::(&chain_spec.as_json(convert_to_raw)?) + .map_err(|e| format!("Conversion to json failed: {e}"))?; + if let Some(path) = runtime_wasm_path { + update_code_in_json_chain_spec( + &mut chain_spec_json, + &fs::read(path.as_path()) + .map_err(|e| format!("Wasm blob file could not be read: {e}"))?[..], + ); + } - let json = - generate_chain_spec(authority_seeds, nominator_accounts, endowed_accounts, sudo_account)?; + serde_json::to_string_pretty(&chain_spec_json) + .map_err(|e| format!("to pretty failed: {e}")) + }, + ChainSpecBuilderCmd::Verify(VerifyCmd { ref input_chain_spec, ref runtime_wasm_path }) => { + write_chain_spec = false; + let chain_spec = GenericChainSpec::<()>::from_json_file(input_chain_spec.clone())?; + let mut chain_spec_json = + serde_json::from_str::(&chain_spec.as_json(true)?) + .map_err(|e| format!("Conversion to json failed: {e}"))?; + if let Some(path) = runtime_wasm_path { + update_code_in_json_chain_spec( + &mut chain_spec_json, + &fs::read(path.as_path()) + .map_err(|e| format!("Wasm blob file could not be read: {e}"))?[..], + ); + }; + serde_json::to_string_pretty(&chain_spec_json) + .map_err(|e| format!("to pretty failed: {e}")) + }, + }?; - fs::write(chain_spec_path, json).map_err(|err| err.to_string()) + if write_chain_spec { + fs::write(chain_spec_path, chain_spec_json).map_err(|err| err.to_string()) + } else { + Ok(()) + } } diff --git a/substrate/bin/utils/chain-spec-builder/src/lib.rs b/substrate/bin/utils/chain-spec-builder/src/lib.rs index 2b88e40ef74f..50a8dd089962 100644 --- a/substrate/bin/utils/chain-spec-builder/src/lib.rs +++ b/substrate/bin/utils/chain-spec-builder/src/lib.rs @@ -29,96 +29,169 @@ //! [`sc-chain-spec`]: ../sc_chain_spec/index.html //! [`node-cli`]: ../node_cli/index.html -use std::path::{Path, PathBuf}; +use std::{ + fs, + path::{Path, PathBuf}, +}; use ansi_term::Style; -use clap::Parser; +use clap::{Parser, Subcommand}; +use sc_chain_spec::GenesisConfigBuilderRuntimeCaller; use node_cli::chain_spec::{self, AccountId}; use sc_keystore::LocalKeystore; +use serde_json::Value; use sp_core::crypto::{ByteArray, Ss58Codec}; use sp_keystore::KeystorePtr; /// A utility to easily create a testnet chain spec definition with a given set /// of authorities and endowed accounts and/or generate random accounts. -#[derive(Parser)] +#[derive(Debug, Parser)] #[command(rename_all = "kebab-case")] -pub enum ChainSpecBuilder { - /// Create a new chain spec with the given authorities, endowed and sudo - /// accounts. - New { - /// Authority key seed. - #[arg(long, short, required = true)] - authority_seeds: Vec, - /// Active nominators (SS58 format), each backing a random subset of the aforementioned - /// authorities. - #[arg(long, short, default_value = "0")] - nominator_accounts: Vec, - /// Endowed account address (SS58 format). - #[arg(long, short)] - endowed_accounts: Vec, - /// Sudo account address (SS58 format). - #[arg(long, short)] - sudo_account: String, - /// The path where the chain spec should be saved. - #[arg(long, short, default_value = "./chain_spec.json")] - chain_spec_path: PathBuf, - }, - /// Create a new chain spec with the given number of authorities and endowed - /// accounts. Random keys will be generated as required. - Generate { - /// The number of authorities. - #[arg(long, short)] - authorities: usize, - /// The number of nominators backing the aforementioned authorities. - /// - /// Will nominate a random subset of `authorities`. - #[arg(long, short, default_value_t = 0)] - nominators: usize, - /// The number of endowed accounts. - #[arg(long, short, default_value_t = 0)] - endowed: usize, - /// The path where the chain spec should be saved. - #[arg(long, short, default_value = "./chain_spec.json")] - chain_spec_path: PathBuf, - /// Path to use when saving generated keystores for each authority. - /// - /// At this path, a new folder will be created for each authority's - /// keystore named `auth-$i` where `i` is the authority index, i.e. - /// `auth-0`, `auth-1`, etc. - #[arg(long, short)] - keystore_path: Option, - }, +pub struct ChainSpecBuilder { + #[command(subcommand)] + pub command: ChainSpecBuilderCmd, + /// The path where the chain spec should be saved. + #[arg(long, short, default_value = "./chain_spec.json")] + pub chain_spec_path: PathBuf, } -impl ChainSpecBuilder { - /// Returns the path where the chain spec should be saved. - pub fn chain_spec_path(&self) -> &Path { - match self { - ChainSpecBuilder::New { chain_spec_path, .. } => chain_spec_path.as_path(), - ChainSpecBuilder::Generate { chain_spec_path, .. } => chain_spec_path.as_path(), - } - } +#[derive(Debug, Subcommand)] +#[command(rename_all = "kebab-case")] +pub enum ChainSpecBuilderCmd { + New(NewCmd), + Generate(GenerateCmd), + Runtime(RuntimeCmd), + Edit(EditCmd), + Verify(VerifyCmd), } -fn genesis_constructor( - authority_seeds: &[String], - nominator_accounts: &[AccountId], - endowed_accounts: &[AccountId], - sudo_account: &AccountId, -) -> chain_spec::RuntimeGenesisConfig { - let authorities = authority_seeds - .iter() - .map(AsRef::as_ref) - .map(chain_spec::authority_keys_from_seed) - .collect::>(); +/// Create a new chain spec with the given authorities, endowed and sudo +/// accounts. Only works for kitchen-sink runtime +#[derive(Parser, Debug)] +#[command(rename_all = "kebab-case")] +pub struct NewCmd { + /// Authority key seed. + #[arg(long, short, required = true)] + pub authority_seeds: Vec, + /// Active nominators (SS58 format), each backing a random subset of the aforementioned + /// authorities. + #[arg(long, short, default_value = "0")] + pub nominator_accounts: Vec, + /// Endowed account address (SS58 format). + #[arg(long, short)] + pub endowed_accounts: Vec, + /// Sudo account address (SS58 format). + #[arg(long, short)] + pub sudo_account: String, +} + +/// Create a new chain spec with the given number of authorities and endowed +/// accounts. Random keys will be generated as required. +#[derive(Parser, Debug)] +pub struct GenerateCmd { + /// The number of authorities. + #[arg(long, short)] + pub authorities: usize, + /// The number of nominators backing the aforementioned authorities. + /// + /// Will nominate a random subset of `authorities`. + #[arg(long, short, default_value_t = 0)] + pub nominators: usize, + /// The number of endowed accounts. + #[arg(long, short, default_value_t = 0)] + pub endowed: usize, + /// Path to use when saving generated keystores for each authority. + /// + /// At this path, a new folder will be created for each authority's + /// keystore named `auth-$i` where `i` is the authority index, i.e. + /// `auth-0`, `auth-1`, etc. + #[arg(long, short)] + pub keystore_path: Option, +} + +/// Create a new chain spec by interacting with the provided runtime wasm blob. +#[derive(Parser, Debug)] +pub struct RuntimeCmd { + /// The name of chain + #[arg(long, short = 'n', default_value = "Custom")] + chain_name: String, + /// The chain id + #[arg(long, short = 'i', default_value = "custom")] + chain_id: String, + /// The path to runtime wasm blob + #[arg(long, short)] + runtime_wasm_path: PathBuf, + /// Export chainspec as raw storage + #[arg(long, short = 's')] + raw_storage: bool, + /// Verify the genesis config. This silently generates the raw storage from genesis config. Any + /// errors will be reported. + #[arg(long, short = 'v')] + verify: bool, + #[command(subcommand)] + action: GenesisBuildAction, +} + +#[derive(Subcommand, Debug, Clone)] +enum GenesisBuildAction { + Patch(PatchCmd), + Full(FullCmd), + Default(DefaultCmd), +} + +/// Patches the runtime's default genesis config with provided patch. +#[derive(Parser, Debug, Clone)] +struct PatchCmd { + /// The path to the runtime genesis config patch. + #[arg(long, short)] + patch_path: PathBuf, +} + +/// Build the genesis config for runtime using provided json file. No defaults will be used. +#[derive(Parser, Debug, Clone)] +struct FullCmd { + /// The path to the full runtime genesis config json file. + #[arg(long, short)] + config_path: PathBuf, +} - chain_spec::testnet_genesis( - authorities, - nominator_accounts.to_vec(), - sudo_account.clone(), - Some(endowed_accounts.to_vec()), - ) +/// Gets the default genesis config for the runtime and uses it in ChainSpec. Please note that +/// default genesis config may not be valid. For some runtimes initial values should be added there +/// (e.g. session keys, babe epoch). +#[derive(Parser, Debug, Clone)] +struct DefaultCmd { + /// If provided stores the default genesis config json file at given path (in addition to + /// chain-spec). + #[arg(long, short)] + default_config_path: Option, +} + +/// Edits provided input chain spec. Input can be converted into raw storage chain-spec. The code +/// can be updated with the runtime provided in the command line. +#[derive(Parser, Debug, Clone)] +pub struct EditCmd { + /// Chain spec to be edited + #[arg(long, short)] + pub input_chain_spec: PathBuf, + /// The path to new runtime wasm blob to be stored into chain-spec + #[arg(long, short = 'r')] + pub runtime_wasm_path: Option, + /// Convert genesis spec to raw format + #[arg(long, short = 's')] + pub convert_to_raw: bool, +} + +/// Verifies provided input chain spec. If the runtime is provided verification is performed against +/// new runtime. +#[derive(Parser, Debug, Clone)] +pub struct VerifyCmd { + /// Chain spec to be edited + #[arg(long, short)] + pub input_chain_spec: PathBuf, + /// The path to new runtime wasm blob to be stored into chain-spec + #[arg(long, short = 'r')] + pub runtime_wasm_path: Option, } /// Generate the chain spec using the given seeds and accounts. @@ -145,27 +218,24 @@ pub fn generate_chain_spec( let sudo_account = parse_account(sudo_account)?; - let chain_spec = chain_spec::ChainSpec::from_genesis( - "Custom", - "custom", - sc_chain_spec::ChainType::Live, - move || { - genesis_constructor( - &authority_seeds, - &nominator_accounts, - &endowed_accounts, - &sudo_account, - ) - }, - vec![], - None, - None, - None, - None, - Default::default(), - ); - - chain_spec.as_json(false) + let authorities = authority_seeds + .iter() + .map(AsRef::as_ref) + .map(chain_spec::authority_keys_from_seed) + .collect::>(); + + chain_spec::ChainSpec::builder(kitchensink_runtime::wasm_binary_unwrap(), Default::default()) + .with_name("Custom") + .with_id("custom") + .with_chain_type(sc_chain_spec::ChainType::Live) + .with_genesis_config_patch(chain_spec::testnet_genesis( + authorities, + nominator_accounts, + sudo_account, + Some(endowed_accounts), + )) + .build() + .as_json(false) } /// Generate the authority keys and store them in the given `keystore_path`. @@ -241,3 +311,54 @@ pub fn print_seeds( println!("{}", header.paint("Sudo seed")); println!("//{}", sudo_seed); } + +/// Processes `RuntimeCmd` and returns JSON version of `ChainSpec` +pub fn generate_chain_spec_for_runtime(cmd: &RuntimeCmd) -> Result { + let code = fs::read(cmd.runtime_wasm_path.as_path()) + .map_err(|e| format!("wasm blob shall be readable {e}"))?; + + let builder = chain_spec::ChainSpec::builder(&code[..], Default::default()) + .with_name(&cmd.chain_name[..]) + .with_id(&cmd.chain_id[..]) + .with_chain_type(sc_chain_spec::ChainType::Live); + + let builder = match cmd.action { + GenesisBuildAction::Patch(PatchCmd { ref patch_path }) => { + let patch = fs::read(patch_path.as_path()) + .map_err(|e| format!("patch file {patch_path:?} shall be readable: {e}"))?; + builder.with_genesis_config_patch(serde_json::from_slice::(&patch[..]).map_err( + |e| format!("patch file {patch_path:?} shall contain a valid json: {e}"), + )?) + }, + GenesisBuildAction::Full(FullCmd { ref config_path }) => { + let config = fs::read(config_path.as_path()) + .map_err(|e| format!("config file {config_path:?} shall be readable: {e}"))?; + builder.with_genesis_config(serde_json::from_slice::(&config[..]).map_err( + |e| format!("config file {config_path:?} shall contain a valid json: {e}"), + )?) + }, + GenesisBuildAction::Default(DefaultCmd { ref default_config_path }) => { + let caller = GenesisConfigBuilderRuntimeCaller::new(&code[..]); + let default_config = caller + .get_default_config() + .map_err(|e| format!("getting default config from runtime should work: {e}"))?; + default_config_path.clone().map(|path| { + fs::write(path.as_path(), serde_json::to_string_pretty(&default_config).unwrap()) + .map_err(|err| err.to_string()) + }); + builder.with_genesis_config(default_config) + }, + }; + + let chain_spec = builder.build(); + + match (cmd.verify, cmd.raw_storage) { + (_, true) => chain_spec.as_json(true), + (true, false) => { + chain_spec.as_json(true)?; + println!("Genesis config verification: OK"); + chain_spec.as_json(false) + }, + (false, false) => chain_spec.as_json(false), + } +} diff --git a/substrate/client/chain-spec/Cargo.toml b/substrate/client/chain-spec/Cargo.toml index e326d84ae50c..b11088190d16 100644 --- a/substrate/client/chain-spec/Cargo.toml +++ b/substrate/client/chain-spec/Cargo.toml @@ -13,15 +13,27 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] +codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } memmap2 = "0.5.0" serde = { version = "1.0.188", features = ["derive"] } serde_json = "1.0.107" sc-client-api = { path = "../api" } sc-chain-spec-derive = { path = "derive" } sc-executor = { path = "../executor" } +sp-io = { default-features = false, path = "../../primitives/io" } sc-network = { path = "../network" } sc-telemetry = { path = "../telemetry" } sp-blockchain = { path = "../../primitives/blockchain" } sp-core = { path = "../../primitives/core" } +sp-genesis-builder = { path = "../../primitives/genesis-builder" } sp-runtime = { path = "../../primitives/runtime" } sp-state-machine = { path = "../../primitives/state-machine" } +log = { version = "0.4.17", default-features = false } +array-bytes = { version = "6.1" } +docify = "0.2.0" + +[dev-dependencies] +substrate-test-runtime = { path = "../../test-utils/runtime" } +sp-keyring = { path = "../../primitives/keyring" } +sp-application-crypto = { default-features = false, path = "../../primitives/application-crypto", features = ["serde"] } +sp-consensus-babe = { default-features = false, path = "../../primitives/consensus/babe", features = ["serde"] } diff --git a/substrate/client/chain-spec/README.md b/substrate/client/chain-spec/README.md index dad1662d3230..9f746d2d4ce7 100644 --- a/substrate/client/chain-spec/README.md +++ b/substrate/client/chain-spec/README.md @@ -1,92 +1,6 @@ Substrate chain configurations. -This crate contains structs and utilities to declare -a runtime-specific configuration file (a.k.a chain spec). - -Basic chain spec type containing all required parameters is -[`ChainSpec`](https://docs.rs/sc-chain-spec/latest/sc_chain_spec/struct.GenericChainSpec.html). It can be extended with -additional options that contain configuration specific to your chain. -Usually the extension is going to be an amalgamate of types exposed -by Substrate core modules. To allow the core modules to retrieve -their configuration from your extension you should use `ChainSpecExtension` -macro exposed by this crate. - -```rust -use std::collections::HashMap; -use sc_chain_spec::{GenericChainSpec, ChainSpecExtension}; - -#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, ChainSpecExtension)] -pub struct MyExtension { - pub known_blocks: HashMap, -} - -pub type MyChainSpec = GenericChainSpec; -``` - -Some parameters may require different values depending on the -current blockchain height (a.k.a. forks). You can use `ChainSpecGroup` -macro and provided [`Forks`](https://docs.rs/sc-chain-spec/latest/sc_chain_spec/struct.Forks.html) structure to put -such parameters to your chain spec. -This will allow to override a single parameter starting at specific -block number. - -```rust -use sc_chain_spec::{Forks, ChainSpecGroup, ChainSpecExtension, GenericChainSpec}; - -#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, ChainSpecGroup)] -pub struct ClientParams { - max_block_size: usize, - max_extrinsic_size: usize, -} - -#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, ChainSpecGroup)] -pub struct PoolParams { - max_transaction_size: usize, -} - -#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, ChainSpecGroup, ChainSpecExtension)] -pub struct Extension { - pub client: ClientParams, - pub pool: PoolParams, -} - -pub type BlockNumber = u64; - -/// A chain spec supporting forkable `ClientParams`. -pub type MyChainSpec1 = GenericChainSpec>; - -/// A chain spec supporting forkable `Extension`. -pub type MyChainSpec2 = GenericChainSpec>; -``` - -It's also possible to have a set of parameters that is allowed to change -with block numbers (i.e. is forkable), and another set that is not subject to changes. -This is also possible by declaring an extension that contains `Forks` within it. - - -```rust -use serde::{Serialize, Deserialize}; -use sc_chain_spec::{Forks, GenericChainSpec, ChainSpecGroup, ChainSpecExtension}; - -#[derive(Clone, Debug, Serialize, Deserialize, ChainSpecGroup)] -pub struct ClientParams { - max_block_size: usize, - max_extrinsic_size: usize, -} - -#[derive(Clone, Debug, Serialize, Deserialize, ChainSpecGroup)] -pub struct PoolParams { - max_transaction_size: usize, -} - -#[derive(Clone, Debug, Serialize, Deserialize, ChainSpecExtension)] -pub struct Extension { - pub client: ClientParams, - #[forks] - pub pool: Forks, -} - -pub type MyChainSpec = GenericChainSpec; -``` +This crate contains structs and utilities to declare a runtime-specific configuration file (a.k.a chain spec). +Refer to crate documentation for details. License: GPL-3.0-or-later WITH Classpath-exception-2.0 diff --git a/substrate/client/chain-spec/res/raw_no_code.json b/substrate/client/chain-spec/res/raw_no_code.json new file mode 100644 index 000000000000..bac7c25582f9 --- /dev/null +++ b/substrate/client/chain-spec/res/raw_no_code.json @@ -0,0 +1,18 @@ +{ + "name": "TestName", + "id": "test_id", + "chainType": "Local", + "bootNodes": [], + "telemetryEndpoints": null, + "protocolId": null, + "properties": null, + "codeSubstitutes": {}, + "genesis": { + "raw": { + "top": { + "0x3a636f6465": "0x010101" + }, + "childrenDefault": {} + } + } +} diff --git a/substrate/client/chain-spec/res/raw_with_code.json b/substrate/client/chain-spec/res/raw_with_code.json new file mode 100644 index 000000000000..c12cec57ba7a --- /dev/null +++ b/substrate/client/chain-spec/res/raw_with_code.json @@ -0,0 +1,19 @@ +{ + "name": "TestName", + "id": "test_id", + "chainType": "Local", + "bootNodes": [], + "telemetryEndpoints": null, + "protocolId": null, + "properties": null, + "codeSubstitutes": {}, + "genesis": { + "raw": { + "top": { + "0x3a636f6465": "0x010101" + }, + "childrenDefault": {} + } + }, + "code": "0x060708" +} diff --git a/substrate/client/chain-spec/res/substrate_test_runtime_from_config.json b/substrate/client/chain-spec/res/substrate_test_runtime_from_config.json new file mode 100644 index 000000000000..d16f26d12540 --- /dev/null +++ b/substrate/client/chain-spec/res/substrate_test_runtime_from_config.json @@ -0,0 +1,128 @@ +{ + "name": "TestName", + "id": "test_id", + "chainType": "Local", + "bootNodes": [], + "telemetryEndpoints": null, + "protocolId": null, + "properties": null, + "codeSubstitutes": {}, + "genesis": { + "runtimeGenesis": { + "config": { + "babe": { + "authorities": [ + [ + "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + 1 + ], + [ + "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", + 1 + ], + [ + "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y", + 1 + ] + ], + "epochConfig": { + "allowed_slots": "PrimaryAndSecondaryPlainSlots", + "c": [ + 3, + 10 + ] + } + }, + "balances": { + "balances": [ + [ + "5D34dL5prEUaGNQtPPZ3yN5Y6BnkfXunKXXz6fo7ZJbLwRRH", + 100000000000000000 + ], + [ + "5GBNeWRhZc2jXu7D55rBimKYDk8PGk8itRYFTPfC8RJLKG5o", + 100000000000000000 + ], + [ + "5Dfis6XL8J2P6JHUnUtArnFWndn62SydeP8ee8sG2ky9nfm9", + 100000000000000000 + ], + [ + "5F4H97f7nQovyrbiq4ZetaaviNwThSVcFobcA5aGab6167dK", + 100000000000000000 + ], + [ + "5DiDShBWa1fQx6gLzpf3SFBhMinCoyvHM1BWjPNsmXS8hkrW", + 100000000000000000 + ], + [ + "5EFb84yH9tpcFuiKUcsmdoF7xeeY3ajG1ZLQimxQoFt9HMKR", + 100000000000000000 + ], + [ + "5DZLHESsfGrJ5YzT3HuRPXsSNb589xQ4Unubh1mYLodzKdVY", + 100000000000000000 + ], + [ + "5GHJzqvG6tXnngCpG7B12qjUvbo5e4e9z8Xjidk3CQZHxTPZ", + 100000000000000000 + ], + [ + "5CUnSsgAyLND3bxxnfNhgWXSe9Wn676JzLpGLgyJv858qhoX", + 100000000000000000 + ], + [ + "5CVKn7HAZW1Ky4r7Vkgsr7VEW88C2sHgUNDiwHY9Ct2hjU8q", + 100000000000000000 + ], + [ + "5H673aukQ4PeDe1U2nuv1bi32xDEziimh3PZz7hDdYUB7TNz", + 100000000000000000 + ], + [ + "5HTe9L15LJryjUAt1jZXZCBPnzbbGnpvFwbjE3NwCWaAqovf", + 100000000000000000 + ], + [ + "5D7LFzGpMwHPyDBavkRbWSKWTtJhCaPPZ379wWLT23bJwXJz", + 100000000000000000 + ], + [ + "5CLepMARnEgtVR1EkUuJVUvKh97gzergpSxUU3yKGx1v6EwC", + 100000000000000000 + ], + [ + "5Chb2UhfvZpmjjEziHbFbotM4quX32ZscRV6QJBt1rUKzz51", + 100000000000000000 + ], + [ + "5HmRp3i3ZZk7xsAvbi8hyXVP6whSMnBJGebVC4FsiZVhx52e", + 100000000000000000 + ], + [ + "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + 100000000000000000 + ], + [ + "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", + 100000000000000000 + ], + [ + "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y", + 100000000000000000 + ] + ] + }, + "substrateTest": { + "authorities": [ + "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", + "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y" + ] + }, + "system": {} + }, + "code": "0x0" + } + } +} diff --git a/substrate/client/chain-spec/res/substrate_test_runtime_from_config_raw.json b/substrate/client/chain-spec/res/substrate_test_runtime_from_config_raw.json new file mode 100644 index 000000000000..f033163ab19b --- /dev/null +++ b/substrate/client/chain-spec/res/substrate_test_runtime_from_config_raw.json @@ -0,0 +1,53 @@ +{ + "name": "TestName", + "id": "test_id", + "chainType": "Local", + "bootNodes": [], + "telemetryEndpoints": null, + "protocolId": null, + "properties": null, + "codeSubstitutes": {}, + "genesis": { + "raw": { + "top": { + "0x00771836bebdd29870ff246d305c578c4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x00771836bebdd29870ff246d305c578c5e0621c4869aa60c02be9adcc98a0d1d": "0x0cd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a4890b5ab205c6974c9ea841be688864633dc9ca8a357843eeacf2314649965fe22", + "0x1cb6f36e027abb2091cfb5110ab5087f4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x1cb6f36e027abb2091cfb5110ab5087f5e0621c4869aa60c02be9adcc98a0d1d": "0x0cd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01000000000000008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48010000000000000090b5ab205c6974c9ea841be688864633dc9ca8a357843eeacf2314649965fe220100000000000000", + "0x1cb6f36e027abb2091cfb5110ab5087f66e8f035c8adbe7f1547b43c51e6f8a4": "0x00000000", + "0x1cb6f36e027abb2091cfb5110ab5087faacf00b9b41fda7a9268821c2a2b3e4c": "0x0cd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01000000000000008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48010000000000000090b5ab205c6974c9ea841be688864633dc9ca8a357843eeacf2314649965fe220100000000000000", + "0x1cb6f36e027abb2091cfb5110ab5087fdc6b171b77304263c292cc3ea5ed31ef": "0x03000000000000000a0000000000000001", + "0x26aa394eea5630e07c48ae0c9558cef74e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x26aa394eea5630e07c48ae0c9558cef75684a022a34dd8bfa2baaf44f172b710": "0x01", + "0x26aa394eea5630e07c48ae0c9558cef78a42f33323cb5ced3b44dd825fda9fcc": "0x4545454545454545454545454545454545454545454545454545454545454545", + "0x26aa394eea5630e07c48ae0c9558cef7a44704b568d21667356a5a050c118746bb1bdbcacd6ac9340000000000000000": "0x4545454545454545454545454545454545454545454545454545454545454545", + "0x26aa394eea5630e07c48ae0c9558cef7a7fd6c28836b9a28522dc924110cf439": "0x01", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da901cae4e3edfbb32c91ed3f01ab964f4eeeab50338d8e5176d3141802d7b010a55dadcd5f23cf8aaafa724627e967e90e": "0x000000000000000000000000010000000000000000008a5d784563010000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da91b614bd4a126f2d5d294e9a8af9da25248d7e931307afb4b68d8d565d4c66e00d856c6d65f5fed6bb82dcfb60e936c67": "0x000000000000000000000000010000000000000000008a5d784563010000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da94b21aff9fe1e8b2fc4b0775b8cbeff28ba8e2c7594dd74730f3ca835e95455d199261897edc9735d602ea29615e2b10b": "0x000000000000000000000000010000000000000000008a5d784563010000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da94f9aea1afa791265fae359272badc1cf8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48": "0x000000000000000000000000010000000000000000008a5d784563010000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da95786a2916fcb81e1bd5dcd81e0d2452884617f575372edb5a36d85c04cdf2e4699f96fe33eb5f94a28c041b88e398d0c": "0x000000000000000000000000010000000000000000008a5d784563010000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da95b8542d9672c7b7e779cc7c1e6b605691c2115d06120ea2bee32dd601d02f36367564e7ddf84ae2717ca3f097459652e": "0x000000000000000000000000010000000000000000008a5d784563010000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da996c30bdbfab640838e6b6d3c33ab4adb4211b79e34ee8072eab506edd4b93a7b85a14c9a05e5cdd056d98e7dbca87730": "0x000000000000000000000000010000000000000000008a5d784563010000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da99dc65b1339ec388fbf2ca0cdef51253512c6cfd663203ea16968594f24690338befd906856c4d2f4ef32dad578dba20c": "0x000000000000000000000000010000000000000000008a5d784563010000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da99e6eb5abd62f5fd54793da91a47e6af6125d57171ff9241f07acaa1bb6a6103517965cf2cd00e643b27e7599ebccba70": "0x000000000000000000000000010000000000000000008a5d784563010000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9b0edae20838083f2cde1c4080db8cf8090b5ab205c6974c9ea841be688864633dc9ca8a357843eeacf2314649965fe22": "0x000000000000000000000000010000000000000000008a5d784563010000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9d0052993b6f3bd0544fd1f5e4125b9fbde3e789ecd53431fe5c06c12b72137153496dace35c695b5f4d7b41f7ed5763b": "0x000000000000000000000000010000000000000000008a5d784563010000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9d6b7e9a5f12bc571053265dade10d3b4b606fc73f57f03cdb4c932d475ab426043e429cecc2ffff0d2672b0df8398c48": "0x000000000000000000000000010000000000000000008a5d784563010000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d": "0x000000000000000000000000010000000000000000008a5d784563010000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9e1a35f56ee295d39287cbffcfc60c4b346f136b564e1fad55031404dd84e5cd3fa76bfe7cc7599b39d38fd06663bbc0a": "0x000000000000000000000000010000000000000000008a5d784563010000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9e2c1dc507e2035edbbd8776c440d870460c57f0008067cc01c5ff9eb2e2f9b3a94299a915a91198bd1021a6c55596f57": "0x000000000000000000000000010000000000000000008a5d784563010000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9eca0e653a94f4080f6311b4e7b6934eb2afba9278e30ccf6a6ceb3a8b6e336b70068f045c666f2e7f4f9cc5f47db8972": "0x000000000000000000000000010000000000000000008a5d784563010000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9ee8bf7ef90fc56a8aa3b90b344c599550c29b161e27ff8ba45bf6bad4711f326fc506a8803453a4d7e3158e993495f10": "0x000000000000000000000000010000000000000000008a5d784563010000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9f5d6f1c082fe63eec7a71fcad00f4a892e3d43b7b0d04e776e69e7be35247cecdac65504c579195731eaf64b7940966e": "0x000000000000000000000000010000000000000000008a5d784563010000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9fbf0818841edf110e05228a6379763c4fc3c37459d9bdc61f58a5ebc01e9e2305a19d390c0543dc733861ec3cf1de01f": "0x000000000000000000000000010000000000000000008a5d784563010000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7f9cce9c888469bb1a0dceaa129672ef8": "0x0000", + "0x3a636f6465": "0x0", + "0x3a65787472696e7369635f696e646578": "0x00000000", + "0xc2261276cc9d1f8598ea4b6a74b15c2f4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0xc2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80": "0x00003ef1ee275e1a" + }, + "childrenDefault": {} + } + } +} diff --git a/substrate/client/chain-spec/res/substrate_test_runtime_from_patch.json b/substrate/client/chain-spec/res/substrate_test_runtime_from_patch.json new file mode 100644 index 000000000000..6cef1dac7ac3 --- /dev/null +++ b/substrate/client/chain-spec/res/substrate_test_runtime_from_patch.json @@ -0,0 +1,32 @@ +{ + "name": "TestName", + "id": "test_id", + "chainType": "Local", + "bootNodes": [], + "telemetryEndpoints": null, + "protocolId": null, + "properties": null, + "codeSubstitutes": {}, + "genesis": { + "runtimeGenesis": { + "patch": { + "babe": { + "epochConfig": { + "allowed_slots": "PrimaryAndSecondaryPlainSlots", + "c": [ + 7, + 10 + ] + } + }, + "substrateTest": { + "authorities": [ + "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL", + "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" + ] + } + }, + "code": "0x0" + } + } +} diff --git a/substrate/client/chain-spec/res/substrate_test_runtime_from_patch_raw.json b/substrate/client/chain-spec/res/substrate_test_runtime_from_patch_raw.json new file mode 100644 index 000000000000..9f4b2731649f --- /dev/null +++ b/substrate/client/chain-spec/res/substrate_test_runtime_from_patch_raw.json @@ -0,0 +1,32 @@ +{ + "name": "TestName", + "id": "test_id", + "chainType": "Local", + "bootNodes": [], + "telemetryEndpoints": null, + "protocolId": null, + "properties": null, + "codeSubstitutes": {}, + "genesis": { + "raw": { + "top": { + "0x00771836bebdd29870ff246d305c578c4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x00771836bebdd29870ff246d305c578c5e0621c4869aa60c02be9adcc98a0d1d": "0x081cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07cd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d", + "0x1cb6f36e027abb2091cfb5110ab5087f4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x1cb6f36e027abb2091cfb5110ab5087f66e8f035c8adbe7f1547b43c51e6f8a4": "0x00000000", + "0x1cb6f36e027abb2091cfb5110ab5087fdc6b171b77304263c292cc3ea5ed31ef": "0x07000000000000000a0000000000000001", + "0x26aa394eea5630e07c48ae0c9558cef74e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x26aa394eea5630e07c48ae0c9558cef75684a022a34dd8bfa2baaf44f172b710": "0x01", + "0x26aa394eea5630e07c48ae0c9558cef78a42f33323cb5ced3b44dd825fda9fcc": "0x4545454545454545454545454545454545454545454545454545454545454545", + "0x26aa394eea5630e07c48ae0c9558cef7a44704b568d21667356a5a050c118746bb1bdbcacd6ac9340000000000000000": "0x4545454545454545454545454545454545454545454545454545454545454545", + "0x26aa394eea5630e07c48ae0c9558cef7a7fd6c28836b9a28522dc924110cf439": "0x01", + "0x26aa394eea5630e07c48ae0c9558cef7f9cce9c888469bb1a0dceaa129672ef8": "0x0000", + "0x3a636f6465": "0x0", + "0x3a65787472696e7369635f696e646578": "0x00000000", + "0xc2261276cc9d1f8598ea4b6a74b15c2f4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0xc2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80": "0x0000000000000000" + }, + "childrenDefault": {} + } + } +} diff --git a/substrate/client/chain-spec/src/chain_spec.rs b/substrate/client/chain-spec/src/chain_spec.rs index 96e36d8399ed..a44a3b4fbd97 100644 --- a/substrate/client/chain-spec/src/chain_spec.rs +++ b/substrate/client/chain-spec/src/chain_spec.rs @@ -18,8 +18,10 @@ //! Substrate chain configurations. #![warn(missing_docs)] - -use crate::{extension::GetExtension, ChainType, Properties, RuntimeGenesis}; +use crate::{ + extension::GetExtension, ChainType, GenesisConfigBuilderRuntimeCaller as RuntimeCaller, + Properties, RuntimeGenesis, +}; use sc_network::config::MultiaddrWithPeerId; use sc_telemetry::TelemetryEndpoints; use serde::{Deserialize, Serialize}; @@ -29,13 +31,32 @@ use sp_core::{ Bytes, }; use sp_runtime::BuildStorage; -use std::{borrow::Cow, collections::BTreeMap, fs::File, path::PathBuf, sync::Arc}; +use std::{ + borrow::Cow, + collections::{BTreeMap, VecDeque}, + fs::File, + marker::PhantomData, + path::PathBuf, + sync::Arc, +}; + +#[derive(Serialize, Deserialize, Clone)] +#[serde(rename_all = "camelCase")] +enum GenesisBuildAction { + Patch(json::Value), + Full(json::Value), +} +#[allow(deprecated)] enum GenesisSource { File(PathBuf), Binary(Cow<'static, [u8]>), - Factory(Arc G + Send + Sync>), + /// factory function + code + //Factory and G type parameter shall be removed togheter with `ChainSpec::from_genesis` + Factory(Arc G + Send + Sync>, Vec), Storage(Storage), + /// build action + code + GenesisBuilderApi(GenesisBuildAction, Vec), } impl Clone for GenesisSource { @@ -43,14 +64,17 @@ impl Clone for GenesisSource { match *self { Self::File(ref path) => Self::File(path.clone()), Self::Binary(ref d) => Self::Binary(d.clone()), - Self::Factory(ref f) => Self::Factory(f.clone()), + Self::Factory(ref f, ref c) => Self::Factory(f.clone(), c.clone()), Self::Storage(ref s) => Self::Storage(s.clone()), + Self::GenesisBuilderApi(ref s, ref c) => Self::GenesisBuilderApi(s.clone(), c.clone()), } } } impl GenesisSource { fn resolve(&self) -> Result, String> { + /// helper container for deserializing genesis from the JSON file (ChainSpec JSON file is + /// also supported here) #[derive(Serialize, Deserialize)] struct GenesisContainer { genesis: Genesis, @@ -79,31 +103,21 @@ impl GenesisSource { .map_err(|e| format!("Error parsing embedded file: {}", e))?; Ok(genesis.genesis) }, - Self::Factory(f) => Ok(Genesis::Runtime(f())), - Self::Storage(storage) => { - let top = storage - .top - .iter() - .map(|(k, v)| (StorageKey(k.clone()), StorageData(v.clone()))) - .collect(); - - let children_default = storage - .children_default - .iter() - .map(|(k, child)| { - ( - StorageKey(k.clone()), - child - .data - .iter() - .map(|(k, v)| (StorageKey(k.clone()), StorageData(v.clone()))) - .collect(), - ) - }) - .collect(); - - Ok(Genesis::Raw(RawGenesis { top, children_default })) - }, + Self::Factory(f, code) => Ok(Genesis::RuntimeAndCode(RuntimeInnerWrapper { + runtime: f(), + code: code.clone(), + })), + Self::Storage(storage) => Ok(Genesis::Raw(RawGenesis::from(storage.clone()))), + Self::GenesisBuilderApi(GenesisBuildAction::Full(config), code) => + Ok(Genesis::RuntimeGenesis(RuntimeGenesisInner { + json_blob: RuntimeGenesisConfigJson::Config(config.clone()), + code: code.clone(), + })), + Self::GenesisBuilderApi(GenesisBuildAction::Patch(patch), code) => + Ok(Genesis::RuntimeGenesis(RuntimeGenesisInner { + json_blob: RuntimeGenesisConfigJson::Patch(patch.clone()), + code: code.clone(), + })), } } } @@ -111,7 +125,18 @@ impl GenesisSource { impl BuildStorage for ChainSpec { fn assimilate_storage(&self, storage: &mut Storage) -> Result<(), String> { match self.genesis.resolve()? { - Genesis::Runtime(gc) => gc.assimilate_storage(storage), + #[allow(deprecated)] + Genesis::Runtime(runtime_genesis_config) => { + runtime_genesis_config.assimilate_storage(storage)?; + }, + #[allow(deprecated)] + Genesis::RuntimeAndCode(RuntimeInnerWrapper { + runtime: runtime_genesis_config, + code, + }) => { + runtime_genesis_config.assimilate_storage(storage)?; + storage.top.insert(sp_core::storage::well_known_keys::CODE.to_vec(), code); + }, Genesis::Raw(RawGenesis { top: map, children_default: children_map }) => { storage.top.extend(map.into_iter().map(|(k, v)| (k.0, v.0))); children_map.into_iter().for_each(|(k, v)| { @@ -123,13 +148,37 @@ impl BuildStorage for ChainSpec { .data .extend(v.into_iter().map(|(k, v)| (k.0, v.0))); }); - Ok(()) }, // The `StateRootHash` variant exists as a way to keep note that other clients support // it, but Substrate itself isn't capable of loading chain specs with just a hash at the // moment. - Genesis::StateRootHash(_) => Err("Genesis storage in hash format not supported".into()), - } + Genesis::StateRootHash(_) => + return Err("Genesis storage in hash format not supported".into()), + Genesis::RuntimeGenesis(RuntimeGenesisInner { + json_blob: RuntimeGenesisConfigJson::Config(config), + code, + }) => { + RuntimeCaller::new(&code[..]) + .get_storage_for_config(config)? + .assimilate_storage(storage)?; + storage + .top + .insert(sp_core::storage::well_known_keys::CODE.to_vec(), code.clone()); + }, + Genesis::RuntimeGenesis(RuntimeGenesisInner { + json_blob: RuntimeGenesisConfigJson::Patch(patch), + code, + }) => { + RuntimeCaller::new(&code[..]) + .get_storage_for_patch(patch)? + .assimilate_storage(storage)?; + storage + .top + .insert(sp_core::storage::well_known_keys::CODE.to_vec(), code.clone()); + }, + }; + + Ok(()) } } @@ -144,20 +193,98 @@ pub struct RawGenesis { pub children_default: BTreeMap, } +impl From for RawGenesis { + fn from(value: sp_core::storage::Storage) -> Self { + Self { + top: value.top.into_iter().map(|(k, v)| (StorageKey(k), StorageData(v))).collect(), + children_default: value + .children_default + .into_iter() + .map(|(sk, child)| { + ( + StorageKey(sk), + child + .data + .into_iter() + .map(|(k, v)| (StorageKey(k), StorageData(v))) + .collect(), + ) + }) + .collect(), + } + } +} + +/// Inner representation of [`Genesis::RuntimeGenesis`] format +#[derive(Serialize, Deserialize, Debug)] +struct RuntimeGenesisInner { + /// Runtime wasm code, expected to be hex-encoded in JSON. + /// The code shall be capable of parsing `json_blob`. + #[serde(default, with = "sp_core::bytes")] + code: Vec, + /// The patch or full representation of runtime's `RuntimeGenesisConfig` struct. + #[serde(flatten)] + json_blob: RuntimeGenesisConfigJson, +} + +/// Represents two possible variants of the contained JSON blob for the +/// [`Genesis::RuntimeGenesis`] format. +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +enum RuntimeGenesisConfigJson { + /// Represents the explicit and comprehensive runtime genesis config in JSON format. + /// The contained object is a JSON blob that can be parsed by a compatible runtime. + /// + /// Using a full config is useful for when someone wants to ensure that a change in the runtime + /// makes the deserialization fail and not silently add some default values. + Config(json::Value), + /// Represents a patch for the default runtime genesis config in JSON format which is + /// essentially a list of keys that are to be customized in runtime genesis config. + /// The contained value is a JSON blob that can be parsed by a compatible runtime. + Patch(json::Value), +} + +/// Inner variant wrapper for deprecated runtime. +#[derive(Serialize, Deserialize, Debug)] +struct RuntimeInnerWrapper { + /// The native `RuntimeGenesisConfig` struct. + runtime: G, + /// Runtime code. + #[serde(with = "sp_core::bytes")] + code: Vec, +} + +/// Represents the different formats of the genesis state within chain spec JSON blob. #[derive(Serialize, Deserialize)] #[serde(rename_all = "camelCase")] #[serde(deny_unknown_fields)] enum Genesis { + /// (Deprecated) Contains the JSON representation of G (the native type representing the + /// runtime's `RuntimeGenesisConfig` struct) (will be removed with `ChainSpec::from_genesis`) + /// without the runtime code. It is required to deserialize the legacy chainspecs genereted + /// with `ChainsSpec::from_genesis` method. Runtime(G), + /// (Deprecated) Contains the JSON representation of G (the native type representing the + /// runtime's `RuntimeGenesisConfig` struct) (will be removed with `ChainSpec::from_genesis`) + /// and the runtime code. It is required to create and deserialize JSON chainspecs created with + /// deprecated `ChainSpec::from_genesis` method. + RuntimeAndCode(RuntimeInnerWrapper), + /// The genesis storage as raw data. Typically raw key-value entries in state. Raw(RawGenesis), /// State root hash of the genesis storage. StateRootHash(StorageData), + /// Represents the runtime genesis config in JSON format toghether with runtime code. + RuntimeGenesis(RuntimeGenesisInner), } /// A configuration of a client. Does not include runtime storage initialization. +/// Note: `genesis` field is ignored due to way how the chain specification is serialized into +/// JSON file. Refer to [`ChainSpecJsonContainer`], which flattens [`ClientSpec`] and denies uknown +/// fields. #[derive(Serialize, Deserialize, Clone, Debug)] #[serde(rename_all = "camelCase")] -#[serde(deny_unknown_fields)] +// we cannot #[serde(deny_unknown_fields)]. Otherwise chain-spec-builder will fail on any +// non-standard spec struct ClientSpec { name: String, id: String, @@ -194,6 +321,137 @@ struct ClientSpec { /// We use `Option` here since `()` is not flattenable by serde. pub type NoExtension = Option<()>; +/// Builder for creating [`ChainSpec`] instances. +pub struct ChainSpecBuilder { + code: Vec, + extensions: E, + name: String, + id: String, + chain_type: ChainType, + genesis_build_action: GenesisBuildAction, + boot_nodes: Option>, + telemetry_endpoints: Option, + protocol_id: Option, + fork_id: Option, + properties: Option, + _genesis: PhantomData, +} + +impl ChainSpecBuilder { + /// Creates a new builder instance with no defaults. + pub fn new(code: &[u8], extensions: E) -> Self { + Self { + code: code.into(), + extensions, + name: "Development".to_string(), + id: "dev".to_string(), + chain_type: ChainType::Local, + genesis_build_action: GenesisBuildAction::Patch(Default::default()), + boot_nodes: None, + telemetry_endpoints: None, + protocol_id: None, + fork_id: None, + properties: None, + _genesis: Default::default(), + } + } + + /// Sets the spec name. + pub fn with_name(mut self, name: &str) -> Self { + self.name = name.into(); + self + } + + /// Sets the spec ID. + pub fn with_id(mut self, id: &str) -> Self { + self.id = id.into(); + self + } + + /// Sets the type of the chain. + pub fn with_chain_type(mut self, chain_type: ChainType) -> Self { + self.chain_type = chain_type; + self + } + + /// Sets a list of bootnode addresses. + pub fn with_boot_nodes(mut self, boot_nodes: Vec) -> Self { + self.boot_nodes = Some(boot_nodes); + self + } + + /// Sets telemetry endpoints. + pub fn with_telemetry_endpoints(mut self, telemetry_endpoints: TelemetryEndpoints) -> Self { + self.telemetry_endpoints = Some(telemetry_endpoints); + self + } + + /// Sets the network protocol ID. + pub fn with_protocol_id(mut self, protocol_id: &str) -> Self { + self.protocol_id = Some(protocol_id.into()); + self + } + + /// Sets an optional network fork identifier. + pub fn with_fork_id(mut self, fork_id: &str) -> Self { + self.fork_id = Some(fork_id.into()); + self + } + + /// Sets additional loosely-typed properties of the chain. + pub fn with_properties(mut self, properties: Properties) -> Self { + self.properties = Some(properties); + self + } + + /// Sets chain spec extensions. + pub fn with_extensions(mut self, extensions: E) -> Self { + self.extensions = extensions; + self + } + + /// Sets the code. + pub fn with_code(mut self, code: &[u8]) -> Self { + self.code = code.into(); + self + } + + /// Sets the JSON patch for runtime's GenesisConfig. + pub fn with_genesis_config_patch(mut self, patch: json::Value) -> Self { + self.genesis_build_action = GenesisBuildAction::Patch(patch); + self + } + + /// Sets the full runtime's GenesisConfig JSON. + pub fn with_genesis_config(mut self, config: json::Value) -> Self { + self.genesis_build_action = GenesisBuildAction::Full(config); + self + } + + /// Builds a [`ChainSpec`] instance using the provided settings. + pub fn build(self) -> ChainSpec { + let client_spec = ClientSpec { + name: self.name, + id: self.id, + chain_type: self.chain_type, + boot_nodes: self.boot_nodes.unwrap_or_default(), + telemetry_endpoints: self.telemetry_endpoints, + protocol_id: self.protocol_id, + fork_id: self.fork_id, + properties: self.properties, + extensions: self.extensions, + consensus_engine: (), + genesis: Default::default(), + code_substitutes: BTreeMap::new(), + }; + + ChainSpec { + client_spec, + genesis: GenesisSource::GenesisBuilderApi(self.genesis_build_action, self.code.into()), + } + } +} + /// A configuration of a chain. Can be used to build a genesis block. pub struct ChainSpec { client_spec: ClientSpec, @@ -260,6 +518,10 @@ impl ChainSpec { } /// Create hardcoded spec. + #[deprecated( + note = "`from_genesis` is planned to be removed in May 2024. Use `builder()` instead." + )] + // deprecated note: Genesis::Runtime + GenesisSource::Factory shall also be removed pub fn from_genesis G + 'static + Send + Sync>( name: &str, id: &str, @@ -271,6 +533,7 @@ impl ChainSpec { fork_id: Option<&str>, properties: Option, extensions: E, + code: &[u8], ) -> Self { let client_spec = ClientSpec { name: name.to_owned(), @@ -287,21 +550,30 @@ impl ChainSpec { code_substitutes: BTreeMap::new(), }; - ChainSpec { client_spec, genesis: GenesisSource::Factory(Arc::new(constructor)) } + ChainSpec { + client_spec, + genesis: GenesisSource::Factory(Arc::new(constructor), code.into()), + } } /// Type of the chain. fn chain_type(&self) -> ChainType { self.client_spec.chain_type.clone() } + + /// Provides a `ChainSpec` builder. + pub fn builder(code: &[u8], extensions: E) -> ChainSpecBuilder { + ChainSpecBuilder::new(code, extensions) + } } -impl ChainSpec { +impl ChainSpec { /// Parse json content into a `ChainSpec` pub fn from_json_bytes(json: impl Into>) -> Result { let json = json.into(); let client_spec = json::from_slice(json.as_ref()) .map_err(|e| format!("Error parsing spec file: {}", e))?; + Ok(ChainSpec { client_spec, genesis: GenesisSource::Binary(json) }) } @@ -318,50 +590,74 @@ impl ChainSpec { memmap2::Mmap::map(&file) .map_err(|e| format!("Error mmaping spec file `{}`: {}", path.display(), e))? }; - let client_spec = json::from_slice(&bytes).map_err(|e| format!("Error parsing spec file: {}", e))?; + Ok(ChainSpec { client_spec, genesis: GenesisSource::File(path) }) } } +/// Helper structure for serializing (and only serializing) the ChainSpec into JSON file. It +/// represents the layout of `ChainSpec` JSON file. #[derive(Serialize, Deserialize)] -struct JsonContainer { +// we cannot #[serde(deny_unknown_fields)]. Otherwise chain-spec-builder will fail on any +// non-standard spec. +struct ChainSpecJsonContainer { #[serde(flatten)] client_spec: ClientSpec, genesis: Genesis, } impl ChainSpec { - fn json_container(&self, raw: bool) -> Result, String> { - let genesis = match (raw, self.genesis.resolve()?) { + fn json_container(&self, raw: bool) -> Result, String> { + let raw_genesis = match (raw, self.genesis.resolve()?) { + ( + true, + Genesis::RuntimeGenesis(RuntimeGenesisInner { + json_blob: RuntimeGenesisConfigJson::Config(config), + code, + }), + ) => { + let mut storage = RuntimeCaller::new(&code[..]).get_storage_for_config(config)?; + storage.top.insert(sp_core::storage::well_known_keys::CODE.to_vec(), code); + RawGenesis::from(storage) + }, + ( + true, + Genesis::RuntimeGenesis(RuntimeGenesisInner { + json_blob: RuntimeGenesisConfigJson::Patch(patch), + code, + }), + ) => { + let mut storage = RuntimeCaller::new(&code[..]).get_storage_for_patch(patch)?; + storage.top.insert(sp_core::storage::well_known_keys::CODE.to_vec(), code); + RawGenesis::from(storage) + }, + + #[allow(deprecated)] + (true, Genesis::RuntimeAndCode(RuntimeInnerWrapper { runtime: g, code })) => { + let mut storage = g.build_storage()?; + storage.top.insert(sp_core::storage::well_known_keys::CODE.to_vec(), code); + RawGenesis::from(storage) + }, + #[allow(deprecated)] (true, Genesis::Runtime(g)) => { let storage = g.build_storage()?; - let top = - storage.top.into_iter().map(|(k, v)| (StorageKey(k), StorageData(v))).collect(); - let children_default = storage - .children_default - .into_iter() - .map(|(sk, child)| { - ( - StorageKey(sk), - child - .data - .into_iter() - .map(|(k, v)| (StorageKey(k), StorageData(v))) - .collect(), - ) - }) - .collect(); - - Genesis::Raw(RawGenesis { top, children_default }) + RawGenesis::from(storage) }, - (_, genesis) => genesis, + (true, Genesis::Raw(raw)) => raw, + + (_, genesis) => + return Ok(ChainSpecJsonContainer { client_spec: self.client_spec.clone(), genesis }), }; - Ok(JsonContainer { client_spec: self.client_spec.clone(), genesis }) + + Ok(ChainSpecJsonContainer { + client_spec: self.client_spec.clone(), + genesis: Genesis::Raw(raw_genesis), + }) } - /// Dump to json string. + /// Dump the chain specification to JSON string. pub fn as_json(&self, raw: bool) -> Result { let container = self.json_container(raw)?; json::to_string_pretty(&container).map_err(|e| format!("Error generating spec json: {}", e)) @@ -442,9 +738,87 @@ where } } +/// The `fun` will be called with the value at `path`. +/// +/// If exists, the value at given `path` will be passed to the `fun` and the result of `fun` +/// call will be returned. Otherwise false is returned. +/// `path` will be modified. +/// +/// # Examples +/// ```ignore +/// use serde_json::{from_str, json, Value}; +/// let doc = json!({"a":{"b":{"c":"5"}}}); +/// let mut path = ["a", "b", "c"].into(); +/// assert!(json_eval_value_at_key(&doc, &mut path, &|v| { assert_eq!(v,"5"); true })); +/// ``` +fn json_eval_value_at_key( + doc: &json::Value, + path: &mut VecDeque<&str>, + fun: &dyn Fn(&json::Value) -> bool, +) -> bool { + let Some(key) = path.pop_front() else { + return false; + }; + + if path.is_empty() { + doc.as_object().map_or(false, |o| o.get(key).map_or(false, |v| fun(v))) + } else { + doc.as_object() + .map_or(false, |o| o.get(key).map_or(false, |v| json_eval_value_at_key(v, path, fun))) + } +} + +macro_rules! json_path { + [ $($x:expr),+ ] => { + VecDeque::<&str>::from([$($x),+]) + }; +} + +fn json_contains_path(doc: &json::Value, path: &mut VecDeque<&str>) -> bool { + json_eval_value_at_key(doc, path, &|_| true) +} + +/// This function updates the code in given chain spec. +/// +/// Function support updating the runtime code in provided JSON chain spec blob. `Genesis::Raw` +/// and `Genesis::RuntimeGenesis` formats are supported. +/// +/// If update was successful `true` is returned, otherwise `false`. Chain spec JSON is modified in +/// place. +pub fn update_code_in_json_chain_spec(chain_spec: &mut json::Value, code: &[u8]) -> bool { + let mut path = json_path!["genesis", "runtimeGenesis", "code"]; + let mut raw_path = json_path!["genesis", "raw", "top"]; + + if json_contains_path(&chain_spec, &mut path) { + #[derive(Serialize)] + struct Container<'a> { + #[serde(with = "sp_core::bytes")] + code: &'a [u8], + } + let code_patch = json::json!({"genesis":{"runtimeGenesis": Container { code }}}); + crate::json_patch::merge(chain_spec, code_patch); + true + } else if json_contains_path(&chain_spec, &mut raw_path) { + #[derive(Serialize)] + struct Container<'a> { + #[serde(with = "sp_core::bytes", rename = "0x3a636f6465")] + code: &'a [u8], + } + let code_patch = json::json!({"genesis":{"raw":{"top": Container { code }}}}); + crate::json_patch::merge(chain_spec, code_patch); + true + } else { + false + } +} + #[cfg(test)] mod tests { use super::*; + use serde_json::{from_str, json, Value}; + use sp_application_crypto::Ss58Codec; + use sp_core::storage::well_known_keys; + use sp_keyring::AccountKeyring; #[derive(Debug, Serialize, Deserialize)] struct Genesis(BTreeMap); @@ -536,4 +910,336 @@ mod tests { ); } } + + #[test] + // some tests for json path utils + fn test_json_eval_value_at_key() { + let doc = json!({"a":{"b1":"20","b":{"c":{"d":"10"}}}}); + + assert!(json_eval_value_at_key(&doc, &mut json_path!["a", "b1"], &|v| { *v == "20" })); + assert!(json_eval_value_at_key(&doc, &mut json_path!["a", "b", "c", "d"], &|v| { + *v == "10" + })); + assert!(!json_eval_value_at_key(&doc, &mut json_path!["a", "c", "d"], &|_| { true })); + assert!(!json_eval_value_at_key(&doc, &mut json_path!["d"], &|_| { true })); + + assert!(json_contains_path(&doc, &mut json_path!["a", "b1"])); + assert!(json_contains_path(&doc, &mut json_path!["a", "b"])); + assert!(json_contains_path(&doc, &mut json_path!["a", "b", "c"])); + assert!(json_contains_path(&doc, &mut json_path!["a", "b", "c", "d"])); + assert!(!json_contains_path(&doc, &mut json_path!["a", "b", "c", "d", "e"])); + assert!(!json_contains_path(&doc, &mut json_path!["a", "b", "b1"])); + assert!(!json_contains_path(&doc, &mut json_path!["d"])); + } + + fn zeroize_code_key_in_json(encoded: bool, json: &str) -> Value { + let mut json = from_str::(json).unwrap(); + let (zeroing_patch, mut path) = if encoded { + ( + json!({"genesis":{"raw":{"top":{"0x3a636f6465":"0x0"}}}}), + json_path!["genesis", "raw", "top", "0x3a636f6465"], + ) + } else { + ( + json!({"genesis":{"runtimeGenesis":{"code":"0x0"}}}), + json_path!["genesis", "runtimeGenesis", "code"], + ) + }; + assert!(json_contains_path(&json, &mut path)); + crate::json_patch::merge(&mut json, zeroing_patch); + json + } + + #[docify::export] + #[test] + fn build_chain_spec_with_patch_works() { + let output: ChainSpec<()> = ChainSpec::builder( + substrate_test_runtime::wasm_binary_unwrap().into(), + Default::default(), + ) + .with_name("TestName") + .with_id("test_id") + .with_chain_type(ChainType::Local) + .with_genesis_config_patch(json!({ + "babe": { + "epochConfig": { + "c": [ + 7, + 10 + ], + "allowed_slots": "PrimaryAndSecondaryPlainSlots" + } + }, + "substrateTest": { + "authorities": [ + AccountKeyring::Ferdie.public().to_ss58check(), + AccountKeyring::Alice.public().to_ss58check() + ], + } + })) + .build(); + + let raw_chain_spec = output.as_json(true); + assert!(raw_chain_spec.is_ok()); + } + + #[docify::export] + #[test] + fn generate_chain_spec_with_patch_works() { + let output: ChainSpec<()> = ChainSpec::builder( + substrate_test_runtime::wasm_binary_unwrap().into(), + Default::default(), + ) + .with_name("TestName") + .with_id("test_id") + .with_chain_type(ChainType::Local) + .with_genesis_config_patch(json!({ + "babe": { + "epochConfig": { + "c": [ + 7, + 10 + ], + "allowed_slots": "PrimaryAndSecondaryPlainSlots" + } + }, + "substrateTest": { + "authorities": [ + AccountKeyring::Ferdie.public().to_ss58check(), + AccountKeyring::Alice.public().to_ss58check() + ], + } + })) + .build(); + + let actual = output.as_json(false).unwrap(); + let actual_raw = output.as_json(true).unwrap(); + + let expected = + from_str::(include_str!("../res/substrate_test_runtime_from_patch.json")) + .unwrap(); + let expected_raw = + from_str::(include_str!("../res/substrate_test_runtime_from_patch_raw.json")) + .unwrap(); + + //wasm blob may change overtime so let's zero it. Also ensure it is there: + let actual = zeroize_code_key_in_json(false, actual.as_str()); + let actual_raw = zeroize_code_key_in_json(true, actual_raw.as_str()); + + assert_eq!(actual, expected); + assert_eq!(actual_raw, expected_raw); + } + + #[test] + fn generate_chain_spec_with_full_config_works() { + let j = include_str!("../../../test-utils/runtime/res/default_genesis_config.json"); + let output: ChainSpec<()> = ChainSpec::builder( + substrate_test_runtime::wasm_binary_unwrap().into(), + Default::default(), + ) + .with_name("TestName") + .with_id("test_id") + .with_chain_type(ChainType::Local) + .with_genesis_config(from_str(j).unwrap()) + .build(); + + let actual = output.as_json(false).unwrap(); + let actual_raw = output.as_json(true).unwrap(); + + let expected = + from_str::(include_str!("../res/substrate_test_runtime_from_config.json")) + .unwrap(); + let expected_raw = + from_str::(include_str!("../res/substrate_test_runtime_from_config_raw.json")) + .unwrap(); + + //wasm blob may change overtime so let's zero it. Also ensure it is there: + let actual = zeroize_code_key_in_json(false, actual.as_str()); + let actual_raw = zeroize_code_key_in_json(true, actual_raw.as_str()); + + assert_eq!(actual, expected); + assert_eq!(actual_raw, expected_raw); + } + + #[test] + fn chain_spec_as_json_fails_with_invalid_config() { + let j = + include_str!("../../../test-utils/runtime/res/default_genesis_config_invalid_2.json"); + let output: ChainSpec<()> = ChainSpec::builder( + substrate_test_runtime::wasm_binary_unwrap().into(), + Default::default(), + ) + .with_name("TestName") + .with_id("test_id") + .with_chain_type(ChainType::Local) + .with_genesis_config(from_str(j).unwrap()) + .build(); + + assert_eq!( + output.as_json(true), + Err("Invalid JSON blob: unknown field `babex`, expected one of `system`, `babe`, `substrateTest`, `balances` at line 1 column 8".to_string()) + ); + } + + #[test] + fn chain_spec_as_json_fails_with_invalid_patch() { + let output: ChainSpec<()> = ChainSpec::builder( + substrate_test_runtime::wasm_binary_unwrap().into(), + Default::default(), + ) + .with_name("TestName") + .with_id("test_id") + .with_chain_type(ChainType::Local) + .with_genesis_config_patch(json!({ + "invalid_pallet": {}, + "substrateTest": { + "authorities": [ + AccountKeyring::Ferdie.public().to_ss58check(), + AccountKeyring::Alice.public().to_ss58check() + ], + } + })) + .build(); + + assert!(output.as_json(true).unwrap_err().contains("Invalid JSON blob: unknown field `invalid_pallet`, expected one of `system`, `babe`, `substrateTest`, `balances`")); + } + + #[test] + fn check_if_code_is_valid_for_raw_without_code() { + let spec = ChainSpec::<()>::from_json_bytes(Cow::Owned( + include_bytes!("../res/raw_no_code.json").to_vec(), + )) + .unwrap(); + + let j = from_str::(&spec.as_json(true).unwrap()).unwrap(); + + assert!(json_eval_value_at_key( + &j, + &mut json_path!["genesis", "raw", "top", "0x3a636f6465"], + &|v| { *v == "0x010101" } + )); + assert!(!json_contains_path(&j, &mut json_path!["code"])); + } + + #[test] + fn check_code_in_assimilated_storage_for_raw_without_code() { + let spec = ChainSpec::<()>::from_json_bytes(Cow::Owned( + include_bytes!("../res/raw_no_code.json").to_vec(), + )) + .unwrap(); + + let storage = spec.build_storage().unwrap(); + assert!(storage + .top + .get(&well_known_keys::CODE.to_vec()) + .map(|v| *v == vec![1, 1, 1]) + .unwrap()) + } + + #[test] + fn update_code_works_with_runtime_genesis_config() { + let j = include_str!("../../../test-utils/runtime/res/default_genesis_config.json"); + let chain_spec: ChainSpec<()> = ChainSpec::builder( + substrate_test_runtime::wasm_binary_unwrap().into(), + Default::default(), + ) + .with_name("TestName") + .with_id("test_id") + .with_chain_type(ChainType::Local) + .with_genesis_config(from_str(j).unwrap()) + .build(); + + let mut chain_spec_json = from_str::(&chain_spec.as_json(false).unwrap()).unwrap(); + assert!(update_code_in_json_chain_spec(&mut chain_spec_json, &[0, 1, 2, 4, 5, 6])); + + assert!(json_eval_value_at_key( + &chain_spec_json, + &mut json_path!["genesis", "runtimeGenesis", "code"], + &|v| { *v == "0x000102040506" } + )); + } + + #[test] + fn update_code_works_for_raw() { + let j = include_str!("../../../test-utils/runtime/res/default_genesis_config.json"); + let chain_spec: ChainSpec<()> = ChainSpec::builder( + substrate_test_runtime::wasm_binary_unwrap().into(), + Default::default(), + ) + .with_name("TestName") + .with_id("test_id") + .with_chain_type(ChainType::Local) + .with_genesis_config(from_str(j).unwrap()) + .build(); + + let mut chain_spec_json = from_str::(&chain_spec.as_json(true).unwrap()).unwrap(); + assert!(update_code_in_json_chain_spec(&mut chain_spec_json, &[0, 1, 2, 4, 5, 6])); + + assert!(json_eval_value_at_key( + &chain_spec_json, + &mut json_path!["genesis", "raw", "top", "0x3a636f6465"], + &|v| { *v == "0x000102040506" } + )); + } + + #[test] + fn update_code_works_with_runtime_genesis_patch() { + let chain_spec: ChainSpec<()> = ChainSpec::builder( + substrate_test_runtime::wasm_binary_unwrap().into(), + Default::default(), + ) + .with_name("TestName") + .with_id("test_id") + .with_chain_type(ChainType::Local) + .with_genesis_config_patch(json!({})) + .build(); + + let mut chain_spec_json = from_str::(&chain_spec.as_json(false).unwrap()).unwrap(); + assert!(update_code_in_json_chain_spec(&mut chain_spec_json, &[0, 1, 2, 4, 5, 6])); + + assert!(json_eval_value_at_key( + &chain_spec_json, + &mut json_path!["genesis", "runtimeGenesis", "code"], + &|v| { *v == "0x000102040506" } + )); + } + + #[test] + fn generate_from_genesis_is_still_supported() { + #[allow(deprecated)] + let chain_spec: ChainSpec = ChainSpec::from_genesis( + "TestName", + "test", + ChainType::Local, + move || substrate_test_runtime::RuntimeGenesisConfig { + babe: substrate_test_runtime::BabeConfig { + epoch_config: Some( + substrate_test_runtime::TEST_RUNTIME_BABE_EPOCH_CONFIGURATION, + ), + ..Default::default() + }, + ..Default::default() + }, + Vec::new(), + None, + None, + None, + None, + Default::default(), + &vec![0, 1, 2, 4, 5, 6], + ); + + let chain_spec_json = from_str::(&chain_spec.as_json(false).unwrap()).unwrap(); + assert!(json_eval_value_at_key( + &chain_spec_json, + &mut json_path!["genesis", "runtimeAndCode", "code"], + &|v| { *v == "0x000102040506" } + )); + let chain_spec_json = from_str::(&chain_spec.as_json(true).unwrap()).unwrap(); + assert!(json_eval_value_at_key( + &chain_spec_json, + &mut json_path!["genesis", "raw", "top", "0x3a636f6465"], + &|v| { *v == "0x000102040506" } + )); + } } diff --git a/substrate/client/chain-spec/src/genesis.rs b/substrate/client/chain-spec/src/genesis_block.rs similarity index 100% rename from substrate/client/chain-spec/src/genesis.rs rename to substrate/client/chain-spec/src/genesis_block.rs diff --git a/substrate/client/chain-spec/src/genesis_config_builder.rs b/substrate/client/chain-spec/src/genesis_config_builder.rs new file mode 100644 index 000000000000..c2c6249563f0 --- /dev/null +++ b/substrate/client/chain-spec/src/genesis_config_builder.rs @@ -0,0 +1,183 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +//! A helper module for calling the GenesisBuilder API from arbitrary runtime wasm blobs. + +use codec::{Decode, Encode}; +use sc_executor::{error::Result, WasmExecutor}; +use serde_json::{from_slice, Value}; +use sp_core::{ + storage::Storage, + traits::{CallContext, CodeExecutor, Externalities, FetchRuntimeCode, RuntimeCode}, +}; +use sp_genesis_builder::Result as BuildResult; +use sp_state_machine::BasicExternalities; +use std::borrow::Cow; + +/// A utility that facilitates calling the GenesisBuilder API from the runtime wasm code blob. +pub struct GenesisConfigBuilderRuntimeCaller<'a> { + code: Cow<'a, [u8]>, + code_hash: Vec, + executor: WasmExecutor, +} + +impl<'a> FetchRuntimeCode for GenesisConfigBuilderRuntimeCaller<'a> { + fn fetch_runtime_code(&self) -> Option> { + Some(self.code.as_ref().into()) + } +} + +impl<'a> GenesisConfigBuilderRuntimeCaller<'a> { + /// Creates new instance using the provided code blob. + /// + /// This code is later referred to as `runtime`. + pub fn new(code: &'a [u8]) -> Self { + GenesisConfigBuilderRuntimeCaller { + code: code.into(), + code_hash: sp_core::blake2_256(code).to_vec(), + executor: WasmExecutor::::builder() + .with_allow_missing_host_functions(true) + .build(), + } + } + + fn call(&self, ext: &mut dyn Externalities, method: &str, data: &[u8]) -> Result> { + self.executor + .call( + ext, + &RuntimeCode { heap_pages: None, code_fetcher: self, hash: self.code_hash.clone() }, + method, + data, + false, + CallContext::Offchain, + ) + .0 + } + + /// Returns the default `GenesisConfig` provided by the `runtime`. + /// + /// Calls [`GenesisBuilder::create_default_config`](sp_genesis_builder::GenesisBuilder::create_default_config) in the `runtime`. + pub fn get_default_config(&self) -> core::result::Result { + let mut t = BasicExternalities::new_empty(); + let call_result = self + .call(&mut t, "GenesisBuilder_create_default_config", &[]) + .map_err(|e| format!("wasm call error {e}"))?; + let default_config = Vec::::decode(&mut &call_result[..]) + .map_err(|e| format!("scale codec error: {e}"))?; + Ok(from_slice(&default_config[..]).expect("returned value is json. qed.")) + } + + /// Build the given `GenesisConfig` and returns the genesis state. + /// + /// Calls [`GenesisBuilder::build_config`](sp_genesis_builder::GenesisBuilder::build_config) + /// provided by the `runtime`. + pub fn get_storage_for_config(&self, config: Value) -> core::result::Result { + let mut ext = BasicExternalities::new_empty(); + + let call_result = self + .call(&mut ext, "GenesisBuilder_build_config", &config.to_string().encode()) + .map_err(|e| format!("wasm call error {e}"))?; + + BuildResult::decode(&mut &call_result[..]) + .map_err(|e| format!("scale codec error: {e}"))??; + + Ok(ext.into_storages()) + } + + /// Creates the genesis state by patching the default `GenesisConfig` and applying it. + /// + /// This function generates the `GenesisConfig` for the runtime by applying a provided JSON + /// patch. The patch modifies the default `GenesisConfig` allowing customization of the specific + /// keys. The resulting `GenesisConfig` is then deserialized from the patched JSON + /// representation and stored in the storage. + /// + /// If the provided JSON patch is incorrect or the deserialization fails the error will be + /// returned. + /// + /// The patching process modifies the default `GenesisConfig` according to the followingΒ rules: + /// 1. Existing keys in the default configuration will be overridden by the corresponding values + /// in the patch. + /// 2. If a key exists in the patch but not in the default configuration, it will be added to + /// the resulting `GenesisConfig`. + /// 3. Keys in the default configuration that have null values in the patch will be removed from + /// the resulting `GenesisConfig`. This is helpful for changing enum variant value. + /// + /// Please note that the patch may contain full `GenesisConfig`. + pub fn get_storage_for_patch(&self, patch: Value) -> core::result::Result { + let mut config = self.get_default_config()?; + crate::json_patch::merge(&mut config, patch); + self.get_storage_for_config(config) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::{from_str, json}; + pub use sp_consensus_babe::{AllowedSlots, BabeEpochConfiguration, Slot}; + + #[test] + fn get_default_config_works() { + let config = + GenesisConfigBuilderRuntimeCaller::new(substrate_test_runtime::wasm_binary_unwrap()) + .get_default_config() + .unwrap(); + let expected = r#"{"system":{},"babe":{"authorities":[],"epochConfig":null},"substrateTest":{"authorities":[]},"balances":{"balances":[]}}"#; + assert_eq!(from_str::(expected).unwrap(), config); + } + + #[test] + fn get_storage_for_patch_works() { + let patch = json!({ + "babe": { + "epochConfig": { + "c": [ + 69, + 696 + ], + "allowed_slots": "PrimaryAndSecondaryPlainSlots" + } + }, + }); + + let storage = + GenesisConfigBuilderRuntimeCaller::new(substrate_test_runtime::wasm_binary_unwrap()) + .get_storage_for_patch(patch) + .unwrap(); + + //Babe|Authorities + let value: Vec = storage + .top + .get( + &array_bytes::hex2bytes( + "1cb6f36e027abb2091cfb5110ab5087fdc6b171b77304263c292cc3ea5ed31ef", + ) + .unwrap(), + ) + .unwrap() + .clone(); + + assert_eq!( + BabeEpochConfiguration::decode(&mut &value[..]).unwrap(), + BabeEpochConfiguration { + c: (69, 696), + allowed_slots: AllowedSlots::PrimaryAndSecondaryPlainSlots + } + ); + } +} diff --git a/substrate/client/chain-spec/src/json_patch.rs b/substrate/client/chain-spec/src/json_patch.rs new file mode 100644 index 000000000000..c3930069a60d --- /dev/null +++ b/substrate/client/chain-spec/src/json_patch.rs @@ -0,0 +1,191 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +//! A helper module providing json patching functions. + +use serde_json::Value; + +/// Recursively merges two JSON objects, `a` and `b`, into a single object. +/// +/// If a key exists in both objects, the value from `b` will override the value from `a`. +/// If a key exists in `b` with a `null` value, it will be removed from `a`. +/// If a key exists only in `b` and not in `a`, it will be added to `a`. +/// +/// # Arguments +/// +/// * `a` - A mutable reference to the target JSON object to merge into. +/// * `b` - The JSON object to merge with `a`. +pub fn merge(a: &mut Value, b: Value) { + match (a, b) { + (Value::Object(a), Value::Object(b)) => + for (k, v) in b { + if v.is_null() { + a.remove(&k); + } else { + merge(a.entry(k).or_insert(Value::Null), v); + } + }, + (a, b) => *a = b, + }; +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + + #[test] + fn test1_simple_merge() { + let mut j1 = json!({ "a":123 }); + merge(&mut j1, json!({ "b":256 })); + assert_eq!(j1, json!({ "a":123, "b":256 })); + } + + #[test] + fn test2_patch_simple_merge_nested() { + let mut j1 = json!({ + "a": { + "name": "xxx", + "value": 123 + }, + "b": { "c" : { "inner_name": "yyy" } } + }); + + let j2 = json!({ + "a": { + "keys": ["a", "b", "c" ] + } + }); + + merge(&mut j1, j2); + assert_eq!( + j1, + json!({"a":{"keys":["a","b","c"],"name":"xxx","value":123}, "b": { "c" : { "inner_name": "yyy" } }}) + ); + } + + #[test] + fn test3_patch_overrides_existing_keys() { + let mut j1 = json!({ + "a": { + "name": "xxx", + "value": 123, + "keys": ["d"] + + } + }); + + let j2 = json!({ + "a": { + "keys": ["a", "b", "c" ] + } + }); + + merge(&mut j1, j2); + assert_eq!(j1, json!({"a":{"keys":["a","b","c"],"name":"xxx","value":123}})); + } + + #[test] + fn test4_patch_overrides_existing_keys() { + let mut j1 = json!({ + "a": { + "name": "xxx", + "value": 123, + "b" : { + "inner_name": "yyy" + } + } + }); + + let j2 = json!({ + "a": { + "name": "new_name", + "b" : { + "inner_name": "inner_new_name" + } + } + }); + + merge(&mut j1, j2); + assert_eq!( + j1, + json!({ "a": {"name":"new_name", "value":123, "b" : { "inner_name": "inner_new_name" }} }) + ); + } + + #[test] + fn test5_patch_overrides_existing_nested_keys() { + let mut j1 = json!({ + "a": { + "name": "xxx", + "value": 123, + "b": { + "c": { + "d": { + "name": "yyy", + "value": 256 + } + } + } + }, + }); + + let j2 = json!({ + "a": { + "value": 456, + "b": { + "c": { + "d": { + "name": "new_name" + } + } + } + } + }); + + merge(&mut j1, j2); + assert_eq!( + j1, + json!({ "a": {"name":"xxx", "value":456, "b": { "c": { "d": { "name": "new_name", "value": 256 }}}}}) + ); + } + + #[test] + fn test6_patch_removes_keys_if_null() { + let mut j1 = json!({ + "a": { + "name": "xxx", + "value": 123, + "enum_variant_1": { + "name": "yyy", + } + }, + }); + + let j2 = json!({ + "a": { + "value": 456, + "enum_variant_1": null, + "enum_variant_2": 32, + } + }); + + merge(&mut j1, j2); + assert_eq!(j1, json!({ "a": {"name":"xxx", "value":456, "enum_variant_2": 32 }})); + } +} diff --git a/substrate/client/chain-spec/src/lib.rs b/substrate/client/chain-spec/src/lib.rs index 341c5f28e4a4..6a922e7b40b2 100644 --- a/substrate/client/chain-spec/src/lib.rs +++ b/substrate/client/chain-spec/src/lib.rs @@ -16,38 +16,253 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -//! Substrate chain configurations. +//! This crate includes structs and utilities for defining configuration files (known as chain +//! specification) for both runtime and node. +//! +//! # Intro: Chain Specification +//! +//! The chain specification comprises parameters and settings that define the properties and an +//! initial state of a chain. Users typically interact with the JSON representation of the chain +//! spec. Internally, the chain spec is embodied by the [`GenericChainSpec`] struct, and specific +//! properties can be accessed using the [`ChainSpec`] trait. +//! +//! In summary, although not restricted to, the primary role of the chain spec is to provide a list +//! of well-known boot nodes for the blockchain network and the means for initializing the genesis +//! storage. This initialization is necessary for creating a genesis block upon which subsequent +//! blocks are built. When the node is launched for the first time, it reads the chain spec, +//! initializes the genesis block, and establishes connections with the boot nodes. +//! +//! The JSON chain spec is divided into two main logical sections: +//! - one section details general chain properties, +//! - second explicitly or indirectly defines the genesis storage, which, in turn, determines the +//! genesis hash of the chain, +//! +//! The chain specification consists of the following fields: +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//!
Chain spec keyDescription
nameThe human readable name of the chain.
idThe id of the chain.
chainTypeThe chain type of this chain +//! (refer to +//! +//! ChainType +//! ). +//!
bootNodesA list of +//! multi addresses +//! that belong to boot nodes of the chain.
telemetryEndpointsOptional list of multi address, verbosity of telemetry endpoints. The +//! verbosity goes from 0 to 9. With 0 being the mode with the lowest verbosity.
protocolIdOptional networking protocol id that identifies the chain.
forkIdOptional fork id. Should most likely be left empty. Can be used to signal a fork on +//! the network level when two chains have the same genesis hash.
propertiesCustom properties. Shall be provided in the form of +//! key-value json object. +//!
consensusEngineDeprecated field. Should be ignored.
codeSubstitutesOptional map of block_number to wasm_code. More details in +//! material to follow.
genesisDefines the initial state of the runtime. More details in material to follow.
+//! +//! # `genesis`: Initial Runtime State +//! +//! All nodes in the network must build subsequent blocks upon exactly the same genesis block. +//! +//! The information configured in the `genesis` section of a chain specification is used to build +//! the genesis storage, which is essential for creating the genesis block, since the block header +//! includes the storage root hash. +//! +//! The `genesis` key of the chain specification definition describes the +//! initial state of the runtime. For example, it may contain: +//! - an initial list of funded accounts, +//! - the administrative account that controls the sudo key, +//! - an initial authorities set for consensus, etc. +//! +//! As the compiled WASM blob of the runtime code is stored in the chain's state, the initial +//! runtime must also be provided within the chain specification. +//! +//! In essence, the most important formats of genesis initial state are: +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//! +//!
FormatDescription
+//! runtime / full config +//! A JSON object that provides an explicit and comprehensive representation of the +//! RuntimeGenesisConfig struct, which is generated by frame::runtime::prelude::construct_runtime macro (example of generated struct). Must contain all the keys of +//! the genesis config, no defaults will be used. +//! +//! This format explicitly provides the code of the runtime. +//!
+//! patch +//! A JSON object that offers a partial representation of the +//! RuntimeGenesisConfig provided by the runtime. It contains a patch, which is +//! essentially a list of key-value pairs to customize in the default runtime's +//! RuntimeGenesisConfig. +//! This format explicitly provides the code of the runtime. +//!
+//! raw +//! A JSON object with two fields: top and children_default. +//! Each field is a map of key => value pairs representing entries in a genesis storage +//! trie. The runtime code is one of such entries.
+//! +//! For production or long-lasting blockchains, using the `raw` format in the chain specification is +//! recommended. Only the `raw` format guarantees that storage root hash will remain unchanged when +//! the `RuntimeGenesisConfig` format changes due to software upgrade. +//! +//! JSON examples in the [following section](#json-chain-specification-example) illustrate the `raw` +//! `patch` and full genesis fields. +//! +//! # From Initial State to Raw Genesis. +//! +//! To generate a raw genesis storage from the JSON representation of the runtime genesis config, +//! the node needs to interact with the runtime. +//! +//! This interaction involves passing the runtime genesis config JSON blob to the runtime using the +//! [`sp_genesis_builder::GenesisBuilder::build_config`] function. During this operation, the +//! runtime converts the JSON representation of the genesis config into [`sp_io::storage`] items. It +//! is a crucial step for computing the storage root hash, which is a key component in determining +//! the genesis hash. +//! +//! Consequently, the runtime must support the [`sp_genesis_builder::GenesisBuilder`] API to +//! utilize either `patch` or `full` formats. +//! +//! This entire process is encapsulated within the implementation of the [`BuildStorage`] trait, +//! which can be accessed through the [`ChainSpec::as_storage_builder`] method. There is an +//! intermediate internal helper that facilitates this interaction, +//! [`GenesisConfigBuilderRuntimeCaller`], which serves as a straightforward wrapper for +//! [`sc_executor::WasmExecutor`]. +//! +//! In case of `raw` genesis state the node does not interact with the runtime regarding the +//! computation of initial state. +//! +//! The plain and `raw` chain specification JSON blobs can be found in +//! [JSON examples](#json-chain-specification-example) section. +//! +//! # Optional Code Mapping +//! +//! Optional map of `block_number` to `wasm_code`. +//! +//! The given `wasm_code` will be used to substitute the on-chain wasm code starting with the +//! given block number until the `spec_version` on-chain changes. The given `wasm_code` should +//! be as close as possible to the on-chain wasm code. A substitute should be used to fix a bug +//! that cannot be fixed with a runtime upgrade, if for example the runtime is constantly +//! panicking. Introducing new runtime APIs isn't supported, because the node +//! will read the runtime version from the on-chain wasm code. +//! +//! Use this functionality only when there is no other way around it, and only patch the problematic +//! bug; the rest should be done with an on-chain runtime upgrade. +//! +//! # Building a Chain Specification +//! +//! The [`ChainSpecBuilder`] should be used to create an instance of a chain specification. Its API +//! allows configuration of all fields of the chain spec. To generate a JSON representation of the +//! specification, use [`ChainSpec::as_json`]. +//! +//! The sample code to generate a chain spec is as follows: +#![doc = docify::embed!("src/chain_spec.rs", build_chain_spec_with_patch_works)] +//! # JSON chain specification example +//! +//! The following are the plain and `raw` versions of the chain specification JSON files, resulting +//! from executing of the above [example](#building-a-chain-specification): +//! ```ignore +#![doc = include_str!("../res/substrate_test_runtime_from_patch.json")] +//! ``` +//! ```ignore +#![doc = include_str!("../res/substrate_test_runtime_from_patch_raw.json")] +//! ``` +//! The following example shows the plain full config version of chain spec: +//! ```ignore +#![doc = include_str!("../res/substrate_test_runtime_from_config.json")] +//! ``` +//! The [`ChainSpec`] trait represents the API to access values defined in the JSON chain specification. +//! //! -//! This crate contains structs and utilities to declare -//! a runtime-specific configuration file (a.k.a chain spec). +//! # Custom Chain Spec Extensions //! -//! Basic chain spec type containing all required parameters is -//! [`GenericChainSpec`]. It can be extended with -//! additional options that contain configuration specific to your chain. -//! Usually the extension is going to be an amalgamate of types exposed -//! by Substrate core modules. To allow the core modules to retrieve -//! their configuration from your extension you should use `ChainSpecExtension` -//! macro exposed by this crate. +//! The basic chain spec type containing all required parameters is [`GenericChainSpec`]. It can be +//! extended with additional options containing configuration specific to your chain. Usually, the +//! extension will be a combination of types exposed by Substrate core modules. //! +//! To allow the core modules to retrieve their configuration from your extension, you should use +//! `ChainSpecExtension` macro exposed by this crate. //! ```rust //! use std::collections::HashMap; //! use sc_chain_spec::{GenericChainSpec, ChainSpecExtension}; //! //! #[derive(Clone, Debug, serde::Serialize, serde::Deserialize, ChainSpecExtension)] //! pub struct MyExtension { -//! pub known_blocks: HashMap, +//! pub known_blocks: HashMap, //! } //! //! pub type MyChainSpec = GenericChainSpec; //! ``` -//! -//! Some parameters may require different values depending on the -//! current blockchain height (a.k.a. forks). You can use `ChainSpecGroup` -//! macro and provided [`Forks`](./struct.Forks.html) structure to put -//! such parameters to your chain spec. -//! This will allow to override a single parameter starting at specific -//! block number. -//! +//! Some parameters may require different values depending on the current blockchain height (a.k.a. +//! forks). You can use the [`ChainSpecGroup`](macro@ChainSpecGroup) macro and the provided [`Forks`] +//! structure to add such parameters to your chain spec. This will allow overriding a single +//! parameter starting at a specific block number. //! ```rust //! use sc_chain_spec::{Forks, ChainSpecGroup, ChainSpecExtension, GenericChainSpec}; //! @@ -76,12 +291,9 @@ //! /// A chain spec supporting forkable `Extension`. //! pub type MyChainSpec2 = GenericChainSpec>; //! ``` -//! -//! It's also possible to have a set of parameters that is allowed to change -//! with block numbers (i.e. is forkable), and another set that is not subject to changes. -//! This is also possible by declaring an extension that contains `Forks` within it. -//! -//! +//! It's also possible to have a set of parameters that are allowed to change with block numbers +//! (i.e., they are forkable), and another set that is not subject to changes. This can also be +//! achieved by declaring an extension that contains [`Forks`] within it. //! ```rust //! use serde::{Serialize, Deserialize}; //! use sc_chain_spec::{Forks, GenericChainSpec, ChainSpecGroup, ChainSpecExtension}; @@ -106,98 +318,26 @@ //! //! pub type MyChainSpec = GenericChainSpec; //! ``` -//! -//! # Substrate chain specification format -//! -//! The Substrate chain specification is a `json` file that describes the basics of a chain. Most -//! importantly it lays out the genesis storage which leads to the genesis hash. The default -//! Substrate chain specification format is the following: -//! -//! ```json -//! // The human readable name of the chain. -//! "name": "Flaming Fir", -//! -//! // The id of the chain. -//! "id": "flamingfir9", -//! -//! // The chain type of this chain. -//! // Possible values are `Live`, `Development`, `Local`. -//! "chainType": "Live", -//! -//! // A list of multi addresses that belong to boot nodes of the chain. -//! "bootNodes": [ -//! "/dns/0.flamingfir.paritytech.net/tcp/30333/p2p/12D3KooWLK2gMLhWsYJzjW3q35zAs9FDDVqfqVfVuskiGZGRSMvR", -//! ], -//! -//! // Optional list of "multi address, verbosity" of telemetry endpoints. -//! // The verbosity goes from `0` to `9`. With `0` being the mode with the lowest verbosity. -//! "telemetryEndpoints": [ -//! [ -//! "/dns/telemetry.polkadot.io/tcp/443/x-parity-wss/%2Fsubmit%2F", -//! 0 -//! ] -//! ], -//! -//! // Optional networking protocol id that identifies the chain. -//! "protocolId": "fir9", -//! -//! // Optional fork id. Should most likely be left empty. -//! // Can be used to signal a fork on the network level when two chains have the -//! // same genesis hash. -//! "forkId": "random_fork", -//! -//! // Custom properties. -//! "properties": { -//! "tokenDecimals": 15, -//! "tokenSymbol": "FIR" -//! }, -//! -//! // Deprecated field. Should be ignored. -//! "consensusEngine": null, -//! -//! // The genesis declaration of the chain. -//! // -//! // `runtime`, `raw`, `stateRootHash` denote the type of the genesis declaration. -//! // -//! // These declarations are in the following formats: -//! // - `runtime` is a `json` object that can be parsed by a compatible `GenesisConfig`. This -//! // `GenesisConfig` is declared by a runtime and opaque to the node. -//! // - `raw` is a `json` object with two fields `top` and `children_default`. Each of these -//! // fields is a map of `key => value`. These key/value pairs represent the genesis storage. -//! // - `stateRootHash` is a single hex encoded hash that represents the genesis hash. The hash -//! // type depends on the hash used by the chain. -//! // -//! "genesis": { "runtime": {} }, -//! -//! /// Optional map of `block_number` to `wasm_code`. -//! /// -//! /// The given `wasm_code` will be used to substitute the on-chain wasm code starting with the -//! /// given block number until the `spec_version` on-chain changes. The given `wasm_code` should -//! /// be as close as possible to the on-chain wasm code. A substitute should be used to fix a bug -//! /// that can not be fixed with a runtime upgrade, if for example the runtime is constantly -//! /// panicking. Introducing new runtime apis isn't supported, because the node -//! /// will read the runtime version from the on-chain wasm code. Use this functionality only when -//! /// there is no other way around it and only patch the problematic bug, the rest should be done -//! /// with a on-chain runtime upgrade. -//! "codeSubstitutes": [], -//! ``` -//! -//! See [`ChainSpec`] for a trait representation of the above. -//! //! The chain spec can be extended with other fields that are opaque to the default chain spec. //! Specific node implementations will need to be able to deserialize these extensions. mod chain_spec; mod extension; -mod genesis; +mod genesis_block; +mod genesis_config_builder; +mod json_patch; pub use self::{ - chain_spec::{ChainSpec as GenericChainSpec, NoExtension}, + chain_spec::{ + update_code_in_json_chain_spec, ChainSpec as GenericChainSpec, ChainSpecBuilder, + NoExtension, + }, extension::{get_extension, get_extension_mut, Extension, Fork, Forks, GetExtension, Group}, - genesis::{ + genesis_block::{ construct_genesis_block, resolve_state_version_from_wasm, BuildGenesisBlock, GenesisBlockBuilder, }, + genesis_config_builder::GenesisConfigBuilderRuntimeCaller, }; pub use sc_chain_spec_derive::{ChainSpecExtension, ChainSpecGroup}; diff --git a/substrate/client/cli/src/commands/insert_key.rs b/substrate/client/cli/src/commands/insert_key.rs index 732d874319a8..3d89610b28b1 100644 --- a/substrate/client/cli/src/commands/insert_key.rs +++ b/substrate/client/cli/src/commands/insert_key.rs @@ -126,18 +126,14 @@ mod tests { } fn load_spec(&self, _: &str) -> std::result::Result, String> { - Ok(Box::new(GenericChainSpec::from_genesis( - "test", - "test_id", - ChainType::Development, - || unimplemented!("Not required in tests"), - Vec::new(), - None, - None, - None, - None, - NoExtension::None, - ))) + Ok(Box::new( + GenericChainSpec::<()>::builder(Default::default(), NoExtension::None) + .with_name("test") + .with_id("test_id") + .with_chain_type(ChainType::Development) + .with_genesis_config_patch(Default::default()) + .build(), + )) } } diff --git a/substrate/client/cli/src/runner.rs b/substrate/client/cli/src/runner.rs index 59f53200a192..1707a76cbe78 100644 --- a/substrate/client/cli/src/runner.rs +++ b/substrate/client/cli/src/runner.rs @@ -251,18 +251,14 @@ mod tests { trie_cache_maximum_size: None, state_pruning: None, blocks_pruning: sc_client_db::BlocksPruning::KeepAll, - chain_spec: Box::new(GenericChainSpec::from_genesis( - "test", - "test_id", - ChainType::Development, - || unimplemented!("Not required in tests"), - Vec::new(), - None, - None, - None, - None, - NoExtension::None, - )), + chain_spec: Box::new( + GenericChainSpec::<()>::builder(Default::default(), NoExtension::None) + .with_name("test") + .with_id("test_id") + .with_chain_type(ChainType::Development) + .with_genesis_config_patch(Default::default()) + .build(), + ), wasm_method: Default::default(), wasm_runtime_overrides: None, rpc_addr: None, diff --git a/substrate/frame/system/src/lib.rs b/substrate/frame/system/src/lib.rs index dfdacc9a8eb9..0e394a110411 100644 --- a/substrate/frame/system/src/lib.rs +++ b/substrate/frame/system/src/lib.rs @@ -724,8 +724,6 @@ pub mod pallet { #[derive(frame_support::DefaultNoBound)] #[pallet::genesis_config] pub struct GenesisConfig { - #[serde(with = "sp_core::bytes")] - pub code: Vec, #[serde(skip)] pub _config: sp_std::marker::PhantomData, } @@ -739,7 +737,6 @@ pub mod pallet { >::put(true); >::put(true); - sp_io::storage::set(well_known_keys::CODE, &self.code); sp_io::storage::set(well_known_keys::EXTRINSIC_INDEX, &0u32.encode()); } } diff --git a/substrate/src/lib.rs b/substrate/src/lib.rs index be9eef9952f8..b5a583fcfcf1 100644 --- a/substrate/src/lib.rs +++ b/substrate/src/lib.rs @@ -141,8 +141,9 @@ //! side features. The corresponding runtime, called [`kitchensink_runtime`] contains all of the //! modules that are provided with `FRAME`. This node and runtime is only used for testing and //! demonstration. -//! * [`chain-spec-builder`]: Utility to build more detailed chain-specs for the aforementioned -//! node. Other projects typically contain a `build-spec` subcommand that does the same. +//! * [`chain-spec-builder`]: Utility to build more detailed [chain-spec][`sc-chain-spec`] for the +//! aforementioned node. Other projects typically contain a `build-spec` subcommand that does the +//! same. //! * [`node-template`]: a template node that contains a minimal set of features and can act as a //! starting point of a project. //! * [`subkey`]: Substrate's key management utility. @@ -177,6 +178,8 @@ //! //! Additional noteworthy crates within substrate: //! +//! - Chain specification of a Substrate node: +//! - [`sc-chain-spec`] //! - RPC APIs of a Substrate node: [`sc-rpc-api`]/[`sc-rpc`] //! - CLI Options of a Substrate node: [`sc-cli`] //! - All of the consensus related crates provided by Substrate: @@ -217,6 +220,7 @@ //! [`sp-api`]: ../sp_api/index.html //! [`sp-api`]: ../sp_api/index.html //! [`sc-client-db`]: ../sc_client_db/index.html +//! [`sc-chain-spec`]: ../sc_chain_spec/index.html //! [`sc-network`]: ../sc_network/index.html //! [`sc-rpc-api`]: ../sc_rpc_api/index.html //! [`sc-rpc`]: ../sc_rpc/index.html diff --git a/substrate/test-utils/runtime/Cargo.toml b/substrate/test-utils/runtime/Cargo.toml index 84b214a0b9cb..6a1f8914444b 100644 --- a/substrate/test-utils/runtime/Cargo.toml +++ b/substrate/test-utils/runtime/Cargo.toml @@ -48,8 +48,6 @@ sp-externalities = { path = "../../primitives/externalities", default-features = # 3rd party array-bytes = { version = "6.1", optional = true } log = { version = "0.4.17", default-features = false } -serde = { version = "1.0.188", features = ["alloc", "derive"], default-features = false } -serde_json = { version = "1.0.107", default-features = false, features = ["alloc"] } [dev-dependencies] futures = "0.3.21" @@ -60,6 +58,8 @@ sp-consensus = { path = "../../primitives/consensus/common" } substrate-test-runtime-client = { path = "client" } sp-tracing = { path = "../../primitives/tracing" } json-patch = { version = "1.0.0", default-features = false } +serde = { version = "1.0.188", features = ["alloc", "derive"], default-features = false } +serde_json = { version = "1.0.107", default-features = false, features = ["alloc"] } [build-dependencies] substrate-wasm-builder = { path = "../../utils/wasm-builder", optional = true } diff --git a/substrate/test-utils/runtime/src/test_json/README.md b/substrate/test-utils/runtime/res/README.md similarity index 100% rename from substrate/test-utils/runtime/src/test_json/README.md rename to substrate/test-utils/runtime/res/README.md diff --git a/substrate/test-utils/runtime/src/test_json/default_genesis_config.json b/substrate/test-utils/runtime/res/default_genesis_config.json similarity index 98% rename from substrate/test-utils/runtime/src/test_json/default_genesis_config.json rename to substrate/test-utils/runtime/res/default_genesis_config.json index b0218d417daa..95c7799a033d 100644 --- a/substrate/test-utils/runtime/src/test_json/default_genesis_config.json +++ b/substrate/test-utils/runtime/res/default_genesis_config.json @@ -1,7 +1,5 @@ { - "system": { - "code": "0x52" - }, + "system": {}, "babe": { "authorities": [ [ diff --git a/substrate/test-utils/runtime/src/test_json/default_genesis_config_incomplete.json b/substrate/test-utils/runtime/res/default_genesis_config_incomplete.json similarity index 98% rename from substrate/test-utils/runtime/src/test_json/default_genesis_config_incomplete.json rename to substrate/test-utils/runtime/res/default_genesis_config_incomplete.json index e25730ee11cf..510ed87c93c5 100644 --- a/substrate/test-utils/runtime/src/test_json/default_genesis_config_incomplete.json +++ b/substrate/test-utils/runtime/res/default_genesis_config_incomplete.json @@ -1,7 +1,5 @@ { - "system": { - "code": "0x52" - }, + "system": {}, "babe": { "epochConfig": { "c": [ diff --git a/substrate/test-utils/runtime/src/test_json/default_genesis_config_invalid.json b/substrate/test-utils/runtime/res/default_genesis_config_invalid.json similarity index 98% rename from substrate/test-utils/runtime/src/test_json/default_genesis_config_invalid.json rename to substrate/test-utils/runtime/res/default_genesis_config_invalid.json index 00550efaeec9..f8e06f91d665 100644 --- a/substrate/test-utils/runtime/src/test_json/default_genesis_config_invalid.json +++ b/substrate/test-utils/runtime/res/default_genesis_config_invalid.json @@ -1,7 +1,5 @@ { - "system": { - "code": "0x52" - }, + "system": {}, "babe": { "renamed_authorities": [ [ diff --git a/substrate/test-utils/runtime/res/default_genesis_config_invalid_2.json b/substrate/test-utils/runtime/res/default_genesis_config_invalid_2.json new file mode 100644 index 000000000000..a1345542bcda --- /dev/null +++ b/substrate/test-utils/runtime/res/default_genesis_config_invalid_2.json @@ -0,0 +1,113 @@ +{ + "system": {}, + "babex": { + "authorities": [ + [ + "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + 1 + ], + [ + "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", + 1 + ], + [ + "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y", + 1 + ] + ], + "epochConfig": { + "c": [ + 3, + 10 + ], + "allowed_slots": "PrimaryAndSecondaryPlainSlots" + } + }, + "substrateTest": { + "authorities": [ + "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", + "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y" + ] + }, + "balances": { + "balances": [ + [ + "5D34dL5prEUaGNQtPPZ3yN5Y6BnkfXunKXXz6fo7ZJbLwRRH", + 100000000000000000 + ], + [ + "5GBNeWRhZc2jXu7D55rBimKYDk8PGk8itRYFTPfC8RJLKG5o", + 100000000000000000 + ], + [ + "5Dfis6XL8J2P6JHUnUtArnFWndn62SydeP8ee8sG2ky9nfm9", + 100000000000000000 + ], + [ + "5F4H97f7nQovyrbiq4ZetaaviNwThSVcFobcA5aGab6167dK", + 100000000000000000 + ], + [ + "5DiDShBWa1fQx6gLzpf3SFBhMinCoyvHM1BWjPNsmXS8hkrW", + 100000000000000000 + ], + [ + "5EFb84yH9tpcFuiKUcsmdoF7xeeY3ajG1ZLQimxQoFt9HMKR", + 100000000000000000 + ], + [ + "5DZLHESsfGrJ5YzT3HuRPXsSNb589xQ4Unubh1mYLodzKdVY", + 100000000000000000 + ], + [ + "5GHJzqvG6tXnngCpG7B12qjUvbo5e4e9z8Xjidk3CQZHxTPZ", + 100000000000000000 + ], + [ + "5CUnSsgAyLND3bxxnfNhgWXSe9Wn676JzLpGLgyJv858qhoX", + 100000000000000000 + ], + [ + "5CVKn7HAZW1Ky4r7Vkgsr7VEW88C2sHgUNDiwHY9Ct2hjU8q", + 100000000000000000 + ], + [ + "5H673aukQ4PeDe1U2nuv1bi32xDEziimh3PZz7hDdYUB7TNz", + 100000000000000000 + ], + [ + "5HTe9L15LJryjUAt1jZXZCBPnzbbGnpvFwbjE3NwCWaAqovf", + 100000000000000000 + ], + [ + "5D7LFzGpMwHPyDBavkRbWSKWTtJhCaPPZ379wWLT23bJwXJz", + 100000000000000000 + ], + [ + "5CLepMARnEgtVR1EkUuJVUvKh97gzergpSxUU3yKGx1v6EwC", + 100000000000000000 + ], + [ + "5Chb2UhfvZpmjjEziHbFbotM4quX32ZscRV6QJBt1rUKzz51", + 100000000000000000 + ], + [ + "5HmRp3i3ZZk7xsAvbi8hyXVP6whSMnBJGebVC4FsiZVhx52e", + 100000000000000000 + ], + [ + "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + 100000000000000000 + ], + [ + "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", + 100000000000000000 + ], + [ + "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y", + 100000000000000000 + ] + ] + } +} diff --git a/substrate/test-utils/runtime/src/genesismap.rs b/substrate/test-utils/runtime/src/genesismap.rs index 8a4d6dbe4a71..5ed9c8a64588 100644 --- a/substrate/test-utils/runtime/src/genesismap.rs +++ b/substrate/test-utils/runtime/src/genesismap.rs @@ -117,10 +117,7 @@ impl GenesisStorageBuilder { .collect(); RuntimeGenesisConfig { - system: frame_system::GenesisConfig { - code: self.wasm_code.clone().unwrap_or(wasm_binary_unwrap().to_vec()), - ..Default::default() - }, + system: Default::default(), babe: pallet_babe::GenesisConfig { authorities: authorities_sr25519 .clone() @@ -149,6 +146,11 @@ impl GenesisStorageBuilder { storage.top.insert(well_known_keys::HEAP_PAGES.into(), heap_pages.encode()); } + storage.top.insert( + well_known_keys::CODE.into(), + self.wasm_code.clone().unwrap_or(wasm_binary_unwrap().to_vec()), + ); + storage.top.extend(self.extra_storage.top.clone()); storage.children_default.extend(self.extra_storage.children_default.clone()); diff --git a/substrate/test-utils/runtime/src/lib.rs b/substrate/test-utils/runtime/src/lib.rs index 6b963deb5890..1a4e9fd04667 100644 --- a/substrate/test-utils/runtime/src/lib.rs +++ b/substrate/test-utils/runtime/src/lib.rs @@ -464,11 +464,10 @@ impl_opaque_keys! { } } -pub(crate) const TEST_RUNTIME_BABE_EPOCH_CONFIGURATION: BabeEpochConfiguration = - BabeEpochConfiguration { - c: (3, 10), - allowed_slots: AllowedSlots::PrimaryAndSecondaryPlainSlots, - }; +pub const TEST_RUNTIME_BABE_EPOCH_CONFIGURATION: BabeEpochConfiguration = BabeEpochConfiguration { + c: (3, 10), + allowed_slots: AllowedSlots::PrimaryAndSecondaryPlainSlots, +}; impl_runtime_apis! { impl sp_api::Core for Runtime { @@ -1237,7 +1236,7 @@ mod tests { #[test] fn build_minimal_genesis_config_works() { sp_tracing::try_init_simple(); - let default_minimal_json = r#"{"system":{"code":"0x"},"babe":{"authorities":[],"epochConfig":{"c": [ 3, 10 ],"allowed_slots":"PrimaryAndSecondaryPlainSlots"}},"substrateTest":{"authorities":[]},"balances":{"balances":[]}}"#; + let default_minimal_json = r#"{"system":{},"babe":{"authorities":[],"epochConfig":{"c": [ 3, 10 ],"allowed_slots":"PrimaryAndSecondaryPlainSlots"}},"substrateTest":{"authorities":[]},"balances":{"balances":[]}}"#; let mut t = BasicExternalities::new_empty(); executor_call(&mut t, "GenesisBuilder_build_config", &default_minimal_json.encode()) @@ -1264,8 +1263,6 @@ mod tests { // System|LastRuntimeUpgrade "26aa394eea5630e07c48ae0c9558cef7f9cce9c888469bb1a0dceaa129672ef8", - // :code - "3a636f6465", // :extrinsic_index "3a65787472696e7369635f696e646578", // Balances|TotalIssuance @@ -1294,35 +1291,55 @@ mod tests { let r = Vec::::decode(&mut &r[..]).unwrap(); let json = String::from_utf8(r.into()).expect("returned value is json. qed."); - let expected = r#"{"system":{"code":"0x"},"babe":{"authorities":[],"epochConfig":null},"substrateTest":{"authorities":[]},"balances":{"balances":[]}}"#; + let expected = r#"{"system":{},"babe":{"authorities":[],"epochConfig":null},"substrateTest":{"authorities":[]},"balances":{"balances":[]}}"#; assert_eq!(expected.to_string(), json); } #[test] fn build_config_from_json_works() { sp_tracing::try_init_simple(); - let j = include_str!("test_json/default_genesis_config.json"); + let j = include_str!("../res/default_genesis_config.json"); let mut t = BasicExternalities::new_empty(); let r = executor_call(&mut t, "GenesisBuilder_build_config", &j.encode()).unwrap(); let r = BuildResult::decode(&mut &r[..]); assert!(r.is_ok()); - let keys = t.into_storages().top.keys().cloned().map(hex).collect::>(); + let mut keys = t.into_storages().top.keys().cloned().map(hex).collect::>(); + + // following keys are not placed during `::build` + // process, add them `keys` to assert against known keys. + keys.push(hex(b":code")); + keys.sort(); + assert_eq!(keys, storage_key_generator::get_expected_storage_hashed_keys(false)); } #[test] fn build_config_from_invalid_json_fails() { sp_tracing::try_init_simple(); - let j = include_str!("test_json/default_genesis_config_invalid.json"); + let j = include_str!("../res/default_genesis_config_invalid.json"); let mut t = BasicExternalities::new_empty(); let r = executor_call(&mut t, "GenesisBuilder_build_config", &j.encode()).unwrap(); let r = BuildResult::decode(&mut &r[..]).unwrap(); log::info!("result: {:#?}", r); assert_eq!(r, Err( sp_runtime::RuntimeString::Owned( - "Invalid JSON blob: unknown field `renamed_authorities`, expected `authorities` or `epochConfig` at line 6 column 25".to_string(), + "Invalid JSON blob: unknown field `renamed_authorities`, expected `authorities` or `epochConfig` at line 4 column 25".to_string(), + )) + ); + } + + #[test] + fn build_config_from_invalid_json_fails_2() { + sp_tracing::try_init_simple(); + let j = include_str!("../res/default_genesis_config_invalid_2.json"); + let mut t = BasicExternalities::new_empty(); + let r = executor_call(&mut t, "GenesisBuilder_build_config", &j.encode()).unwrap(); + let r = BuildResult::decode(&mut &r[..]).unwrap(); + assert_eq!(r, Err( + sp_runtime::RuntimeString::Owned( + "Invalid JSON blob: unknown field `babex`, expected one of `system`, `babe`, `substrateTest`, `balances` at line 3 column 9".to_string(), )) ); } @@ -1330,7 +1347,7 @@ mod tests { #[test] fn build_config_from_incomplete_json_fails() { sp_tracing::try_init_simple(); - let j = include_str!("test_json/default_genesis_config_incomplete.json"); + let j = include_str!("../res/default_genesis_config_incomplete.json"); let mut t = BasicExternalities::new_empty(); let r = executor_call(&mut t, "GenesisBuilder_build_config", &j.encode()).unwrap(); @@ -1339,7 +1356,7 @@ mod tests { assert_eq!( r, Err(sp_runtime::RuntimeString::Owned( - "Invalid JSON blob: missing field `authorities` at line 13 column 3" + "Invalid JSON blob: missing field `authorities` at line 11 column 3" .to_string() )) ); @@ -1438,10 +1455,6 @@ mod tests { ); assert_eq!(u64::decode(&mut &value[..]).unwrap(), 0); - // :code - let value: Vec = get_from_storage("3a636f6465"); - assert!(Vec::::decode(&mut &value[..]).is_err()); - //System|ParentHash let value: Vec = get_from_storage( "26aa394eea5630e07c48ae0c9558cef78a42f33323cb5ced3b44dd825fda9fcc", From 15beef20fe96a6b5480b659a23d9ddcf82c6f8ba Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Sun, 5 Nov 2023 22:47:18 +0100 Subject: [PATCH 18/31] Fix update-ui-tests.sh (#2161) Related https://github.com/paritytech/polkadot-sdk/issues/2013 Signed-off-by: Oliver Tale-Yazdi --- scripts/update-ui-tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/update-ui-tests.sh b/scripts/update-ui-tests.sh index 785cc7bd3291..02c1eec8c4b9 100755 --- a/scripts/update-ui-tests.sh +++ b/scripts/update-ui-tests.sh @@ -34,7 +34,7 @@ export SKIP_WASM_BUILD=1 export TRYBUILD=overwrite # ./substrate -$RUSTUP_RUN cargo test -p sp-runtime-interface ui +$RUSTUP_RUN cargo test --manifest-path substrate/primitives/runtime-interface/Cargo.toml ui $RUSTUP_RUN cargo test -p sp-api-test ui $RUSTUP_RUN cargo test -p frame-election-provider-solution-type ui $RUSTUP_RUN cargo test -p frame-support-test ui From f6ee4781f633f0f89598f7b230595afe401da8dc Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Sun, 5 Nov 2023 23:35:40 +0100 Subject: [PATCH 19/31] [CI] Update deps (#2159) Otherwise the return code is not correctly propagated (ref https://github.com/ggwpez/zepter/pull/48). --------- Signed-off-by: Oliver Tale-Yazdi --- .gitlab/pipeline/check.yml | 2 +- .../parachains/runtimes/assets/asset-hub-rococo/Cargo.toml | 7 ++++++- .../runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml | 5 +++++ .../parachains/runtimes/glutton/glutton-kusama/Cargo.toml | 1 + substrate/frame/bags-list/Cargo.toml | 2 +- 5 files changed, 14 insertions(+), 3 deletions(-) diff --git a/.gitlab/pipeline/check.yml b/.gitlab/pipeline/check.yml index ce3a61557920..cbb3baf277cd 100644 --- a/.gitlab/pipeline/check.yml +++ b/.gitlab/pipeline/check.yml @@ -87,7 +87,7 @@ check-rust-feature-propagation: - .kubernetes-env - .common-refs script: - - cargo install --locked --version 0.13.2 -q -f zepter && zepter --version + - cargo install --locked --version 0.13.3 -q -f zepter && zepter --version - zepter run check # More info can be found here: https://github.com/paritytech/polkadot/pull/5865 diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml b/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml index 0b163ae1a207..f4f33677d4a2 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml @@ -109,9 +109,11 @@ default = [ "std" ] state-trie-version-1 = [ "pallet-state-trie-migration" ] runtime-benchmarks = [ "assets-common/runtime-benchmarks", + "cumulus-pallet-dmp-queue/runtime-benchmarks", "cumulus-pallet-parachain-system/runtime-benchmarks", "cumulus-pallet-session-benchmarking/runtime-benchmarks", "cumulus-pallet-xcmp-queue/runtime-benchmarks", + "cumulus-primitives-core/runtime-benchmarks", "cumulus-primitives-utility/runtime-benchmarks", "frame-benchmarking/runtime-benchmarks", "frame-support/runtime-benchmarks", @@ -121,6 +123,7 @@ runtime-benchmarks = [ "pallet-assets/runtime-benchmarks", "pallet-balances/runtime-benchmarks", "pallet-collator-selection/runtime-benchmarks", + "pallet-message-queue/runtime-benchmarks", "pallet-multisig/runtime-benchmarks", "pallet-nft-fractionalization/runtime-benchmarks", "pallet-nfts/runtime-benchmarks", @@ -156,6 +159,7 @@ try-runtime = [ "pallet-authorship/try-runtime", "pallet-balances/try-runtime", "pallet-collator-selection/try-runtime", + "pallet-message-queue/try-runtime", "pallet-multisig/try-runtime", "pallet-nft-fractionalization/try-runtime", "pallet-nfts/try-runtime", @@ -178,8 +182,8 @@ std = [ "bp-asset-hub-westend/std", "bp-asset-hub-wococo/std", "bp-bridge-hub-rococo/std", - "bp-bridge-hub-wococo/std", "bp-bridge-hub-westend/std", + "bp-bridge-hub-wococo/std", "codec/std", "cumulus-pallet-aura-ext/std", "cumulus-pallet-dmp-queue/std", @@ -204,6 +208,7 @@ std = [ "pallet-authorship/std", "pallet-balances/std", "pallet-collator-selection/std", + "pallet-message-queue/std", "pallet-multisig/std", "pallet-nft-fractionalization/std", "pallet-nfts-runtime-api/std", diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml index c4461a0d4c38..7e384126ab67 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml @@ -138,6 +138,7 @@ std = [ "pallet-bridge-parachains/std", "pallet-bridge-relayers/std", "pallet-collator-selection/std", + "pallet-message-queue/std", "pallet-multisig/std", "pallet-session/std", "pallet-timestamp/std", @@ -176,9 +177,11 @@ std = [ runtime-benchmarks = [ "bridge-runtime-common/runtime-benchmarks", + "cumulus-pallet-dmp-queue/runtime-benchmarks", "cumulus-pallet-parachain-system/runtime-benchmarks", "cumulus-pallet-session-benchmarking/runtime-benchmarks", "cumulus-pallet-xcmp-queue/runtime-benchmarks", + "cumulus-primitives-core/runtime-benchmarks", "cumulus-primitives-utility/runtime-benchmarks", "frame-benchmarking/runtime-benchmarks", "frame-support/runtime-benchmarks", @@ -190,6 +193,7 @@ runtime-benchmarks = [ "pallet-bridge-parachains/runtime-benchmarks", "pallet-bridge-relayers/runtime-benchmarks", "pallet-collator-selection/runtime-benchmarks", + "pallet-message-queue/runtime-benchmarks", "pallet-multisig/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", "pallet-utility/runtime-benchmarks", @@ -221,6 +225,7 @@ try-runtime = [ "pallet-bridge-parachains/try-runtime", "pallet-bridge-relayers/try-runtime", "pallet-collator-selection/try-runtime", + "pallet-message-queue/try-runtime", "pallet-multisig/try-runtime", "pallet-session/try-runtime", "pallet-timestamp/try-runtime", diff --git a/cumulus/parachains/runtimes/glutton/glutton-kusama/Cargo.toml b/cumulus/parachains/runtimes/glutton/glutton-kusama/Cargo.toml index efae74049a79..7f5feb1c880f 100644 --- a/cumulus/parachains/runtimes/glutton/glutton-kusama/Cargo.toml +++ b/cumulus/parachains/runtimes/glutton/glutton-kusama/Cargo.toml @@ -95,6 +95,7 @@ std = [ "pallet-timestamp/std", "parachain-info/std", "parachains-common/std", + "scale-info/std", "sp-api/std", "sp-block-builder/std", "sp-consensus-aura/std", diff --git a/substrate/frame/bags-list/Cargo.toml b/substrate/frame/bags-list/Cargo.toml index b99726ebf2dd..cb07ef94ff59 100644 --- a/substrate/frame/bags-list/Cargo.toml +++ b/substrate/frame/bags-list/Cargo.toml @@ -50,7 +50,7 @@ frame-election-provider-support = { path = "../election-provider-support" } frame-benchmarking = { path = "../benchmarking" } [features] -default = ["std"] +default = [ "std" ] std = [ "codec/std", "frame-benchmarking?/std", From 7b06e634fe749508b639e50cccbeb58ce794323c Mon Sep 17 00:00:00 2001 From: Dmitry Markin Date: Mon, 6 Nov 2023 12:42:48 +0200 Subject: [PATCH 20/31] Get rid of `NetworkService` in `ChainSync` (#2143) Move peer banning from `ChainSync` to `SyncingEngine`. --- substrate/bin/minimal/runtime/Cargo.toml | 2 + .../client/network/sync/src/chain_sync.rs | 324 +++++++++--------- .../network/sync/src/chain_sync/test.rs | 111 +----- substrate/client/network/sync/src/engine.rs | 38 +- 4 files changed, 197 insertions(+), 278 deletions(-) diff --git a/substrate/bin/minimal/runtime/Cargo.toml b/substrate/bin/minimal/runtime/Cargo.toml index 08feb445879f..b75816a5ea3b 100644 --- a/substrate/bin/minimal/runtime/Cargo.toml +++ b/substrate/bin/minimal/runtime/Cargo.toml @@ -28,6 +28,7 @@ substrate-wasm-builder = { path = "../../../utils/wasm-builder", optional = true [features] default = [ "std" ] std = [ + "frame-support/std", "frame/std", "pallet-balances/std", "pallet-sudo/std", @@ -36,5 +37,6 @@ std = [ "pallet-transaction-payment/std", "parity-scale-codec/std", "scale-info/std", + "sp-genesis-builder/std", "substrate-wasm-builder", ] diff --git a/substrate/client/network/sync/src/chain_sync.rs b/substrate/client/network/sync/src/chain_sync.rs index 5721bd59a847..858125f93f1f 100644 --- a/substrate/client/network/sync/src/chain_sync.rs +++ b/substrate/client/network/sync/src/chain_sync.rs @@ -32,7 +32,6 @@ use crate::{ blocks::BlockCollection, extra_requests::ExtraRequests, schema::v1::StateResponse, - service::network::NetworkServiceHandle, state::{ImportResult, StateSync}, types::{ BadPeer, Metrics, OpaqueStateRequest, OpaqueStateResponse, PeerInfo, SyncMode, SyncState, @@ -50,7 +49,6 @@ use log::{debug, error, info, trace, warn}; use sc_client_api::{BlockBackend, ProofProvider}; use sc_consensus::{BlockImportError, BlockImportStatus, IncomingBlock}; -use sc_network::types::ProtocolName; use sc_network_common::sync::message::{ BlockAnnounce, BlockAttributes, BlockData, BlockRequest, BlockResponse, Direction, FromBlock, }; @@ -212,7 +210,7 @@ pub struct ImportJustificationsAction { /// Result of [`ChainSync::on_block_data`]. #[derive(Debug, Clone, PartialEq, Eq)] -pub enum OnBlockData { +enum OnBlockData { /// The block should be imported. Import(ImportBlocksAction), /// A new block request needs to be made to the given peer. @@ -223,7 +221,7 @@ pub enum OnBlockData { /// Result of [`ChainSync::on_block_justification`]. #[derive(Debug, Clone, PartialEq, Eq)] -pub enum OnBlockJustification { +enum OnBlockJustification { /// The justification needs no further handling. Nothing, /// The justification should be imported. @@ -237,7 +235,7 @@ pub enum OnBlockJustification { // Result of [`ChainSync::on_state_data`]. #[derive(Debug)] -pub enum OnStateData { +enum OnStateData { /// The block and state that should be imported. Import(BlockOrigin, IncomingBlock), /// A new state request needs to be made to the given peer. @@ -247,7 +245,7 @@ pub enum OnStateData { /// Action that the parent of [`ChainSync`] should perform after reporting block response with /// [`ChainSync::on_block_response`]. pub enum OnBlockResponse { - /// Nothing to do + /// Nothing to do. Nothing, /// Perform block request. SendBlockRequest { peer_id: PeerId, request: BlockRequest }, @@ -255,6 +253,19 @@ pub enum OnBlockResponse { ImportBlocks(ImportBlocksAction), /// Import justifications. ImportJustifications(ImportJustificationsAction), + /// Invalid block response, the peer should be disconnected and reported. + DisconnectPeer(BadPeer), +} + +/// Action that the parent of [`ChainSync`] should perform after reporting state response with +/// [`ChainSync::on_state_response`]. +pub enum OnStateResponse { + /// Nothing to do. + Nothing, + /// Import blocks. + ImportBlocks(ImportBlocksAction), + /// Invalid state response, the peer should be disconnected and reported. + DisconnectPeer(BadPeer), } /// The main data structure which contains all the state for a chains @@ -302,10 +313,6 @@ pub struct ChainSync { import_existing: bool, /// Gap download process. gap_sync: Option>, - /// Handle for communicating with `NetworkService` - network_service: NetworkServiceHandle, - /// Protocol name used for block announcements - block_announce_protocol_name: ProtocolName, } /// All the data we have about a Peer that we are trying to sync with @@ -396,11 +403,9 @@ where pub fn new( mode: SyncMode, client: Arc, - block_announce_protocol_name: ProtocolName, max_parallel_downloads: u32, max_blocks_per_request: u32, warp_sync_config: Option>, - network_service: NetworkServiceHandle, ) -> Result { let mut sync = Self { client, @@ -420,10 +425,8 @@ where warp_sync: None, import_existing: false, gap_sync: None, - network_service, warp_sync_config, warp_sync_target_block_header: None, - block_announce_protocol_name, }; sync.reset_sync_start_point()?; @@ -431,9 +434,9 @@ where } /// Get peer's best hash & number. - pub fn peer_info(&self, who: &PeerId) -> Option> { + pub fn peer_info(&self, peer_id: &PeerId) -> Option> { self.peers - .get(who) + .get(peer_id) .map(|p| PeerInfo { best_hash: p.best_hash, best_number: p.best_number }) } @@ -509,7 +512,7 @@ where #[must_use] pub fn new_peer( &mut self, - who: PeerId, + peer_id: PeerId, best_hash: B::Hash, best_number: NumberFor, ) -> Result>, BadPeer> { @@ -517,19 +520,21 @@ where match self.block_status(&best_hash) { Err(e) => { debug!(target:LOG_TARGET, "Error reading blockchain: {e}"); - Err(BadPeer(who, rep::BLOCKCHAIN_READ_ERROR)) + Err(BadPeer(peer_id, rep::BLOCKCHAIN_READ_ERROR)) }, Ok(BlockStatus::KnownBad) => { - info!("πŸ’” New peer {who} with known bad best block {best_hash} ({best_number})."); - Err(BadPeer(who, rep::BAD_BLOCK)) + info!( + "πŸ’” New peer {peer_id} with known bad best block {best_hash} ({best_number})." + ); + Err(BadPeer(peer_id, rep::BAD_BLOCK)) }, Ok(BlockStatus::Unknown) => { if best_number.is_zero() { info!( "πŸ’” New peer {} with unknown genesis hash {} ({}).", - who, best_hash, best_number, + peer_id, best_hash, best_number, ); - return Err(BadPeer(who, rep::GENESIS_MISMATCH)) + return Err(BadPeer(peer_id, rep::GENESIS_MISMATCH)) } // If there are more than `MAJOR_SYNC_BLOCKS` in the import queue then we have @@ -539,14 +544,14 @@ where debug!( target:LOG_TARGET, "New peer {} with unknown best hash {} ({}), assuming common block.", - who, + peer_id, self.best_queued_hash, self.best_queued_number ); self.peers.insert( - who, + peer_id, PeerSync { - peer_id: who, + peer_id, common_number: self.best_queued_number, best_hash, best_number, @@ -560,7 +565,7 @@ where let (state, req) = if self.best_queued_number.is_zero() { debug!( target:LOG_TARGET, - "New peer {who} with best hash {best_hash} ({best_number}).", + "New peer {peer_id} with best hash {best_hash} ({best_number}).", ); (PeerSyncState::Available, None) @@ -570,7 +575,7 @@ where debug!( target:LOG_TARGET, "New peer {} with unknown best hash {} ({}), searching for common ancestor.", - who, + peer_id, best_hash, best_number ); @@ -585,11 +590,11 @@ where ) }; - self.allowed_requests.add(&who); + self.allowed_requests.add(&peer_id); self.peers.insert( - who, + peer_id, PeerSync { - peer_id: who, + peer_id, common_number: Zero::zero(), best_hash, best_number, @@ -618,19 +623,19 @@ where Ok(BlockStatus::InChainPruned) => { debug!( target: LOG_TARGET, - "New peer {who} with known best hash {best_hash} ({best_number}).", + "New peer {peer_id} with known best hash {best_hash} ({best_number}).", ); self.peers.insert( - who, + peer_id, PeerSync { - peer_id: who, + peer_id, common_number: std::cmp::min(self.best_queued_number, best_number), best_hash, best_number, state: PeerSyncState::Available, }, ); - self.allowed_requests.add(&who); + self.allowed_requests.add(&peer_id); Ok(None) }, } @@ -717,41 +722,41 @@ where /// Submit a block response for processing. #[must_use] - pub fn on_block_data( + fn on_block_data( &mut self, - who: &PeerId, + peer_id: &PeerId, request: Option>, response: BlockResponse, ) -> Result, BadPeer> { self.downloaded_blocks += response.blocks.len(); let mut gap = false; - let new_blocks: Vec> = if let Some(peer) = self.peers.get_mut(who) { + let new_blocks: Vec> = if let Some(peer) = self.peers.get_mut(peer_id) { let mut blocks = response.blocks; if request.as_ref().map_or(false, |r| r.direction == Direction::Descending) { trace!(target: LOG_TARGET, "Reversing incoming block list"); blocks.reverse() } - self.allowed_requests.add(who); + self.allowed_requests.add(peer_id); if let Some(request) = request { match &mut peer.state { PeerSyncState::DownloadingNew(_) => { - self.blocks.clear_peer_download(who); + self.blocks.clear_peer_download(peer_id); peer.state = PeerSyncState::Available; if let Some(start_block) = - validate_blocks::(&blocks, who, Some(request))? + validate_blocks::(&blocks, peer_id, Some(request))? { - self.blocks.insert(start_block, blocks, *who); + self.blocks.insert(start_block, blocks, *peer_id); } self.ready_blocks() }, PeerSyncState::DownloadingGap(_) => { peer.state = PeerSyncState::Available; if let Some(gap_sync) = &mut self.gap_sync { - gap_sync.blocks.clear_peer_download(who); + gap_sync.blocks.clear_peer_download(peer_id); if let Some(start_block) = - validate_blocks::(&blocks, who, Some(request))? + validate_blocks::(&blocks, peer_id, Some(request))? { - gap_sync.blocks.insert(start_block, blocks, *who); + gap_sync.blocks.insert(start_block, blocks, *peer_id); } gap = true; let blocks: Vec<_> = gap_sync @@ -787,17 +792,17 @@ where ); blocks } else { - debug!(target: LOG_TARGET, "Unexpected gap block response from {who}"); - return Err(BadPeer(*who, rep::NO_BLOCK)) + debug!(target: LOG_TARGET, "Unexpected gap block response from {peer_id}"); + return Err(BadPeer(*peer_id, rep::NO_BLOCK)) } }, PeerSyncState::DownloadingStale(_) => { peer.state = PeerSyncState::Available; if blocks.is_empty() { - debug!(target: LOG_TARGET, "Empty block response from {who}"); - return Err(BadPeer(*who, rep::NO_BLOCK)) + debug!(target: LOG_TARGET, "Empty block response from {peer_id}"); + return Err(BadPeer(*peer_id, rep::NO_BLOCK)) } - validate_blocks::(&blocks, who, Some(request))?; + validate_blocks::(&blocks, peer_id, Some(request))?; blocks .into_iter() .map(|b| { @@ -810,7 +815,7 @@ where body: b.body, indexed_body: None, justifications, - origin: Some(*who), + origin: Some(*peer_id), allow_missing_state: true, import_existing: self.import_existing, skip_execution: self.skip_execution(), @@ -827,23 +832,23 @@ where "Got ancestry block #{} ({}) from peer {}", current, block.hash, - who, + peer_id, ); maybe_our_block_hash.filter(|x| x == &block.hash) }, (None, _) => { debug!( target: LOG_TARGET, - "Invalid response when searching for ancestor from {who}", + "Invalid response when searching for ancestor from {peer_id}", ); - return Err(BadPeer(*who, rep::UNKNOWN_ANCESTOR)) + return Err(BadPeer(*peer_id, rep::UNKNOWN_ANCESTOR)) }, (_, Err(e)) => { info!( target: LOG_TARGET, "❌ Error answering legitimate blockchain query: {e}", ); - return Err(BadPeer(*who, rep::BLOCKCHAIN_READ_ERROR)) + return Err(BadPeer(*peer_id, rep::BLOCKCHAIN_READ_ERROR)) }, }; if matching_hash.is_some() { @@ -856,7 +861,7 @@ where trace!( target: LOG_TARGET, "Ancestry search: opportunistically updating peer {} common number from={} => to={}.", - *who, + *peer_id, peer.common_number, self.best_queued_number, ); @@ -865,7 +870,7 @@ where trace!( target: LOG_TARGET, "Ancestry search: updating peer {} common number from={} => to={}.", - *who, + *peer_id, peer.common_number, *current, ); @@ -875,9 +880,9 @@ where if matching_hash.is_none() && current.is_zero() { trace!( target:LOG_TARGET, - "Ancestry search: genesis mismatch for peer {who}", + "Ancestry search: genesis mismatch for peer {peer_id}", ); - return Err(BadPeer(*who, rep::GENESIS_MISMATCH)) + return Err(BadPeer(*peer_id, rep::GENESIS_MISMATCH)) } if let Some((next_state, next_num)) = handle_ancestor_search_state(state, *current, matching_hash.is_some()) @@ -887,7 +892,10 @@ where start: *start, state: next_state, }; - return Ok(OnBlockData::Request(*who, ancestry_request::(next_num))) + return Ok(OnBlockData::Request( + *peer_id, + ancestry_request::(next_num), + )) } else { // Ancestry search is complete. Check if peer is on a stale fork unknown // to us and add it to sync targets if necessary. @@ -908,7 +916,7 @@ where target: LOG_TARGET, "Added fork target {} for {}", peer.best_hash, - who, + peer_id, ); self.fork_targets .entry(peer.best_hash) @@ -918,7 +926,7 @@ where peers: Default::default(), }) .peers - .insert(*who); + .insert(*peer_id); } peer.state = PeerSyncState::Available; Vec::new() @@ -928,32 +936,32 @@ where peer.state = PeerSyncState::Available; if let Some(warp_sync) = &mut self.warp_sync { if blocks.len() == 1 { - validate_blocks::(&blocks, who, Some(request))?; + validate_blocks::(&blocks, peer_id, Some(request))?; match warp_sync.import_target_block( blocks.pop().expect("`blocks` len checked above."), ) { warp::TargetBlockImportResult::Success => return Ok(OnBlockData::Continue), warp::TargetBlockImportResult::BadResponse => - return Err(BadPeer(*who, rep::VERIFICATION_FAIL)), + return Err(BadPeer(*peer_id, rep::VERIFICATION_FAIL)), } } else if blocks.is_empty() { - debug!(target: LOG_TARGET, "Empty block response from {who}"); - return Err(BadPeer(*who, rep::NO_BLOCK)) + debug!(target: LOG_TARGET, "Empty block response from {peer_id}"); + return Err(BadPeer(*peer_id, rep::NO_BLOCK)) } else { debug!( target: LOG_TARGET, "Too many blocks ({}) in warp target block response from {}", blocks.len(), - who, + peer_id, ); - return Err(BadPeer(*who, rep::NOT_REQUESTED)) + return Err(BadPeer(*peer_id, rep::NOT_REQUESTED)) } } else { debug!( target: LOG_TARGET, "Logic error: we think we are downloading warp target block from {}, but no warp sync is happening.", - who, + peer_id, ); return Ok(OnBlockData::Continue) } @@ -965,7 +973,7 @@ where } } else { // When request.is_none() this is a block announcement. Just accept blocks. - validate_blocks::(&blocks, who, None)?; + validate_blocks::(&blocks, peer_id, None)?; blocks .into_iter() .map(|b| { @@ -978,7 +986,7 @@ where body: b.body, indexed_body: None, justifications, - origin: Some(*who), + origin: Some(*peer_id), allow_missing_state: true, import_existing: false, skip_execution: true, @@ -989,7 +997,7 @@ where } } else { // We don't know of this peer, so we also did not request anything from it. - return Err(BadPeer(*who, rep::NOT_REQUESTED)) + return Err(BadPeer(*peer_id, rep::NOT_REQUESTED)) }; Ok(OnBlockData::Import(self.validate_and_queue_blocks(new_blocks, gap))) @@ -997,12 +1005,12 @@ where /// Submit a justification response for processing. #[must_use] - pub fn on_block_justification( + fn on_block_justification( &mut self, - who: PeerId, + peer_id: PeerId, response: BlockResponse, ) -> Result, BadPeer> { - let peer = if let Some(peer) = self.peers.get_mut(&who) { + let peer = if let Some(peer) = self.peers.get_mut(&peer_id) { peer } else { error!( @@ -1012,7 +1020,7 @@ where return Ok(OnBlockJustification::Nothing) }; - self.allowed_requests.add(&who); + self.allowed_requests.add(&peer_id); if let PeerSyncState::DownloadingJustification(hash) = peer.state { peer.state = PeerSyncState::Available; @@ -1022,11 +1030,11 @@ where warn!( target: LOG_TARGET, "πŸ’” Invalid block justification provided by {}: requested: {:?} got: {:?}", - who, + peer_id, hash, block.hash, ); - return Err(BadPeer(who, rep::BAD_JUSTIFICATION)) + return Err(BadPeer(peer_id, rep::BAD_JUSTIFICATION)) } block @@ -1037,14 +1045,14 @@ where // had but didn't (regardless of whether it had a justification for it or not). trace!( target: LOG_TARGET, - "Peer {who:?} provided empty response for justification request {hash:?}", + "Peer {peer_id:?} provided empty response for justification request {hash:?}", ); None }; if let Some((peer_id, hash, number, justifications)) = - self.extra_justifications.on_response(who, justification) + self.extra_justifications.on_response(peer_id, justification) { return Ok(OnBlockJustification::Import { peer_id, hash, number, justifications }) } @@ -1105,7 +1113,7 @@ where pub fn on_validated_block_announce( &mut self, is_best: bool, - who: PeerId, + peer_id: PeerId, announce: &BlockAnnounce, ) { let number = *announce.header.number(); @@ -1116,7 +1124,7 @@ where let ancient_parent = parent_status == BlockStatus::InChainPruned; let known = self.is_known(&hash); - let peer = if let Some(peer) = self.peers.get_mut(&who) { + let peer = if let Some(peer) = self.peers.get_mut(&peer_id) { peer } else { error!(target: LOG_TARGET, "πŸ’” Called `on_validated_block_announce` with a bad peer ID"); @@ -1124,7 +1132,7 @@ where }; if let PeerSyncState::AncestorSearch { .. } = peer.state { - trace!(target: LOG_TARGET, "Peer {} is in the ancestor search state.", who); + trace!(target: LOG_TARGET, "Peer {} is in the ancestor search state.", peer_id); return } @@ -1138,20 +1146,20 @@ where // is either one further ahead or it's the one they just announced, if we know about it. if is_best { if known && self.best_queued_number >= number { - self.update_peer_common_number(&who, number); + self.update_peer_common_number(&peer_id, number); } else if announce.header.parent_hash() == &self.best_queued_hash || known_parent && self.best_queued_number >= number { - self.update_peer_common_number(&who, number.saturating_sub(One::one())); + self.update_peer_common_number(&peer_id, number.saturating_sub(One::one())); } } - self.allowed_requests.add(&who); + self.allowed_requests.add(&peer_id); // known block case if known || self.is_already_downloading(&hash) { - trace!(target: "sync", "Known block announce from {}: {}", who, hash); + trace!(target: "sync", "Known block announce from {}: {}", peer_id, hash); if let Some(target) = self.fork_targets.get_mut(&hash) { - target.peers.insert(who); + target.peers.insert(peer_id); } return } @@ -1160,7 +1168,7 @@ where trace!( target: "sync", "Ignored ancient block announced from {}: {} {:?}", - who, + peer_id, hash, announce.header, ); @@ -1171,7 +1179,7 @@ where trace!( target: "sync", "Added sync target for block announced from {}: {} {:?}", - who, + peer_id, hash, announce.summary(), ); @@ -1183,22 +1191,22 @@ where peers: Default::default(), }) .peers - .insert(who); + .insert(peer_id); } } /// Notify that a sync peer has disconnected. #[must_use] - pub fn peer_disconnected(&mut self, who: &PeerId) -> Option> { - self.blocks.clear_peer_download(who); + pub fn peer_disconnected(&mut self, peer_id: &PeerId) -> Option> { + self.blocks.clear_peer_download(peer_id); if let Some(gap_sync) = &mut self.gap_sync { - gap_sync.blocks.clear_peer_download(who) + gap_sync.blocks.clear_peer_download(peer_id) } - self.peers.remove(who); - self.extra_justifications.peer_disconnected(who); + self.peers.remove(peer_id); + self.extra_justifications.peer_disconnected(peer_id); self.allowed_requests.set_all(); self.fork_targets.retain(|_, target| { - target.peers.remove(who); + target.peers.remove(peer_id); !target.peers.is_empty() }); @@ -1565,12 +1573,7 @@ where number, justifications, }), - Err(BadPeer(id, repu)) => { - self.network_service - .disconnect_peer(id, self.block_announce_protocol_name.clone()); - self.network_service.report_peer(id, repu); - OnBlockResponse::Nothing - }, + Err(bad_peer) => OnBlockResponse::DisconnectPeer(bad_peer), } } else { match self.on_block_data(&peer_id, Some(request), block_response) { @@ -1578,12 +1581,7 @@ where Ok(OnBlockData::Request(peer_id, request)) => OnBlockResponse::SendBlockRequest { peer_id, request }, Ok(OnBlockData::Continue) => OnBlockResponse::Nothing, - Err(BadPeer(id, repu)) => { - self.network_service - .disconnect_peer(id, self.block_announce_protocol_name.clone()); - self.network_service.report_peer(id, repu); - OnBlockResponse::Nothing - }, + Err(bad_peer) => OnBlockResponse::DisconnectPeer(bad_peer), } } } @@ -1594,26 +1592,12 @@ where &mut self, peer_id: PeerId, response: OpaqueStateResponse, - ) -> Option> { + ) -> OnStateResponse { match self.on_state_data(&peer_id, response) { Ok(OnStateData::Import(origin, block)) => - Some(ImportBlocksAction { origin, blocks: vec![block] }), - Ok(OnStateData::Continue) => None, - Err(BadPeer(id, repu)) => { - self.network_service - .disconnect_peer(id, self.block_announce_protocol_name.clone()); - self.network_service.report_peer(id, repu); - None - }, - } - } - - /// Submit a warp proof response received. - pub fn on_warp_sync_response(&mut self, peer_id: PeerId, response: EncodedProof) { - if let Err(BadPeer(id, repu)) = self.on_warp_sync_data(&peer_id, response) { - self.network_service - .disconnect_peer(id, self.block_announce_protocol_name.clone()); - self.network_service.report_peer(id, repu); + OnStateResponse::ImportBlocks(ImportBlocksAction { origin, blocks: vec![block] }), + Ok(OnStateData::Continue) => OnStateResponse::Nothing, + Err(bad_peer) => OnStateResponse::DisconnectPeer(bad_peer), } } @@ -1851,7 +1835,7 @@ where fn on_state_data( &mut self, - who: &PeerId, + peer_id: &PeerId, response: OpaqueStateResponse, ) -> Result, BadPeer> { let response: Box = response.0.downcast().map_err(|_error| { @@ -1860,10 +1844,10 @@ where "Failed to downcast opaque state response, this is an implementation bug." ); - BadPeer(*who, rep::BAD_RESPONSE) + BadPeer(*peer_id, rep::BAD_RESPONSE) })?; - if let Some(peer) = self.peers.get_mut(who) { + if let Some(peer) = self.peers.get_mut(peer_id) { if let PeerSyncState::DownloadingState = peer.state { peer.state = PeerSyncState::Available; self.allowed_requests.set_all(); @@ -1873,7 +1857,7 @@ where debug!( target: LOG_TARGET, "Importing state data from {} with {} keys, {} proof nodes.", - who, + peer_id, response.entries.len(), response.proof.len(), ); @@ -1882,14 +1866,14 @@ where debug!( target: LOG_TARGET, "Importing state data from {} with {} keys, {} proof nodes.", - who, + peer_id, response.entries.len(), response.proof.len(), ); sync.import_state(*response) } else { - debug!(target: LOG_TARGET, "Ignored obsolete state response from {who}"); - return Err(BadPeer(*who, rep::NOT_REQUESTED)) + debug!(target: LOG_TARGET, "Ignored obsolete state response from {peer_id}"); + return Err(BadPeer(*peer_id, rep::NOT_REQUESTED)) }; match import_result { @@ -1912,14 +1896,20 @@ where }, ImportResult::Continue => Ok(OnStateData::Continue), ImportResult::BadResponse => { - debug!(target: LOG_TARGET, "Bad state data received from {who}"); - Err(BadPeer(*who, rep::BAD_BLOCK)) + debug!(target: LOG_TARGET, "Bad state data received from {peer_id}"); + Err(BadPeer(*peer_id, rep::BAD_BLOCK)) }, } } - fn on_warp_sync_data(&mut self, who: &PeerId, response: EncodedProof) -> Result<(), BadPeer> { - if let Some(peer) = self.peers.get_mut(who) { + /// Submit a warp proof response received. + #[must_use] + pub fn on_warp_sync_response( + &mut self, + peer_id: &PeerId, + response: EncodedProof, + ) -> Result<(), BadPeer> { + if let Some(peer) = self.peers.get_mut(peer_id) { if let PeerSyncState::DownloadingWarpProof = peer.state { peer.state = PeerSyncState::Available; self.allowed_requests.set_all(); @@ -1929,20 +1919,20 @@ where debug!( target: LOG_TARGET, "Importing warp proof data from {}, {} bytes.", - who, + peer_id, response.0.len(), ); sync.import_warp_proof(response) } else { - debug!(target: LOG_TARGET, "Ignored obsolete warp sync response from {who}"); - return Err(BadPeer(*who, rep::NOT_REQUESTED)) + debug!(target: LOG_TARGET, "Ignored obsolete warp sync response from {peer_id}"); + return Err(BadPeer(*peer_id, rep::NOT_REQUESTED)) }; match import_result { WarpProofImportResult::Success => Ok(()), WarpProofImportResult::BadResponse => { - debug!(target: LOG_TARGET, "Bad proof data received from {who}"); - Err(BadPeer(*who, rep::BAD_BLOCK)) + debug!(target: LOG_TARGET, "Bad proof data received from {peer_id}"); + Err(BadPeer(*peer_id, rep::BAD_BLOCK)) }, } } @@ -1979,11 +1969,11 @@ where has_error |= result.is_err(); match result { - Ok(BlockImportStatus::ImportedKnown(number, who)) => - if let Some(peer) = who { + Ok(BlockImportStatus::ImportedKnown(number, peer_id)) => + if let Some(peer) = peer_id { self.update_peer_common_number(&peer, number); }, - Ok(BlockImportStatus::ImportedUnknown(number, aux, who)) => { + Ok(BlockImportStatus::ImportedUnknown(number, aux, peer_id)) => { if aux.clear_justification_requests { trace!( target: LOG_TARGET, @@ -2001,13 +1991,13 @@ where } if aux.bad_justification { - if let Some(ref peer) = who { + if let Some(ref peer) = peer_id { warn!("πŸ’” Sent block with bad justification to import"); output.push(Err(BadPeer(*peer, rep::BAD_JUSTIFICATION))); } } - if let Some(peer) = who { + if let Some(peer) = peer_id { self.update_peer_common_number(&peer, number); } let state_sync_complete = @@ -2046,8 +2036,8 @@ where self.gap_sync = None; } }, - Err(BlockImportError::IncompleteHeader(who)) => - if let Some(peer) = who { + Err(BlockImportError::IncompleteHeader(peer_id)) => + if let Some(peer) = peer_id { warn!( target: LOG_TARGET, "πŸ’” Peer sent block with incomplete header to import", @@ -2055,23 +2045,23 @@ where output.push(Err(BadPeer(peer, rep::INCOMPLETE_HEADER))); output.extend(self.restart()); }, - Err(BlockImportError::VerificationFailed(who, e)) => { - let extra_message = - who.map_or_else(|| "".into(), |peer| format!(" received from ({peer})")); + Err(BlockImportError::VerificationFailed(peer_id, e)) => { + let extra_message = peer_id + .map_or_else(|| "".into(), |peer| format!(" received from ({peer})")); warn!( target: LOG_TARGET, "πŸ’” Verification failed for block {hash:?}{extra_message}: {e:?}", ); - if let Some(peer) = who { + if let Some(peer) = peer_id { output.push(Err(BadPeer(peer, rep::VERIFICATION_FAIL))); } output.extend(self.restart()); }, - Err(BlockImportError::BadBlock(who)) => - if let Some(peer) = who { + Err(BlockImportError::BadBlock(peer_id)) => + if let Some(peer) = peer_id { warn!( target: LOG_TARGET, "πŸ’” Block {hash:?} received from peer {peer} has been blacklisted", @@ -2365,7 +2355,7 @@ where /// It is expected that `blocks` are in ascending order. fn validate_blocks( blocks: &Vec>, - who: &PeerId, + peer_id: &PeerId, request: Option>, ) -> Result>, BadPeer> { if let Some(request) = request { @@ -2373,12 +2363,12 @@ fn validate_blocks( debug!( target: LOG_TARGET, "Received more blocks than requested from {}. Expected in maximum {:?}, got {}.", - who, + peer_id, request.max, blocks.len(), ); - return Err(BadPeer(*who, rep::NOT_REQUESTED)) + return Err(BadPeer(*peer_id, rep::NOT_REQUESTED)) } let block_header = @@ -2398,7 +2388,7 @@ fn validate_blocks( block_header, ); - return Err(BadPeer(*who, rep::NOT_REQUESTED)) + return Err(BadPeer(*peer_id, rep::NOT_REQUESTED)) } if request.fields.contains(BlockAttributes::HEADER) && @@ -2406,20 +2396,20 @@ fn validate_blocks( { trace!( target: LOG_TARGET, - "Missing requested header for a block in response from {who}.", + "Missing requested header for a block in response from {peer_id}.", ); - return Err(BadPeer(*who, rep::BAD_RESPONSE)) + return Err(BadPeer(*peer_id, rep::BAD_RESPONSE)) } if request.fields.contains(BlockAttributes::BODY) && blocks.iter().any(|b| b.body.is_none()) { trace!( target: LOG_TARGET, - "Missing requested body for a block in response from {who}.", + "Missing requested body for a block in response from {peer_id}.", ); - return Err(BadPeer(*who, rep::BAD_RESPONSE)) + return Err(BadPeer(*peer_id, rep::BAD_RESPONSE)) } } @@ -2430,11 +2420,11 @@ fn validate_blocks( debug!( target:LOG_TARGET, "Bad header received from {}. Expected hash {:?}, got {:?}", - who, + peer_id, b.hash, hash, ); - return Err(BadPeer(*who, rep::BAD_BLOCK)) + return Err(BadPeer(*peer_id, rep::BAD_BLOCK)) } } if let (Some(header), Some(body)) = (&b.header, &b.body) { @@ -2448,11 +2438,11 @@ fn validate_blocks( target:LOG_TARGET, "Bad extrinsic root for a block {} received from {}. Expected {:?}, got {:?}", b.hash, - who, + peer_id, expected, got, ); - return Err(BadPeer(*who, rep::BAD_BLOCK)) + return Err(BadPeer(*peer_id, rep::BAD_BLOCK)) } } } diff --git a/substrate/client/network/sync/src/chain_sync/test.rs b/substrate/client/network/sync/src/chain_sync/test.rs index de908f0449c8..2eefd2ad13ef 100644 --- a/substrate/client/network/sync/src/chain_sync/test.rs +++ b/substrate/client/network/sync/src/chain_sync/test.rs @@ -19,7 +19,6 @@ //! Tests of [`ChainSync`]. use super::*; -use crate::service::network::NetworkServiceProvider; use futures::executor::block_on; use sc_block_builder::BlockBuilderBuilder; use sc_network_common::sync::message::{BlockAnnounce, BlockData, BlockState, FromBlock}; @@ -39,17 +38,7 @@ fn processes_empty_response_on_justification_request_for_unknown_block() { let client = Arc::new(TestClientBuilder::new().build()); let peer_id = PeerId::random(); - let (_chain_sync_network_provider, chain_sync_network_handle) = NetworkServiceProvider::new(); - let mut sync = ChainSync::new( - SyncMode::Full, - client.clone(), - ProtocolName::from("test-block-announce-protocol"), - 1, - 64, - None, - chain_sync_network_handle, - ) - .unwrap(); + let mut sync = ChainSync::new(SyncMode::Full, client.clone(), 1, 64, None).unwrap(); let (a1_hash, a1_number) = { let a1 = BlockBuilderBuilder::new(&*client) @@ -103,18 +92,8 @@ fn processes_empty_response_on_justification_request_for_unknown_block() { #[test] fn restart_doesnt_affect_peers_downloading_finality_data() { let mut client = Arc::new(TestClientBuilder::new().build()); - let (_chain_sync_network_provider, chain_sync_network_handle) = NetworkServiceProvider::new(); - - let mut sync = ChainSync::new( - SyncMode::Full, - client.clone(), - ProtocolName::from("test-block-announce-protocol"), - 1, - 64, - None, - chain_sync_network_handle, - ) - .unwrap(); + + let mut sync = ChainSync::new(SyncMode::Full, client.clone(), 1, 64, None).unwrap(); let peer_id1 = PeerId::random(); let peer_id2 = PeerId::random(); @@ -291,19 +270,9 @@ fn do_ancestor_search_when_common_block_to_best_qeued_gap_is_to_big() { }; let mut client = Arc::new(TestClientBuilder::new().build()); - let (_chain_sync_network_provider, chain_sync_network_handle) = NetworkServiceProvider::new(); let info = client.info(); - let mut sync = ChainSync::new( - SyncMode::Full, - client.clone(), - ProtocolName::from("test-block-announce-protocol"), - 5, - 64, - None, - chain_sync_network_handle, - ) - .unwrap(); + let mut sync = ChainSync::new(SyncMode::Full, client.clone(), 5, 64, None).unwrap(); let peer_id1 = PeerId::random(); let peer_id2 = PeerId::random(); @@ -417,7 +386,6 @@ fn do_ancestor_search_when_common_block_to_best_qeued_gap_is_to_big() { fn can_sync_huge_fork() { sp_tracing::try_init_simple(); - let (_chain_sync_network_provider, chain_sync_network_handle) = NetworkServiceProvider::new(); let mut client = Arc::new(TestClientBuilder::new().build()); let blocks = (0..MAX_BLOCKS_TO_LOOK_BACKWARDS * 4) .map(|_| build_block(&mut client, None, false)) @@ -442,16 +410,7 @@ fn can_sync_huge_fork() { let info = client.info(); - let mut sync = ChainSync::new( - SyncMode::Full, - client.clone(), - ProtocolName::from("test-block-announce-protocol"), - 5, - 64, - None, - chain_sync_network_handle, - ) - .unwrap(); + let mut sync = ChainSync::new(SyncMode::Full, client.clone(), 5, 64, None).unwrap(); let finalized_block = blocks[MAX_BLOCKS_TO_LOOK_BACKWARDS as usize * 2 - 1].clone(); let just = (*b"TEST", Vec::new()); @@ -545,7 +504,6 @@ fn can_sync_huge_fork() { fn syncs_fork_without_duplicate_requests() { sp_tracing::try_init_simple(); - let (_chain_sync_network_provider, chain_sync_network_handle) = NetworkServiceProvider::new(); let mut client = Arc::new(TestClientBuilder::new().build()); let blocks = (0..MAX_BLOCKS_TO_LOOK_BACKWARDS * 4) .map(|_| build_block(&mut client, None, false)) @@ -570,16 +528,7 @@ fn syncs_fork_without_duplicate_requests() { let info = client.info(); - let mut sync = ChainSync::new( - SyncMode::Full, - client.clone(), - ProtocolName::from("test-block-announce-protocol"), - 5, - 64, - None, - chain_sync_network_handle, - ) - .unwrap(); + let mut sync = ChainSync::new(SyncMode::Full, client.clone(), 5, 64, None).unwrap(); let finalized_block = blocks[MAX_BLOCKS_TO_LOOK_BACKWARDS as usize * 2 - 1].clone(); let just = (*b"TEST", Vec::new()); @@ -696,20 +645,10 @@ fn syncs_fork_without_duplicate_requests() { #[test] fn removes_target_fork_on_disconnect() { sp_tracing::try_init_simple(); - let (_chain_sync_network_provider, chain_sync_network_handle) = NetworkServiceProvider::new(); let mut client = Arc::new(TestClientBuilder::new().build()); let blocks = (0..3).map(|_| build_block(&mut client, None, false)).collect::>(); - let mut sync = ChainSync::new( - SyncMode::Full, - client.clone(), - ProtocolName::from("test-block-announce-protocol"), - 1, - 64, - None, - chain_sync_network_handle, - ) - .unwrap(); + let mut sync = ChainSync::new(SyncMode::Full, client.clone(), 1, 64, None).unwrap(); let peer_id1 = PeerId::random(); let common_block = blocks[1].clone(); @@ -730,22 +669,12 @@ fn removes_target_fork_on_disconnect() { #[test] fn can_import_response_with_missing_blocks() { sp_tracing::try_init_simple(); - let (_chain_sync_network_provider, chain_sync_network_handle) = NetworkServiceProvider::new(); let mut client2 = Arc::new(TestClientBuilder::new().build()); let blocks = (0..4).map(|_| build_block(&mut client2, None, false)).collect::>(); let empty_client = Arc::new(TestClientBuilder::new().build()); - let mut sync = ChainSync::new( - SyncMode::Full, - empty_client.clone(), - ProtocolName::from("test-block-announce-protocol"), - 1, - 64, - None, - chain_sync_network_handle, - ) - .unwrap(); + let mut sync = ChainSync::new(SyncMode::Full, empty_client.clone(), 1, 64, None).unwrap(); let peer_id1 = PeerId::random(); let best_block = blocks[3].clone(); @@ -777,17 +706,7 @@ fn ancestor_search_repeat() { #[test] fn sync_restart_removes_block_but_not_justification_requests() { let mut client = Arc::new(TestClientBuilder::new().build()); - let (_chain_sync_network_provider, chain_sync_network_handle) = NetworkServiceProvider::new(); - let mut sync = ChainSync::new( - SyncMode::Full, - client.clone(), - ProtocolName::from("test-block-announce-protocol"), - 1, - 64, - None, - chain_sync_network_handle, - ) - .unwrap(); + let mut sync = ChainSync::new(SyncMode::Full, client.clone(), 1, 64, None).unwrap(); let peers = vec![PeerId::random(), PeerId::random()]; @@ -889,7 +808,6 @@ fn sync_restart_removes_block_but_not_justification_requests() { fn request_across_forks() { sp_tracing::try_init_simple(); - let (_chain_sync_network_provider, chain_sync_network_handle) = NetworkServiceProvider::new(); let mut client = Arc::new(TestClientBuilder::new().build()); let blocks = (0..100).map(|_| build_block(&mut client, None, false)).collect::>(); @@ -925,16 +843,7 @@ fn request_across_forks() { fork_blocks }; - let mut sync = ChainSync::new( - SyncMode::Full, - client.clone(), - ProtocolName::from("test-block-announce-protocol"), - 5, - 64, - None, - chain_sync_network_handle, - ) - .unwrap(); + let mut sync = ChainSync::new(SyncMode::Full, client.clone(), 5, 64, None).unwrap(); // Add the peers, all at the common ancestor 100. let common_block = blocks.last().unwrap(); diff --git a/substrate/client/network/sync/src/engine.rs b/substrate/client/network/sync/src/engine.rs index 02a0dc2e9151..560887132e3a 100644 --- a/substrate/client/network/sync/src/engine.rs +++ b/substrate/client/network/sync/src/engine.rs @@ -27,7 +27,7 @@ use crate::{ block_request_handler::MAX_BLOCKS_IN_RESPONSE, chain_sync::{ BlockRequestAction, ChainSync, ImportBlocksAction, ImportJustificationsAction, - OnBlockResponse, + OnBlockResponse, OnStateResponse, }, pending_responses::{PendingResponses, ResponseEvent}, schema::v1::{StateRequest, StateResponse}, @@ -455,11 +455,9 @@ where let chain_sync = ChainSync::new( mode, client.clone(), - block_announce_protocol_name.clone(), max_parallel_downloads, max_blocks_per_request, warp_sync_config, - network_service.clone(), )?; let (tx, service_rx) = tracing_unbounded("mpsc_chain_sync", 100_000); @@ -917,7 +915,7 @@ where /// Called by peer when it is disconnecting. /// /// Returns a result if the handshake of this peer was indeed accepted. - pub fn on_sync_peer_disconnected(&mut self, peer_id: PeerId) -> Result<(), ()> { + fn on_sync_peer_disconnected(&mut self, peer_id: PeerId) -> Result<(), ()> { if let Some(info) = self.peers.remove(&peer_id) { if self.important_peers.contains(&peer_id) { log::warn!(target: LOG_TARGET, "Reserved peer {peer_id} disconnected"); @@ -961,7 +959,7 @@ where /// /// Returns `Ok` if the handshake is accepted and the peer added to the list of peers we sync /// from. - pub fn on_sync_peer_connected( + fn on_sync_peer_connected( &mut self, peer_id: PeerId, status: &BlockAnnouncesHandshake, @@ -1212,6 +1210,13 @@ where OnBlockResponse::ImportJustifications(action) => self.import_justifications(action), OnBlockResponse::Nothing => {}, + OnBlockResponse::DisconnectPeer(BadPeer(peer_id, rep)) => { + self.network_service.disconnect_peer( + peer_id, + self.block_announce_protocol_name.clone(), + ); + self.network_service.report_peer(peer_id, rep); + }, } }, Err(BlockResponseError::DecodeFailed(e)) => { @@ -1257,14 +1262,27 @@ where }, }; - if let Some(import_blocks_action) = - self.chain_sync.on_state_response(peer_id, response) - { - self.import_blocks(import_blocks_action); + match self.chain_sync.on_state_response(peer_id, response) { + OnStateResponse::ImportBlocks(import_blocks_action) => + self.import_blocks(import_blocks_action), + OnStateResponse::DisconnectPeer(BadPeer(peer_id, rep)) => { + self.network_service.disconnect_peer( + peer_id, + self.block_announce_protocol_name.clone(), + ); + self.network_service.report_peer(peer_id, rep); + }, + OnStateResponse::Nothing => {}, } }, PeerRequest::WarpProof => { - self.chain_sync.on_warp_sync_response(peer_id, EncodedProof(resp)); + if let Err(BadPeer(peer_id, rep)) = + self.chain_sync.on_warp_sync_response(&peer_id, EncodedProof(resp)) + { + self.network_service + .disconnect_peer(peer_id, self.block_announce_protocol_name.clone()); + self.network_service.report_peer(peer_id, rep); + } }, }, Ok(Err(e)) => { From 305aefc43d14eb06c32c6f49d6c576a6751afbe5 Mon Sep 17 00:00:00 2001 From: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Date: Mon, 6 Nov 2023 13:00:06 +0100 Subject: [PATCH 21/31] `serde_json`: bumped to 1.0.108 (#2168) This PR updates the version of `serde_json` to `1.0.108` throughout the codebase. --- Cargo.lock | 8 ++++---- cumulus/client/relay-chain-rpc-interface/Cargo.toml | 2 +- cumulus/parachain-template/node/Cargo.toml | 2 +- .../integration-tests/emulated/common/Cargo.toml | 2 +- cumulus/polkadot-parachain/Cargo.toml | 2 +- cumulus/test/service/Cargo.toml | 2 +- polkadot/node/service/Cargo.toml | 2 +- polkadot/node/test/service/Cargo.toml | 2 +- polkadot/node/zombienet-backchannel/Cargo.toml | 2 +- polkadot/runtime/common/Cargo.toml | 2 +- polkadot/runtime/parachains/Cargo.toml | 2 +- polkadot/runtime/rococo/Cargo.toml | 2 +- polkadot/runtime/test-runtime/Cargo.toml | 2 +- polkadot/runtime/westend/Cargo.toml | 2 +- substrate/bin/minimal/node/Cargo.toml | 2 +- substrate/bin/node-template/node/Cargo.toml | 2 +- substrate/bin/node-template/runtime/Cargo.toml | 2 +- substrate/bin/node/bench/Cargo.toml | 2 +- substrate/bin/node/cli/Cargo.toml | 2 +- substrate/bin/node/executor/Cargo.toml | 2 +- substrate/bin/node/runtime/Cargo.toml | 2 +- substrate/bin/utils/chain-spec-builder/Cargo.toml | 2 +- substrate/client/chain-spec/Cargo.toml | 2 +- substrate/client/cli/Cargo.toml | 2 +- substrate/client/consensus/babe/rpc/Cargo.toml | 2 +- substrate/client/consensus/beefy/rpc/Cargo.toml | 2 +- substrate/client/consensus/grandpa/Cargo.toml | 2 +- substrate/client/keystore/Cargo.toml | 2 +- substrate/client/merkle-mountain-range/rpc/Cargo.toml | 2 +- substrate/client/network/Cargo.toml | 2 +- substrate/client/rpc-api/Cargo.toml | 2 +- substrate/client/rpc-servers/Cargo.toml | 2 +- substrate/client/rpc-spec-v2/Cargo.toml | 2 +- substrate/client/rpc/Cargo.toml | 2 +- substrate/client/service/Cargo.toml | 2 +- substrate/client/sync-state-rpc/Cargo.toml | 2 +- substrate/client/sysinfo/Cargo.toml | 2 +- substrate/client/telemetry/Cargo.toml | 2 +- substrate/client/transaction-pool/api/Cargo.toml | 2 +- substrate/frame/support/Cargo.toml | 2 +- substrate/frame/transaction-payment/Cargo.toml | 2 +- .../frame/transaction-payment/asset-tx-payment/Cargo.toml | 2 +- substrate/primitives/consensus/sassafras/Cargo.toml | 2 +- substrate/primitives/core/Cargo.toml | 2 +- substrate/primitives/genesis-builder/Cargo.toml | 2 +- substrate/primitives/rpc/Cargo.toml | 2 +- substrate/primitives/runtime/Cargo.toml | 2 +- substrate/test-utils/client/Cargo.toml | 2 +- substrate/test-utils/runtime/Cargo.toml | 2 +- substrate/utils/frame/benchmarking-cli/Cargo.toml | 2 +- .../utils/frame/rpc/state-trie-migration-rpc/Cargo.toml | 2 +- substrate/utils/frame/try-runtime/cli/Cargo.toml | 2 +- 52 files changed, 55 insertions(+), 55 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1b4496368ee8..1a4c532f9c78 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1470,8 +1470,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93f2635620bf0b9d4576eb7bb9a38a55df78bd1205d26fa994b25911a69f212f" dependencies = [ "bitcoin_hashes", - "rand 0.7.3", - "rand_core 0.5.1", + "rand 0.8.5", + "rand_core 0.6.4", "serde", "unicode-normalization", ] @@ -16459,9 +16459,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.107" +version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" dependencies = [ "itoa", "ryu", diff --git a/cumulus/client/relay-chain-rpc-interface/Cargo.toml b/cumulus/client/relay-chain-rpc-interface/Cargo.toml index 102ce75508d2..9e5a8f1eccbe 100644 --- a/cumulus/client/relay-chain-rpc-interface/Cargo.toml +++ b/cumulus/client/relay-chain-rpc-interface/Cargo.toml @@ -33,7 +33,7 @@ jsonrpsee = { version = "0.16.2", features = ["ws-client"] } tracing = "0.1.37" async-trait = "0.1.73" url = "2.4.0" -serde_json = "1.0.107" +serde_json = "1.0.108" serde = "1.0.188" schnellru = "0.2.1" smoldot = { version = "0.11.0", default_features = false, features = ["std"]} diff --git a/cumulus/parachain-template/node/Cargo.toml b/cumulus/parachain-template/node/Cargo.toml index 2a08395008f8..73bbbb6d7714 100644 --- a/cumulus/parachain-template/node/Cargo.toml +++ b/cumulus/parachain-template/node/Cargo.toml @@ -17,7 +17,7 @@ codec = { package = "parity-scale-codec", version = "3.0.0" } serde = { version = "1.0.188", features = ["derive"] } jsonrpsee = { version = "0.16.2", features = ["server"] } futures = "0.3.28" -serde_json = "1.0.104" +serde_json = "1.0.108" # Local parachain-template-runtime = { path = "../runtime" } diff --git a/cumulus/parachains/integration-tests/emulated/common/Cargo.toml b/cumulus/parachains/integration-tests/emulated/common/Cargo.toml index 4182fea52748..6688c80eb9ad 100644 --- a/cumulus/parachains/integration-tests/emulated/common/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/common/Cargo.toml @@ -10,7 +10,7 @@ publish = false [dependencies] codec = { package = "parity-scale-codec", version = "3.4.0", default-features = false } paste = "1.0.14" -serde_json = "1.0.104" +serde_json = "1.0.108" # Substrate grandpa = { package = "sc-consensus-grandpa", path = "../../../../../substrate/client/consensus/grandpa" } diff --git a/cumulus/polkadot-parachain/Cargo.toml b/cumulus/polkadot-parachain/Cargo.toml index c7a79e3e97ab..f7252a39a38a 100644 --- a/cumulus/polkadot-parachain/Cargo.toml +++ b/cumulus/polkadot-parachain/Cargo.toml @@ -19,7 +19,7 @@ futures = "0.3.28" hex-literal = "0.4.1" log = "0.4.20" serde = { version = "1.0.188", features = ["derive"] } -serde_json = "1.0.107" +serde_json = "1.0.108" # Local rococo-parachain-runtime = { path = "../parachains/runtimes/testing/rococo-parachain" } diff --git a/cumulus/test/service/Cargo.toml b/cumulus/test/service/Cargo.toml index 5d73ba83d523..ed8c8748cc80 100644 --- a/cumulus/test/service/Cargo.toml +++ b/cumulus/test/service/Cargo.toml @@ -17,7 +17,7 @@ criterion = { version = "0.5.1", features = [ "async_tokio" ] } jsonrpsee = { version = "0.16.2", features = ["server"] } rand = "0.8.5" serde = { version = "1.0.188", features = ["derive"] } -serde_json = "1.0.107" +serde_json = "1.0.108" tokio = { version = "1.32.0", features = ["macros"] } tracing = "0.1.37" url = "2.4.0" diff --git a/polkadot/node/service/Cargo.toml b/polkadot/node/service/Cargo.toml index 3429f4e0a3a4..41b86f2151e9 100644 --- a/polkadot/node/service/Cargo.toml +++ b/polkadot/node/service/Cargo.toml @@ -82,7 +82,7 @@ gum = { package = "tracing-gum", path = "../gum" } log = "0.4.17" schnellru = "0.2.1" serde = { version = "1.0.188", features = ["derive"] } -serde_json = "1.0.107" +serde_json = "1.0.108" thiserror = "1.0.48" kvdb = "0.13.0" kvdb-rocksdb = { version = "0.19.0", optional = true } diff --git a/polkadot/node/test/service/Cargo.toml b/polkadot/node/test/service/Cargo.toml index 6c13e45a5667..437fa66b75a2 100644 --- a/polkadot/node/test/service/Cargo.toml +++ b/polkadot/node/test/service/Cargo.toml @@ -11,7 +11,7 @@ futures = "0.3.21" hex = "0.4.3" gum = { package = "tracing-gum", path = "../../gum" } rand = "0.8.5" -serde_json = "1.0.106" +serde_json = "1.0.108" tempfile = "3.2.0" tokio = "1.24.2" diff --git a/polkadot/node/zombienet-backchannel/Cargo.toml b/polkadot/node/zombienet-backchannel/Cargo.toml index 9bf56b550bbc..c1b08b4a2bb9 100644 --- a/polkadot/node/zombienet-backchannel/Cargo.toml +++ b/polkadot/node/zombienet-backchannel/Cargo.toml @@ -19,4 +19,4 @@ reqwest = { version = "0.11", features = ["rustls-tls"], default-features = fals thiserror = "1.0.48" gum = { package = "tracing-gum", path = "../gum" } serde = { version = "1.0", features = ["derive"] } -serde_json = "1" +serde_json = "1.0.108" diff --git a/polkadot/runtime/common/Cargo.toml b/polkadot/runtime/common/Cargo.toml index 18332156d99a..a6642f9c9c77 100644 --- a/polkadot/runtime/common/Cargo.toml +++ b/polkadot/runtime/common/Cargo.toml @@ -64,7 +64,7 @@ pallet-babe = { path = "../../../substrate/frame/babe" } pallet-treasury = { path = "../../../substrate/frame/treasury" } sp-keystore = { path = "../../../substrate/primitives/keystore" } sp-keyring = { path = "../../../substrate/primitives/keyring" } -serde_json = "1.0.107" +serde_json = "1.0.108" libsecp256k1 = "0.7.0" test-helpers = { package = "polkadot-primitives-test-helpers", path = "../../primitives/test-helpers" } diff --git a/polkadot/runtime/parachains/Cargo.toml b/polkadot/runtime/parachains/Cargo.toml index e49c00459dec..004a0545d55f 100644 --- a/polkadot/runtime/parachains/Cargo.toml +++ b/polkadot/runtime/parachains/Cargo.toml @@ -62,7 +62,7 @@ test-helpers = { package = "polkadot-primitives-test-helpers", path = "../../pri sp-tracing = { path = "../../../substrate/primitives/tracing" } thousands = "0.2.0" assert_matches = "1" -serde_json = "1.0.107" +serde_json = "1.0.108" [features] default = [ "std" ] diff --git a/polkadot/runtime/rococo/Cargo.toml b/polkadot/runtime/rococo/Cargo.toml index 135e5e940569..9693d351cf40 100644 --- a/polkadot/runtime/rococo/Cargo.toml +++ b/polkadot/runtime/rococo/Cargo.toml @@ -108,7 +108,7 @@ keyring = { package = "sp-keyring", path = "../../../substrate/primitives/keyrin remote-externalities = { package = "frame-remote-externalities" , path = "../../../substrate/utils/frame/remote-externalities" } sp-trie = { path = "../../../substrate/primitives/trie" } separator = "0.4.1" -serde_json = "1.0.107" +serde_json = "1.0.108" sp-tracing = { path = "../../../substrate/primitives/tracing", default-features = false } tokio = { version = "1.24.2", features = ["macros"] } diff --git a/polkadot/runtime/test-runtime/Cargo.toml b/polkadot/runtime/test-runtime/Cargo.toml index db014a5d864c..29debad7b53b 100644 --- a/polkadot/runtime/test-runtime/Cargo.toml +++ b/polkadot/runtime/test-runtime/Cargo.toml @@ -71,7 +71,7 @@ hex-literal = "0.4.1" tiny-keccak = { version = "2.0.2", features = ["keccak"] } keyring = { package = "sp-keyring", path = "../../../substrate/primitives/keyring" } sp-trie = { path = "../../../substrate/primitives/trie" } -serde_json = "1.0.107" +serde_json = "1.0.108" [build-dependencies] substrate-wasm-builder = { path = "../../../substrate/utils/wasm-builder" } diff --git a/polkadot/runtime/westend/Cargo.toml b/polkadot/runtime/westend/Cargo.toml index 4148d6105917..eaebf01e3a76 100644 --- a/polkadot/runtime/westend/Cargo.toml +++ b/polkadot/runtime/westend/Cargo.toml @@ -116,7 +116,7 @@ xcm-builder = { package = "staging-xcm-builder", path = "../../xcm/xcm-builder", hex-literal = "0.4.1" tiny-keccak = { version = "2.0.2", features = ["keccak"] } keyring = { package = "sp-keyring", path = "../../../substrate/primitives/keyring" } -serde_json = "1.0.107" +serde_json = "1.0.108" remote-externalities = { package = "frame-remote-externalities" , path = "../../../substrate/utils/frame/remote-externalities" } tokio = { version = "1.24.2", features = ["macros"] } sp-tracing = { path = "../../../substrate/primitives/tracing", default-features = false } diff --git a/substrate/bin/minimal/node/Cargo.toml b/substrate/bin/minimal/node/Cargo.toml index 39d39d7e3122..0506d0838f1f 100644 --- a/substrate/bin/minimal/node/Cargo.toml +++ b/substrate/bin/minimal/node/Cargo.toml @@ -21,7 +21,7 @@ clap = { version = "4.0.9", features = ["derive"] } futures = { version = "0.3.21", features = ["thread-pool"] } futures-timer = "3.0.1" jsonrpsee = { version = "0.16.2", features = ["server"] } -serde_json = "1.0.107" +serde_json = "1.0.108" sc-cli = { path = "../../../client/cli" } sc-executor = { path = "../../../client/executor" } diff --git a/substrate/bin/node-template/node/Cargo.toml b/substrate/bin/node-template/node/Cargo.toml index 4e87675514d5..61953631d799 100644 --- a/substrate/bin/node-template/node/Cargo.toml +++ b/substrate/bin/node-template/node/Cargo.toml @@ -19,7 +19,7 @@ name = "node-template" [dependencies] clap = { version = "4.4.6", features = ["derive"] } futures = { version = "0.3.21", features = ["thread-pool"]} -serde_json = "1.0.85" +serde_json = "1.0.108" sc-cli = { path = "../../../client/cli" } sp-core = { path = "../../../primitives/core" } diff --git a/substrate/bin/node-template/runtime/Cargo.toml b/substrate/bin/node-template/runtime/Cargo.toml index 0008c82a49d4..7711ddba34d0 100644 --- a/substrate/bin/node-template/runtime/Cargo.toml +++ b/substrate/bin/node-template/runtime/Cargo.toml @@ -39,7 +39,7 @@ sp-std = { path = "../../../primitives/std", default-features = false} sp-storage = { path = "../../../primitives/storage", default-features = false} sp-transaction-pool = { path = "../../../primitives/transaction-pool", default-features = false} sp-version = { path = "../../../primitives/version", default-features = false, features = ["serde"] } -serde_json = { version = "1.0.85", default-features = false, features = ["alloc"] } +serde_json = { version = "1.0.108", default-features = false, features = ["alloc"] } sp-genesis-builder = { default-features = false, path = "../../../primitives/genesis-builder" } # Used for the node template's RPCs diff --git a/substrate/bin/node/bench/Cargo.toml b/substrate/bin/node/bench/Cargo.toml index c111d345623d..ee429ee8c0c1 100644 --- a/substrate/bin/node/bench/Cargo.toml +++ b/substrate/bin/node/bench/Cargo.toml @@ -22,7 +22,7 @@ sc-client-api = { path = "../../../client/api" } sp-runtime = { path = "../../../primitives/runtime" } sp-state-machine = { path = "../../../primitives/state-machine" } serde = "1.0.188" -serde_json = "1.0.107" +serde_json = "1.0.108" derive_more = { version = "0.99.17", default-features = false, features = ["display"] } kvdb = "0.13.0" kvdb-rocksdb = "0.19.0" diff --git a/substrate/bin/node/cli/Cargo.toml b/substrate/bin/node/cli/Cargo.toml index 01c532ad20dc..5e7ffebaa8ed 100644 --- a/substrate/bin/node/cli/Cargo.toml +++ b/substrate/bin/node/cli/Cargo.toml @@ -108,7 +108,7 @@ sc-cli = { path = "../../../client/cli", optional = true} frame-benchmarking-cli = { path = "../../../utils/frame/benchmarking-cli", optional = true} node-inspect = { package = "staging-node-inspect", path = "../inspect", optional = true} try-runtime-cli = { path = "../../../utils/frame/try-runtime/cli", optional = true} -serde_json = "1.0.107" +serde_json = "1.0.108" [dev-dependencies] sc-keystore = { path = "../../../client/keystore" } diff --git a/substrate/bin/node/executor/Cargo.toml b/substrate/bin/node/executor/Cargo.toml index 12280a9ba1d7..595a313d2cb9 100644 --- a/substrate/bin/node/executor/Cargo.toml +++ b/substrate/bin/node/executor/Cargo.toml @@ -47,7 +47,7 @@ sp-consensus-babe = { path = "../../../primitives/consensus/babe" } sp-externalities = { path = "../../../primitives/externalities" } sp-keyring = { path = "../../../primitives/keyring" } sp-runtime = { path = "../../../primitives/runtime" } -serde_json = "1.0.85" +serde_json = "1.0.108" [features] stress-test = [] diff --git a/substrate/bin/node/runtime/Cargo.toml b/substrate/bin/node/runtime/Cargo.toml index 85d3971e4921..836f90e76542 100644 --- a/substrate/bin/node/runtime/Cargo.toml +++ b/substrate/bin/node/runtime/Cargo.toml @@ -23,7 +23,7 @@ codec = { package = "parity-scale-codec", version = "3.6.1", default-features = scale-info = { version = "2.10.0", default-features = false, features = ["derive", "serde"] } static_assertions = "1.1.0" log = { version = "0.4.17", default-features = false } -serde_json = { version = "1.0.85", default-features = false, features = ["alloc", "arbitrary_precision"] } +serde_json = { version = "1.0.108", default-features = false, features = ["alloc", "arbitrary_precision"] } # pallet-asset-conversion: turn on "num-traits" feature primitive-types = { version = "0.12.0", default-features = false, features = ["codec", "scale-info", "num-traits"] } diff --git a/substrate/bin/utils/chain-spec-builder/Cargo.toml b/substrate/bin/utils/chain-spec-builder/Cargo.toml index 5c7e21abeab9..0b373b8e9247 100644 --- a/substrate/bin/utils/chain-spec-builder/Cargo.toml +++ b/substrate/bin/utils/chain-spec-builder/Cargo.toml @@ -28,7 +28,7 @@ log = "0.4.17" node-cli = { package = "staging-node-cli", path = "../../node/cli" } sc-chain-spec = { path = "../../../client/chain-spec" } sc-keystore = { path = "../../../client/keystore" } -serde_json = "1.0.100" +serde_json = "1.0.108" sp-core = { path = "../../../primitives/core" } sp-keystore = { path = "../../../primitives/keystore" } sp-tracing = { version = "10.0.0", path = "../../../primitives/tracing" } diff --git a/substrate/client/chain-spec/Cargo.toml b/substrate/client/chain-spec/Cargo.toml index b11088190d16..5b7cdda8ebe4 100644 --- a/substrate/client/chain-spec/Cargo.toml +++ b/substrate/client/chain-spec/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } memmap2 = "0.5.0" serde = { version = "1.0.188", features = ["derive"] } -serde_json = "1.0.107" +serde_json = "1.0.108" sc-client-api = { path = "../api" } sc-chain-spec-derive = { path = "derive" } sc-executor = { path = "../executor" } diff --git a/substrate/client/cli/Cargo.toml b/substrate/client/cli/Cargo.toml index 5a98b43f43c2..c415527c372c 100644 --- a/substrate/client/cli/Cargo.toml +++ b/substrate/client/cli/Cargo.toml @@ -27,7 +27,7 @@ rand = "0.8.5" regex = "1.6.0" rpassword = "7.0.0" serde = "1.0.188" -serde_json = "1.0.107" +serde_json = "1.0.108" thiserror = "1.0.48" bip39 = "2.0.0" tokio = { version = "1.22.0", features = ["signal", "rt-multi-thread", "parking_lot"] } diff --git a/substrate/client/consensus/babe/rpc/Cargo.toml b/substrate/client/consensus/babe/rpc/Cargo.toml index f54edb296842..c95d95ae427a 100644 --- a/substrate/client/consensus/babe/rpc/Cargo.toml +++ b/substrate/client/consensus/babe/rpc/Cargo.toml @@ -30,7 +30,7 @@ sp-keystore = { path = "../../../../primitives/keystore" } sp-runtime = { path = "../../../../primitives/runtime" } [dev-dependencies] -serde_json = "1.0.107" +serde_json = "1.0.108" tokio = "1.22.0" sc-consensus = { path = "../../common" } sc-keystore = { path = "../../../keystore" } diff --git a/substrate/client/consensus/beefy/rpc/Cargo.toml b/substrate/client/consensus/beefy/rpc/Cargo.toml index 74733ea9edd9..c7464fdc6532 100644 --- a/substrate/client/consensus/beefy/rpc/Cargo.toml +++ b/substrate/client/consensus/beefy/rpc/Cargo.toml @@ -23,7 +23,7 @@ sp-core = { path = "../../../../primitives/core" } sp-runtime = { path = "../../../../primitives/runtime" } [dev-dependencies] -serde_json = "1.0.107" +serde_json = "1.0.108" sc-rpc = { path = "../../../rpc", features = ["test-helpers"]} substrate-test-runtime-client = { path = "../../../../test-utils/runtime/client" } tokio = { version = "1.22.0", features = ["macros"] } diff --git a/substrate/client/consensus/grandpa/Cargo.toml b/substrate/client/consensus/grandpa/Cargo.toml index 921b9c539e37..85f98e7546e0 100644 --- a/substrate/client/consensus/grandpa/Cargo.toml +++ b/substrate/client/consensus/grandpa/Cargo.toml @@ -25,7 +25,7 @@ log = "0.4.17" parity-scale-codec = { version = "3.6.1", features = ["derive"] } parking_lot = "0.12.1" rand = "0.8.5" -serde_json = "1.0.107" +serde_json = "1.0.108" thiserror = "1.0" fork-tree = { path = "../../../utils/fork-tree" } prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../../utils/prometheus" } diff --git a/substrate/client/keystore/Cargo.toml b/substrate/client/keystore/Cargo.toml index 6303ff5c27e1..3fd88ae8b87e 100644 --- a/substrate/client/keystore/Cargo.toml +++ b/substrate/client/keystore/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] array-bytes = "6.1" parking_lot = "0.12.1" -serde_json = "1.0.107" +serde_json = "1.0.108" thiserror = "1.0" sp-application-crypto = { path = "../../primitives/application-crypto" } sp-core = { path = "../../primitives/core" } diff --git a/substrate/client/merkle-mountain-range/rpc/Cargo.toml b/substrate/client/merkle-mountain-range/rpc/Cargo.toml index 05d8547d243f..e75c5f1baa86 100644 --- a/substrate/client/merkle-mountain-range/rpc/Cargo.toml +++ b/substrate/client/merkle-mountain-range/rpc/Cargo.toml @@ -23,4 +23,4 @@ sp-runtime = { path = "../../../primitives/runtime" } anyhow = "1" [dev-dependencies] -serde_json = "1.0.107" +serde_json = "1.0.108" diff --git a/substrate/client/network/Cargo.toml b/substrate/client/network/Cargo.toml index 8b188176fc5e..7b0536addda4 100644 --- a/substrate/client/network/Cargo.toml +++ b/substrate/client/network/Cargo.toml @@ -34,7 +34,7 @@ partial_sort = "0.2.0" pin-project = "1.0.12" rand = "0.8.5" serde = { version = "1.0.188", features = ["derive"] } -serde_json = "1.0.107" +serde_json = "1.0.108" smallvec = "1.11.0" thiserror = "1.0" unsigned-varint = { version = "0.7.1", features = ["futures", "asynchronous_codec"] } diff --git a/substrate/client/rpc-api/Cargo.toml b/substrate/client/rpc-api/Cargo.toml index e07bdf0d15a1..e72bbe48ee3e 100644 --- a/substrate/client/rpc-api/Cargo.toml +++ b/substrate/client/rpc-api/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] codec = { package = "parity-scale-codec", version = "3.6.1" } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } serde = { version = "1.0.188", features = ["derive"] } -serde_json = "1.0.107" +serde_json = "1.0.108" thiserror = "1.0" sc-chain-spec = { path = "../chain-spec" } sc-mixnet = { path = "../mixnet" } diff --git a/substrate/client/rpc-servers/Cargo.toml b/substrate/client/rpc-servers/Cargo.toml index 674a8db39cb5..a7cc374f97a1 100644 --- a/substrate/client/rpc-servers/Cargo.toml +++ b/substrate/client/rpc-servers/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] jsonrpsee = { version = "0.16.2", features = ["server"] } log = "0.4.17" -serde_json = "1.0.107" +serde_json = "1.0.108" tokio = { version = "1.22.0", features = ["parking_lot"] } prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../utils/prometheus" } tower-http = { version = "0.4.0", features = ["cors"] } diff --git a/substrate/client/rpc-spec-v2/Cargo.toml b/substrate/client/rpc-spec-v2/Cargo.toml index 1eaed65706e0..cfe7f8a117dd 100644 --- a/substrate/client/rpc-spec-v2/Cargo.toml +++ b/substrate/client/rpc-spec-v2/Cargo.toml @@ -37,7 +37,7 @@ array-bytes = "6.1" log = "0.4.17" futures-util = { version = "0.3.19", default-features = false } [dev-dependencies] -serde_json = "1.0" +serde_json = "1.0.108" tokio = { version = "1.22.0", features = ["macros"] } substrate-test-runtime-client = { path = "../../test-utils/runtime/client" } substrate-test-runtime = { path = "../../test-utils/runtime" } diff --git a/substrate/client/rpc/Cargo.toml b/substrate/client/rpc/Cargo.toml index dd1120e5b0f8..1cedcb3a6d08 100644 --- a/substrate/client/rpc/Cargo.toml +++ b/substrate/client/rpc/Cargo.toml @@ -18,7 +18,7 @@ futures = "0.3.21" jsonrpsee = { version = "0.16.2", features = ["server"] } log = "0.4.17" parking_lot = "0.12.1" -serde_json = "1.0.107" +serde_json = "1.0.108" sc-block-builder = { path = "../block-builder" } sc-chain-spec = { path = "../chain-spec" } sc-client-api = { path = "../api" } diff --git a/substrate/client/service/Cargo.toml b/substrate/client/service/Cargo.toml index e5520eb21c3e..de69c50702ac 100644 --- a/substrate/client/service/Cargo.toml +++ b/substrate/client/service/Cargo.toml @@ -35,7 +35,7 @@ futures-timer = "3.0.1" exit-future = "0.2.0" pin-project = "1.0.12" serde = "1.0.188" -serde_json = "1.0.107" +serde_json = "1.0.108" sc-keystore = { path = "../keystore" } sp-runtime = { path = "../../primitives/runtime" } sp-trie = { path = "../../primitives/trie" } diff --git a/substrate/client/sync-state-rpc/Cargo.toml b/substrate/client/sync-state-rpc/Cargo.toml index e582d0b7e4ff..569cd067f27d 100644 --- a/substrate/client/sync-state-rpc/Cargo.toml +++ b/substrate/client/sync-state-rpc/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] codec = { package = "parity-scale-codec", version = "3.6.1" } jsonrpsee = { version = "0.16.2", features = ["client-core", "server", "macros"] } serde = { version = "1.0.188", features = ["derive"] } -serde_json = "1.0.107" +serde_json = "1.0.108" thiserror = "1.0.48" sc-chain-spec = { path = "../chain-spec" } sc-client-api = { path = "../api" } diff --git a/substrate/client/sysinfo/Cargo.toml b/substrate/client/sysinfo/Cargo.toml index 86f4d217dd9f..86f03050c44e 100644 --- a/substrate/client/sysinfo/Cargo.toml +++ b/substrate/client/sysinfo/Cargo.toml @@ -22,7 +22,7 @@ rand_pcg = "0.3.1" derive_more = "0.99" regex = "1" serde = { version = "1.0.188", features = ["derive"] } -serde_json = "1.0.107" +serde_json = "1.0.108" sc-telemetry = { path = "../telemetry" } sp-core = { path = "../../primitives/core" } sp-io = { path = "../../primitives/io" } diff --git a/substrate/client/telemetry/Cargo.toml b/substrate/client/telemetry/Cargo.toml index 6d59b9dfc1a6..a693a2884b54 100644 --- a/substrate/client/telemetry/Cargo.toml +++ b/substrate/client/telemetry/Cargo.toml @@ -23,6 +23,6 @@ pin-project = "1.0.12" sc-utils = { path = "../utils" } rand = "0.8.5" serde = { version = "1.0.188", features = ["derive"] } -serde_json = "1.0.107" +serde_json = "1.0.108" thiserror = "1.0.48" wasm-timer = "0.2.5" diff --git a/substrate/client/transaction-pool/api/Cargo.toml b/substrate/client/transaction-pool/api/Cargo.toml index 5ff5a4149ca9..f5760c271ad7 100644 --- a/substrate/client/transaction-pool/api/Cargo.toml +++ b/substrate/client/transaction-pool/api/Cargo.toml @@ -20,4 +20,4 @@ sp-core = { path = "../../../primitives/core", default-features = false} sp-runtime = { path = "../../../primitives/runtime", default-features = false} [dev-dependencies] -serde_json = "1.0" +serde_json = "1.0.108" diff --git a/substrate/frame/support/Cargo.toml b/substrate/frame/support/Cargo.toml index 82942c3a4f15..f8c8edc05104 100644 --- a/substrate/frame/support/Cargo.toml +++ b/substrate/frame/support/Cargo.toml @@ -42,7 +42,7 @@ sp-core-hashing-proc-macro = { path = "../../primitives/core/hashing/proc-macro" k256 = { version = "0.13.1", default-features = false, features = ["ecdsa"] } environmental = { version = "1.1.4", default-features = false } sp-genesis-builder = { path = "../../primitives/genesis-builder", default-features=false} -serde_json = { version = "1.0.107", default-features = false, features = ["alloc"] } +serde_json = { version = "1.0.108", default-features = false, features = ["alloc"] } docify = "0.2.6" static_assertions = "1.1.0" diff --git a/substrate/frame/transaction-payment/Cargo.toml b/substrate/frame/transaction-payment/Cargo.toml index e3a2965e2a02..ad384755614f 100644 --- a/substrate/frame/transaction-payment/Cargo.toml +++ b/substrate/frame/transaction-payment/Cargo.toml @@ -26,7 +26,7 @@ sp-runtime = { path = "../../primitives/runtime", default-features = false} sp-std = { path = "../../primitives/std", default-features = false} [dev-dependencies] -serde_json = "1.0.107" +serde_json = "1.0.108" pallet-balances = { path = "../balances" } [features] diff --git a/substrate/frame/transaction-payment/asset-tx-payment/Cargo.toml b/substrate/frame/transaction-payment/asset-tx-payment/Cargo.toml index 2b1ee52692c7..ae236728cd58 100644 --- a/substrate/frame/transaction-payment/asset-tx-payment/Cargo.toml +++ b/substrate/frame/transaction-payment/asset-tx-payment/Cargo.toml @@ -30,7 +30,7 @@ scale-info = { version = "2.10.0", default-features = false, features = ["derive serde = { version = "1.0.188", optional = true } [dev-dependencies] -serde_json = "1.0.107" +serde_json = "1.0.108" sp-storage = { path = "../../../primitives/storage", default-features = false} diff --git a/substrate/primitives/consensus/sassafras/Cargo.toml b/substrate/primitives/consensus/sassafras/Cargo.toml index 03fdd4439cba..696e0a645961 100644 --- a/substrate/primitives/consensus/sassafras/Cargo.toml +++ b/substrate/primitives/consensus/sassafras/Cargo.toml @@ -17,7 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] scale-codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.163", default-features = false, features = ["derive"], optional = true } +serde = { version = "1.0.188", default-features = false, features = ["derive"], optional = true } sp-api = { default-features = false, path = "../../api" } sp-application-crypto = { default-features = false, path = "../../application-crypto", features = ["bandersnatch-experimental"] } sp-consensus-slots = { default-features = false, path = "../slots" } diff --git a/substrate/primitives/core/Cargo.toml b/substrate/primitives/core/Cargo.toml index fe0905c3bed4..79df81e62c66 100644 --- a/substrate/primitives/core/Cargo.toml +++ b/substrate/primitives/core/Cargo.toml @@ -62,7 +62,7 @@ bandersnatch_vrfs = { git = "https://github.com/w3f/ring-vrf", rev = "cbc342e", [dev-dependencies] criterion = "0.4.0" -serde_json = "1.0" +serde_json = "1.0.108" sp-core-hashing-proc-macro = { path = "hashing/proc-macro" } [[bench]] diff --git a/substrate/primitives/genesis-builder/Cargo.toml b/substrate/primitives/genesis-builder/Cargo.toml index 8f083e2d7d54..cf7ce9957115 100644 --- a/substrate/primitives/genesis-builder/Cargo.toml +++ b/substrate/primitives/genesis-builder/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] sp-api = { path = "../api", default-features = false} sp-runtime = { path = "../runtime", default-features = false} sp-std = { path = "../std", default-features = false} -serde_json = { version = "1.0.107", default-features = false, features = ["alloc"] } +serde_json = { version = "1.0.108", default-features = false, features = ["alloc"] } [features] default = [ "std" ] diff --git a/substrate/primitives/rpc/Cargo.toml b/substrate/primitives/rpc/Cargo.toml index 4739c96740e9..77bdcc4f89a1 100644 --- a/substrate/primitives/rpc/Cargo.toml +++ b/substrate/primitives/rpc/Cargo.toml @@ -18,4 +18,4 @@ serde = { version = "1.0.188", features = ["derive"] } sp-core = { path = "../core" } [dev-dependencies] -serde_json = "1.0.107" +serde_json = "1.0.108" diff --git a/substrate/primitives/runtime/Cargo.toml b/substrate/primitives/runtime/Cargo.toml index fcd1779fb5a6..bf6cf93c5f04 100644 --- a/substrate/primitives/runtime/Cargo.toml +++ b/substrate/primitives/runtime/Cargo.toml @@ -32,7 +32,7 @@ sp-weights = { path = "../weights", default-features = false} [dev-dependencies] rand = "0.8.5" -serde_json = "1.0.107" +serde_json = "1.0.108" zstd = { version = "0.12.4", default-features = false } sp-api = { path = "../api" } sp-state-machine = { path = "../state-machine" } diff --git a/substrate/test-utils/client/Cargo.toml b/substrate/test-utils/client/Cargo.toml index 12863ac184c1..032fbaf4e654 100644 --- a/substrate/test-utils/client/Cargo.toml +++ b/substrate/test-utils/client/Cargo.toml @@ -18,7 +18,7 @@ async-trait = "0.1.57" codec = { package = "parity-scale-codec", version = "3.6.1" } futures = "0.3.21" serde = "1.0.188" -serde_json = "1.0.107" +serde_json = "1.0.108" sc-client-api = { path = "../../client/api" } sc-client-db = { path = "../../client/db", default-features = false, features = [ "test-helpers", diff --git a/substrate/test-utils/runtime/Cargo.toml b/substrate/test-utils/runtime/Cargo.toml index 6a1f8914444b..2f1e192eded0 100644 --- a/substrate/test-utils/runtime/Cargo.toml +++ b/substrate/test-utils/runtime/Cargo.toml @@ -59,7 +59,7 @@ substrate-test-runtime-client = { path = "client" } sp-tracing = { path = "../../primitives/tracing" } json-patch = { version = "1.0.0", default-features = false } serde = { version = "1.0.188", features = ["alloc", "derive"], default-features = false } -serde_json = { version = "1.0.107", default-features = false, features = ["alloc"] } +serde_json = { version = "1.0.108", default-features = false, features = ["alloc"] } [build-dependencies] substrate-wasm-builder = { path = "../../utils/wasm-builder", optional = true } diff --git a/substrate/utils/frame/benchmarking-cli/Cargo.toml b/substrate/utils/frame/benchmarking-cli/Cargo.toml index e32fe47b7297..b67d08a85c2e 100644 --- a/substrate/utils/frame/benchmarking-cli/Cargo.toml +++ b/substrate/utils/frame/benchmarking-cli/Cargo.toml @@ -27,7 +27,7 @@ log = "0.4.17" rand = { version = "0.8.4", features = ["small_rng"] } rand_pcg = "0.3.1" serde = "1.0.188" -serde_json = "1.0.107" +serde_json = "1.0.108" thiserror = "1.0.48" thousands = "0.2.0" frame-benchmarking = { path = "../../../frame/benchmarking" } diff --git a/substrate/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml b/substrate/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml index 8ce9d06c2d11..d25e6ec67c9a 100644 --- a/substrate/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml +++ b/substrate/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml @@ -29,4 +29,4 @@ sc-rpc-api = { path = "../../../../client/rpc-api" } sp-runtime = { path = "../../../../primitives/runtime" } [dev-dependencies] -serde_json = "1" +serde_json = "1.0.108" diff --git a/substrate/utils/frame/try-runtime/cli/Cargo.toml b/substrate/utils/frame/try-runtime/cli/Cargo.toml index 65380a22ce6e..6be4306193ce 100644 --- a/substrate/utils/frame/try-runtime/cli/Cargo.toml +++ b/substrate/utils/frame/try-runtime/cli/Cargo.toml @@ -40,7 +40,7 @@ hex = { version = "0.4.3", default-features = false } log = "0.4.17" parity-scale-codec = "3.6.1" serde = "1.0.188" -serde_json = "1.0.107" +serde_json = "1.0.108" zstd = { version = "0.12.4", default-features = false } [dev-dependencies] From fb2dc6df8d01ef472be30e27960fa02d6a90e906 Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe <49718502+alexggh@users.noreply.github.com> Date: Mon, 6 Nov 2023 14:28:29 +0200 Subject: [PATCH 22/31] Add warning when peer_id is not available when building topology (#2140) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ... see https://github.com/paritytech/polkadot-sdk/issues/2138 for why is not good, until we fix it let's add a warning to understand if this is happening in the wild. --------- Signed-off-by: Alexandru Gheorghe Co-authored-by: Bastian KΓΆcher --- polkadot/node/network/bridge/src/rx/mod.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/polkadot/node/network/bridge/src/rx/mod.rs b/polkadot/node/network/bridge/src/rx/mod.rs index 7e86b46a7e03..f64410c1353d 100644 --- a/polkadot/node/network/bridge/src/rx/mod.rs +++ b/polkadot/node/network/bridge/src/rx/mod.rs @@ -551,6 +551,15 @@ where let mut peers = Vec::with_capacity(neighbors.len()); for (discovery_id, validator_index) in neighbors { let addr = get_peer_id_by_authority_id(ads, discovery_id.clone()).await; + if addr.is_none() { + // See on why is not good in https://github.com/paritytech/polkadot-sdk/issues/2138 + gum::debug!( + target: LOG_TARGET, + ?validator_index, + "Could not determine peer_id for validator, let the team know in \n + https://github.com/paritytech/polkadot-sdk/issues/2138" + ) + } peers.push(TopologyPeerInfo { peer_ids: addr.into_iter().collect(), validator_index, From 318e5969c72de5339210f5f9edd5d4be85bcde70 Mon Sep 17 00:00:00 2001 From: Richard Melkonian <35300528+0xmovses@users.noreply.github.com> Date: Mon, 6 Nov 2023 12:44:53 +0000 Subject: [PATCH 23/31] Add force remove vesting (#1982) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR exposes a `force_remove_vesting` through a ROOT call. See linked [issue](https://github.com/paritytech/polkadot-sdk/issues/269) --------- Co-authored-by: georgepisaltu <52418509+georgepisaltu@users.noreply.github.com> Co-authored-by: command-bot <> Co-authored-by: DΓ³nal Murray --- .../rococo/src/weights/pallet_vesting.rs | 29 +- .../westend/src/weights/pallet_vesting.rs | 24 + substrate/frame/vesting/src/benchmarking.rs | 27 + substrate/frame/vesting/src/lib.rs | 30 ++ substrate/frame/vesting/src/tests.rs | 36 ++ substrate/frame/vesting/src/weights.rs | 510 ++++++++++-------- 6 files changed, 415 insertions(+), 241 deletions(-) diff --git a/polkadot/runtime/rococo/src/weights/pallet_vesting.rs b/polkadot/runtime/rococo/src/weights/pallet_vesting.rs index fd820f1aea32..2596207d5837 100644 --- a/polkadot/runtime/rococo/src/weights/pallet_vesting.rs +++ b/polkadot/runtime/rococo/src/weights/pallet_vesting.rs @@ -187,16 +187,29 @@ impl pallet_vesting::WeightInfo for WeightInfo { .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(4)) } - /// Storage: Vesting Vesting (r:1 w:1) - /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Vesting::Vesting` (r:1 w:1) + /// Proof: `Vesting::Vesting` (`max_values`: None, `max_size`: Some(1057), added: 3532, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[2, 28]`. + fn force_remove_vesting_schedule(l: u32, s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `555 + l * (25 Β±0) + s * (36 Β±0)` + // Estimated: `4764` + // Minimum execution time: 41_497_000 picoseconds. + Weight::from_parts(38_763_834, 4764) + // Standard Error: 2_030 + .saturating_add(Weight::from_parts(99_580, 0).saturating_mul(l.into())) + // Standard Error: 3_750 + .saturating_add(Weight::from_parts(132_188, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } fn not_unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `378 + l * (25 Β±0) + s * (36 Β±0)` diff --git a/polkadot/runtime/westend/src/weights/pallet_vesting.rs b/polkadot/runtime/westend/src/weights/pallet_vesting.rs index 4ccd524ffc28..dfd87d1c550e 100644 --- a/polkadot/runtime/westend/src/weights/pallet_vesting.rs +++ b/polkadot/runtime/westend/src/weights/pallet_vesting.rs @@ -238,4 +238,28 @@ impl pallet_vesting::WeightInfo for WeightInfo { .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) } + + /// Storage: `Vesting::Vesting` (r:1 w:1) + /// Proof: `Vesting::Vesting` (`max_values`: None, `max_size`: Some(1057), added: 3532, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `l` is `[0, 49]`. + /// The range of component `s` is `[2, 28]`. + fn force_remove_vesting_schedule(l: u32, s: u32) -> Weight { + // Proof Size summary in bytes: + // Measured: `555 + l * (25 Β±0) + s * (36 Β±0)` + // Estimated: `4764` + // Minimum execution time: 41_497_000 picoseconds. + Weight::from_parts(38_763_834, 4764) + // Standard Error: 2_030 + .saturating_add(Weight::from_parts(99_580, 0).saturating_mul(l.into())) + // Standard Error: 3_750 + .saturating_add(Weight::from_parts(132_188, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } } diff --git a/substrate/frame/vesting/src/benchmarking.rs b/substrate/frame/vesting/src/benchmarking.rs index 4af48f5d368d..34aa04607add 100644 --- a/substrate/frame/vesting/src/benchmarking.rs +++ b/substrate/frame/vesting/src/benchmarking.rs @@ -383,6 +383,33 @@ benchmarks! { ); } +force_remove_vesting_schedule { + let l in 0 .. MaxLocksOf::::get() - 1; + let s in 2 .. T::MAX_VESTING_SCHEDULES; + + let source: T::AccountId = account("source", 0, SEED); + let source_lookup: ::Source = T::Lookup::unlookup(source.clone()); + T::Currency::make_free_balance_be(&source, BalanceOf::::max_value()); + + let target: T::AccountId = account("target", 0, SEED); + let target_lookup: ::Source = T::Lookup::unlookup(target.clone()); + T::Currency::make_free_balance_be(&target, T::Currency::minimum_balance()); + + // Give target existing locks. + add_locks::(&target, l as u8); + let _ = add_vesting_schedules::(target_lookup.clone(), s)?; + + // The last vesting schedule. + let schedule_index = s - 1; + }: _(RawOrigin::Root, target_lookup, schedule_index) + verify { + assert_eq!( + Vesting::::vesting(&target).unwrap().len(), + schedule_index as usize, + "Schedule count should reduce by 1" + ); + } + impl_benchmark_test_suite!( Vesting, crate::mock::ExtBuilder::default().existential_deposit(256).build(), diff --git a/substrate/frame/vesting/src/lib.rs b/substrate/frame/vesting/src/lib.rs index ee67a038e4d7..dbad7926a30f 100644 --- a/substrate/frame/vesting/src/lib.rs +++ b/substrate/frame/vesting/src/lib.rs @@ -427,6 +427,36 @@ pub mod pallet { Ok(()) } + + /// Force remove a vesting schedule + /// + /// The dispatch origin for this call must be _Root_. + /// + /// - `target`: An account that has a vesting schedule + /// - `schedule_index`: The vesting schedule index that should be removed + #[pallet::call_index(5)] + #[pallet::weight( + T::WeightInfo::force_remove_vesting_schedule(MaxLocksOf::::get(), T::MAX_VESTING_SCHEDULES) + )] + pub fn force_remove_vesting_schedule( + origin: OriginFor, + target: ::Source, + schedule_index: u32, + ) -> DispatchResultWithPostInfo { + ensure_root(origin)?; + let who = T::Lookup::lookup(target)?; + + let schedules_count = Vesting::::decode_len(&who).unwrap_or_default(); + ensure!(schedule_index < schedules_count as u32, Error::::InvalidScheduleParams); + + Self::remove_vesting_schedule(&who, schedule_index)?; + + Ok(Some(T::WeightInfo::force_remove_vesting_schedule( + MaxLocksOf::::get(), + schedules_count as u32, + )) + .into()) + } } } diff --git a/substrate/frame/vesting/src/tests.rs b/substrate/frame/vesting/src/tests.rs index c35686bd5146..2e1e41fc9578 100644 --- a/substrate/frame/vesting/src/tests.rs +++ b/substrate/frame/vesting/src/tests.rs @@ -1155,3 +1155,39 @@ fn vested_transfer_less_than_existential_deposit_fails() { ); }); } + +#[test] +fn remove_vesting_schedule() { + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { + assert_eq!(Balances::free_balance(&3), 256 * 30); + assert_eq!(Balances::free_balance(&4), 256 * 40); + // Account 4 should not have any vesting yet. + assert_eq!(Vesting::vesting(&4), None); + // Make the schedule for the new transfer. + let new_vesting_schedule = VestingInfo::new( + ED * 5, + (ED * 5) / 20, // Vesting over 20 blocks + 10, + ); + assert_ok!(Vesting::vested_transfer(Some(3).into(), 4, new_vesting_schedule)); + // Now account 4 should have vesting. + assert_eq!(Vesting::vesting(&4).unwrap(), vec![new_vesting_schedule]); + // Account 4 has 5 * 256 locked. + assert_eq!(Vesting::vesting_balance(&4), Some(256 * 5)); + // Verify only root can call. + assert_noop!(Vesting::force_remove_vesting_schedule(Some(4).into(), 4, 0), BadOrigin); + // Verify that root can remove the schedule. + assert_ok!(Vesting::force_remove_vesting_schedule(RawOrigin::Root.into(), 4, 0)); + // Verify that last event is VestingCompleted. + System::assert_last_event(Event::VestingCompleted { account: 4 }.into()); + // Appropriate storage is cleaned up. + assert!(!>::contains_key(4)); + // Check the vesting balance is zero. + assert_eq!(Vesting::vesting(&4), None); + // Verifies that trying to remove a schedule when it doesnt exist throws error. + assert_noop!( + Vesting::force_remove_vesting_schedule(RawOrigin::Root.into(), 4, 0), + Error::::InvalidScheduleParams + ); + }); +} diff --git a/substrate/frame/vesting/src/weights.rs b/substrate/frame/vesting/src/weights.rs index 17bde8885777..ddc7db8fa61f 100644 --- a/substrate/frame/vesting/src/weights.rs +++ b/substrate/frame/vesting/src/weights.rs @@ -15,32 +15,29 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_vesting +//! Autogenerated weights for `pallet_vesting` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-06-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-10-29, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-e8ezs4ez-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-vmdtonbz-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` // Executed Command: -// ./target/production/substrate +// target/production/substrate-node // benchmark // pallet -// --chain=dev // --steps=50 // --repeat=20 -// --pallet=pallet_vesting -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/vesting/src/weights.rs -// --header=./HEADER-APACHE2 -// --template=./.maintain/frame-weight-template.hbs +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_vesting +// --chain=dev +// --header=./substrate/HEADER-APACHE2 +// --output=./substrate/frame/vesting/src/weights.rs +// --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,7 +47,7 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions needed for pallet_vesting. +/// Weight functions needed for `pallet_vesting`. pub trait WeightInfo { fn vest_locked(l: u32, s: u32, ) -> Weight; fn vest_unlocked(l: u32, s: u32, ) -> Weight; @@ -60,372 +57,419 @@ pub trait WeightInfo { fn force_vested_transfer(l: u32, s: u32, ) -> Weight; fn not_unlocking_merge_schedules(l: u32, s: u32, ) -> Weight; fn unlocking_merge_schedules(l: u32, s: u32, ) -> Weight; + fn force_remove_vesting_schedule(l: u32, s: u32, ) -> Weight; } -/// Weights for pallet_vesting using the Substrate node and recommended hardware. +/// Weights for `pallet_vesting` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - /// Storage: Vesting Vesting (r:1 w:1) - /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: `Vesting::Vesting` (r:1 w:1) + /// Proof: `Vesting::Vesting` (`max_values`: None, `max_size`: Some(1057), added: 3532, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[1, 28]`. fn vest_locked(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `381 + l * (25 Β±0) + s * (36 Β±0)` // Estimated: `4764` - // Minimum execution time: 35_336_000 picoseconds. - Weight::from_parts(34_290_169, 4764) - // Standard Error: 1_381 - .saturating_add(Weight::from_parts(76_354, 0).saturating_mul(l.into())) - // Standard Error: 2_457 - .saturating_add(Weight::from_parts(81_362, 0).saturating_mul(s.into())) + // Minimum execution time: 32_846_000 picoseconds. + Weight::from_parts(30_974_459, 4764) + // Standard Error: 1_755 + .saturating_add(Weight::from_parts(73_138, 0).saturating_mul(l.into())) + // Standard Error: 3_123 + .saturating_add(Weight::from_parts(82_417, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Vesting Vesting (r:1 w:1) - /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: `Vesting::Vesting` (r:1 w:1) + /// Proof: `Vesting::Vesting` (`max_values`: None, `max_size`: Some(1057), added: 3532, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[1, 28]`. fn vest_unlocked(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `381 + l * (25 Β±0) + s * (36 Β±0)` // Estimated: `4764` - // Minimum execution time: 38_540_000 picoseconds. - Weight::from_parts(38_893_820, 4764) - // Standard Error: 1_710 - .saturating_add(Weight::from_parts(62_106, 0).saturating_mul(l.into())) - // Standard Error: 3_043 - .saturating_add(Weight::from_parts(41_966, 0).saturating_mul(s.into())) + // Minimum execution time: 34_360_000 picoseconds. + Weight::from_parts(34_964_080, 4764) + // Standard Error: 1_996 + .saturating_add(Weight::from_parts(48_024, 0).saturating_mul(l.into())) + // Standard Error: 3_552 + .saturating_add(Weight::from_parts(34_411, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: Vesting Vesting (r:1 w:1) - /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Vesting::Vesting` (r:1 w:1) + /// Proof: `Vesting::Vesting` (`max_values`: None, `max_size`: Some(1057), added: 3532, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[1, 28]`. fn vest_other_locked(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `484 + l * (25 Β±0) + s * (36 Β±0)` // Estimated: `4764` - // Minimum execution time: 37_529_000 picoseconds. - Weight::from_parts(36_781_151, 4764) - // Standard Error: 1_490 - .saturating_add(Weight::from_parts(76_322, 0).saturating_mul(l.into())) - // Standard Error: 2_652 - .saturating_add(Weight::from_parts(76_914, 0).saturating_mul(s.into())) + // Minimum execution time: 33_859_000 picoseconds. + Weight::from_parts(32_950_681, 4764) + // Standard Error: 1_745 + .saturating_add(Weight::from_parts(69_323, 0).saturating_mul(l.into())) + // Standard Error: 3_105 + .saturating_add(Weight::from_parts(86_370, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } - /// Storage: Vesting Vesting (r:1 w:1) - /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Vesting::Vesting` (r:1 w:1) + /// Proof: `Vesting::Vesting` (`max_values`: None, `max_size`: Some(1057), added: 3532, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[1, 28]`. fn vest_other_unlocked(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `484 + l * (25 Β±0) + s * (36 Β±0)` // Estimated: `4764` - // Minimum execution time: 41_217_000 picoseconds. - Weight::from_parts(40_942_515, 4764) - // Standard Error: 2_098 - .saturating_add(Weight::from_parts(65_213, 0).saturating_mul(l.into())) - // Standard Error: 3_733 - .saturating_add(Weight::from_parts(63_326, 0).saturating_mul(s.into())) + // Minimum execution time: 36_239_000 picoseconds. + Weight::from_parts(36_459_756, 4764) + // Standard Error: 2_051 + .saturating_add(Weight::from_parts(56_670, 0).saturating_mul(l.into())) + // Standard Error: 3_650 + .saturating_add(Weight::from_parts(49_663, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } - /// Storage: Vesting Vesting (r:1 w:1) - /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: `Vesting::Vesting` (r:1 w:1) + /// Proof: `Vesting::Vesting` (`max_values`: None, `max_size`: Some(1057), added: 3532, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[0, 27]`. fn vested_transfer(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `555 + l * (25 Β±0) + s * (36 Β±0)` // Estimated: `4764` - // Minimum execution time: 76_396_000 picoseconds. - Weight::from_parts(77_085_336, 4764) - // Standard Error: 2_795 - .saturating_add(Weight::from_parts(88_995, 0).saturating_mul(l.into())) - // Standard Error: 4_974 - .saturating_add(Weight::from_parts(135_384, 0).saturating_mul(s.into())) + // Minimum execution time: 70_330_000 picoseconds. + Weight::from_parts(71_196_328, 4764) + // Standard Error: 2_923 + .saturating_add(Weight::from_parts(67_238, 0).saturating_mul(l.into())) + // Standard Error: 5_201 + .saturating_add(Weight::from_parts(89_102, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } - /// Storage: Vesting Vesting (r:1 w:1) - /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) - /// Storage: System Account (r:2 w:2) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: `Vesting::Vesting` (r:1 w:1) + /// Proof: `Vesting::Vesting` (`max_values`: None, `max_size`: Some(1057), added: 3532, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[0, 27]`. fn force_vested_transfer(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `658 + l * (25 Β±0) + s * (36 Β±0)` // Estimated: `6196` - // Minimum execution time: 77_312_000 picoseconds. - Weight::from_parts(79_600_900, 6196) - // Standard Error: 3_232 - .saturating_add(Weight::from_parts(78_018, 0).saturating_mul(l.into())) - // Standard Error: 5_750 - .saturating_add(Weight::from_parts(100_848, 0).saturating_mul(s.into())) + // Minimum execution time: 70_235_000 picoseconds. + Weight::from_parts(71_960_020, 6196) + // Standard Error: 2_493 + .saturating_add(Weight::from_parts(64_835, 0).saturating_mul(l.into())) + // Standard Error: 4_436 + .saturating_add(Weight::from_parts(102_159, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } - /// Storage: Vesting Vesting (r:1 w:1) - /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Vesting::Vesting` (r:1 w:1) + /// Proof: `Vesting::Vesting` (`max_values`: None, `max_size`: Some(1057), added: 3532, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[2, 28]`. fn not_unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `482 + l * (25 Β±0) + s * (36 Β±0)` // Estimated: `4764` - // Minimum execution time: 38_769_000 picoseconds. - Weight::from_parts(37_752_437, 4764) - // Standard Error: 1_415 - .saturating_add(Weight::from_parts(78_398, 0).saturating_mul(l.into())) - // Standard Error: 2_614 - .saturating_add(Weight::from_parts(78_922, 0).saturating_mul(s.into())) + // Minimum execution time: 34_352_000 picoseconds. + Weight::from_parts(33_697_027, 4764) + // Standard Error: 2_008 + .saturating_add(Weight::from_parts(79_270, 0).saturating_mul(l.into())) + // Standard Error: 3_710 + .saturating_add(Weight::from_parts(60_691, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } - /// Storage: Vesting Vesting (r:1 w:1) - /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Vesting::Vesting` (r:1 w:1) + /// Proof: `Vesting::Vesting` (`max_values`: None, `max_size`: Some(1057), added: 3532, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[2, 28]`. fn unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `482 + l * (25 Β±0) + s * (36 Β±0)` // Estimated: `4764` - // Minimum execution time: 43_021_000 picoseconds. - Weight::from_parts(42_182_858, 4764) - // Standard Error: 1_747 - .saturating_add(Weight::from_parts(83_938, 0).saturating_mul(l.into())) - // Standard Error: 3_227 - .saturating_add(Weight::from_parts(84_652, 0).saturating_mul(s.into())) + // Minimum execution time: 37_467_000 picoseconds. + Weight::from_parts(36_866_847, 4764) + // Standard Error: 1_692 + .saturating_add(Weight::from_parts(57_882, 0).saturating_mul(l.into())) + // Standard Error: 3_124 + .saturating_add(Weight::from_parts(80_266, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `Vesting::Vesting` (r:1 w:1) + /// Proof: `Vesting::Vesting` (`max_values`: None, `max_size`: Some(1057), added: 3532, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `l` is `[0, 49]`. + /// The range of component `s` is `[2, 28]`. + fn force_remove_vesting_schedule(l: u32, s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `555 + l * (25 Β±0) + s * (36 Β±0)` + // Estimated: `4764` + // Minimum execution time: 41_497_000 picoseconds. + Weight::from_parts(38_763_834, 4764) + // Standard Error: 2_030 + .saturating_add(Weight::from_parts(99_580, 0).saturating_mul(l.into())) + // Standard Error: 3_750 + .saturating_add(Weight::from_parts(132_188, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } } -// For backwards compatibility and tests +// For backwards compatibility and tests. impl WeightInfo for () { - /// Storage: Vesting Vesting (r:1 w:1) - /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: `Vesting::Vesting` (r:1 w:1) + /// Proof: `Vesting::Vesting` (`max_values`: None, `max_size`: Some(1057), added: 3532, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[1, 28]`. fn vest_locked(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `381 + l * (25 Β±0) + s * (36 Β±0)` // Estimated: `4764` - // Minimum execution time: 35_336_000 picoseconds. - Weight::from_parts(34_290_169, 4764) - // Standard Error: 1_381 - .saturating_add(Weight::from_parts(76_354, 0).saturating_mul(l.into())) - // Standard Error: 2_457 - .saturating_add(Weight::from_parts(81_362, 0).saturating_mul(s.into())) + // Minimum execution time: 32_846_000 picoseconds. + Weight::from_parts(30_974_459, 4764) + // Standard Error: 1_755 + .saturating_add(Weight::from_parts(73_138, 0).saturating_mul(l.into())) + // Standard Error: 3_123 + .saturating_add(Weight::from_parts(82_417, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Vesting Vesting (r:1 w:1) - /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: `Vesting::Vesting` (r:1 w:1) + /// Proof: `Vesting::Vesting` (`max_values`: None, `max_size`: Some(1057), added: 3532, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[1, 28]`. fn vest_unlocked(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `381 + l * (25 Β±0) + s * (36 Β±0)` // Estimated: `4764` - // Minimum execution time: 38_540_000 picoseconds. - Weight::from_parts(38_893_820, 4764) - // Standard Error: 1_710 - .saturating_add(Weight::from_parts(62_106, 0).saturating_mul(l.into())) - // Standard Error: 3_043 - .saturating_add(Weight::from_parts(41_966, 0).saturating_mul(s.into())) + // Minimum execution time: 34_360_000 picoseconds. + Weight::from_parts(34_964_080, 4764) + // Standard Error: 1_996 + .saturating_add(Weight::from_parts(48_024, 0).saturating_mul(l.into())) + // Standard Error: 3_552 + .saturating_add(Weight::from_parts(34_411, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: Vesting Vesting (r:1 w:1) - /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Vesting::Vesting` (r:1 w:1) + /// Proof: `Vesting::Vesting` (`max_values`: None, `max_size`: Some(1057), added: 3532, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[1, 28]`. fn vest_other_locked(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `484 + l * (25 Β±0) + s * (36 Β±0)` // Estimated: `4764` - // Minimum execution time: 37_529_000 picoseconds. - Weight::from_parts(36_781_151, 4764) - // Standard Error: 1_490 - .saturating_add(Weight::from_parts(76_322, 0).saturating_mul(l.into())) - // Standard Error: 2_652 - .saturating_add(Weight::from_parts(76_914, 0).saturating_mul(s.into())) + // Minimum execution time: 33_859_000 picoseconds. + Weight::from_parts(32_950_681, 4764) + // Standard Error: 1_745 + .saturating_add(Weight::from_parts(69_323, 0).saturating_mul(l.into())) + // Standard Error: 3_105 + .saturating_add(Weight::from_parts(86_370, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } - /// Storage: Vesting Vesting (r:1 w:1) - /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Vesting::Vesting` (r:1 w:1) + /// Proof: `Vesting::Vesting` (`max_values`: None, `max_size`: Some(1057), added: 3532, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[1, 28]`. fn vest_other_unlocked(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `484 + l * (25 Β±0) + s * (36 Β±0)` // Estimated: `4764` - // Minimum execution time: 41_217_000 picoseconds. - Weight::from_parts(40_942_515, 4764) - // Standard Error: 2_098 - .saturating_add(Weight::from_parts(65_213, 0).saturating_mul(l.into())) - // Standard Error: 3_733 - .saturating_add(Weight::from_parts(63_326, 0).saturating_mul(s.into())) + // Minimum execution time: 36_239_000 picoseconds. + Weight::from_parts(36_459_756, 4764) + // Standard Error: 2_051 + .saturating_add(Weight::from_parts(56_670, 0).saturating_mul(l.into())) + // Standard Error: 3_650 + .saturating_add(Weight::from_parts(49_663, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } - /// Storage: Vesting Vesting (r:1 w:1) - /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: `Vesting::Vesting` (r:1 w:1) + /// Proof: `Vesting::Vesting` (`max_values`: None, `max_size`: Some(1057), added: 3532, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[0, 27]`. fn vested_transfer(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `555 + l * (25 Β±0) + s * (36 Β±0)` // Estimated: `4764` - // Minimum execution time: 76_396_000 picoseconds. - Weight::from_parts(77_085_336, 4764) - // Standard Error: 2_795 - .saturating_add(Weight::from_parts(88_995, 0).saturating_mul(l.into())) - // Standard Error: 4_974 - .saturating_add(Weight::from_parts(135_384, 0).saturating_mul(s.into())) + // Minimum execution time: 70_330_000 picoseconds. + Weight::from_parts(71_196_328, 4764) + // Standard Error: 2_923 + .saturating_add(Weight::from_parts(67_238, 0).saturating_mul(l.into())) + // Standard Error: 5_201 + .saturating_add(Weight::from_parts(89_102, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } - /// Storage: Vesting Vesting (r:1 w:1) - /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) - /// Storage: System Account (r:2 w:2) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: `Vesting::Vesting` (r:1 w:1) + /// Proof: `Vesting::Vesting` (`max_values`: None, `max_size`: Some(1057), added: 3532, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[0, 27]`. fn force_vested_transfer(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `658 + l * (25 Β±0) + s * (36 Β±0)` // Estimated: `6196` - // Minimum execution time: 77_312_000 picoseconds. - Weight::from_parts(79_600_900, 6196) - // Standard Error: 3_232 - .saturating_add(Weight::from_parts(78_018, 0).saturating_mul(l.into())) - // Standard Error: 5_750 - .saturating_add(Weight::from_parts(100_848, 0).saturating_mul(s.into())) + // Minimum execution time: 70_235_000 picoseconds. + Weight::from_parts(71_960_020, 6196) + // Standard Error: 2_493 + .saturating_add(Weight::from_parts(64_835, 0).saturating_mul(l.into())) + // Standard Error: 4_436 + .saturating_add(Weight::from_parts(102_159, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } - /// Storage: Vesting Vesting (r:1 w:1) - /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Vesting::Vesting` (r:1 w:1) + /// Proof: `Vesting::Vesting` (`max_values`: None, `max_size`: Some(1057), added: 3532, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[2, 28]`. fn not_unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `482 + l * (25 Β±0) + s * (36 Β±0)` // Estimated: `4764` - // Minimum execution time: 38_769_000 picoseconds. - Weight::from_parts(37_752_437, 4764) - // Standard Error: 1_415 - .saturating_add(Weight::from_parts(78_398, 0).saturating_mul(l.into())) - // Standard Error: 2_614 - .saturating_add(Weight::from_parts(78_922, 0).saturating_mul(s.into())) + // Minimum execution time: 34_352_000 picoseconds. + Weight::from_parts(33_697_027, 4764) + // Standard Error: 2_008 + .saturating_add(Weight::from_parts(79_270, 0).saturating_mul(l.into())) + // Standard Error: 3_710 + .saturating_add(Weight::from_parts(60_691, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } - /// Storage: Vesting Vesting (r:1 w:1) - /// Proof: Vesting Vesting (max_values: None, max_size: Some(1057), added: 3532, mode: MaxEncodedLen) - /// Storage: Balances Locks (r:1 w:1) - /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) - /// Storage: Balances Freezes (r:1 w:0) - /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) - /// Storage: System Account (r:1 w:1) - /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: `Vesting::Vesting` (r:1 w:1) + /// Proof: `Vesting::Vesting` (`max_values`: None, `max_size`: Some(1057), added: 3532, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// The range of component `l` is `[0, 49]`. /// The range of component `s` is `[2, 28]`. fn unlocking_merge_schedules(l: u32, s: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `482 + l * (25 Β±0) + s * (36 Β±0)` // Estimated: `4764` - // Minimum execution time: 43_021_000 picoseconds. - Weight::from_parts(42_182_858, 4764) - // Standard Error: 1_747 - .saturating_add(Weight::from_parts(83_938, 0).saturating_mul(l.into())) - // Standard Error: 3_227 - .saturating_add(Weight::from_parts(84_652, 0).saturating_mul(s.into())) + // Minimum execution time: 37_467_000 picoseconds. + Weight::from_parts(36_866_847, 4764) + // Standard Error: 1_692 + .saturating_add(Weight::from_parts(57_882, 0).saturating_mul(l.into())) + // Standard Error: 3_124 + .saturating_add(Weight::from_parts(80_266, 0).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: `Vesting::Vesting` (r:1 w:1) + /// Proof: `Vesting::Vesting` (`max_values`: None, `max_size`: Some(1057), added: 3532, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `l` is `[0, 49]`. + /// The range of component `s` is `[2, 28]`. + fn force_remove_vesting_schedule(l: u32, s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `555 + l * (25 Β±0) + s * (36 Β±0)` + // Estimated: `4764` + // Minimum execution time: 41_497_000 picoseconds. + Weight::from_parts(38_763_834, 4764) + // Standard Error: 2_030 + .saturating_add(Weight::from_parts(99_580, 0).saturating_mul(l.into())) + // Standard Error: 3_750 + .saturating_add(Weight::from_parts(132_188, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } From 4ac9c4a36419439eb3c7288eade40e67c2af28ff Mon Sep 17 00:00:00 2001 From: Richard Melkonian <35300528+0xmovses@users.noreply.github.com> Date: Mon, 6 Nov 2023 12:54:37 +0000 Subject: [PATCH 24/31] rename benchmark (#2173) A quick fix where a benchmark test was wrongly renamed in this PR https://github.com/paritytech/polkadot-sdk/pull/1868 --- substrate/frame/alliance/src/benchmarking.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substrate/frame/alliance/src/benchmarking.rs b/substrate/frame/alliance/src/benchmarking.rs index 77294c6b6646..37cc3314037e 100644 --- a/substrate/frame/alliance/src/benchmarking.rs +++ b/substrate/frame/alliance/src/benchmarking.rs @@ -782,7 +782,7 @@ mod benchmarks { } #[benchmark] - fn add_scrupulous_items( + fn add_unscrupulous_items( n: Linear<0, { T::MaxUnscrupulousItems::get() }>, l: Linear<0, { T::MaxWebsiteUrlLength::get() }>, ) -> Result<(), BenchmarkError> { From 0570b6fa9e605258623ec55851111534d21d2f8a Mon Sep 17 00:00:00 2001 From: Andrei Sandu <54316454+sandreim@users.noreply.github.com> Date: Mon, 6 Nov 2023 15:21:32 +0200 Subject: [PATCH 25/31] approval-voting improvement: include all tranche0 assignments in one certificate (#1178) **_PR migrated from https://github.com/paritytech/polkadot/pull/6782_** This PR will upgrade the network protocol to version 3 -> VStaging which will later be renamed to V3. This version introduces a new kind of assignment certificate that will be used for tranche0 assignments. Instead of issuing/importing one tranche0 assignment per candidate, there will be just one certificate per relay chain block per validator. However, we will not be sending out the new assignment certificates, yet. So everything should work exactly as before. Once the majority of the validators have been upgraded to the new protocol version we will enable the new certificates (starting at a specific relay chain block) with a new client update. There are still a few things that need to be done: - [x] Use bitfield instead of Vec: https://github.com/paritytech/polkadot/pull/6802 - [x] Fix existing approval-distribution and approval-voting tests - [x] Fix bitfield-distribution and statement-distribution tests - [x] Fix network bridge tests - [x] Implement todos in the code - [x] Add tests to cover new code - [x] Update metrics - [x] Remove the approval distribution aggression levels: TBD PR - [x] Parachains DB migration - [x] Test network protocol upgrade on Versi - [x] Versi Load test - [x] Add Zombienet test - [x] Documentation updates - [x] Fix for sending DistributeAssignment for each candidate claimed by a v2 assignment (warning: Importing locally an already known assignment) - [x] Fix AcceptedDuplicate - [x] Fix DB migration so that we can still keep old data. - [x] Final Versi burn in --------- Signed-off-by: Andrei Sandu Signed-off-by: Alexandru Gheorghe Co-authored-by: Alexandru Gheorghe --- .gitlab/pipeline/zombienet/polkadot.yml | 8 + Cargo.lock | 10 + polkadot/Cargo.toml | 2 + polkadot/cli/Cargo.toml | 2 + polkadot/node/core/approval-voting/Cargo.toml | 6 + .../approval-voting/src/approval_checking.rs | 60 +- .../approval-voting/src/approval_db/mod.rs | 1 + .../approval-voting/src/approval_db/v1/mod.rs | 268 +--- .../src/approval_db/v2/migration_helpers.rs | 244 +++ .../approval-voting/src/approval_db/v2/mod.rs | 394 +++++ .../src/approval_db/v2/tests.rs | 570 +++++++ .../node/core/approval-voting/src/backend.rs | 16 +- .../node/core/approval-voting/src/criteria.rs | 546 +++++-- .../node/core/approval-voting/src/import.rs | 38 +- polkadot/node/core/approval-voting/src/lib.rs | 520 +++++-- polkadot/node/core/approval-voting/src/ops.rs | 2 +- .../approval-voting/src/persisted_entries.rs | 135 +- .../node/core/approval-voting/src/tests.rs | 395 ++++- .../node/core/approval-voting/src/time.rs | 2 +- .../core/dispute-coordinator/src/import.rs | 2 +- .../network/approval-distribution/Cargo.toml | 2 + .../network/approval-distribution/src/lib.rs | 1375 +++++++++++------ .../approval-distribution/src/metrics.rs | 45 +- .../approval-distribution/src/tests.rs | 502 ++++-- .../network/bitfield-distribution/src/lib.rs | 52 +- polkadot/node/network/bridge/src/network.rs | 127 +- polkadot/node/network/bridge/src/rx/mod.rs | 155 +- polkadot/node/network/bridge/src/rx/tests.rs | 3 + polkadot/node/network/bridge/src/tx/mod.rs | 101 +- polkadot/node/network/bridge/src/tx/tests.rs | 5 +- .../src/collator_side/mod.rs | 11 +- .../src/validator_side/mod.rs | 12 +- .../node/network/gossip-support/src/lib.rs | 1 + polkadot/node/network/protocol/Cargo.toml | 3 + polkadot/node/network/protocol/src/lib.rs | 130 +- .../node/network/protocol/src/peer_set.rs | 12 + .../src/legacy_v1/mod.rs | 57 +- .../network/statement-distribution/src/lib.rs | 7 +- .../statement-distribution/src/v2/mod.rs | 318 +++- polkadot/node/primitives/Cargo.toml | 1 + polkadot/node/primitives/src/approval.rs | 655 ++++++-- polkadot/node/service/Cargo.toml | 5 + .../node/service/src/parachains_db/mod.rs | 36 +- .../node/service/src/parachains_db/upgrade.rs | 369 ++++- polkadot/node/service/src/tests.rs | 2 +- polkadot/node/subsystem-types/Cargo.toml | 1 + polkadot/node/subsystem-types/src/messages.rs | 13 +- .../undying/collator/Cargo.toml | 3 + .../src/node/approval/approval-voting.md | 61 +- .../src/protocol-approval.md | 33 +- .../implementers-guide/src/types/approval.md | 29 + .../implementers-guide/src/types/runtime.md | 2 +- .../0006-parachains-max-tranche0.toml | 39 + .../0006-parachains-max-tranche0.zndsl | 27 + prdoc/pr_1178.prdoc | 23 + 55 files changed, 5641 insertions(+), 1797 deletions(-) create mode 100644 polkadot/node/core/approval-voting/src/approval_db/v2/migration_helpers.rs create mode 100644 polkadot/node/core/approval-voting/src/approval_db/v2/mod.rs create mode 100644 polkadot/node/core/approval-voting/src/approval_db/v2/tests.rs create mode 100644 polkadot/zombienet_tests/functional/0006-parachains-max-tranche0.toml create mode 100644 polkadot/zombienet_tests/functional/0006-parachains-max-tranche0.zndsl create mode 100644 prdoc/pr_1178.prdoc diff --git a/.gitlab/pipeline/zombienet/polkadot.yml b/.gitlab/pipeline/zombienet/polkadot.yml index 0402c194134b..8fc8b280bba8 100644 --- a/.gitlab/pipeline/zombienet/polkadot.yml +++ b/.gitlab/pipeline/zombienet/polkadot.yml @@ -105,6 +105,14 @@ zombienet-polkadot-functional-0005-parachains-disputes-past-session: --local-dir="${LOCAL_DIR}/functional" --test="0005-parachains-disputes-past-session.zndsl" +zombienet-polkadot-functional-0006-parachains-max-tranche0: + extends: + - .zombienet-polkadot-common + script: + - /home/nonroot/zombie-net/scripts/ci/run-test-local-env-manager.sh + --local-dir="${LOCAL_DIR}/functional" + --test="0006-parachains-max-tranche0.zndsl" + zombienet-polkadot-smoke-0001-parachains-smoke-test: extends: - .zombienet-polkadot-common diff --git a/Cargo.lock b/Cargo.lock index 1a4c532f9c78..764d4ed01aa5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11761,9 +11761,11 @@ name = "polkadot-approval-distribution" version = "1.0.0" dependencies = [ "assert_matches", + "bitvec", "env_logger 0.9.3", "futures", "futures-timer", + "itertools 0.10.5", "log", "polkadot-node-jaeger", "polkadot-node-metrics", @@ -12071,10 +12073,13 @@ dependencies = [ "async-trait", "bitvec", "derive_more", + "env_logger 0.9.3", "futures", "futures-timer", + "itertools 0.10.5", "kvdb", "kvdb-memorydb", + "log", "merlin 2.0.1", "parity-scale-codec", "parking_lot 0.12.1", @@ -12086,6 +12091,8 @@ dependencies = [ "polkadot-overseer", "polkadot-primitives", "polkadot-primitives-test-helpers", + "rand 0.8.5", + "rand_chacha 0.3.1", "rand_core 0.5.1", "sc-keystore", "schnellru", @@ -12541,6 +12548,7 @@ dependencies = [ name = "polkadot-node-primitives" version = "1.0.0" dependencies = [ + "bitvec", "bounded-vec", "futures", "parity-scale-codec", @@ -12592,6 +12600,7 @@ name = "polkadot-node-subsystem-types" version = "1.0.0" dependencies = [ "async-trait", + "bitvec", "derive_more", "futures", "orchestra", @@ -13033,6 +13042,7 @@ dependencies = [ "polkadot-overseer", "polkadot-parachain-primitives", "polkadot-primitives", + "polkadot-primitives-test-helpers", "polkadot-rpc", "polkadot-runtime-parachains", "polkadot-statement-distribution", diff --git a/polkadot/Cargo.toml b/polkadot/Cargo.toml index 8540f65d70c4..fb161848fb6c 100644 --- a/polkadot/Cargo.toml +++ b/polkadot/Cargo.toml @@ -64,6 +64,8 @@ jemalloc-allocator = [ "polkadot-node-core-pvf/jemalloc-allocator", "polkadot-overseer/jemalloc-allocator", ] +network-protocol-staging = [ "polkadot-cli/network-protocol-staging" ] + # Enables timeout-based tests supposed to be run only in CI environment as they may be flaky # when run locally depending on system load diff --git a/polkadot/cli/Cargo.toml b/polkadot/cli/Cargo.toml index 9cad7e88dfe9..0c2925c76e82 100644 --- a/polkadot/cli/Cargo.toml +++ b/polkadot/cli/Cargo.toml @@ -74,3 +74,5 @@ runtime-metrics = [ "polkadot-node-metrics/runtime-metrics", "service/runtime-metrics", ] + +network-protocol-staging = [ "service/network-protocol-staging" ] diff --git a/polkadot/node/core/approval-voting/Cargo.toml b/polkadot/node/core/approval-voting/Cargo.toml index acad0d1fa4e4..f7ea1d210777 100644 --- a/polkadot/node/core/approval-voting/Cargo.toml +++ b/polkadot/node/core/approval-voting/Cargo.toml @@ -17,6 +17,7 @@ schnorrkel = "0.9.1" kvdb = "0.13.0" derive_more = "0.99.17" thiserror = "1.0.48" +itertools = "0.10.5" polkadot-node-subsystem = { path = "../../subsystem" } polkadot-node-subsystem-util = { path = "../../subsystem-util" } @@ -30,6 +31,9 @@ sp-consensus = { path = "../../../../substrate/primitives/consensus/common", def sp-consensus-slots = { path = "../../../../substrate/primitives/consensus/slots", default-features = false } sp-application-crypto = { path = "../../../../substrate/primitives/application-crypto", default-features = false, features = ["full_crypto"] } sp-runtime = { path = "../../../../substrate/primitives/runtime", default-features = false } +rand_core = "0.5.1" +rand_chacha = { version = "0.3.1" } +rand = "0.8.5" [dev-dependencies] async-trait = "0.1.57" @@ -43,3 +47,5 @@ polkadot-node-subsystem-test-helpers = { path = "../../subsystem-test-helpers" } assert_matches = "1.4.0" kvdb-memorydb = "0.13.0" test-helpers = { package = "polkadot-primitives-test-helpers", path = "../../../primitives/test-helpers" } +log = "0.4.17" +env_logger = "0.9.0" diff --git a/polkadot/node/core/approval-voting/src/approval_checking.rs b/polkadot/node/core/approval-voting/src/approval_checking.rs index f345b57029b5..5d24ff164193 100644 --- a/polkadot/node/core/approval-voting/src/approval_checking.rs +++ b/polkadot/node/core/approval-voting/src/approval_checking.rs @@ -17,7 +17,7 @@ //! Utilities for checking whether a candidate has been approved under a given block. use bitvec::{order::Lsb0 as BitOrderLsb0, slice::BitSlice}; -use polkadot_node_primitives::approval::DelayTranche; +use polkadot_node_primitives::approval::v1::DelayTranche; use polkadot_primitives::ValidatorIndex; use crate::{ @@ -472,9 +472,9 @@ mod tests { } .into(); - let approval_entry = approval_db::v1::ApprovalEntry { + let approval_entry = approval_db::v2::ApprovalEntry { tranches: Vec::new(), - assignments: BitVec::default(), + assigned_validators: BitVec::default(), our_assignment: None, our_approval_sig: None, backing_group: GroupIndex(0), @@ -509,22 +509,22 @@ mod tests { candidate.mark_approval(ValidatorIndex(i)); } - let approval_entry = approval_db::v1::ApprovalEntry { + let approval_entry = approval_db::v2::ApprovalEntry { tranches: vec![ - approval_db::v1::TrancheEntry { + approval_db::v2::TrancheEntry { tranche: 0, assignments: (0..2).map(|i| (ValidatorIndex(i), 0.into())).collect(), }, - approval_db::v1::TrancheEntry { + approval_db::v2::TrancheEntry { tranche: 1, assignments: (2..5).map(|i| (ValidatorIndex(i), 1.into())).collect(), }, - approval_db::v1::TrancheEntry { + approval_db::v2::TrancheEntry { tranche: 2, assignments: (5..10).map(|i| (ValidatorIndex(i), 0.into())).collect(), }, ], - assignments: bitvec![u8, BitOrderLsb0; 1; 10], + assigned_validators: bitvec![u8, BitOrderLsb0; 1; 10], our_assignment: None, our_approval_sig: None, backing_group: GroupIndex(0), @@ -581,22 +581,22 @@ mod tests { candidate.mark_approval(ValidatorIndex(i)); } - let approval_entry = approval_db::v1::ApprovalEntry { + let approval_entry = approval_db::v2::ApprovalEntry { tranches: vec![ - approval_db::v1::TrancheEntry { + approval_db::v2::TrancheEntry { tranche: 0, assignments: (0..4).map(|i| (ValidatorIndex(i), 0.into())).collect(), }, - approval_db::v1::TrancheEntry { + approval_db::v2::TrancheEntry { tranche: 1, assignments: (4..6).map(|i| (ValidatorIndex(i), 1.into())).collect(), }, - approval_db::v1::TrancheEntry { + approval_db::v2::TrancheEntry { tranche: 2, assignments: (6..10).map(|i| (ValidatorIndex(i), 0.into())).collect(), }, ], - assignments: bitvec![u8, BitOrderLsb0; 1; 10], + assigned_validators: bitvec![u8, BitOrderLsb0; 1; 10], our_assignment: None, our_approval_sig: None, backing_group: GroupIndex(0), @@ -647,9 +647,9 @@ mod tests { let no_show_duration = 10; let needed_approvals = 4; - let mut approval_entry: ApprovalEntry = approval_db::v1::ApprovalEntry { + let mut approval_entry: ApprovalEntry = approval_db::v2::ApprovalEntry { tranches: Vec::new(), - assignments: bitvec![u8, BitOrderLsb0; 0; 5], + assigned_validators: bitvec![u8, BitOrderLsb0; 0; 5], our_assignment: None, our_approval_sig: None, backing_group: GroupIndex(0), @@ -691,9 +691,9 @@ mod tests { let no_show_duration = 10; let needed_approvals = 4; - let mut approval_entry: ApprovalEntry = approval_db::v1::ApprovalEntry { + let mut approval_entry: ApprovalEntry = approval_db::v2::ApprovalEntry { tranches: Vec::new(), - assignments: bitvec![u8, BitOrderLsb0; 0; 10], + assigned_validators: bitvec![u8, BitOrderLsb0; 0; 10], our_assignment: None, our_approval_sig: None, backing_group: GroupIndex(0), @@ -731,9 +731,9 @@ mod tests { let no_show_duration = 10; let needed_approvals = 4; - let mut approval_entry: ApprovalEntry = approval_db::v1::ApprovalEntry { + let mut approval_entry: ApprovalEntry = approval_db::v2::ApprovalEntry { tranches: Vec::new(), - assignments: bitvec![u8, BitOrderLsb0; 0; 10], + assigned_validators: bitvec![u8, BitOrderLsb0; 0; 10], our_assignment: None, our_approval_sig: None, backing_group: GroupIndex(0), @@ -776,9 +776,9 @@ mod tests { let needed_approvals = 4; let n_validators = 8; - let mut approval_entry: ApprovalEntry = approval_db::v1::ApprovalEntry { + let mut approval_entry: ApprovalEntry = approval_db::v2::ApprovalEntry { tranches: Vec::new(), - assignments: bitvec![u8, BitOrderLsb0; 0; n_validators], + assigned_validators: bitvec![u8, BitOrderLsb0; 0; n_validators], our_assignment: None, our_approval_sig: None, backing_group: GroupIndex(0), @@ -843,9 +843,9 @@ mod tests { let needed_approvals = 4; let n_validators = 8; - let mut approval_entry: ApprovalEntry = approval_db::v1::ApprovalEntry { + let mut approval_entry: ApprovalEntry = approval_db::v2::ApprovalEntry { tranches: Vec::new(), - assignments: bitvec![u8, BitOrderLsb0; 0; n_validators], + assigned_validators: bitvec![u8, BitOrderLsb0; 0; n_validators], our_assignment: None, our_approval_sig: None, backing_group: GroupIndex(0), @@ -934,9 +934,9 @@ mod tests { let needed_approvals = 4; let n_validators = 8; - let mut approval_entry: ApprovalEntry = approval_db::v1::ApprovalEntry { + let mut approval_entry: ApprovalEntry = approval_db::v2::ApprovalEntry { tranches: Vec::new(), - assignments: bitvec![u8, BitOrderLsb0; 0; n_validators], + assigned_validators: bitvec![u8, BitOrderLsb0; 0; n_validators], our_assignment: None, our_approval_sig: None, backing_group: GroupIndex(0), @@ -1041,15 +1041,15 @@ mod tests { candidate.mark_approval(ValidatorIndex(i)); } - let approval_entry = approval_db::v1::ApprovalEntry { + let approval_entry = approval_db::v2::ApprovalEntry { tranches: vec![ // Assignments with invalid validator indexes. - approval_db::v1::TrancheEntry { + approval_db::v2::TrancheEntry { tranche: 1, assignments: (2..5).map(|i| (ValidatorIndex(i), 1.into())).collect(), }, ], - assignments: bitvec![u8, BitOrderLsb0; 1; 3], + assigned_validators: bitvec![u8, BitOrderLsb0; 1; 3], our_assignment: None, our_approval_sig: None, backing_group: GroupIndex(0), @@ -1094,12 +1094,12 @@ mod tests { ]; for test_tranche in test_tranches { - let mut approval_entry: ApprovalEntry = approval_db::v1::ApprovalEntry { + let mut approval_entry: ApprovalEntry = approval_db::v2::ApprovalEntry { tranches: Vec::new(), backing_group: GroupIndex(0), our_assignment: None, our_approval_sig: None, - assignments: bitvec![u8, BitOrderLsb0; 0; 3], + assigned_validators: bitvec![u8, BitOrderLsb0; 0; 3], approved: false, } .into(); diff --git a/polkadot/node/core/approval-voting/src/approval_db/mod.rs b/polkadot/node/core/approval-voting/src/approval_db/mod.rs index f0ace95613da..20fb6aa82d8d 100644 --- a/polkadot/node/core/approval-voting/src/approval_db/mod.rs +++ b/polkadot/node/core/approval-voting/src/approval_db/mod.rs @@ -31,3 +31,4 @@ //! time being we share the same DB with the rest of Substrate. pub mod v1; +pub mod v2; diff --git a/polkadot/node/core/approval-voting/src/approval_db/v1/mod.rs b/polkadot/node/core/approval-voting/src/approval_db/v1/mod.rs index c31389269d2e..011d0a559c02 100644 --- a/polkadot/node/core/approval-voting/src/approval_db/v1/mod.rs +++ b/polkadot/node/core/approval-voting/src/approval_db/v1/mod.rs @@ -23,144 +23,15 @@ //! require a db migration (check `node/service/src/parachains_db/upgrade.rs`). use parity_scale_codec::{Decode, Encode}; -use polkadot_node_primitives::approval::{AssignmentCert, DelayTranche}; -use polkadot_node_subsystem::{SubsystemError, SubsystemResult}; -use polkadot_node_subsystem_util::database::{DBTransaction, Database}; +use polkadot_node_primitives::approval::v1::{AssignmentCert, DelayTranche}; use polkadot_primitives::{ BlockNumber, CandidateHash, CandidateReceipt, CoreIndex, GroupIndex, Hash, SessionIndex, ValidatorIndex, ValidatorSignature, }; use sp_consensus_slots::Slot; +use std::collections::BTreeMap; -use bitvec::{order::Lsb0 as BitOrderLsb0, vec::BitVec}; -use std::{collections::BTreeMap, sync::Arc}; - -use crate::{ - backend::{Backend, BackendWriteOp}, - persisted_entries, -}; - -const STORED_BLOCKS_KEY: &[u8] = b"Approvals_StoredBlocks"; - -#[cfg(test)] -pub mod tests; - -/// `DbBackend` is a concrete implementation of the higher-level Backend trait -pub struct DbBackend { - inner: Arc, - config: Config, -} - -impl DbBackend { - /// Create a new [`DbBackend`] with the supplied key-value store and - /// config. - pub fn new(db: Arc, config: Config) -> Self { - DbBackend { inner: db, config } - } -} - -impl Backend for DbBackend { - fn load_block_entry( - &self, - block_hash: &Hash, - ) -> SubsystemResult> { - load_block_entry(&*self.inner, &self.config, block_hash).map(|e| e.map(Into::into)) - } - - fn load_candidate_entry( - &self, - candidate_hash: &CandidateHash, - ) -> SubsystemResult> { - load_candidate_entry(&*self.inner, &self.config, candidate_hash).map(|e| e.map(Into::into)) - } - - fn load_blocks_at_height(&self, block_height: &BlockNumber) -> SubsystemResult> { - load_blocks_at_height(&*self.inner, &self.config, block_height) - } - - fn load_all_blocks(&self) -> SubsystemResult> { - load_all_blocks(&*self.inner, &self.config) - } - - fn load_stored_blocks(&self) -> SubsystemResult> { - load_stored_blocks(&*self.inner, &self.config) - } - - /// Atomically write the list of operations, with later operations taking precedence over prior. - fn write(&mut self, ops: I) -> SubsystemResult<()> - where - I: IntoIterator, - { - let mut tx = DBTransaction::new(); - for op in ops { - match op { - BackendWriteOp::WriteStoredBlockRange(stored_block_range) => { - tx.put_vec( - self.config.col_approval_data, - &STORED_BLOCKS_KEY, - stored_block_range.encode(), - ); - }, - BackendWriteOp::DeleteStoredBlockRange => { - tx.delete(self.config.col_approval_data, &STORED_BLOCKS_KEY); - }, - BackendWriteOp::WriteBlocksAtHeight(h, blocks) => { - tx.put_vec( - self.config.col_approval_data, - &blocks_at_height_key(h), - blocks.encode(), - ); - }, - BackendWriteOp::DeleteBlocksAtHeight(h) => { - tx.delete(self.config.col_approval_data, &blocks_at_height_key(h)); - }, - BackendWriteOp::WriteBlockEntry(block_entry) => { - let block_entry: BlockEntry = block_entry.into(); - tx.put_vec( - self.config.col_approval_data, - &block_entry_key(&block_entry.block_hash), - block_entry.encode(), - ); - }, - BackendWriteOp::DeleteBlockEntry(hash) => { - tx.delete(self.config.col_approval_data, &block_entry_key(&hash)); - }, - BackendWriteOp::WriteCandidateEntry(candidate_entry) => { - let candidate_entry: CandidateEntry = candidate_entry.into(); - tx.put_vec( - self.config.col_approval_data, - &candidate_entry_key(&candidate_entry.candidate.hash()), - candidate_entry.encode(), - ); - }, - BackendWriteOp::DeleteCandidateEntry(candidate_hash) => { - tx.delete(self.config.col_approval_data, &candidate_entry_key(&candidate_hash)); - }, - } - } - - self.inner.write(tx).map_err(|e| e.into()) - } -} - -/// A range from earliest..last block number stored within the DB. -#[derive(Encode, Decode, Debug, Clone, PartialEq)] -pub struct StoredBlockRange(pub BlockNumber, pub BlockNumber); - -// slot_duration * 2 + DelayTranche gives the number of delay tranches since the -// unix epoch. -#[derive(Encode, Decode, Clone, Copy, Debug, PartialEq)] -pub struct Tick(u64); - -/// Convenience type definition -pub type Bitfield = BitVec; - -/// The database config. -#[derive(Debug, Clone, Copy)] -pub struct Config { - /// The column family in the database where data is stored. - pub col_approval_data: u32, -} +use super::v2::Bitfield; /// Details pertaining to our assignment on a block. #[derive(Encode, Decode, Debug, Clone, PartialEq)] @@ -171,15 +42,7 @@ pub struct OurAssignment { // Whether the assignment has been triggered already. pub triggered: bool, } - -/// Metadata regarding a specific tranche of assignments for a specific candidate. -#[derive(Encode, Decode, Debug, Clone, PartialEq)] -pub struct TrancheEntry { - pub tranche: DelayTranche, - // Assigned validators, and the instant we received their assignment, rounded - // to the nearest tick. - pub assignments: Vec<(ValidatorIndex, Tick)>, -} +use super::v2::TrancheEntry; /// Metadata regarding approval of a particular candidate within the context of some /// particular block. @@ -226,126 +89,3 @@ pub struct BlockEntry { pub approved_bitfield: Bitfield, pub children: Vec, } - -impl From for Tick { - fn from(tick: crate::Tick) -> Tick { - Tick(tick) - } -} - -impl From for crate::Tick { - fn from(tick: Tick) -> crate::Tick { - tick.0 - } -} - -/// Errors while accessing things from the DB. -#[derive(Debug, derive_more::From, derive_more::Display)] -pub enum Error { - Io(std::io::Error), - InvalidDecoding(parity_scale_codec::Error), -} - -impl std::error::Error for Error {} - -/// Result alias for DB errors. -pub type Result = std::result::Result; - -pub(crate) fn load_decode( - store: &dyn Database, - col_approval_data: u32, - key: &[u8], -) -> Result> { - match store.get(col_approval_data, key)? { - None => Ok(None), - Some(raw) => D::decode(&mut &raw[..]).map(Some).map_err(Into::into), - } -} - -/// The key a given block entry is stored under. -pub(crate) fn block_entry_key(block_hash: &Hash) -> [u8; 46] { - const BLOCK_ENTRY_PREFIX: [u8; 14] = *b"Approvals_blck"; - - let mut key = [0u8; 14 + 32]; - key[0..14].copy_from_slice(&BLOCK_ENTRY_PREFIX); - key[14..][..32].copy_from_slice(block_hash.as_ref()); - - key -} - -/// The key a given candidate entry is stored under. -pub(crate) fn candidate_entry_key(candidate_hash: &CandidateHash) -> [u8; 46] { - const CANDIDATE_ENTRY_PREFIX: [u8; 14] = *b"Approvals_cand"; - - let mut key = [0u8; 14 + 32]; - key[0..14].copy_from_slice(&CANDIDATE_ENTRY_PREFIX); - key[14..][..32].copy_from_slice(candidate_hash.0.as_ref()); - - key -} - -/// The key a set of block hashes corresponding to a block number is stored under. -pub(crate) fn blocks_at_height_key(block_number: BlockNumber) -> [u8; 16] { - const BLOCKS_AT_HEIGHT_PREFIX: [u8; 12] = *b"Approvals_at"; - - let mut key = [0u8; 12 + 4]; - key[0..12].copy_from_slice(&BLOCKS_AT_HEIGHT_PREFIX); - block_number.using_encoded(|s| key[12..16].copy_from_slice(s)); - - key -} - -/// Return all blocks which have entries in the DB, ascending, by height. -pub fn load_all_blocks(store: &dyn Database, config: &Config) -> SubsystemResult> { - let mut hashes = Vec::new(); - if let Some(stored_blocks) = load_stored_blocks(store, config)? { - for height in stored_blocks.0..stored_blocks.1 { - let blocks = load_blocks_at_height(store, config, &height)?; - hashes.extend(blocks); - } - } - - Ok(hashes) -} - -/// Load the stored-blocks key from the state. -pub fn load_stored_blocks( - store: &dyn Database, - config: &Config, -) -> SubsystemResult> { - load_decode(store, config.col_approval_data, STORED_BLOCKS_KEY) - .map_err(|e| SubsystemError::with_origin("approval-voting", e)) -} - -/// Load a blocks-at-height entry for a given block number. -pub fn load_blocks_at_height( - store: &dyn Database, - config: &Config, - block_number: &BlockNumber, -) -> SubsystemResult> { - load_decode(store, config.col_approval_data, &blocks_at_height_key(*block_number)) - .map(|x| x.unwrap_or_default()) - .map_err(|e| SubsystemError::with_origin("approval-voting", e)) -} - -/// Load a block entry from the aux store. -pub fn load_block_entry( - store: &dyn Database, - config: &Config, - block_hash: &Hash, -) -> SubsystemResult> { - load_decode(store, config.col_approval_data, &block_entry_key(block_hash)) - .map(|u: Option| u.map(|v| v.into())) - .map_err(|e| SubsystemError::with_origin("approval-voting", e)) -} - -/// Load a candidate entry from the aux store. -pub fn load_candidate_entry( - store: &dyn Database, - config: &Config, - candidate_hash: &CandidateHash, -) -> SubsystemResult> { - load_decode(store, config.col_approval_data, &candidate_entry_key(candidate_hash)) - .map(|u: Option| u.map(|v| v.into())) - .map_err(|e| SubsystemError::with_origin("approval-voting", e)) -} diff --git a/polkadot/node/core/approval-voting/src/approval_db/v2/migration_helpers.rs b/polkadot/node/core/approval-voting/src/approval_db/v2/migration_helpers.rs new file mode 100644 index 000000000000..74e997c7af84 --- /dev/null +++ b/polkadot/node/core/approval-voting/src/approval_db/v2/migration_helpers.rs @@ -0,0 +1,244 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! Approval DB migration helpers. +use super::*; +use crate::backend::Backend; +use polkadot_node_primitives::approval::v1::{ + AssignmentCert, AssignmentCertKind, VrfOutput, VrfProof, VrfSignature, RELAY_VRF_MODULO_CONTEXT, +}; +use polkadot_node_subsystem_util::database::Database; +use sp_application_crypto::sp_core::H256; +use std::{collections::HashSet, sync::Arc}; + +fn dummy_assignment_cert(kind: AssignmentCertKind) -> AssignmentCert { + let ctx = schnorrkel::signing_context(RELAY_VRF_MODULO_CONTEXT); + let msg = b"test-garbage"; + let mut prng = rand_core::OsRng; + let keypair = schnorrkel::Keypair::generate_with(&mut prng); + let (inout, proof, _) = keypair.vrf_sign(ctx.bytes(msg)); + let out = inout.to_output(); + + AssignmentCert { kind, vrf: VrfSignature { output: VrfOutput(out), proof: VrfProof(proof) } } +} + +fn make_block_entry_v1( + block_hash: Hash, + parent_hash: Hash, + block_number: BlockNumber, + candidates: Vec<(CoreIndex, CandidateHash)>, +) -> crate::approval_db::v1::BlockEntry { + crate::approval_db::v1::BlockEntry { + block_hash, + parent_hash, + block_number, + session: 1, + slot: Slot::from(1), + relay_vrf_story: [0u8; 32], + approved_bitfield: make_bitvec(candidates.len()), + candidates, + children: Vec::new(), + } +} + +fn make_bitvec(len: usize) -> BitVec { + bitvec::bitvec![u8, BitOrderLsb0; 0; len] +} + +/// Migrates `OurAssignment`, `CandidateEntry` and `ApprovalEntry` to version 2. +/// Returns on any error. +/// Must only be used in parachains DB migration code - `polkadot-service` crate. +pub fn v1_to_v2(db: Arc, config: Config) -> Result<()> { + let mut backend = crate::DbBackend::new(db, config); + let all_blocks = backend + .load_all_blocks() + .map_err(|e| Error::InternalError(e))? + .iter() + .filter_map(|block_hash| { + backend + .load_block_entry_v1(block_hash) + .map_err(|e| Error::InternalError(e)) + .ok()? + }) + .collect::>(); + + gum::info!( + target: crate::LOG_TARGET, + "Migrating candidate entries on top of {} blocks", + all_blocks.len() + ); + + let mut overlay = crate::OverlayedBackend::new(&backend); + let mut counter = 0; + // Get all candidate entries, approval entries and convert each of them. + for block in all_blocks { + for (_core_index, candidate_hash) in block.candidates() { + // Loading the candidate will also perform the conversion to the updated format and + // return that represantation. + if let Some(candidate_entry) = backend + .load_candidate_entry_v1(&candidate_hash) + .map_err(|e| Error::InternalError(e))? + { + // Write the updated representation. + overlay.write_candidate_entry(candidate_entry); + counter += 1; + } + } + overlay.write_block_entry(block); + } + + gum::info!(target: crate::LOG_TARGET, "Migrated {} entries", counter); + + // Commit all changes to DB. + let write_ops = overlay.into_write_ops(); + backend.write(write_ops).unwrap(); + + Ok(()) +} + +// Checks if the migration doesn't leave the DB in an unsane state. +// This function is to be used in tests. +pub fn v1_to_v2_sanity_check( + db: Arc, + config: Config, + expected_candidates: HashSet, +) -> Result<()> { + let backend = crate::DbBackend::new(db, config); + + let all_blocks = backend + .load_all_blocks() + .unwrap() + .iter() + .map(|block_hash| backend.load_block_entry(block_hash).unwrap().unwrap()) + .collect::>(); + + let mut candidates = HashSet::new(); + + // Iterate all blocks and approval entries. + for block in all_blocks { + for (_core_index, candidate_hash) in block.candidates() { + // Loading the candidate will also perform the conversion to the updated format and + // return that represantation. + if let Some(candidate_entry) = backend.load_candidate_entry(&candidate_hash).unwrap() { + candidates.insert(candidate_entry.candidate.hash()); + } + } + } + + assert_eq!(candidates, expected_candidates); + + Ok(()) +} + +// Fills the db with dummy data in v1 scheme. +pub fn v1_to_v2_fill_test_data( + db: Arc, + config: Config, + dummy_candidate_create: F, +) -> Result> +where + F: Fn(H256) -> CandidateReceipt, +{ + let mut backend = crate::DbBackend::new(db.clone(), config); + let mut overlay_db = crate::OverlayedBackend::new(&backend); + let mut expected_candidates = HashSet::new(); + + const RELAY_BLOCK_COUNT: u32 = 10; + + let range = StoredBlockRange(1, 11); + overlay_db.write_stored_block_range(range.clone()); + + for relay_number in 1..=RELAY_BLOCK_COUNT { + let relay_hash = Hash::repeat_byte(relay_number as u8); + let assignment_core_index = CoreIndex(relay_number); + let candidate = dummy_candidate_create(relay_hash); + let candidate_hash = candidate.hash(); + + let at_height = vec![relay_hash]; + + let block_entry = make_block_entry_v1( + relay_hash, + Default::default(), + relay_number, + vec![(assignment_core_index, candidate_hash)], + ); + + let dummy_assignment = crate::approval_db::v1::OurAssignment { + cert: dummy_assignment_cert(AssignmentCertKind::RelayVRFModulo { sample: 0 }).into(), + tranche: 0, + validator_index: ValidatorIndex(0), + triggered: false, + }; + + let candidate_entry = crate::approval_db::v1::CandidateEntry { + candidate, + session: 123, + block_assignments: vec![( + relay_hash, + crate::approval_db::v1::ApprovalEntry { + tranches: Vec::new(), + backing_group: GroupIndex(1), + our_assignment: Some(dummy_assignment), + our_approval_sig: None, + assignments: Default::default(), + approved: false, + }, + )] + .into_iter() + .collect(), + approvals: Default::default(), + }; + + overlay_db.write_blocks_at_height(relay_number, at_height.clone()); + expected_candidates.insert(candidate_entry.candidate.hash()); + + db.write(write_candidate_entry_v1(candidate_entry, config)).unwrap(); + db.write(write_block_entry_v1(block_entry, config)).unwrap(); + } + + let write_ops = overlay_db.into_write_ops(); + backend.write(write_ops).unwrap(); + + Ok(expected_candidates) +} + +// Low level DB helper to write a candidate entry in v1 scheme. +fn write_candidate_entry_v1( + candidate_entry: crate::approval_db::v1::CandidateEntry, + config: Config, +) -> DBTransaction { + let mut tx = DBTransaction::new(); + tx.put_vec( + config.col_approval_data, + &candidate_entry_key(&candidate_entry.candidate.hash()), + candidate_entry.encode(), + ); + tx +} + +// Low level DB helper to write a block entry in v1 scheme. +fn write_block_entry_v1( + block_entry: crate::approval_db::v1::BlockEntry, + config: Config, +) -> DBTransaction { + let mut tx = DBTransaction::new(); + tx.put_vec( + config.col_approval_data, + &block_entry_key(&block_entry.block_hash), + block_entry.encode(), + ); + tx +} diff --git a/polkadot/node/core/approval-voting/src/approval_db/v2/mod.rs b/polkadot/node/core/approval-voting/src/approval_db/v2/mod.rs new file mode 100644 index 000000000000..66df6ee8f653 --- /dev/null +++ b/polkadot/node/core/approval-voting/src/approval_db/v2/mod.rs @@ -0,0 +1,394 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! Version 2 of the DB schema. + +use parity_scale_codec::{Decode, Encode}; +use polkadot_node_primitives::approval::{v1::DelayTranche, v2::AssignmentCertV2}; +use polkadot_node_subsystem::{SubsystemError, SubsystemResult}; +use polkadot_node_subsystem_util::database::{DBTransaction, Database}; +use polkadot_primitives::{ + BlockNumber, CandidateHash, CandidateReceipt, CoreIndex, GroupIndex, Hash, SessionIndex, + ValidatorIndex, ValidatorSignature, +}; + +use sp_consensus_slots::Slot; + +use bitvec::{order::Lsb0 as BitOrderLsb0, vec::BitVec}; +use std::{collections::BTreeMap, sync::Arc}; + +use crate::{ + backend::{Backend, BackendWriteOp, V1ReadBackend}, + persisted_entries, +}; + +const STORED_BLOCKS_KEY: &[u8] = b"Approvals_StoredBlocks"; + +pub mod migration_helpers; +#[cfg(test)] +pub mod tests; + +/// `DbBackend` is a concrete implementation of the higher-level Backend trait +pub struct DbBackend { + inner: Arc, + config: Config, +} + +impl DbBackend { + /// Create a new [`DbBackend`] with the supplied key-value store and + /// config. + pub fn new(db: Arc, config: Config) -> Self { + DbBackend { inner: db, config } + } +} + +impl V1ReadBackend for DbBackend { + fn load_candidate_entry_v1( + &self, + candidate_hash: &CandidateHash, + ) -> SubsystemResult> { + load_candidate_entry_v1(&*self.inner, &self.config, candidate_hash) + .map(|e| e.map(Into::into)) + } + + fn load_block_entry_v1( + &self, + block_hash: &Hash, + ) -> SubsystemResult> { + load_block_entry_v1(&*self.inner, &self.config, block_hash).map(|e| e.map(Into::into)) + } +} + +impl Backend for DbBackend { + fn load_block_entry( + &self, + block_hash: &Hash, + ) -> SubsystemResult> { + load_block_entry(&*self.inner, &self.config, block_hash).map(|e| e.map(Into::into)) + } + + fn load_candidate_entry( + &self, + candidate_hash: &CandidateHash, + ) -> SubsystemResult> { + load_candidate_entry(&*self.inner, &self.config, candidate_hash).map(|e| e.map(Into::into)) + } + + fn load_blocks_at_height(&self, block_height: &BlockNumber) -> SubsystemResult> { + load_blocks_at_height(&*self.inner, &self.config, block_height) + } + + fn load_all_blocks(&self) -> SubsystemResult> { + load_all_blocks(&*self.inner, &self.config) + } + + fn load_stored_blocks(&self) -> SubsystemResult> { + load_stored_blocks(&*self.inner, &self.config) + } + + /// Atomically write the list of operations, with later operations taking precedence over prior. + fn write(&mut self, ops: I) -> SubsystemResult<()> + where + I: IntoIterator, + { + let mut tx = DBTransaction::new(); + for op in ops { + match op { + BackendWriteOp::WriteStoredBlockRange(stored_block_range) => { + tx.put_vec( + self.config.col_approval_data, + &STORED_BLOCKS_KEY, + stored_block_range.encode(), + ); + }, + BackendWriteOp::DeleteStoredBlockRange => { + tx.delete(self.config.col_approval_data, &STORED_BLOCKS_KEY); + }, + BackendWriteOp::WriteBlocksAtHeight(h, blocks) => { + tx.put_vec( + self.config.col_approval_data, + &blocks_at_height_key(h), + blocks.encode(), + ); + }, + BackendWriteOp::DeleteBlocksAtHeight(h) => { + tx.delete(self.config.col_approval_data, &blocks_at_height_key(h)); + }, + BackendWriteOp::WriteBlockEntry(block_entry) => { + let block_entry: BlockEntry = block_entry.into(); + tx.put_vec( + self.config.col_approval_data, + &block_entry_key(&block_entry.block_hash), + block_entry.encode(), + ); + }, + BackendWriteOp::DeleteBlockEntry(hash) => { + tx.delete(self.config.col_approval_data, &block_entry_key(&hash)); + }, + BackendWriteOp::WriteCandidateEntry(candidate_entry) => { + let candidate_entry: CandidateEntry = candidate_entry.into(); + tx.put_vec( + self.config.col_approval_data, + &candidate_entry_key(&candidate_entry.candidate.hash()), + candidate_entry.encode(), + ); + }, + BackendWriteOp::DeleteCandidateEntry(candidate_hash) => { + tx.delete(self.config.col_approval_data, &candidate_entry_key(&candidate_hash)); + }, + } + } + + self.inner.write(tx).map_err(|e| e.into()) + } +} + +/// A range from earliest..last block number stored within the DB. +#[derive(Encode, Decode, Debug, Clone, PartialEq)] +pub struct StoredBlockRange(pub BlockNumber, pub BlockNumber); + +// slot_duration * 2 + DelayTranche gives the number of delay tranches since the +// unix epoch. +#[derive(Encode, Decode, Clone, Copy, Debug, PartialEq)] +pub struct Tick(u64); + +/// Convenience type definition +pub type Bitfield = BitVec; + +/// The database config. +#[derive(Debug, Clone, Copy)] +pub struct Config { + /// The column family in the database where data is stored. + pub col_approval_data: u32, +} + +/// Details pertaining to our assignment on a block. +#[derive(Encode, Decode, Debug, Clone, PartialEq)] +pub struct OurAssignment { + /// Our assignment certificate. + pub cert: AssignmentCertV2, + /// The tranche for which the assignment refers to. + pub tranche: DelayTranche, + /// Our validator index for the session in which the candidates were included. + pub validator_index: ValidatorIndex, + /// Whether the assignment has been triggered already. + pub triggered: bool, +} + +/// Metadata regarding a specific tranche of assignments for a specific candidate. +#[derive(Encode, Decode, Debug, Clone, PartialEq)] +pub struct TrancheEntry { + pub tranche: DelayTranche, + // Assigned validators, and the instant we received their assignment, rounded + // to the nearest tick. + pub assignments: Vec<(ValidatorIndex, Tick)>, +} + +/// Metadata regarding approval of a particular candidate within the context of some +/// particular block. +#[derive(Encode, Decode, Debug, Clone, PartialEq)] +pub struct ApprovalEntry { + pub tranches: Vec, + pub backing_group: GroupIndex, + pub our_assignment: Option, + pub our_approval_sig: Option, + // `n_validators` bits. + pub assigned_validators: Bitfield, + pub approved: bool, +} + +/// Metadata regarding approval of a particular candidate. +#[derive(Encode, Decode, Debug, Clone, PartialEq)] +pub struct CandidateEntry { + pub candidate: CandidateReceipt, + pub session: SessionIndex, + // Assignments are based on blocks, so we need to track assignments separately + // based on the block we are looking at. + pub block_assignments: BTreeMap, + pub approvals: Bitfield, +} + +/// Metadata regarding approval of a particular block, by way of approval of the +/// candidates contained within it. +#[derive(Encode, Decode, Debug, Clone, PartialEq)] +pub struct BlockEntry { + pub block_hash: Hash, + pub block_number: BlockNumber, + pub parent_hash: Hash, + pub session: SessionIndex, + pub slot: Slot, + /// Random bytes derived from the VRF submitted within the block by the block + /// author as a credential and used as input to approval assignment criteria. + pub relay_vrf_story: [u8; 32], + // The candidates included as-of this block and the index of the core they are + // leaving. Sorted ascending by core index. + pub candidates: Vec<(CoreIndex, CandidateHash)>, + // A bitfield where the i'th bit corresponds to the i'th candidate in `candidates`. + // The i'th bit is `true` iff the candidate has been approved in the context of this + // block. The block can be considered approved if the bitfield has all bits set to `true`. + pub approved_bitfield: Bitfield, + pub children: Vec, + // Assignments we already distributed. A 1 bit means the candidate index for which + // we already have sent out an assignment. We need this to avoid distributing + // multiple core assignments more than once. + pub distributed_assignments: Bitfield, +} + +impl From for Tick { + fn from(tick: crate::Tick) -> Tick { + Tick(tick) + } +} + +impl From for crate::Tick { + fn from(tick: Tick) -> crate::Tick { + tick.0 + } +} + +/// Errors while accessing things from the DB. +#[derive(Debug, derive_more::From, derive_more::Display)] +pub enum Error { + Io(std::io::Error), + InvalidDecoding(parity_scale_codec::Error), + InternalError(SubsystemError), +} + +impl std::error::Error for Error {} + +/// Result alias for DB errors. +pub type Result = std::result::Result; + +pub(crate) fn load_decode( + store: &dyn Database, + col_approval_data: u32, + key: &[u8], +) -> Result> { + match store.get(col_approval_data, key)? { + None => Ok(None), + Some(raw) => D::decode(&mut &raw[..]).map(Some).map_err(Into::into), + } +} + +/// The key a given block entry is stored under. +pub(crate) fn block_entry_key(block_hash: &Hash) -> [u8; 46] { + const BLOCK_ENTRY_PREFIX: [u8; 14] = *b"Approvals_blck"; + + let mut key = [0u8; 14 + 32]; + key[0..14].copy_from_slice(&BLOCK_ENTRY_PREFIX); + key[14..][..32].copy_from_slice(block_hash.as_ref()); + + key +} + +/// The key a given candidate entry is stored under. +pub(crate) fn candidate_entry_key(candidate_hash: &CandidateHash) -> [u8; 46] { + const CANDIDATE_ENTRY_PREFIX: [u8; 14] = *b"Approvals_cand"; + + let mut key = [0u8; 14 + 32]; + key[0..14].copy_from_slice(&CANDIDATE_ENTRY_PREFIX); + key[14..][..32].copy_from_slice(candidate_hash.0.as_ref()); + + key +} + +/// The key a set of block hashes corresponding to a block number is stored under. +pub(crate) fn blocks_at_height_key(block_number: BlockNumber) -> [u8; 16] { + const BLOCKS_AT_HEIGHT_PREFIX: [u8; 12] = *b"Approvals_at"; + + let mut key = [0u8; 12 + 4]; + key[0..12].copy_from_slice(&BLOCKS_AT_HEIGHT_PREFIX); + block_number.using_encoded(|s| key[12..16].copy_from_slice(s)); + + key +} + +/// Return all blocks which have entries in the DB, ascending, by height. +pub fn load_all_blocks(store: &dyn Database, config: &Config) -> SubsystemResult> { + let mut hashes = Vec::new(); + if let Some(stored_blocks) = load_stored_blocks(store, config)? { + for height in stored_blocks.0..stored_blocks.1 { + let blocks = load_blocks_at_height(store, config, &height)?; + hashes.extend(blocks); + } + } + + Ok(hashes) +} + +/// Load the stored-blocks key from the state. +pub fn load_stored_blocks( + store: &dyn Database, + config: &Config, +) -> SubsystemResult> { + load_decode(store, config.col_approval_data, STORED_BLOCKS_KEY) + .map_err(|e| SubsystemError::with_origin("approval-voting", e)) +} + +/// Load a blocks-at-height entry for a given block number. +pub fn load_blocks_at_height( + store: &dyn Database, + config: &Config, + block_number: &BlockNumber, +) -> SubsystemResult> { + load_decode(store, config.col_approval_data, &blocks_at_height_key(*block_number)) + .map(|x| x.unwrap_or_default()) + .map_err(|e| SubsystemError::with_origin("approval-voting", e)) +} + +/// Load a block entry from the aux store. +pub fn load_block_entry( + store: &dyn Database, + config: &Config, + block_hash: &Hash, +) -> SubsystemResult> { + load_decode(store, config.col_approval_data, &block_entry_key(block_hash)) + .map(|u: Option| u.map(|v| v.into())) + .map_err(|e| SubsystemError::with_origin("approval-voting", e)) +} + +/// Load a candidate entry from the aux store in current version format. +pub fn load_candidate_entry( + store: &dyn Database, + config: &Config, + candidate_hash: &CandidateHash, +) -> SubsystemResult> { + load_decode(store, config.col_approval_data, &candidate_entry_key(candidate_hash)) + .map(|u: Option| u.map(|v| v.into())) + .map_err(|e| SubsystemError::with_origin("approval-voting", e)) +} + +/// Load a candidate entry from the aux store in v1 format. +pub fn load_candidate_entry_v1( + store: &dyn Database, + config: &Config, + candidate_hash: &CandidateHash, +) -> SubsystemResult> { + load_decode(store, config.col_approval_data, &candidate_entry_key(candidate_hash)) + .map(|u: Option| u.map(|v| v.into())) + .map_err(|e| SubsystemError::with_origin("approval-voting", e)) +} + +/// Load a block entry from the aux store in v1 format. +pub fn load_block_entry_v1( + store: &dyn Database, + config: &Config, + block_hash: &Hash, +) -> SubsystemResult> { + load_decode(store, config.col_approval_data, &block_entry_key(block_hash)) + .map(|u: Option| u.map(|v| v.into())) + .map_err(|e| SubsystemError::with_origin("approval-voting", e)) +} diff --git a/polkadot/node/core/approval-voting/src/approval_db/v2/tests.rs b/polkadot/node/core/approval-voting/src/approval_db/v2/tests.rs new file mode 100644 index 000000000000..50a5a924ca8d --- /dev/null +++ b/polkadot/node/core/approval-voting/src/approval_db/v2/tests.rs @@ -0,0 +1,570 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! Tests for the aux-schema of approval voting. + +use super::{DbBackend, StoredBlockRange, *}; +use crate::{ + backend::{Backend, OverlayedBackend}, + ops::{add_block_entry, canonicalize, force_approve, NewCandidateInfo}, +}; +use polkadot_node_subsystem_util::database::Database; +use polkadot_primitives::Id as ParaId; +use std::{collections::HashMap, sync::Arc}; + +use ::test_helpers::{dummy_candidate_receipt, dummy_candidate_receipt_bad_sig, dummy_hash}; + +const DATA_COL: u32 = 0; + +const NUM_COLUMNS: u32 = 1; + +const TEST_CONFIG: Config = Config { col_approval_data: DATA_COL }; + +fn make_db() -> (DbBackend, Arc) { + let db = kvdb_memorydb::create(NUM_COLUMNS); + let db = polkadot_node_subsystem_util::database::kvdb_impl::DbAdapter::new(db, &[]); + let db_writer: Arc = Arc::new(db); + (DbBackend::new(db_writer.clone(), TEST_CONFIG), db_writer) +} + +fn make_block_entry( + block_hash: Hash, + parent_hash: Hash, + block_number: BlockNumber, + candidates: Vec<(CoreIndex, CandidateHash)>, +) -> BlockEntry { + BlockEntry { + block_hash, + parent_hash, + block_number, + session: 1, + slot: Slot::from(1), + relay_vrf_story: [0u8; 32], + approved_bitfield: make_bitvec(candidates.len()), + candidates, + children: Vec::new(), + distributed_assignments: Default::default(), + } +} + +fn make_bitvec(len: usize) -> BitVec { + bitvec::bitvec![u8, BitOrderLsb0; 0; len] +} + +fn make_candidate(para_id: ParaId, relay_parent: Hash) -> CandidateReceipt { + let mut c = dummy_candidate_receipt(dummy_hash()); + + c.descriptor.para_id = para_id; + c.descriptor.relay_parent = relay_parent; + + c +} + +#[test] +fn read_write() { + let (mut db, store) = make_db(); + + let hash_a = Hash::repeat_byte(1); + let hash_b = Hash::repeat_byte(2); + let candidate_hash = dummy_candidate_receipt_bad_sig(dummy_hash(), None).hash(); + + let range = StoredBlockRange(10, 20); + let at_height = vec![hash_a, hash_b]; + + let block_entry = + make_block_entry(hash_a, Default::default(), 1, vec![(CoreIndex(0), candidate_hash)]); + + let candidate_entry = CandidateEntry { + candidate: dummy_candidate_receipt_bad_sig(dummy_hash(), None), + session: 5, + block_assignments: vec![( + hash_a, + ApprovalEntry { + tranches: Vec::new(), + backing_group: GroupIndex(1), + our_assignment: None, + our_approval_sig: None, + assigned_validators: Default::default(), + approved: false, + }, + )] + .into_iter() + .collect(), + approvals: Default::default(), + }; + + let mut overlay_db = OverlayedBackend::new(&db); + overlay_db.write_stored_block_range(range.clone()); + overlay_db.write_blocks_at_height(1, at_height.clone()); + overlay_db.write_block_entry(block_entry.clone().into()); + overlay_db.write_candidate_entry(candidate_entry.clone().into()); + + let write_ops = overlay_db.into_write_ops(); + db.write(write_ops).unwrap(); + + assert_eq!(load_stored_blocks(store.as_ref(), &TEST_CONFIG).unwrap(), Some(range)); + assert_eq!(load_blocks_at_height(store.as_ref(), &TEST_CONFIG, &1).unwrap(), at_height); + assert_eq!( + load_block_entry(store.as_ref(), &TEST_CONFIG, &hash_a).unwrap(), + Some(block_entry.into()) + ); + assert_eq!( + load_candidate_entry(store.as_ref(), &TEST_CONFIG, &candidate_hash).unwrap(), + Some(candidate_entry.into()), + ); + + let mut overlay_db = OverlayedBackend::new(&db); + overlay_db.delete_blocks_at_height(1); + overlay_db.delete_block_entry(&hash_a); + overlay_db.delete_candidate_entry(&candidate_hash); + let write_ops = overlay_db.into_write_ops(); + db.write(write_ops).unwrap(); + + assert!(load_blocks_at_height(store.as_ref(), &TEST_CONFIG, &1).unwrap().is_empty()); + assert!(load_block_entry(store.as_ref(), &TEST_CONFIG, &hash_a).unwrap().is_none()); + assert!(load_candidate_entry(store.as_ref(), &TEST_CONFIG, &candidate_hash) + .unwrap() + .is_none()); +} + +#[test] +fn add_block_entry_works() { + let (mut db, store) = make_db(); + + let parent_hash = Hash::repeat_byte(1); + let block_hash_a = Hash::repeat_byte(2); + let block_hash_b = Hash::repeat_byte(69); + + let candidate_receipt_a = make_candidate(ParaId::from(1_u32), parent_hash); + let candidate_receipt_b = make_candidate(ParaId::from(2_u32), parent_hash); + + let candidate_hash_a = candidate_receipt_a.hash(); + let candidate_hash_b = candidate_receipt_b.hash(); + + let block_number = 10; + + let block_entry_a = make_block_entry( + block_hash_a, + parent_hash, + block_number, + vec![(CoreIndex(0), candidate_hash_a)], + ); + + let block_entry_b = make_block_entry( + block_hash_b, + parent_hash, + block_number, + vec![(CoreIndex(0), candidate_hash_a), (CoreIndex(1), candidate_hash_b)], + ); + + let n_validators = 10; + + let mut new_candidate_info = HashMap::new(); + new_candidate_info + .insert(candidate_hash_a, NewCandidateInfo::new(candidate_receipt_a, GroupIndex(0), None)); + + let mut overlay_db = OverlayedBackend::new(&db); + add_block_entry(&mut overlay_db, block_entry_a.clone().into(), n_validators, |h| { + new_candidate_info.get(h).map(|x| x.clone()) + }) + .unwrap(); + let write_ops = overlay_db.into_write_ops(); + db.write(write_ops).unwrap(); + + new_candidate_info + .insert(candidate_hash_b, NewCandidateInfo::new(candidate_receipt_b, GroupIndex(1), None)); + + let mut overlay_db = OverlayedBackend::new(&db); + add_block_entry(&mut overlay_db, block_entry_b.clone().into(), n_validators, |h| { + new_candidate_info.get(h).map(|x| x.clone()) + }) + .unwrap(); + let write_ops = overlay_db.into_write_ops(); + db.write(write_ops).unwrap(); + + assert_eq!( + load_block_entry(store.as_ref(), &TEST_CONFIG, &block_hash_a).unwrap(), + Some(block_entry_a.into()) + ); + assert_eq!( + load_block_entry(store.as_ref(), &TEST_CONFIG, &block_hash_b).unwrap(), + Some(block_entry_b.into()) + ); + + let candidate_entry_a = load_candidate_entry(store.as_ref(), &TEST_CONFIG, &candidate_hash_a) + .unwrap() + .unwrap(); + assert_eq!( + candidate_entry_a.block_assignments.keys().collect::>(), + vec![&block_hash_a, &block_hash_b] + ); + + let candidate_entry_b = load_candidate_entry(store.as_ref(), &TEST_CONFIG, &candidate_hash_b) + .unwrap() + .unwrap(); + assert_eq!(candidate_entry_b.block_assignments.keys().collect::>(), vec![&block_hash_b]); +} + +#[test] +fn add_block_entry_adds_child() { + let (mut db, store) = make_db(); + + let parent_hash = Hash::repeat_byte(1); + let block_hash_a = Hash::repeat_byte(2); + let block_hash_b = Hash::repeat_byte(69); + + let mut block_entry_a = make_block_entry(block_hash_a, parent_hash, 1, Vec::new()); + + let block_entry_b = make_block_entry(block_hash_b, block_hash_a, 2, Vec::new()); + + let n_validators = 10; + + let mut overlay_db = OverlayedBackend::new(&db); + add_block_entry(&mut overlay_db, block_entry_a.clone().into(), n_validators, |_| None).unwrap(); + + add_block_entry(&mut overlay_db, block_entry_b.clone().into(), n_validators, |_| None).unwrap(); + + let write_ops = overlay_db.into_write_ops(); + db.write(write_ops).unwrap(); + + block_entry_a.children.push(block_hash_b); + + assert_eq!( + load_block_entry(store.as_ref(), &TEST_CONFIG, &block_hash_a).unwrap(), + Some(block_entry_a.into()) + ); + assert_eq!( + load_block_entry(store.as_ref(), &TEST_CONFIG, &block_hash_b).unwrap(), + Some(block_entry_b.into()) + ); +} + +#[test] +fn canonicalize_works() { + let (mut db, store) = make_db(); + + // -> B1 -> C1 -> D1 + // A -> B2 -> C2 -> D2 + // + // We'll canonicalize C1. Everytning except D1 should disappear. + // + // Candidates: + // Cand1 in B2 + // Cand2 in C2 + // Cand3 in C2 and D1 + // Cand4 in D1 + // Cand5 in D2 + // Only Cand3 and Cand4 should remain after canonicalize. + + let n_validators = 10; + + let mut overlay_db = OverlayedBackend::new(&db); + overlay_db.write_stored_block_range(StoredBlockRange(1, 5)); + let write_ops = overlay_db.into_write_ops(); + db.write(write_ops).unwrap(); + + let genesis = Hash::repeat_byte(0); + + let block_hash_a = Hash::repeat_byte(1); + let block_hash_b1 = Hash::repeat_byte(2); + let block_hash_b2 = Hash::repeat_byte(3); + let block_hash_c1 = Hash::repeat_byte(4); + let block_hash_c2 = Hash::repeat_byte(5); + let block_hash_d1 = Hash::repeat_byte(6); + let block_hash_d2 = Hash::repeat_byte(7); + + let candidate_receipt_genesis = make_candidate(ParaId::from(1_u32), genesis); + let candidate_receipt_a = make_candidate(ParaId::from(2_u32), block_hash_a); + let candidate_receipt_b = make_candidate(ParaId::from(3_u32), block_hash_a); + let candidate_receipt_b1 = make_candidate(ParaId::from(4_u32), block_hash_b1); + let candidate_receipt_c1 = make_candidate(ParaId::from(5_u32), block_hash_c1); + + let cand_hash_1 = candidate_receipt_genesis.hash(); + let cand_hash_2 = candidate_receipt_a.hash(); + let cand_hash_3 = candidate_receipt_b.hash(); + let cand_hash_4 = candidate_receipt_b1.hash(); + let cand_hash_5 = candidate_receipt_c1.hash(); + + let block_entry_a = make_block_entry(block_hash_a, genesis, 1, Vec::new()); + let block_entry_b1 = make_block_entry(block_hash_b1, block_hash_a, 2, Vec::new()); + let block_entry_b2 = + make_block_entry(block_hash_b2, block_hash_a, 2, vec![(CoreIndex(0), cand_hash_1)]); + let block_entry_c1 = make_block_entry(block_hash_c1, block_hash_b1, 3, Vec::new()); + let block_entry_c2 = make_block_entry( + block_hash_c2, + block_hash_b2, + 3, + vec![(CoreIndex(0), cand_hash_2), (CoreIndex(1), cand_hash_3)], + ); + let block_entry_d1 = make_block_entry( + block_hash_d1, + block_hash_c1, + 4, + vec![(CoreIndex(0), cand_hash_3), (CoreIndex(1), cand_hash_4)], + ); + let block_entry_d2 = + make_block_entry(block_hash_d2, block_hash_c2, 4, vec![(CoreIndex(0), cand_hash_5)]); + + let candidate_info = { + let mut candidate_info = HashMap::new(); + candidate_info.insert( + cand_hash_1, + NewCandidateInfo::new(candidate_receipt_genesis, GroupIndex(1), None), + ); + + candidate_info + .insert(cand_hash_2, NewCandidateInfo::new(candidate_receipt_a, GroupIndex(2), None)); + + candidate_info + .insert(cand_hash_3, NewCandidateInfo::new(candidate_receipt_b, GroupIndex(3), None)); + + candidate_info + .insert(cand_hash_4, NewCandidateInfo::new(candidate_receipt_b1, GroupIndex(4), None)); + + candidate_info + .insert(cand_hash_5, NewCandidateInfo::new(candidate_receipt_c1, GroupIndex(5), None)); + + candidate_info + }; + + // now insert all the blocks. + let blocks = vec![ + block_entry_a.clone(), + block_entry_b1.clone(), + block_entry_b2.clone(), + block_entry_c1.clone(), + block_entry_c2.clone(), + block_entry_d1.clone(), + block_entry_d2.clone(), + ]; + + let mut overlay_db = OverlayedBackend::new(&db); + for block_entry in blocks { + add_block_entry(&mut overlay_db, block_entry.into(), n_validators, |h| { + candidate_info.get(h).map(|x| x.clone()) + }) + .unwrap(); + } + let write_ops = overlay_db.into_write_ops(); + db.write(write_ops).unwrap(); + + let check_candidates_in_store = |expected: Vec<(CandidateHash, Option>)>| { + for (c_hash, in_blocks) in expected { + let (entry, in_blocks) = match in_blocks { + None => { + assert!(load_candidate_entry(store.as_ref(), &TEST_CONFIG, &c_hash) + .unwrap() + .is_none()); + continue + }, + Some(i) => ( + load_candidate_entry(store.as_ref(), &TEST_CONFIG, &c_hash).unwrap().unwrap(), + i, + ), + }; + + assert_eq!(entry.block_assignments.len(), in_blocks.len()); + + for x in in_blocks { + assert!(entry.block_assignments.contains_key(&x)); + } + } + }; + + let check_blocks_in_store = |expected: Vec<(Hash, Option>)>| { + for (hash, with_candidates) in expected { + let (entry, with_candidates) = match with_candidates { + None => { + assert!(load_block_entry(store.as_ref(), &TEST_CONFIG, &hash) + .unwrap() + .is_none()); + continue + }, + Some(i) => + (load_block_entry(store.as_ref(), &TEST_CONFIG, &hash).unwrap().unwrap(), i), + }; + + assert_eq!(entry.candidates.len(), with_candidates.len()); + + for x in with_candidates { + assert!(entry.candidates.iter().any(|(_, c)| c == &x)); + } + } + }; + + check_candidates_in_store(vec![ + (cand_hash_1, Some(vec![block_hash_b2])), + (cand_hash_2, Some(vec![block_hash_c2])), + (cand_hash_3, Some(vec![block_hash_c2, block_hash_d1])), + (cand_hash_4, Some(vec![block_hash_d1])), + (cand_hash_5, Some(vec![block_hash_d2])), + ]); + + check_blocks_in_store(vec![ + (block_hash_a, Some(vec![])), + (block_hash_b1, Some(vec![])), + (block_hash_b2, Some(vec![cand_hash_1])), + (block_hash_c1, Some(vec![])), + (block_hash_c2, Some(vec![cand_hash_2, cand_hash_3])), + (block_hash_d1, Some(vec![cand_hash_3, cand_hash_4])), + (block_hash_d2, Some(vec![cand_hash_5])), + ]); + + let mut overlay_db = OverlayedBackend::new(&db); + canonicalize(&mut overlay_db, 3, block_hash_c1).unwrap(); + let write_ops = overlay_db.into_write_ops(); + db.write(write_ops).unwrap(); + + assert_eq!( + load_stored_blocks(store.as_ref(), &TEST_CONFIG).unwrap().unwrap(), + StoredBlockRange(4, 5) + ); + + check_candidates_in_store(vec![ + (cand_hash_1, None), + (cand_hash_2, None), + (cand_hash_3, Some(vec![block_hash_d1])), + (cand_hash_4, Some(vec![block_hash_d1])), + (cand_hash_5, None), + ]); + + check_blocks_in_store(vec![ + (block_hash_a, None), + (block_hash_b1, None), + (block_hash_b2, None), + (block_hash_c1, None), + (block_hash_c2, None), + (block_hash_d1, Some(vec![cand_hash_3, cand_hash_4])), + (block_hash_d2, None), + ]); +} + +#[test] +fn force_approve_works() { + let (mut db, store) = make_db(); + let n_validators = 10; + + let mut overlay_db = OverlayedBackend::new(&db); + overlay_db.write_stored_block_range(StoredBlockRange(1, 4)); + let write_ops = overlay_db.into_write_ops(); + db.write(write_ops).unwrap(); + + let candidate_hash = CandidateHash(Hash::repeat_byte(42)); + let single_candidate_vec = vec![(CoreIndex(0), candidate_hash)]; + let candidate_info = { + let mut candidate_info = HashMap::new(); + candidate_info.insert( + candidate_hash, + NewCandidateInfo::new( + make_candidate(ParaId::from(1_u32), Default::default()), + GroupIndex(1), + None, + ), + ); + + candidate_info + }; + + let block_hash_a = Hash::repeat_byte(1); // 1 + let block_hash_b = Hash::repeat_byte(2); + let block_hash_c = Hash::repeat_byte(3); + let block_hash_d = Hash::repeat_byte(4); // 4 + + let block_entry_a = + make_block_entry(block_hash_a, Default::default(), 1, single_candidate_vec.clone()); + let block_entry_b = + make_block_entry(block_hash_b, block_hash_a, 2, single_candidate_vec.clone()); + let block_entry_c = + make_block_entry(block_hash_c, block_hash_b, 3, single_candidate_vec.clone()); + let block_entry_d = + make_block_entry(block_hash_d, block_hash_c, 4, single_candidate_vec.clone()); + + let blocks = vec![ + block_entry_a.clone(), + block_entry_b.clone(), + block_entry_c.clone(), + block_entry_d.clone(), + ]; + + let mut overlay_db = OverlayedBackend::new(&db); + for block_entry in blocks { + add_block_entry(&mut overlay_db, block_entry.into(), n_validators, |h| { + candidate_info.get(h).map(|x| x.clone()) + }) + .unwrap(); + } + let approved_hashes = force_approve(&mut overlay_db, block_hash_d, 2).unwrap(); + let write_ops = overlay_db.into_write_ops(); + db.write(write_ops).unwrap(); + + assert!(load_block_entry(store.as_ref(), &TEST_CONFIG, &block_hash_a,) + .unwrap() + .unwrap() + .approved_bitfield + .all()); + assert!(load_block_entry(store.as_ref(), &TEST_CONFIG, &block_hash_b,) + .unwrap() + .unwrap() + .approved_bitfield + .all()); + assert!(load_block_entry(store.as_ref(), &TEST_CONFIG, &block_hash_c,) + .unwrap() + .unwrap() + .approved_bitfield + .not_any()); + assert!(load_block_entry(store.as_ref(), &TEST_CONFIG, &block_hash_d,) + .unwrap() + .unwrap() + .approved_bitfield + .not_any()); + assert_eq!(approved_hashes, vec![block_hash_b, block_hash_a]); +} + +#[test] +fn load_all_blocks_works() { + let (mut db, store) = make_db(); + + let parent_hash = Hash::repeat_byte(1); + let block_hash_a = Hash::repeat_byte(2); + let block_hash_b = Hash::repeat_byte(69); + let block_hash_c = Hash::repeat_byte(42); + + let block_number = 10; + + let block_entry_a = make_block_entry(block_hash_a, parent_hash, block_number, vec![]); + + let block_entry_b = make_block_entry(block_hash_b, parent_hash, block_number, vec![]); + + let block_entry_c = make_block_entry(block_hash_c, block_hash_a, block_number + 1, vec![]); + + let n_validators = 10; + + let mut overlay_db = OverlayedBackend::new(&db); + add_block_entry(&mut overlay_db, block_entry_a.clone().into(), n_validators, |_| None).unwrap(); + + // add C before B to test sorting. + add_block_entry(&mut overlay_db, block_entry_c.clone().into(), n_validators, |_| None).unwrap(); + + add_block_entry(&mut overlay_db, block_entry_b.clone().into(), n_validators, |_| None).unwrap(); + + let write_ops = overlay_db.into_write_ops(); + db.write(write_ops).unwrap(); + + assert_eq!( + load_all_blocks(store.as_ref(), &TEST_CONFIG).unwrap(), + vec![block_hash_a, block_hash_b, block_hash_c], + ) +} diff --git a/polkadot/node/core/approval-voting/src/backend.rs b/polkadot/node/core/approval-voting/src/backend.rs index 87d67c52c467..d98f3c5fd202 100644 --- a/polkadot/node/core/approval-voting/src/backend.rs +++ b/polkadot/node/core/approval-voting/src/backend.rs @@ -27,7 +27,7 @@ use polkadot_primitives::{BlockNumber, CandidateHash, Hash}; use std::collections::HashMap; use super::{ - approval_db::v1::StoredBlockRange, + approval_db::v2::StoredBlockRange, persisted_entries::{BlockEntry, CandidateEntry}, }; @@ -44,6 +44,7 @@ pub enum BackendWriteOp { } /// An abstraction over backend storage for the logic of this subsystem. +/// Implementation must always target latest storage version. pub trait Backend { /// Load a block entry from the DB. fn load_block_entry(&self, hash: &Hash) -> SubsystemResult>; @@ -52,6 +53,7 @@ pub trait Backend { &self, candidate_hash: &CandidateHash, ) -> SubsystemResult>; + /// Load all blocks at a specific height. fn load_blocks_at_height(&self, height: &BlockNumber) -> SubsystemResult>; /// Load all block from the DB. @@ -64,6 +66,18 @@ pub trait Backend { I: IntoIterator; } +/// A read only backend to enable db migration from version 1 of DB. +pub trait V1ReadBackend: Backend { + /// Load a candidate entry from the DB with scheme version 1. + fn load_candidate_entry_v1( + &self, + candidate_hash: &CandidateHash, + ) -> SubsystemResult>; + + /// Load a block entry from the DB with scheme version 1. + fn load_block_entry_v1(&self, block_hash: &Hash) -> SubsystemResult>; +} + // Status of block range in the `OverlayedBackend`. #[derive(PartialEq)] enum BlockRangeStatus { diff --git a/polkadot/node/core/approval-voting/src/criteria.rs b/polkadot/node/core/approval-voting/src/criteria.rs index 0e1d18198c21..2bb5a151fe23 100644 --- a/polkadot/node/core/approval-voting/src/criteria.rs +++ b/polkadot/node/core/approval-voting/src/criteria.rs @@ -16,28 +16,36 @@ //! Assignment criteria VRF generation and checking. +use itertools::Itertools; use parity_scale_codec::{Decode, Encode}; use polkadot_node_primitives::approval::{ - self as approval_types, AssignmentCert, AssignmentCertKind, DelayTranche, RelayVRFStory, + self as approval_types, + v1::{AssignmentCert, AssignmentCertKind, DelayTranche, RelayVRFStory}, + v2::{AssignmentCertKindV2, AssignmentCertV2, CoreBitfield, VrfOutput, VrfProof, VrfSignature}, }; use polkadot_primitives::{ AssignmentId, AssignmentPair, CandidateHash, CoreIndex, GroupIndex, IndexedVec, SessionInfo, ValidatorIndex, }; +use rand::{seq::SliceRandom, SeedableRng}; +use rand_chacha::ChaCha20Rng; use sc_keystore::LocalKeystore; use sp_application_crypto::ByteArray; use merlin::Transcript; use schnorrkel::vrf::VRFInOut; -use std::collections::{hash_map::Entry, HashMap}; +use std::{ + cmp::min, + collections::{hash_map::Entry, HashMap}, +}; use super::LOG_TARGET; /// Details pertaining to our assignment on a block. #[derive(Debug, Clone, Encode, Decode, PartialEq)] pub struct OurAssignment { - cert: AssignmentCert, + cert: AssignmentCertV2, tranche: DelayTranche, validator_index: ValidatorIndex, // Whether the assignment has been triggered already. @@ -45,7 +53,7 @@ pub struct OurAssignment { } impl OurAssignment { - pub(crate) fn cert(&self) -> &AssignmentCert { + pub(crate) fn cert(&self) -> &AssignmentCertV2 { &self.cert } @@ -66,8 +74,8 @@ impl OurAssignment { } } -impl From for OurAssignment { - fn from(entry: crate::approval_db::v1::OurAssignment) -> Self { +impl From for OurAssignment { + fn from(entry: crate::approval_db::v2::OurAssignment) -> Self { OurAssignment { cert: entry.cert, tranche: entry.tranche, @@ -77,7 +85,7 @@ impl From for OurAssignment { } } -impl From for crate::approval_db::v1::OurAssignment { +impl From for crate::approval_db::v2::OurAssignment { fn from(entry: OurAssignment) -> Self { Self { cert: entry.cert, @@ -88,17 +96,97 @@ impl From for crate::approval_db::v1::OurAssignment { } } -fn relay_vrf_modulo_transcript(relay_vrf_story: RelayVRFStory, sample: u32) -> Transcript { - // combine the relay VRF story with a sample number. - let mut t = Transcript::new(approval_types::RELAY_VRF_MODULO_CONTEXT); - t.append_message(b"RC-VRF", &relay_vrf_story.0); - sample.using_encoded(|s| t.append_message(b"sample", s)); +// Combines the relay VRF story with a sample number if any. +fn relay_vrf_modulo_transcript_inner( + mut transcript: Transcript, + relay_vrf_story: RelayVRFStory, + sample: Option, +) -> Transcript { + transcript.append_message(b"RC-VRF", &relay_vrf_story.0); - t + if let Some(sample) = sample { + sample.using_encoded(|s| transcript.append_message(b"sample", s)); + } + + transcript +} + +fn relay_vrf_modulo_transcript_v1(relay_vrf_story: RelayVRFStory, sample: u32) -> Transcript { + relay_vrf_modulo_transcript_inner( + Transcript::new(approval_types::v1::RELAY_VRF_MODULO_CONTEXT), + relay_vrf_story, + Some(sample), + ) +} + +fn relay_vrf_modulo_transcript_v2(relay_vrf_story: RelayVRFStory) -> Transcript { + relay_vrf_modulo_transcript_inner( + Transcript::new(approval_types::v2::RELAY_VRF_MODULO_CONTEXT), + relay_vrf_story, + None, + ) +} + +/// A hard upper bound on num_cores * target_checkers / num_validators +const MAX_MODULO_SAMPLES: usize = 40; + +/// Takes the VRF output as input and returns a Vec of cores the validator is assigned +/// to as a tranche0 checker. +fn relay_vrf_modulo_cores( + vrf_in_out: &VRFInOut, + // Configuration - `relay_vrf_modulo_samples`. + num_samples: u32, + // Configuration - `n_cores`. + max_cores: u32, +) -> Vec { + let rand_chacha = + ChaCha20Rng::from_seed(vrf_in_out.make_bytes::<::Seed>( + approval_types::v2::CORE_RANDOMNESS_CONTEXT, + )); + generate_samples(rand_chacha, num_samples as usize, max_cores as usize) +} + +/// Generates `num_sumples` randomly from (0..max_cores) range +/// +/// Note! The algorithm can't change because validators on the other +/// side won't be able to check the assignments until they update. +/// This invariant is tested with `generate_samples_invariant`, so the +/// tests will catch any subtle changes in the implementation of this function +/// and its dependencies. +fn generate_samples( + mut rand_chacha: ChaCha20Rng, + num_samples: usize, + max_cores: usize, +) -> Vec { + if num_samples as usize > MAX_MODULO_SAMPLES { + gum::warn!( + target: LOG_TARGET, + n_cores = max_cores, + num_samples, + max_modulo_samples = MAX_MODULO_SAMPLES, + "`num_samples` is greater than `MAX_MODULO_SAMPLES`", + ); + } + + if 2 * num_samples > max_cores { + gum::debug!( + target: LOG_TARGET, + n_cores = max_cores, + num_samples, + max_modulo_samples = MAX_MODULO_SAMPLES, + "Suboptimal configuration `num_samples` should be less than `n_cores` / 2", + ); + } + + let num_samples = min(MAX_MODULO_SAMPLES, min(num_samples, max_cores)); + + let mut random_cores = (0..max_cores as u32).map(|val| val.into()).collect::>(); + let (samples, _) = random_cores.partial_shuffle(&mut rand_chacha, num_samples as usize); + samples.into_iter().map(|val| *val).collect_vec() } fn relay_vrf_modulo_core(vrf_in_out: &VRFInOut, n_cores: u32) -> CoreIndex { - let bytes: [u8; 4] = vrf_in_out.make_bytes(approval_types::CORE_RANDOMNESS_CONTEXT); + let bytes: [u8; 4] = vrf_in_out.make_bytes(approval_types::v1::CORE_RANDOMNESS_CONTEXT); // interpret as little-endian u32. let random_core = u32::from_le_bytes(bytes) % n_cores; @@ -106,7 +194,7 @@ fn relay_vrf_modulo_core(vrf_in_out: &VRFInOut, n_cores: u32) -> CoreIndex { } fn relay_vrf_delay_transcript(relay_vrf_story: RelayVRFStory, core_index: CoreIndex) -> Transcript { - let mut t = Transcript::new(approval_types::RELAY_VRF_DELAY_CONTEXT); + let mut t = Transcript::new(approval_types::v1::RELAY_VRF_DELAY_CONTEXT); t.append_message(b"RC-VRF", &relay_vrf_story.0); core_index.0.using_encoded(|s| t.append_message(b"core", s)); t @@ -117,7 +205,7 @@ fn relay_vrf_delay_tranche( num_delay_tranches: u32, zeroth_delay_tranche_width: u32, ) -> DelayTranche { - let bytes: [u8; 4] = vrf_in_out.make_bytes(approval_types::TRANCHE_RANDOMNESS_CONTEXT); + let bytes: [u8; 4] = vrf_in_out.make_bytes(approval_types::v1::TRANCHE_RANDOMNESS_CONTEXT); // interpret as little-endian u32 and reduce by the number of tranches. let wide_tranche = @@ -128,13 +216,13 @@ fn relay_vrf_delay_tranche( } fn assigned_core_transcript(core_index: CoreIndex) -> Transcript { - let mut t = Transcript::new(approval_types::ASSIGNED_CORE_CONTEXT); + let mut t = Transcript::new(approval_types::v1::ASSIGNED_CORE_CONTEXT); core_index.0.using_encoded(|s| t.append_message(b"core", s)); t } /// Information about the world assignments are being produced in. -#[derive(Clone)] +#[derive(Clone, Debug)] pub(crate) struct Config { /// The assignment public keys for validators. assignment_keys: Vec, @@ -175,12 +263,13 @@ pub(crate) trait AssignmentCriteria { fn check_assignment_cert( &self, - claimed_core_index: CoreIndex, + claimed_core_bitfield: CoreBitfield, validator_index: ValidatorIndex, config: &Config, relay_vrf_story: RelayVRFStory, - assignment: &AssignmentCert, - backing_group: GroupIndex, + assignment: &AssignmentCertV2, + // Backing groups for each "leaving core". + backing_groups: Vec, ) -> Result; } @@ -194,25 +283,25 @@ impl AssignmentCriteria for RealAssignmentCriteria { config: &Config, leaving_cores: Vec<(CandidateHash, CoreIndex, GroupIndex)>, ) -> HashMap { - compute_assignments(keystore, relay_vrf_story, config, leaving_cores) + compute_assignments(keystore, relay_vrf_story, config, leaving_cores, false) } fn check_assignment_cert( &self, - claimed_core_index: CoreIndex, + claimed_core_bitfield: CoreBitfield, validator_index: ValidatorIndex, config: &Config, relay_vrf_story: RelayVRFStory, - assignment: &AssignmentCert, - backing_group: GroupIndex, + assignment: &AssignmentCertV2, + backing_groups: Vec, ) -> Result { check_assignment_cert( - claimed_core_index, + claimed_core_bitfield, validator_index, config, relay_vrf_story, assignment, - backing_group, + backing_groups, ) } } @@ -233,6 +322,7 @@ pub(crate) fn compute_assignments( relay_vrf_story: RelayVRFStory, config: &Config, leaving_cores: impl IntoIterator + Clone, + enable_v2_assignments: bool, ) -> HashMap { if config.n_cores == 0 || config.assignment_keys.is_empty() || @@ -291,14 +381,25 @@ pub(crate) fn compute_assignments( let mut assignments = HashMap::new(); // First run `RelayVRFModulo` for each sample. - compute_relay_vrf_modulo_assignments( - &assignments_key, - index, - config, - relay_vrf_story.clone(), - leaving_cores.iter().cloned(), - &mut assignments, - ); + if enable_v2_assignments { + compute_relay_vrf_modulo_assignments_v2( + &assignments_key, + index, + config, + relay_vrf_story.clone(), + leaving_cores.clone(), + &mut assignments, + ); + } else { + compute_relay_vrf_modulo_assignments_v1( + &assignments_key, + index, + config, + relay_vrf_story.clone(), + leaving_cores.clone(), + &mut assignments, + ); + } // Then run `RelayVRFDelay` once for the whole block. compute_relay_vrf_delay_assignments( @@ -313,7 +414,7 @@ pub(crate) fn compute_assignments( assignments } -fn compute_relay_vrf_modulo_assignments( +fn compute_relay_vrf_modulo_assignments_v1( assignments_key: &schnorrkel::Keypair, validator_index: ValidatorIndex, config: &Config, @@ -329,7 +430,7 @@ fn compute_relay_vrf_modulo_assignments( // into closure. let core = &mut core; assignments_key.vrf_sign_extra_after_check( - relay_vrf_modulo_transcript(relay_vrf_story.clone(), rvm_sample), + relay_vrf_modulo_transcript_v1(relay_vrf_story.clone(), rvm_sample), |vrf_in_out| { *core = relay_vrf_modulo_core(&vrf_in_out, config.n_cores); if let Some((candidate_hash, _)) = @@ -357,15 +458,15 @@ fn compute_relay_vrf_modulo_assignments( // has been executed. let cert = AssignmentCert { kind: AssignmentCertKind::RelayVRFModulo { sample: rvm_sample }, - vrf: approval_types::VrfSignature { - output: approval_types::VrfOutput(vrf_in_out.to_output()), - proof: approval_types::VrfProof(vrf_proof), + vrf: VrfSignature { + output: VrfOutput(vrf_in_out.to_output()), + proof: VrfProof(vrf_proof), }, }; // All assignments of type RelayVRFModulo have tranche 0. assignments.entry(core).or_insert(OurAssignment { - cert, + cert: cert.into(), tranche: 0, validator_index, triggered: false, @@ -374,6 +475,84 @@ fn compute_relay_vrf_modulo_assignments( } } +fn assigned_cores_transcript(core_bitfield: &CoreBitfield) -> Transcript { + let mut t = Transcript::new(approval_types::v2::ASSIGNED_CORE_CONTEXT); + core_bitfield.using_encoded(|s| t.append_message(b"cores", s)); + t +} + +fn compute_relay_vrf_modulo_assignments_v2( + assignments_key: &schnorrkel::Keypair, + validator_index: ValidatorIndex, + config: &Config, + relay_vrf_story: RelayVRFStory, + leaving_cores: Vec<(CandidateHash, CoreIndex)>, + assignments: &mut HashMap, +) { + let mut assigned_cores = Vec::new(); + let leaving_cores = leaving_cores.iter().map(|(_, core)| core).collect::>(); + + let maybe_assignment = { + let assigned_cores = &mut assigned_cores; + assignments_key.vrf_sign_extra_after_check( + relay_vrf_modulo_transcript_v2(relay_vrf_story.clone()), + |vrf_in_out| { + *assigned_cores = relay_vrf_modulo_cores( + &vrf_in_out, + config.relay_vrf_modulo_samples, + config.n_cores, + ) + .into_iter() + .filter(|core| leaving_cores.contains(&core)) + .collect::>(); + + if !assigned_cores.is_empty() { + gum::trace!( + target: LOG_TARGET, + ?assigned_cores, + ?validator_index, + tranche = 0, + "RelayVRFModuloCompact Assignment." + ); + + let assignment_bitfield: CoreBitfield = assigned_cores + .clone() + .try_into() + .expect("Just checked `!assigned_cores.is_empty()`; qed"); + + Some(assigned_cores_transcript(&assignment_bitfield)) + } else { + None + } + }, + ) + }; + + if let Some(assignment) = maybe_assignment.map(|(vrf_in_out, vrf_proof, _)| { + let assignment_bitfield: CoreBitfield = assigned_cores + .clone() + .try_into() + .expect("Just checked `!assigned_cores.is_empty()`; qed"); + + let cert = AssignmentCertV2 { + kind: AssignmentCertKindV2::RelayVRFModuloCompact { + core_bitfield: assignment_bitfield.clone(), + }, + vrf: VrfSignature { + output: VrfOutput(vrf_in_out.to_output()), + proof: VrfProof(vrf_proof), + }, + }; + + // All assignments of type RelayVRFModulo have tranche 0. + OurAssignment { cert, tranche: 0, validator_index, triggered: false } + }) { + for core_index in assigned_cores { + assignments.insert(core_index, assignment.clone()); + } + } +} + fn compute_relay_vrf_delay_assignments( assignments_key: &schnorrkel::Keypair, validator_index: ValidatorIndex, @@ -392,11 +571,11 @@ fn compute_relay_vrf_delay_assignments( config.zeroth_delay_tranche_width, ); - let cert = AssignmentCert { - kind: AssignmentCertKind::RelayVRFDelay { core_index: core }, - vrf: approval_types::VrfSignature { - output: approval_types::VrfOutput(vrf_in_out.to_output()), - proof: approval_types::VrfProof(vrf_proof), + let cert = AssignmentCertV2 { + kind: AssignmentCertKindV2::RelayVRFDelay { core_index: core }, + vrf: VrfSignature { + output: VrfOutput(vrf_in_out.to_output()), + proof: VrfProof(vrf_proof), }, }; @@ -453,12 +632,15 @@ pub(crate) enum InvalidAssignmentReason { VRFModuloOutputMismatch, VRFDelayCoreIndexMismatch, VRFDelayOutputMismatch, + InvalidArguments, + /// Assignment vrf check resulted in 0 assigned cores. + NullAssignment, } /// Checks the crypto of an assignment cert. Failure conditions: /// * Validator index out of bounds /// * VRF signature check fails -/// * VRF output doesn't match assigned core +/// * VRF output doesn't match assigned cores /// * Core is not covered by extra data in signature /// * Core index out of bounds /// * Sample is out of bounds @@ -467,12 +649,12 @@ pub(crate) enum InvalidAssignmentReason { /// This function does not check whether the core is actually a valid assignment or not. That should /// be done outside the scope of this function. pub(crate) fn check_assignment_cert( - claimed_core_index: CoreIndex, + claimed_core_indices: CoreBitfield, validator_index: ValidatorIndex, config: &Config, relay_vrf_story: RelayVRFStory, - assignment: &AssignmentCert, - backing_group: GroupIndex, + assignment: &AssignmentCertV2, + backing_groups: Vec, ) -> Result { use InvalidAssignmentReason as Reason; @@ -484,52 +666,133 @@ pub(crate) fn check_assignment_cert( let public = schnorrkel::PublicKey::from_bytes(validator_public.as_slice()) .map_err(|_| InvalidAssignment(Reason::InvalidAssignmentKey))?; - if claimed_core_index.0 >= config.n_cores { - return Err(InvalidAssignment(Reason::CoreIndexOutOfBounds)) + // Check that we have all backing groups for claimed cores. + if claimed_core_indices.count_ones() == 0 || + claimed_core_indices.count_ones() != backing_groups.len() + { + return Err(InvalidAssignment(Reason::InvalidArguments)) } // Check that the validator was not part of the backing group // and not already assigned. - let is_in_backing = - is_in_backing_group(&config.validator_groups, validator_index, backing_group); + for (claimed_core, backing_group) in claimed_core_indices.iter_ones().zip(backing_groups.iter()) + { + if claimed_core >= config.n_cores as usize { + return Err(InvalidAssignment(Reason::CoreIndexOutOfBounds)) + } + + let is_in_backing = + is_in_backing_group(&config.validator_groups, validator_index, *backing_group); - if is_in_backing { - return Err(InvalidAssignment(Reason::IsInBackingGroup)) + if is_in_backing { + return Err(InvalidAssignment(Reason::IsInBackingGroup)) + } } - let vrf_signature = &assignment.vrf; - match assignment.kind { - AssignmentCertKind::RelayVRFModulo { sample } => { - if sample >= config.relay_vrf_modulo_samples { + let vrf_output = &assignment.vrf.output; + let vrf_proof = &assignment.vrf.proof; + let first_claimed_core_index = + claimed_core_indices.first_one().expect("Checked above; qed") as u32; + + match &assignment.kind { + AssignmentCertKindV2::RelayVRFModuloCompact { core_bitfield } => { + // Check that claimed core bitfield match the one from certificate. + if &claimed_core_indices != core_bitfield { + return Err(InvalidAssignment(Reason::VRFModuloCoreIndexMismatch)) + } + + let (vrf_in_out, _) = public + .vrf_verify_extra( + relay_vrf_modulo_transcript_v2(relay_vrf_story), + &vrf_output.0, + &vrf_proof.0, + assigned_cores_transcript(core_bitfield), + ) + .map_err(|_| InvalidAssignment(Reason::VRFModuloOutputMismatch))?; + + let resulting_cores = relay_vrf_modulo_cores( + &vrf_in_out, + config.relay_vrf_modulo_samples, + config.n_cores, + ); + + // Currently validators can opt out of checking specific cores. + // This is the same issue to how validator can opt out and not send their assignments in + // the first place. Ensure that the `vrf_in_out` actually includes all of the claimed + // cores. + for claimed_core_index in claimed_core_indices.iter_ones() { + if !resulting_cores.contains(&CoreIndex(claimed_core_index as u32)) { + gum::debug!( + target: LOG_TARGET, + ?resulting_cores, + ?claimed_core_indices, + vrf_modulo_cores = ?resulting_cores, + "Assignment claimed cores mismatch", + ); + return Err(InvalidAssignment(Reason::VRFModuloCoreIndexMismatch)) + } + } + + Ok(0) + }, + AssignmentCertKindV2::RelayVRFModulo { sample } => { + if *sample >= config.relay_vrf_modulo_samples { return Err(InvalidAssignment(Reason::SampleOutOfBounds)) } + // Enforce claimed candidates is 1. + if claimed_core_indices.count_ones() != 1 { + gum::warn!( + target: LOG_TARGET, + ?claimed_core_indices, + "`RelayVRFModulo` assignment must always claim 1 core", + ); + return Err(InvalidAssignment(Reason::InvalidArguments)) + } + let (vrf_in_out, _) = public .vrf_verify_extra( - relay_vrf_modulo_transcript(relay_vrf_story, sample), - &vrf_signature.output.0, - &vrf_signature.proof.0, - assigned_core_transcript(claimed_core_index), + relay_vrf_modulo_transcript_v1(relay_vrf_story, *sample), + &vrf_output.0, + &vrf_proof.0, + assigned_core_transcript(CoreIndex(first_claimed_core_index)), ) .map_err(|_| InvalidAssignment(Reason::VRFModuloOutputMismatch))?; + let core = relay_vrf_modulo_core(&vrf_in_out, config.n_cores); // ensure that the `vrf_in_out` actually gives us the claimed core. - if relay_vrf_modulo_core(&vrf_in_out, config.n_cores) == claimed_core_index { + if core.0 == first_claimed_core_index { Ok(0) } else { + gum::debug!( + target: LOG_TARGET, + ?core, + ?claimed_core_indices, + "Assignment claimed cores mismatch", + ); Err(InvalidAssignment(Reason::VRFModuloCoreIndexMismatch)) } }, - AssignmentCertKind::RelayVRFDelay { core_index } => { - if core_index != claimed_core_index { + AssignmentCertKindV2::RelayVRFDelay { core_index } => { + // Enforce claimed candidates is 1. + if claimed_core_indices.count_ones() != 1 { + gum::debug!( + target: LOG_TARGET, + ?claimed_core_indices, + "`RelayVRFDelay` assignment must always claim 1 core", + ); + return Err(InvalidAssignment(Reason::InvalidArguments)) + } + + if core_index.0 != first_claimed_core_index { return Err(InvalidAssignment(Reason::VRFDelayCoreIndexMismatch)) } let (vrf_in_out, _) = public .vrf_verify( - relay_vrf_delay_transcript(relay_vrf_story, core_index), - &vrf_signature.output.0, - &vrf_signature.proof.0, + relay_vrf_delay_transcript(relay_vrf_story, *core_index), + &vrf_output.0, + &vrf_proof.0, ) .map_err(|_| InvalidAssignment(Reason::VRFDelayOutputMismatch))?; @@ -550,6 +813,19 @@ fn is_in_backing_group( validator_groups.get(group).map_or(false, |g| g.contains(&validator)) } +/// Migration helpers. +impl From for OurAssignment { + fn from(value: crate::approval_db::v1::OurAssignment) -> Self { + Self { + cert: value.cert.into(), + tranche: value.tranche, + validator_index: value.validator_index, + // Whether the assignment has been triggered already. + triggered: value.triggered, + } + } +} + #[cfg(test)] mod tests { use super::*; @@ -630,10 +906,11 @@ mod tests { ]), n_cores: 2, zeroth_delay_tranche_width: 10, - relay_vrf_modulo_samples: 3, + relay_vrf_modulo_samples: 10, n_delay_tranches: 40, }, vec![(c_a, CoreIndex(0), GroupIndex(1)), (c_b, CoreIndex(1), GroupIndex(0))], + false, ); // Note that alice is in group 0, which was the backing group for core 1. @@ -665,10 +942,11 @@ mod tests { ]), n_cores: 2, zeroth_delay_tranche_width: 10, - relay_vrf_modulo_samples: 3, + relay_vrf_modulo_samples: 10, n_delay_tranches: 40, }, vec![(c_a, CoreIndex(0), GroupIndex(0)), (c_b, CoreIndex(1), GroupIndex(1))], + false, ); assert_eq!(assignments.len(), 1); @@ -692,19 +970,21 @@ mod tests { validator_groups: Default::default(), n_cores: 0, zeroth_delay_tranche_width: 10, - relay_vrf_modulo_samples: 3, + relay_vrf_modulo_samples: 10, n_delay_tranches: 40, }, vec![], + false, ); assert!(assignments.is_empty()); } + #[derive(Debug)] struct MutatedAssignment { - core: CoreIndex, - cert: AssignmentCert, - group: GroupIndex, + cores: CoreBitfield, + cert: AssignmentCertV2, + groups: Vec, own_group: GroupIndex, val_index: ValidatorIndex, config: Config, @@ -729,12 +1009,12 @@ mod tests { validator_groups: basic_groups(n_validators, n_cores), n_cores: n_cores as u32, zeroth_delay_tranche_width: 10, - relay_vrf_modulo_samples: 3, + relay_vrf_modulo_samples: 15, n_delay_tranches: 40, }; let relay_vrf_story = RelayVRFStory([42u8; 32]); - let assignments = compute_assignments( + let mut assignments = compute_assignments( &keystore, relay_vrf_story.clone(), &config, @@ -747,19 +1027,42 @@ mod tests { ) }) .collect::>(), + false, ); + // Extend with v2 assignments as well + assignments.extend(compute_assignments( + &keystore, + relay_vrf_story.clone(), + &config, + (0..n_cores) + .map(|i| { + ( + CandidateHash(Hash::repeat_byte(i as u8)), + CoreIndex(i as u32), + group_for_core(i), + ) + }) + .collect::>(), + true, + )); + let mut counted = 0; for (core, assignment) in assignments { + let cores = match assignment.cert.kind.clone() { + AssignmentCertKindV2::RelayVRFModuloCompact { core_bitfield } => core_bitfield, + AssignmentCertKindV2::RelayVRFModulo { sample: _ } => core.into(), + AssignmentCertKindV2::RelayVRFDelay { core_index } => core_index.into(), + }; + let mut mutated = MutatedAssignment { - core, - group: group_for_core(core.0 as _), + cores: cores.clone(), + groups: cores.iter_ones().map(|core| group_for_core(core)).collect(), cert: assignment.cert, own_group: GroupIndex(0), val_index: ValidatorIndex(0), config: config.clone(), }; - let expected = match f(&mut mutated) { None => continue, Some(e) => e, @@ -768,16 +1071,16 @@ mod tests { counted += 1; let is_good = check_assignment_cert( - mutated.core, + mutated.cores, mutated.val_index, &mutated.config, relay_vrf_story.clone(), &mutated.cert, - mutated.group, + mutated.groups, ) .is_ok(); - assert_eq!(expected, is_good) + assert_eq!(expected, is_good); } assert!(counted > 0); @@ -791,7 +1094,7 @@ mod tests { #[test] fn check_rejects_claimed_core_out_of_bounds() { check_mutated_assignments(200, 100, 25, |m| { - m.core.0 += 100; + m.cores = CoreIndex(100).into(); Some(false) }); } @@ -799,7 +1102,7 @@ mod tests { #[test] fn check_rejects_in_backing_group() { check_mutated_assignments(200, 100, 25, |m| { - m.group = m.own_group; + m.groups[0] = m.own_group; Some(false) }); } @@ -814,10 +1117,11 @@ mod tests { #[test] fn check_rejects_delay_bad_vrf() { - check_mutated_assignments(40, 10, 8, |m| { + check_mutated_assignments(40, 100, 8, |m| { + let vrf_signature = garbage_vrf_signature(); match m.cert.kind.clone() { - AssignmentCertKind::RelayVRFDelay { .. } => { - m.cert.vrf = garbage_vrf_signature(); + AssignmentCertKindV2::RelayVRFDelay { .. } => { + m.cert.vrf = vrf_signature; Some(false) }, _ => None, // skip everything else. @@ -828,9 +1132,14 @@ mod tests { #[test] fn check_rejects_modulo_bad_vrf() { check_mutated_assignments(200, 100, 25, |m| { + let vrf_signature = garbage_vrf_signature(); match m.cert.kind.clone() { - AssignmentCertKind::RelayVRFModulo { .. } => { - m.cert.vrf = garbage_vrf_signature(); + AssignmentCertKindV2::RelayVRFModulo { .. } => { + m.cert.vrf = vrf_signature; + Some(false) + }, + AssignmentCertKindV2::RelayVRFModuloCompact { .. } => { + m.cert.vrf = vrf_signature; Some(false) }, _ => None, // skip everything else. @@ -842,10 +1151,11 @@ mod tests { fn check_rejects_modulo_sample_out_of_bounds() { check_mutated_assignments(200, 100, 25, |m| { match m.cert.kind.clone() { - AssignmentCertKind::RelayVRFModulo { sample } => { + AssignmentCertKindV2::RelayVRFModulo { sample } => { m.config.relay_vrf_modulo_samples = sample; Some(false) }, + AssignmentCertKindV2::RelayVRFModuloCompact { core_bitfield: _ } => Some(true), _ => None, // skip everything else. } }); @@ -855,8 +1165,11 @@ mod tests { fn check_rejects_delay_claimed_core_wrong() { check_mutated_assignments(200, 100, 25, |m| { match m.cert.kind.clone() { - AssignmentCertKind::RelayVRFDelay { .. } => { - m.core = CoreIndex((m.core.0 + 1) % 100); + AssignmentCertKindV2::RelayVRFDelay { .. } => { + // for core in &mut m.cores { + // core.0 = (core.0 + 1) % 100; + // } + m.cores = CoreIndex((m.cores.first_one().unwrap() + 1) as u32 % 100).into(); Some(false) }, _ => None, // skip everything else. @@ -868,12 +1181,53 @@ mod tests { fn check_rejects_modulo_core_wrong() { check_mutated_assignments(200, 100, 25, |m| { match m.cert.kind.clone() { - AssignmentCertKind::RelayVRFModulo { .. } => { - m.core = CoreIndex((m.core.0 + 1) % 100); + AssignmentCertKindV2::RelayVRFModulo { .. } | + AssignmentCertKindV2::RelayVRFModuloCompact { .. } => { + m.cores = CoreIndex((m.cores.first_one().unwrap() + 1) as u32 % 100).into(); + Some(false) }, _ => None, // skip everything else. } }); } + + #[test] + fn generate_samples_invariant() { + let seed = [ + 1, 0, 52, 0, 0, 0, 0, 0, 1, 0, 10, 0, 22, 32, 0, 0, 2, 0, 55, 49, 0, 11, 0, 0, 3, 0, 0, + 0, 0, 0, 2, 92, + ]; + let rand_chacha = ChaCha20Rng::from_seed(seed); + + let samples = generate_samples(rand_chacha.clone(), 6, 100); + let expected = vec![19, 79, 17, 75, 66, 30].into_iter().map(Into::into).collect_vec(); + assert_eq!(samples, expected); + + let samples = generate_samples(rand_chacha.clone(), 6, 7); + let expected = vec![0, 3, 6, 5, 4, 2].into_iter().map(Into::into).collect_vec(); + assert_eq!(samples, expected); + + let samples = generate_samples(rand_chacha.clone(), 6, 12); + let expected = vec![2, 4, 7, 5, 11, 3].into_iter().map(Into::into).collect_vec(); + assert_eq!(samples, expected); + + let samples = generate_samples(rand_chacha.clone(), 1, 100); + let expected = vec![30].into_iter().map(Into::into).collect_vec(); + assert_eq!(samples, expected); + + let samples = generate_samples(rand_chacha.clone(), 0, 100); + let expected = vec![]; + assert_eq!(samples, expected); + + let samples = generate_samples(rand_chacha, MAX_MODULO_SAMPLES + 1, 100); + let expected = vec![ + 42, 54, 55, 93, 64, 27, 49, 15, 83, 71, 62, 1, 43, 77, 97, 41, 7, 69, 0, 88, 59, 14, + 23, 87, 47, 4, 51, 12, 74, 56, 50, 44, 9, 82, 19, 79, 17, 75, 66, 30, + ] + .into_iter() + .map(Into::into) + .collect_vec(); + assert_eq!(samples, expected); + } } diff --git a/polkadot/node/core/approval-voting/src/import.rs b/polkadot/node/core/approval-voting/src/import.rs index df712fc45a53..d7667e8e405a 100644 --- a/polkadot/node/core/approval-voting/src/import.rs +++ b/polkadot/node/core/approval-voting/src/import.rs @@ -30,7 +30,10 @@ use polkadot_node_jaeger as jaeger; use polkadot_node_primitives::{ - approval::{self as approval_types, BlockApprovalMeta, RelayVRFStory}, + approval::{ + self as approval_types, + v1::{BlockApprovalMeta, RelayVRFStory}, + }, MAX_FINALITY_LAG, }; use polkadot_node_subsystem::{ @@ -53,7 +56,7 @@ use futures::{channel::oneshot, prelude::*}; use std::collections::HashMap; -use super::approval_db::v1; +use super::approval_db::v2; use crate::{ backend::{Backend, OverlayedBackend}, criteria::{AssignmentCriteria, OurAssignment}, @@ -92,7 +95,7 @@ enum ImportedBlockInfoError { FutureCancelled(&'static str, futures::channel::oneshot::Canceled), #[error(transparent)] - ApprovalError(approval_types::ApprovalError), + ApprovalError(approval_types::v1::ApprovalError), #[error("block is already finalized")] BlockAlreadyFinalized, @@ -216,7 +219,7 @@ async fn imported_block_info( .ok_or(ImportedBlockInfoError::SessionInfoUnavailable)?; let (assignments, slot, relay_vrf_story) = { - let unsafe_vrf = approval_types::babe_unsafe_vrf_info(&block_header); + let unsafe_vrf = approval_types::v1::babe_unsafe_vrf_info(&block_header); match unsafe_vrf { Some(unsafe_vrf) => { @@ -497,7 +500,7 @@ pub(crate) async fn handle_new_head( ctx.send_message(ChainSelectionMessage::Approved(block_hash)).await; } - let block_entry = v1::BlockEntry { + let block_entry = v2::BlockEntry { block_hash, parent_hash: block_header.parent_hash, block_number: block_header.number, @@ -510,6 +513,7 @@ pub(crate) async fn handle_new_head( .collect(), approved_bitfield, children: Vec::new(), + distributed_assignments: Default::default(), }; gum::trace!( @@ -588,11 +592,11 @@ pub(crate) async fn handle_new_head( #[cfg(test)] pub(crate) mod tests { use super::*; - use crate::{approval_db::v1::DbBackend, RuntimeInfo, RuntimeInfoConfig}; + use crate::{approval_db::v2::DbBackend, RuntimeInfo, RuntimeInfoConfig}; use ::test_helpers::{dummy_candidate_receipt, dummy_hash}; use assert_matches::assert_matches; use polkadot_node_primitives::{ - approval::{VrfSignature, VrfTranscript}, + approval::v1::{VrfSignature, VrfTranscript}, DISPUTE_WINDOW, }; use polkadot_node_subsystem::messages::{AllMessages, ApprovalVotingMessage}; @@ -610,7 +614,7 @@ pub(crate) mod tests { pub(crate) use sp_runtime::{Digest, DigestItem}; use std::{pin::Pin, sync::Arc}; - use crate::{approval_db::v1::Config as DatabaseConfig, criteria, BlockEntry}; + use crate::{approval_db::v2::Config as DatabaseConfig, criteria, BlockEntry}; const DATA_COL: u32 = 0; @@ -656,7 +660,7 @@ pub(crate) mod tests { fn compute_assignments( &self, _keystore: &LocalKeystore, - _relay_vrf_story: polkadot_node_primitives::approval::RelayVRFStory, + _relay_vrf_story: polkadot_node_primitives::approval::v1::RelayVRFStory, _config: &criteria::Config, _leaving_cores: Vec<( CandidateHash, @@ -669,13 +673,14 @@ pub(crate) mod tests { fn check_assignment_cert( &self, - _claimed_core_index: polkadot_primitives::CoreIndex, + _claimed_core_bitfield: polkadot_node_primitives::approval::v2::CoreBitfield, _validator_index: polkadot_primitives::ValidatorIndex, _config: &criteria::Config, - _relay_vrf_story: polkadot_node_primitives::approval::RelayVRFStory, - _assignment: &polkadot_node_primitives::approval::AssignmentCert, - _backing_group: polkadot_primitives::GroupIndex, - ) -> Result { + _relay_vrf_story: polkadot_node_primitives::approval::v1::RelayVRFStory, + _assignment: &polkadot_node_primitives::approval::v2::AssignmentCertV2, + _backing_groups: Vec, + ) -> Result + { Ok(0) } } @@ -1296,7 +1301,7 @@ pub(crate) mod tests { let (state, mut session_info_provider) = single_session_state(); overlay_db.write_block_entry( - v1::BlockEntry { + v2::BlockEntry { block_hash: parent_hash, parent_hash: Default::default(), block_number: 4, @@ -1306,6 +1311,7 @@ pub(crate) mod tests { candidates: Vec::new(), approved_bitfield: Default::default(), children: Vec::new(), + distributed_assignments: Default::default(), } .into(), ); @@ -1338,7 +1344,7 @@ pub(crate) mod tests { // the first candidate should be insta-approved // the second should not let entry: BlockEntry = - v1::load_block_entry(db_writer.as_ref(), &TEST_CONFIG, &hash) + v2::load_block_entry(db_writer.as_ref(), &TEST_CONFIG, &hash) .unwrap() .unwrap() .into(); diff --git a/polkadot/node/core/approval-voting/src/lib.rs b/polkadot/node/core/approval-voting/src/lib.rs index ddef736feab7..e7cbde567392 100644 --- a/polkadot/node/core/approval-voting/src/lib.rs +++ b/polkadot/node/core/approval-voting/src/lib.rs @@ -25,7 +25,11 @@ use jaeger::{hash_to_trace_identifier, PerLeafSpan}; use polkadot_node_jaeger as jaeger; use polkadot_node_primitives::{ approval::{ - BlockApprovalMeta, DelayTranche, IndirectAssignmentCert, IndirectSignedApprovalVote, + v1::{BlockApprovalMeta, DelayTranche, IndirectSignedApprovalVote}, + v2::{ + AssignmentCertKindV2, BitfieldError, CandidateBitfield, CoreBitfield, + IndirectAssignmentCertV2, + }, }, ValidationResult, DISPUTE_WINDOW, }; @@ -76,12 +80,13 @@ use std::{ use schnellru::{ByLength, LruMap}; use approval_checking::RequiredTranches; +use bitvec::{order::Lsb0, vec::BitVec}; use criteria::{AssignmentCriteria, RealAssignmentCriteria}; use persisted_entries::{ApprovalEntry, BlockEntry, CandidateEntry}; use time::{slot_number_to_tick, Clock, ClockExt, SystemClock, Tick}; mod approval_checking; -mod approval_db; +pub mod approval_db; mod backend; mod criteria; mod import; @@ -90,8 +95,9 @@ mod persisted_entries; mod time; use crate::{ - approval_db::v1::{Config as DatabaseConfig, DbBackend}, + approval_db::v2::{Config as DatabaseConfig, DbBackend}, backend::{Backend, OverlayedBackend}, + criteria::InvalidAssignmentReason, }; #[cfg(test)] @@ -107,7 +113,7 @@ const APPROVAL_CACHE_SIZE: u32 = 1024; const TICK_TOO_FAR_IN_FUTURE: Tick = 20; // 10 seconds. const APPROVAL_DELAY: Tick = 2; -const LOG_TARGET: &str = "parachain::approval-voting"; +pub(crate) const LOG_TARGET: &str = "parachain::approval-voting"; /// Configuration for the approval voting subsystem #[derive(Debug, Clone)] @@ -377,8 +383,8 @@ impl ApprovalVotingSubsystem { /// The operation is not allowed for blocks older than the last finalized one. pub fn revert_to(&self, hash: Hash) -> Result<(), SubsystemError> { let config = - approval_db::v1::Config { col_approval_data: self.db_config.col_approval_data }; - let mut backend = approval_db::v1::DbBackend::new(self.db.clone(), config); + approval_db::v2::Config { col_approval_data: self.db_config.col_approval_data }; + let mut backend = approval_db::v2::DbBackend::new(self.db.clone(), config); let mut overlay = OverlayedBackend::new(&backend); ops::revert_to(&mut overlay, hash)?; @@ -754,15 +760,16 @@ enum Action { tick: Tick, }, LaunchApproval { + claimed_candidate_indices: CandidateBitfield, candidate_hash: CandidateHash, - indirect_cert: IndirectAssignmentCert, + indirect_cert: IndirectAssignmentCertV2, assignment_tranche: DelayTranche, relay_block_hash: Hash, - candidate_index: CandidateIndex, session: SessionIndex, executor_params: ExecutorParams, candidate: CandidateReceipt, backing_group: GroupIndex, + distribute_assignment: bool, }, NoteApprovedInChainSelection(Hash), IssueApproval(CandidateHash, ApprovalVoteRequest), @@ -977,15 +984,16 @@ async fn handle_actions( actions_iter = next_actions.into_iter(); }, Action::LaunchApproval { + claimed_candidate_indices, candidate_hash, indirect_cert, assignment_tranche, relay_block_hash, - candidate_index, session, executor_params, candidate, backing_group, + distribute_assignment, } => { // Don't launch approval work if the node is syncing. if let Mode::Syncing(_) = *mode { @@ -1006,10 +1014,12 @@ async fn handle_actions( launch_approval_span.add_string_tag("block-hash", format!("{:?}", block_hash)); let validator_index = indirect_cert.validator; - ctx.send_unbounded_message(ApprovalDistributionMessage::DistributeAssignment( - indirect_cert, - candidate_index, - )); + if distribute_assignment { + ctx.send_unbounded_message(ApprovalDistributionMessage::DistributeAssignment( + indirect_cert, + claimed_candidate_indices, + )); + } match approvals_cache.get(&candidate_hash) { Some(ApprovalOutcome::Approved) => { @@ -1078,6 +1088,49 @@ async fn handle_actions( Ok(conclude) } +fn cores_to_candidate_indices( + core_indices: &CoreBitfield, + block_entry: &BlockEntry, +) -> Result { + let mut candidate_indices = Vec::new(); + + // Map from core index to candidate index. + for claimed_core_index in core_indices.iter_ones() { + if let Some(candidate_index) = block_entry + .candidates() + .iter() + .position(|(core_index, _)| core_index.0 == claimed_core_index as u32) + { + candidate_indices.push(candidate_index as CandidateIndex); + } + } + + CandidateBitfield::try_from(candidate_indices) +} + +// Returns the claimed core bitfield from the assignment cert, the candidate hash and a +// `BlockEntry`. Can fail only for VRF Delay assignments for which we cannot find the candidate hash +// in the block entry which indicates a bug or corrupted storage. +fn get_assignment_core_indices( + assignment: &AssignmentCertKindV2, + candidate_hash: &CandidateHash, + block_entry: &BlockEntry, +) -> Option { + match &assignment { + AssignmentCertKindV2::RelayVRFModuloCompact { core_bitfield } => + Some(core_bitfield.clone()), + AssignmentCertKindV2::RelayVRFModulo { sample: _ } => block_entry + .candidates() + .iter() + .find(|(_core_index, h)| candidate_hash == h) + .map(|(core_index, _candidate_hash)| { + CoreBitfield::try_from(vec![*core_index]).expect("Not an empty vec; qed") + }), + AssignmentCertKindV2::RelayVRFDelay { core_index } => + Some(CoreBitfield::try_from(vec![*core_index]).expect("Not an empty vec; qed")), + } +} + fn distribution_messages_for_activation( db: &OverlayedBackend<'_, impl Backend>, state: &State, @@ -1142,33 +1195,95 @@ fn distribution_messages_for_activation( match approval_entry.local_statements() { (None, None) | (None, Some(_)) => {}, // second is impossible case. (Some(assignment), None) => { - messages.push(ApprovalDistributionMessage::DistributeAssignment( - IndirectAssignmentCert { - block_hash, - validator: assignment.validator_index(), - cert: assignment.cert().clone(), - }, - i as _, - )); + if let Some(claimed_core_indices) = get_assignment_core_indices( + &assignment.cert().kind, + &candidate_hash, + &block_entry, + ) { + match cores_to_candidate_indices( + &claimed_core_indices, + &block_entry, + ) { + Ok(bitfield) => messages.push( + ApprovalDistributionMessage::DistributeAssignment( + IndirectAssignmentCertV2 { + block_hash, + validator: assignment.validator_index(), + cert: assignment.cert().clone(), + }, + bitfield, + ), + ), + Err(err) => { + // Should never happen. If we fail here it means the + // assignment is null (no cores claimed). + gum::warn!( + target: LOG_TARGET, + ?block_hash, + ?candidate_hash, + ?err, + "Failed to create assignment bitfield", + ); + }, + } + } else { + gum::warn!( + target: LOG_TARGET, + ?block_hash, + ?candidate_hash, + "Cannot get assignment claimed core indices", + ); + } }, (Some(assignment), Some(approval_sig)) => { - messages.push(ApprovalDistributionMessage::DistributeAssignment( - IndirectAssignmentCert { - block_hash, - validator: assignment.validator_index(), - cert: assignment.cert().clone(), - }, - i as _, - )); + if let Some(claimed_core_indices) = get_assignment_core_indices( + &assignment.cert().kind, + &candidate_hash, + &block_entry, + ) { + match cores_to_candidate_indices( + &claimed_core_indices, + &block_entry, + ) { + Ok(bitfield) => messages.push( + ApprovalDistributionMessage::DistributeAssignment( + IndirectAssignmentCertV2 { + block_hash, + validator: assignment.validator_index(), + cert: assignment.cert().clone(), + }, + bitfield, + ), + ), + Err(err) => { + gum::warn!( + target: LOG_TARGET, + ?block_hash, + ?candidate_hash, + ?err, + "Failed to create assignment bitfield", + ); + // If we didn't send assignment, we don't send approval. + continue + }, + } - messages.push(ApprovalDistributionMessage::DistributeApproval( - IndirectSignedApprovalVote { - block_hash, - candidate_index: i as _, - validator: assignment.validator_index(), - signature: approval_sig, - }, - )) + messages.push(ApprovalDistributionMessage::DistributeApproval( + IndirectSignedApprovalVote { + block_hash, + candidate_index: i as _, + validator: assignment.validator_index(), + signature: approval_sig, + }, + )); + } else { + gum::warn!( + target: LOG_TARGET, + ?block_hash, + ?candidate_hash, + "Cannot get assignment claimed core indices", + ); + } }, } }, @@ -1288,14 +1403,14 @@ async fn handle_from_overseer( vec![Action::Conclude] }, FromOrchestra::Communication { msg } => match msg { - ApprovalVotingMessage::CheckAndImportAssignment(a, claimed_core, res) => { + ApprovalVotingMessage::CheckAndImportAssignment(a, claimed_cores, res) => { let (check_outcome, actions) = check_and_import_assignment( ctx.sender(), state, db, session_info_provider, a, - claimed_core, + claimed_cores, ) .await?; let _ = res.send(check_outcome); @@ -1465,7 +1580,6 @@ async fn handle_approved_ancestor( let mut span = span .child("handle-approved-ancestor") .with_stage(jaeger::Stage::ApprovalChecking); - use bitvec::{order::Lsb0, vec::BitVec}; let mut all_approved_max = None; @@ -1804,8 +1918,8 @@ async fn check_and_import_assignment( state: &State, db: &mut OverlayedBackend<'_, impl Backend>, session_info_provider: &mut RuntimeInfo, - assignment: IndirectAssignmentCert, - candidate_index: CandidateIndex, + assignment: IndirectAssignmentCertV2, + candidate_indices: CandidateBitfield, ) -> SubsystemResult<(AssignmentCheckResult, Vec)> where Sender: SubsystemSender, @@ -1818,9 +1932,12 @@ where .map(|span| span.child("check-and-import-assignment")) .unwrap_or_else(|| jaeger::Span::new(assignment.block_hash, "check-and-import-assignment")) .with_relay_parent(assignment.block_hash) - .with_uint_tag("candidate-index", candidate_index as u64) .with_stage(jaeger::Stage::ApprovalChecking); + for candidate_index in candidate_indices.iter_ones() { + check_and_import_assignment_span.add_uint_tag("candidate-index", candidate_index as u64); + } + let block_entry = match db.load_block_entry(&assignment.block_hash)? { Some(b) => b, None => @@ -1850,39 +1967,64 @@ where )), }; - let (claimed_core_index, assigned_candidate_hash) = - match block_entry.candidate(candidate_index as usize) { - Some((c, h)) => (*c, *h), + let n_cores = session_info.n_cores as usize; + + // Early check the candidate bitfield and core bitfields lengths < `n_cores`. + // Core bitfield length is checked later in `check_assignment_cert`. + if candidate_indices.len() > n_cores { + gum::debug!( + target: LOG_TARGET, + validator = assignment.validator.0, + n_cores, + candidate_bitfield_len = ?candidate_indices.len(), + "Oversized bitfield", + ); + + return Ok(( + AssignmentCheckResult::Bad(AssignmentCheckError::InvalidBitfield( + candidate_indices.len(), + )), + Vec::new(), + )) + } + + // The Compact VRF modulo assignment cert has multiple core assignments. + let mut backing_groups = Vec::new(); + let mut claimed_core_indices = Vec::new(); + let mut assigned_candidate_hashes = Vec::new(); + + for candidate_index in candidate_indices.iter_ones() { + let (claimed_core_index, assigned_candidate_hash) = + match block_entry.candidate(candidate_index) { + Some((c, h)) => (*c, *h), + None => + return Ok(( + AssignmentCheckResult::Bad(AssignmentCheckError::InvalidCandidateIndex( + candidate_index as _, + )), + Vec::new(), + )), // no candidate at core. + }; + + let mut candidate_entry = match db.load_candidate_entry(&assigned_candidate_hash)? { + Some(c) => c, None => return Ok(( - AssignmentCheckResult::Bad(AssignmentCheckError::InvalidCandidateIndex( - candidate_index, + AssignmentCheckResult::Bad(AssignmentCheckError::InvalidCandidate( + candidate_index as _, + assigned_candidate_hash, )), Vec::new(), )), // no candidate at core. }; - check_and_import_assignment_span - .add_string_tag("candidate-hash", format!("{:?}", assigned_candidate_hash)); - check_and_import_assignment_span.add_string_tag( - "traceID", - format!("{:?}", jaeger::hash_to_trace_identifier(assigned_candidate_hash.0)), - ); - - let mut candidate_entry = match db.load_candidate_entry(&assigned_candidate_hash)? { - Some(c) => c, - None => - return Ok(( - AssignmentCheckResult::Bad(AssignmentCheckError::InvalidCandidate( - candidate_index, - assigned_candidate_hash, - )), - Vec::new(), - )), - }; + check_and_import_assignment_span + .add_string_tag("candidate-hash", format!("{:?}", assigned_candidate_hash)); + check_and_import_assignment_span.add_string_tag( + "traceID", + format!("{:?}", jaeger::hash_to_trace_identifier(assigned_candidate_hash.0)), + ); - let res = { - // import the assignment. let approval_entry = match candidate_entry.approval_entry_mut(&assignment.block_hash) { Some(a) => a, None => @@ -1895,79 +2037,144 @@ where )), }; - let res = state.assignment_criteria.check_assignment_cert( - claimed_core_index, - assignment.validator, - &criteria::Config::from(session_info), - block_entry.relay_vrf_story(), - &assignment.cert, - approval_entry.backing_group(), - ); + backing_groups.push(approval_entry.backing_group()); + claimed_core_indices.push(claimed_core_index); + assigned_candidate_hashes.push(assigned_candidate_hash); + } - let tranche = match res { - Err(crate::criteria::InvalidAssignment(reason)) => - return Ok(( - AssignmentCheckResult::Bad(AssignmentCheckError::InvalidCert( - assignment.validator, - format!("{:?}", reason), - )), - Vec::new(), + // Error on null assignments. + if claimed_core_indices.is_empty() { + return Ok(( + AssignmentCheckResult::Bad(AssignmentCheckError::InvalidCert( + assignment.validator, + format!("{:?}", InvalidAssignmentReason::NullAssignment), + )), + Vec::new(), + )) + } + + // Check the assignment certificate. + let res = state.assignment_criteria.check_assignment_cert( + claimed_core_indices + .clone() + .try_into() + .expect("Checked for null assignment above; qed"), + assignment.validator, + &criteria::Config::from(session_info), + block_entry.relay_vrf_story(), + &assignment.cert, + backing_groups, + ); + + let tranche = match res { + Err(crate::criteria::InvalidAssignment(reason)) => + return Ok(( + AssignmentCheckResult::Bad(AssignmentCheckError::InvalidCert( + assignment.validator, + format!("{:?}", reason), )), - Ok(tranche) => { - let current_tranche = - state.clock.tranche_now(state.slot_duration_millis, block_entry.slot()); + Vec::new(), + )), + Ok(tranche) => { + let current_tranche = + state.clock.tranche_now(state.slot_duration_millis, block_entry.slot()); - let too_far_in_future = current_tranche + TICK_TOO_FAR_IN_FUTURE as DelayTranche; + let too_far_in_future = current_tranche + TICK_TOO_FAR_IN_FUTURE as DelayTranche; - if tranche >= too_far_in_future { - return Ok((AssignmentCheckResult::TooFarInFuture, Vec::new())) - } + if tranche >= too_far_in_future { + return Ok((AssignmentCheckResult::TooFarInFuture, Vec::new())) + } - tranche - }, - }; + tranche + }, + }; - check_and_import_assignment_span.add_uint_tag("tranche", tranche as u64); + let mut actions = Vec::new(); + let res = { + let mut is_duplicate = true; + // Import the assignments for all cores in the cert. + for (assigned_candidate_hash, candidate_index) in + assigned_candidate_hashes.iter().zip(candidate_indices.iter_ones()) + { + let mut candidate_entry = match db.load_candidate_entry(&assigned_candidate_hash)? { + Some(c) => c, + None => + return Ok(( + AssignmentCheckResult::Bad(AssignmentCheckError::InvalidCandidate( + candidate_index as _, + *assigned_candidate_hash, + )), + Vec::new(), + )), + }; - let is_duplicate = approval_entry.is_assigned(assignment.validator); - approval_entry.import_assignment(tranche, assignment.validator, tick_now); + let approval_entry = match candidate_entry.approval_entry_mut(&assignment.block_hash) { + Some(a) => a, + None => + return Ok(( + AssignmentCheckResult::Bad(AssignmentCheckError::Internal( + assignment.block_hash, + *assigned_candidate_hash, + )), + Vec::new(), + )), + }; + is_duplicate &= approval_entry.is_assigned(assignment.validator); + approval_entry.import_assignment(tranche, assignment.validator, tick_now); + check_and_import_assignment_span.add_uint_tag("tranche", tranche as u64); + + // We've imported a new assignment, so we need to schedule a wake-up for when that might + // no-show. + if let Some((approval_entry, status)) = state + .approval_status(sender, session_info_provider, &block_entry, &candidate_entry) + .await + { + actions.extend(schedule_wakeup_action( + approval_entry, + block_entry.block_hash(), + block_entry.block_number(), + *assigned_candidate_hash, + status.block_tick, + tick_now, + status.required_tranches, + )); + } + + // We also write the candidate entry as it now contains the new candidate. + db.write_candidate_entry(candidate_entry.into()); + } + // Since we don't account for tranche in distribution message fingerprinting, some + // validators can be assigned to the same core (VRF modulo vs VRF delay). These can be + // safely ignored. However, if an assignment is for multiple cores (these are only + // tranche0), we cannot ignore it, because it would mean ignoring other non duplicate + // assignments. if is_duplicate { AssignmentCheckResult::AcceptedDuplicate + } else if candidate_indices.count_ones() > 1 { + gum::trace!( + target: LOG_TARGET, + validator = assignment.validator.0, + candidate_hashes = ?assigned_candidate_hashes, + assigned_cores = ?claimed_core_indices, + ?tranche, + "Imported assignments for multiple cores.", + ); + + AssignmentCheckResult::Accepted } else { gum::trace!( target: LOG_TARGET, validator = assignment.validator.0, - candidate_hash = ?assigned_candidate_hash, - para_id = ?candidate_entry.candidate_receipt().descriptor.para_id, - "Imported assignment.", + candidate_hashes = ?assigned_candidate_hashes, + assigned_cores = ?claimed_core_indices, + "Imported assignment for a single core.", ); AssignmentCheckResult::Accepted } }; - let mut actions = Vec::new(); - - // We've imported a new approval, so we need to schedule a wake-up for when that might no-show. - if let Some((approval_entry, status)) = state - .approval_status(sender, session_info_provider, &block_entry, &candidate_entry) - .await - { - actions.extend(schedule_wakeup_action( - approval_entry, - block_entry.block_hash(), - block_entry.block_number(), - assigned_candidate_hash, - status.block_tick, - tick_now, - status.required_tranches, - )); - } - - // We also write the candidate entry as it now contains the new candidate. - db.write_candidate_entry(candidate_entry.into()); - Ok((res, actions)) } @@ -2341,7 +2548,7 @@ async fn process_wakeup( let candidate_entry = db.load_candidate_entry(&candidate_hash)?; // If either is not present, we have nothing to wakeup. Might have lost a race with finality - let (block_entry, mut candidate_entry) = match (block_entry, candidate_entry) { + let (mut block_entry, mut candidate_entry) = match (block_entry, candidate_entry) { (Some(b), Some(c)) => (b, c), _ => return Ok(Vec::new()), }; @@ -2422,32 +2629,59 @@ async fn process_wakeup( if let Some((cert, val_index, tranche)) = maybe_cert { let indirect_cert = - IndirectAssignmentCert { block_hash: relay_block, validator: val_index, cert }; + IndirectAssignmentCertV2 { block_hash: relay_block, validator: val_index, cert }; + + gum::trace!( + target: LOG_TARGET, + ?candidate_hash, + para_id = ?candidate_receipt.descriptor.para_id, + block_hash = ?relay_block, + "Launching approval work.", + ); - let index_in_candidate = - block_entry.candidates().iter().position(|(_, h)| &candidate_hash == h); + if let Some(claimed_core_indices) = + get_assignment_core_indices(&indirect_cert.cert.kind, &candidate_hash, &block_entry) + { + match cores_to_candidate_indices(&claimed_core_indices, &block_entry) { + Ok(claimed_candidate_indices) => { + // Ensure we distribute multiple core assignments just once. + let distribute_assignment = if claimed_candidate_indices.count_ones() > 1 { + !block_entry.mark_assignment_distributed(claimed_candidate_indices.clone()) + } else { + true + }; + db.write_block_entry(block_entry.clone()); - if let Some(i) = index_in_candidate { - gum::trace!( + actions.push(Action::LaunchApproval { + claimed_candidate_indices, + candidate_hash, + indirect_cert, + assignment_tranche: tranche, + relay_block_hash: relay_block, + session: block_entry.session(), + executor_params: executor_params.clone(), + candidate: candidate_receipt, + backing_group, + distribute_assignment, + }); + }, + Err(err) => { + // Never happens, it should only happen if no cores are claimed, which is a bug. + gum::warn!( + target: LOG_TARGET, + block_hash = ?relay_block, + ?err, + "Failed to create assignment bitfield" + ); + }, + }; + } else { + gum::warn!( target: LOG_TARGET, - ?candidate_hash, - para_id = ?candidate_receipt.descriptor.para_id, block_hash = ?relay_block, - "Launching approval work.", + ?candidate_hash, + "Cannot get assignment claimed core indices", ); - - // sanity: should always be present. - actions.push(Action::LaunchApproval { - candidate_hash, - indirect_cert, - assignment_tranche: tranche, - relay_block_hash: relay_block, - candidate_index: i as _, - session: block_entry.session(), - executor_params: executor_params.clone(), - candidate: candidate_receipt, - backing_group, - }); } } // Although we checked approval earlier in this function, diff --git a/polkadot/node/core/approval-voting/src/ops.rs b/polkadot/node/core/approval-voting/src/ops.rs index 6f57b2f80e8a..a6f0ecf9d1f0 100644 --- a/polkadot/node/core/approval-voting/src/ops.rs +++ b/polkadot/node/core/approval-voting/src/ops.rs @@ -25,7 +25,7 @@ use polkadot_primitives::{BlockNumber, CandidateHash, CandidateReceipt, GroupInd use std::collections::{hash_map::Entry, BTreeMap, HashMap}; use super::{ - approval_db::v1::{OurAssignment, StoredBlockRange}, + approval_db::v2::{OurAssignment, StoredBlockRange}, backend::{Backend, OverlayedBackend}, persisted_entries::{ApprovalEntry, BlockEntry, CandidateEntry}, LOG_TARGET, diff --git a/polkadot/node/core/approval-voting/src/persisted_entries.rs b/polkadot/node/core/approval-voting/src/persisted_entries.rs index 9b6592220275..9cfe1c4cf8da 100644 --- a/polkadot/node/core/approval-voting/src/persisted_entries.rs +++ b/polkadot/node/core/approval-voting/src/persisted_entries.rs @@ -20,16 +20,21 @@ //! Within that context, things are plain-old-data. Within this module, //! data and logic are intertwined. -use polkadot_node_primitives::approval::{AssignmentCert, DelayTranche, RelayVRFStory}; +use polkadot_node_primitives::approval::{ + v1::{DelayTranche, RelayVRFStory}, + v2::{AssignmentCertV2, CandidateBitfield}, +}; use polkadot_primitives::{ BlockNumber, CandidateHash, CandidateReceipt, CoreIndex, GroupIndex, Hash, SessionIndex, ValidatorIndex, ValidatorSignature, }; use sp_consensus_slots::Slot; -use bitvec::{order::Lsb0 as BitOrderLsb0, slice::BitSlice, vec::BitVec}; +use bitvec::{order::Lsb0 as BitOrderLsb0, slice::BitSlice}; use std::collections::BTreeMap; +use crate::approval_db::v2::Bitfield; + use super::{criteria::OurAssignment, time::Tick}; /// Metadata regarding a specific tranche of assignments for a specific candidate. @@ -53,8 +58,8 @@ impl TrancheEntry { } } -impl From for TrancheEntry { - fn from(entry: crate::approval_db::v1::TrancheEntry) -> Self { +impl From for TrancheEntry { + fn from(entry: crate::approval_db::v2::TrancheEntry) -> Self { TrancheEntry { tranche: entry.tranche, assignments: entry.assignments.into_iter().map(|(v, t)| (v, t.into())).collect(), @@ -62,7 +67,7 @@ impl From for TrancheEntry { } } -impl From for crate::approval_db::v1::TrancheEntry { +impl From for crate::approval_db::v2::TrancheEntry { fn from(entry: TrancheEntry) -> Self { Self { tranche: entry.tranche, @@ -80,7 +85,7 @@ pub struct ApprovalEntry { our_assignment: Option, our_approval_sig: Option, // `n_validators` bits. - assignments: BitVec, + assigned_validators: Bitfield, approved: bool, } @@ -92,10 +97,17 @@ impl ApprovalEntry { our_assignment: Option, our_approval_sig: Option, // `n_validators` bits. - assignments: BitVec, + assigned_validators: Bitfield, approved: bool, ) -> Self { - Self { tranches, backing_group, our_assignment, our_approval_sig, assignments, approved } + Self { + tranches, + backing_group, + our_assignment, + our_approval_sig, + assigned_validators, + approved, + } } // Access our assignment for this approval entry. @@ -107,7 +119,7 @@ impl ApprovalEntry { pub fn trigger_our_assignment( &mut self, tick_now: Tick, - ) -> Option<(AssignmentCert, ValidatorIndex, DelayTranche)> { + ) -> Option<(AssignmentCertV2, ValidatorIndex, DelayTranche)> { let our = self.our_assignment.as_mut().and_then(|a| { if a.triggered() { return None @@ -131,7 +143,10 @@ impl ApprovalEntry { /// Whether a validator is already assigned. pub fn is_assigned(&self, validator_index: ValidatorIndex) -> bool { - self.assignments.get(validator_index.0 as usize).map(|b| *b).unwrap_or(false) + self.assigned_validators + .get(validator_index.0 as usize) + .map(|b| *b) + .unwrap_or(false) } /// Import an assignment. No-op if already assigned on the same tranche. @@ -158,14 +173,14 @@ impl ApprovalEntry { }; self.tranches[idx].assignments.push((validator_index, tick_now)); - self.assignments.set(validator_index.0 as _, true); + self.assigned_validators.set(validator_index.0 as _, true); } // Produce a bitvec indicating the assignments of all validators up to and // including `tranche`. - pub fn assignments_up_to(&self, tranche: DelayTranche) -> BitVec { + pub fn assignments_up_to(&self, tranche: DelayTranche) -> Bitfield { self.tranches.iter().take_while(|e| e.tranche <= tranche).fold( - bitvec::bitvec![u8, BitOrderLsb0; 0; self.assignments.len()], + bitvec::bitvec![u8, BitOrderLsb0; 0; self.assigned_validators.len()], |mut a, e| { for &(v, _) in &e.assignments { a.set(v.0 as _, true); @@ -193,12 +208,12 @@ impl ApprovalEntry { /// Get the number of validators in this approval entry. pub fn n_validators(&self) -> usize { - self.assignments.len() + self.assigned_validators.len() } /// Get the number of assignments by validators, including the local validator. pub fn n_assignments(&self) -> usize { - self.assignments.count_ones() + self.assigned_validators.count_ones() } /// Get the backing group index of the approval entry. @@ -219,27 +234,27 @@ impl ApprovalEntry { } } -impl From for ApprovalEntry { - fn from(entry: crate::approval_db::v1::ApprovalEntry) -> Self { +impl From for ApprovalEntry { + fn from(entry: crate::approval_db::v2::ApprovalEntry) -> Self { ApprovalEntry { tranches: entry.tranches.into_iter().map(Into::into).collect(), backing_group: entry.backing_group, our_assignment: entry.our_assignment.map(Into::into), our_approval_sig: entry.our_approval_sig.map(Into::into), - assignments: entry.assignments, + assigned_validators: entry.assigned_validators, approved: entry.approved, } } } -impl From for crate::approval_db::v1::ApprovalEntry { +impl From for crate::approval_db::v2::ApprovalEntry { fn from(entry: ApprovalEntry) -> Self { Self { tranches: entry.tranches.into_iter().map(Into::into).collect(), backing_group: entry.backing_group, our_assignment: entry.our_assignment.map(Into::into), our_approval_sig: entry.our_approval_sig.map(Into::into), - assignments: entry.assignments, + assigned_validators: entry.assigned_validators, approved: entry.approved, } } @@ -253,7 +268,7 @@ pub struct CandidateEntry { // Assignments are based on blocks, so we need to track assignments separately // based on the block we are looking at. pub block_assignments: BTreeMap, - pub approvals: BitVec, + pub approvals: Bitfield, } impl CandidateEntry { @@ -290,8 +305,8 @@ impl CandidateEntry { } } -impl From for CandidateEntry { - fn from(entry: crate::approval_db::v1::CandidateEntry) -> Self { +impl From for CandidateEntry { + fn from(entry: crate::approval_db::v2::CandidateEntry) -> Self { CandidateEntry { candidate: entry.candidate, session: entry.session, @@ -305,7 +320,7 @@ impl From for CandidateEntry { } } -impl From for crate::approval_db::v1::CandidateEntry { +impl From for crate::approval_db::v2::CandidateEntry { fn from(entry: CandidateEntry) -> Self { Self { candidate: entry.candidate, @@ -336,8 +351,12 @@ pub struct BlockEntry { // A bitfield where the i'th bit corresponds to the i'th candidate in `candidates`. // The i'th bit is `true` iff the candidate has been approved in the context of this // block. The block can be considered approved if the bitfield has all bits set to `true`. - pub approved_bitfield: BitVec, + pub approved_bitfield: Bitfield, pub children: Vec, + // A list of assignments for which we already distributed the assignment. + // We use this to ensure we don't distribute multiple core assignments twice as we track + // individual wakeups for each core. + distributed_assignments: Bitfield, } impl BlockEntry { @@ -412,6 +431,39 @@ impl BlockEntry { pub fn parent_hash(&self) -> Hash { self.parent_hash } + + /// Mark distributed assignment for many candidate indices. + /// Returns `true` if an assignment was already distributed for the `candidates`. + pub fn mark_assignment_distributed(&mut self, candidates: CandidateBitfield) -> bool { + let bitfield = candidates.into_inner(); + let total_one_bits = self.distributed_assignments.count_ones(); + + let new_len = std::cmp::max(self.distributed_assignments.len(), bitfield.len()); + self.distributed_assignments.resize(new_len, false); + self.distributed_assignments |= bitfield; + + // If the an operation did not change our current bitfied, we return true. + let distributed = total_one_bits == self.distributed_assignments.count_ones(); + + distributed + } +} + +impl From for BlockEntry { + fn from(entry: crate::approval_db::v2::BlockEntry) -> Self { + BlockEntry { + block_hash: entry.block_hash, + parent_hash: entry.parent_hash, + block_number: entry.block_number, + session: entry.session, + slot: entry.slot, + relay_vrf_story: RelayVRFStory(entry.relay_vrf_story), + candidates: entry.candidates, + approved_bitfield: entry.approved_bitfield, + children: entry.children, + distributed_assignments: entry.distributed_assignments, + } + } } impl From for BlockEntry { @@ -426,11 +478,12 @@ impl From for BlockEntry { candidates: entry.candidates, approved_bitfield: entry.approved_bitfield, children: entry.children, + distributed_assignments: Default::default(), } } } -impl From for crate::approval_db::v1::BlockEntry { +impl From for crate::approval_db::v2::BlockEntry { fn from(entry: BlockEntry) -> Self { Self { block_hash: entry.block_hash, @@ -442,6 +495,36 @@ impl From for crate::approval_db::v1::BlockEntry { candidates: entry.candidates, approved_bitfield: entry.approved_bitfield, children: entry.children, + distributed_assignments: entry.distributed_assignments, + } + } +} + +/// Migration helpers. +impl From for CandidateEntry { + fn from(value: crate::approval_db::v1::CandidateEntry) -> Self { + Self { + approvals: value.approvals, + block_assignments: value + .block_assignments + .into_iter() + .map(|(h, ae)| (h, ae.into())) + .collect(), + candidate: value.candidate, + session: value.session, + } + } +} + +impl From for ApprovalEntry { + fn from(value: crate::approval_db::v1::ApprovalEntry) -> Self { + ApprovalEntry { + tranches: value.tranches.into_iter().map(|tranche| tranche.into()).collect(), + backing_group: value.backing_group, + our_assignment: value.our_assignment.map(|assignment| assignment.into()), + our_approval_sig: value.our_approval_sig, + assigned_validators: value.assignments, + approved: value.approved, } } } diff --git a/polkadot/node/core/approval-voting/src/tests.rs b/polkadot/node/core/approval-voting/src/tests.rs index 0b98f28fbbf0..13213c158cdb 100644 --- a/polkadot/node/core/approval-voting/src/tests.rs +++ b/polkadot/node/core/approval-voting/src/tests.rs @@ -16,10 +16,14 @@ use self::test_helpers::mock::new_leaf; use super::*; +use crate::backend::V1ReadBackend; use polkadot_node_primitives::{ approval::{ - AssignmentCert, AssignmentCertKind, DelayTranche, VrfOutput, VrfProof, VrfSignature, - RELAY_VRF_MODULO_CONTEXT, + v1::{ + AssignmentCert, AssignmentCertKind, DelayTranche, VrfOutput, VrfProof, VrfSignature, + RELAY_VRF_MODULO_CONTEXT, + }, + v2::{AssignmentCertKindV2, AssignmentCertV2}, }, AvailableData, BlockData, PoV, }; @@ -52,7 +56,7 @@ use std::{ }; use super::{ - approval_db::v1::StoredBlockRange, + approval_db::v2::StoredBlockRange, backend::BackendWriteOp, import::tests::{ garbage_vrf_signature, AllowedSlots, BabeEpoch, BabeEpochConfiguration, @@ -112,7 +116,7 @@ fn make_sync_oracle(val: bool) -> (Box, TestSyncOracleHan #[cfg(test)] pub mod test_constants { - use crate::approval_db::v1::Config as DatabaseConfig; + use crate::approval_db::v2::Config as DatabaseConfig; const DATA_COL: u32 = 0; pub(crate) const NUM_COLUMNS: u32 = 1; @@ -162,7 +166,7 @@ impl Clock for MockClock { // This mock clock allows us to manipulate the time and // be notified when wakeups have been triggered. -#[derive(Default)] +#[derive(Default, Debug)] struct MockClockInner { tick: Tick, wakeups: Vec<(Tick, oneshot::Sender<()>)>, @@ -232,7 +236,7 @@ where fn compute_assignments( &self, _keystore: &LocalKeystore, - _relay_vrf_story: polkadot_node_primitives::approval::RelayVRFStory, + _relay_vrf_story: polkadot_node_primitives::approval::v1::RelayVRFStory, _config: &criteria::Config, _leaving_cores: Vec<( CandidateHash, @@ -245,13 +249,13 @@ where fn check_assignment_cert( &self, - _claimed_core_index: polkadot_primitives::CoreIndex, + _claimed_core_bitfield: polkadot_node_primitives::approval::v2::CoreBitfield, validator_index: ValidatorIndex, _config: &criteria::Config, - _relay_vrf_story: polkadot_node_primitives::approval::RelayVRFStory, - _assignment: &polkadot_node_primitives::approval::AssignmentCert, - _backing_group: polkadot_primitives::GroupIndex, - ) -> Result { + _relay_vrf_story: polkadot_node_primitives::approval::v1::RelayVRFStory, + _assignment: &polkadot_node_primitives::approval::v2::AssignmentCertV2, + _backing_groups: Vec, + ) -> Result { self.1(validator_index) } } @@ -272,6 +276,18 @@ struct TestStoreInner { candidate_entries: HashMap, } +impl V1ReadBackend for TestStoreInner { + fn load_candidate_entry_v1( + &self, + candidate_hash: &CandidateHash, + ) -> SubsystemResult> { + self.load_candidate_entry(candidate_hash) + } + fn load_block_entry_v1(&self, block_hash: &Hash) -> SubsystemResult> { + self.load_block_entry(block_hash) + } +} + impl Backend for TestStoreInner { fn load_block_entry(&self, block_hash: &Hash) -> SubsystemResult> { Ok(self.block_entries.get(block_hash).cloned()) @@ -343,6 +359,18 @@ pub struct TestStore { store: Arc>, } +impl V1ReadBackend for TestStore { + fn load_candidate_entry_v1( + &self, + candidate_hash: &CandidateHash, + ) -> SubsystemResult> { + self.load_candidate_entry(candidate_hash) + } + fn load_block_entry_v1(&self, block_hash: &Hash) -> SubsystemResult> { + self.load_block_entry(block_hash) + } +} + impl Backend for TestStore { fn load_block_entry(&self, block_hash: &Hash) -> SubsystemResult> { let store = self.store.lock(); @@ -392,6 +420,17 @@ fn garbage_assignment_cert(kind: AssignmentCertKind) -> AssignmentCert { AssignmentCert { kind, vrf: VrfSignature { output: VrfOutput(out), proof: VrfProof(proof) } } } +fn garbage_assignment_cert_v2(kind: AssignmentCertKindV2) -> AssignmentCertV2 { + let ctx = schnorrkel::signing_context(RELAY_VRF_MODULO_CONTEXT); + let msg = b"test-garbage"; + let mut prng = rand_core::OsRng; + let keypair = schnorrkel::Keypair::generate_with(&mut prng); + let (inout, proof, _) = keypair.vrf_sign(ctx.bytes(msg)); + let out = inout.to_output(); + + AssignmentCertV2 { kind, vrf: VrfSignature { output: VrfOutput(out), proof: VrfProof(proof) } } +} + fn sign_approval( key: Sr25519Keyring, candidate_hash: CandidateHash, @@ -468,6 +507,12 @@ fn test_harness>( config: HarnessConfig, test: impl FnOnce(TestHarness) -> T, ) { + let _ = env_logger::builder() + .is_test(true) + .filter(Some("polkadot_node_core_approval_voting"), log::LevelFilter::Trace) + .filter(Some(LOG_TARGET), log::LevelFilter::Trace) + .try_init(); + let HarnessConfig { sync_oracle, sync_oracle_handle, clock, backend, assignment_criteria } = config; @@ -617,12 +662,13 @@ async fn check_and_import_assignment( overseer, FromOrchestra::Communication { msg: ApprovalVotingMessage::CheckAndImportAssignment( - IndirectAssignmentCert { + IndirectAssignmentCertV2 { block_hash, validator, - cert: garbage_assignment_cert(AssignmentCertKind::RelayVRFModulo { sample: 0 }), + cert: garbage_assignment_cert(AssignmentCertKind::RelayVRFModulo { sample: 0 }) + .into(), }, - candidate_index, + candidate_index.into(), tx, ), }, @@ -631,6 +677,38 @@ async fn check_and_import_assignment( rx } +async fn check_and_import_assignment_v2( + overseer: &mut VirtualOverseer, + block_hash: Hash, + core_indices: Vec, + validator: ValidatorIndex, +) -> oneshot::Receiver { + let (tx, rx) = oneshot::channel(); + overseer_send( + overseer, + FromOrchestra::Communication { + msg: ApprovalVotingMessage::CheckAndImportAssignment( + IndirectAssignmentCertV2 { + block_hash, + validator, + cert: garbage_assignment_cert_v2(AssignmentCertKindV2::RelayVRFModuloCompact { + core_bitfield: core_indices + .clone() + .into_iter() + .map(|c| CoreIndex(c)) + .collect::>() + .try_into() + .unwrap(), + }), + }, + core_indices.try_into().unwrap(), + tx, + ), + }, + ) + .await; + rx +} struct BlockConfig { slot: Slot, candidates: Option>, @@ -743,7 +821,7 @@ fn session_info(keys: &[Sr25519Keyring]) -> SessionInfo { vec![ValidatorIndex(0)], vec![ValidatorIndex(1)], ]), - n_cores: keys.len() as _, + n_cores: 10, needed_approvals: 2, zeroth_delay_tranche_width: 5, relay_vrf_modulo_samples: 3, @@ -1068,14 +1146,15 @@ fn blank_subsystem_act_on_bad_block() { &mut virtual_overseer, FromOrchestra::Communication { msg: ApprovalVotingMessage::CheckAndImportAssignment( - IndirectAssignmentCert { + IndirectAssignmentCertV2 { block_hash: bad_block_hash, validator: 0u32.into(), cert: garbage_assignment_cert(AssignmentCertKind::RelayVRFModulo { sample: 0, - }), + }) + .into(), }, - 0u32, + 0u32.into(), tx, ), }, @@ -1331,9 +1410,22 @@ fn subsystem_accepts_duplicate_assignment() { } ); - let block_hash = Hash::repeat_byte(0x01); - let candidate_index = 0; let validator = ValidatorIndex(0); + let candidate_index = 0; + let block_hash = Hash::repeat_byte(0x01); + + let candidate_receipt1 = { + let mut receipt = dummy_candidate_receipt(block_hash); + receipt.descriptor.para_id = ParaId::from(1_u32); + receipt + }; + let candidate_receipt2 = { + let mut receipt = dummy_candidate_receipt(block_hash); + receipt.descriptor.para_id = ParaId::from(2_u32); + receipt + }; + let candidate_index1 = 0; + let candidate_index2 = 1; // Add block hash 00. ChainBuilder::new() @@ -1341,21 +1433,30 @@ fn subsystem_accepts_duplicate_assignment() { block_hash, ChainBuilder::GENESIS_HASH, 1, - BlockConfig { slot: Slot::from(1), candidates: None, session_info: None }, + BlockConfig { + slot: Slot::from(1), + candidates: Some(vec![ + (candidate_receipt1, CoreIndex(0), GroupIndex(1)), + (candidate_receipt2, CoreIndex(1), GroupIndex(1)), + ]), + session_info: None, + }, ) .build(&mut virtual_overseer) .await; - let rx = check_and_import_assignment( + // Initial assignment. + let rx = check_and_import_assignment_v2( &mut virtual_overseer, block_hash, - candidate_index, + vec![candidate_index1, candidate_index2], validator, ) .await; assert_eq!(rx.await, Ok(AssignmentCheckResult::Accepted)); + // Test with single assigned core. let rx = check_and_import_assignment( &mut virtual_overseer, block_hash, @@ -1366,6 +1467,18 @@ fn subsystem_accepts_duplicate_assignment() { assert_eq!(rx.await, Ok(AssignmentCheckResult::AcceptedDuplicate)); + // Test with multiple assigned cores. This cannot happen in practice, as tranche0 + // assignments are sent first, but we should still ensure correct behavior. + let rx = check_and_import_assignment_v2( + &mut virtual_overseer, + block_hash, + vec![candidate_index1, candidate_index2], + validator, + ) + .await; + + assert_eq!(rx.await, Ok(AssignmentCheckResult::AcceptedDuplicate)); + virtual_overseer }); } @@ -1416,6 +1529,63 @@ fn subsystem_rejects_assignment_with_unknown_candidate() { }); } +#[test] +fn subsystem_rejects_oversized_bitfields() { + test_harness(HarnessConfig::default(), |test_harness| async move { + let TestHarness { mut virtual_overseer, sync_oracle_handle: _sync_oracle_handle, .. } = + test_harness; + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::ChainApi(ChainApiMessage::FinalizedBlockNumber(rx)) => { + rx.send(Ok(0)).unwrap(); + } + ); + + let block_hash = Hash::repeat_byte(0x01); + let candidate_index = 10; + let validator = ValidatorIndex(0); + + ChainBuilder::new() + .add_block( + block_hash, + ChainBuilder::GENESIS_HASH, + 1, + BlockConfig { slot: Slot::from(1), candidates: None, session_info: None }, + ) + .build(&mut virtual_overseer) + .await; + + let rx = check_and_import_assignment( + &mut virtual_overseer, + block_hash, + candidate_index, + validator, + ) + .await; + + assert_eq!( + rx.await, + Ok(AssignmentCheckResult::Bad(AssignmentCheckError::InvalidBitfield( + candidate_index as usize + 1 + ))), + ); + + let rx = check_and_import_assignment_v2( + &mut virtual_overseer, + block_hash, + vec![1, 2, 10, 50], + validator, + ) + .await; + + assert_eq!( + rx.await, + Ok(AssignmentCheckResult::Bad(AssignmentCheckError::InvalidBitfield(51))), + ); + virtual_overseer + }); +} + #[test] fn subsystem_accepts_and_imports_approval_after_assignment() { test_harness(HarnessConfig::default(), |test_harness| async move { @@ -1736,14 +1906,15 @@ fn linear_import_act_on_leaf() { &mut virtual_overseer, FromOrchestra::Communication { msg: ApprovalVotingMessage::CheckAndImportAssignment( - IndirectAssignmentCert { + IndirectAssignmentCertV2 { block_hash: head, validator: 0u32.into(), cert: garbage_assignment_cert(AssignmentCertKind::RelayVRFModulo { sample: 0, - }), + }) + .into(), }, - 0u32, + 0u32.into(), tx, ), }, @@ -1806,14 +1977,15 @@ fn forkful_import_at_same_height_act_on_leaf() { &mut virtual_overseer, FromOrchestra::Communication { msg: ApprovalVotingMessage::CheckAndImportAssignment( - IndirectAssignmentCert { + IndirectAssignmentCertV2 { block_hash: head, validator: 0u32.into(), cert: garbage_assignment_cert(AssignmentCertKind::RelayVRFModulo { sample: 0, - }), + }) + .into(), }, - 0u32, + 0u32.into(), tx, ), }, @@ -2257,8 +2429,24 @@ fn subsystem_validate_approvals_cache() { let mut assignments = HashMap::new(); let _ = assignments.insert( CoreIndex(0), - approval_db::v1::OurAssignment { - cert: garbage_assignment_cert(AssignmentCertKind::RelayVRFModulo { sample: 0 }), + approval_db::v2::OurAssignment { + cert: garbage_assignment_cert(AssignmentCertKind::RelayVRFModulo { sample: 0 }) + .into(), + tranche: 0, + validator_index: ValidatorIndex(0), + triggered: false, + } + .into(), + ); + + let _ = assignments.insert( + CoreIndex(0), + approval_db::v2::OurAssignment { + cert: garbage_assignment_cert_v2(AssignmentCertKindV2::RelayVRFModuloCompact { + core_bitfield: vec![CoreIndex(0), CoreIndex(1), CoreIndex(2)] + .try_into() + .unwrap(), + }), tranche: 0, validator_index: ValidatorIndex(0), triggered: false, @@ -2355,6 +2543,137 @@ fn subsystem_validate_approvals_cache() { }); } +#[test] +fn subsystem_doesnt_distribute_duplicate_compact_assignments() { + let assignment_criteria = Box::new(MockAssignmentCriteria( + || { + let mut assignments = HashMap::new(); + let cert = garbage_assignment_cert_v2(AssignmentCertKindV2::RelayVRFModuloCompact { + core_bitfield: vec![CoreIndex(0), CoreIndex(1)].try_into().unwrap(), + }); + + let _ = assignments.insert( + CoreIndex(0), + approval_db::v2::OurAssignment { + cert: cert.clone(), + tranche: 0, + validator_index: ValidatorIndex(0), + triggered: false, + } + .into(), + ); + + let _ = assignments.insert( + CoreIndex(1), + approval_db::v2::OurAssignment { + cert, + tranche: 0, + validator_index: ValidatorIndex(0), + triggered: false, + } + .into(), + ); + assignments + }, + |_| Ok(0), + )); + + let config = HarnessConfigBuilder::default().assignment_criteria(assignment_criteria).build(); + let store = config.backend(); + + test_harness(config, |test_harness| async move { + let TestHarness { + mut virtual_overseer, + sync_oracle_handle: _sync_oracle_handle, + clock, + .. + } = test_harness; + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::ChainApi(ChainApiMessage::FinalizedBlockNumber(rx)) => { + rx.send(Ok(0)).unwrap(); + } + ); + + let block_hash = Hash::repeat_byte(0x01); + + let candidate_receipt1 = { + let mut receipt = dummy_candidate_receipt(block_hash); + receipt.descriptor.para_id = ParaId::from(1_u32); + receipt + }; + let candidate_receipt2 = { + let mut receipt = dummy_candidate_receipt(block_hash); + receipt.descriptor.para_id = ParaId::from(2_u32); + receipt + }; + let candidate_index1 = 0; + let candidate_index2 = 1; + + // Add block hash 00. + ChainBuilder::new() + .add_block( + block_hash, + ChainBuilder::GENESIS_HASH, + 1, + BlockConfig { + slot: Slot::from(0), + candidates: Some(vec![ + (candidate_receipt1.clone(), CoreIndex(0), GroupIndex(1)), + (candidate_receipt2.clone(), CoreIndex(1), GroupIndex(1)), + ]), + session_info: None, + }, + ) + .build(&mut virtual_overseer) + .await; + + // Activate the wakeup present above, and sleep to allow process_wakeups to execute.. + assert_eq!(Some(2), clock.inner.lock().next_wakeup()); + gum::trace!("clock \n{:?}\n", clock.inner.lock()); + + clock.inner.lock().wakeup_all(100); + + assert_eq!(clock.inner.lock().wakeups.len(), 0); + + futures_timer::Delay::new(Duration::from_millis(100)).await; + + // Assignment is distributed only once from `approval-voting` + assert_matches!( + overseer_recv(&mut virtual_overseer).await, + AllMessages::ApprovalDistribution(ApprovalDistributionMessage::DistributeAssignment( + _, + c_indices, + )) => { + assert_eq!(c_indices, vec![candidate_index1, candidate_index2].try_into().unwrap()); + } + ); + + // Candidate 1 + recover_available_data(&mut virtual_overseer).await; + fetch_validation_code(&mut virtual_overseer).await; + + // Candidate 2 + recover_available_data(&mut virtual_overseer).await; + fetch_validation_code(&mut virtual_overseer).await; + + // Check if assignment was triggered for candidate 1. + let candidate_entry = + store.load_candidate_entry(&candidate_receipt1.hash()).unwrap().unwrap(); + let our_assignment = + candidate_entry.approval_entry(&block_hash).unwrap().our_assignment().unwrap(); + assert!(our_assignment.triggered()); + + // Check if assignment was triggered for candidate 2. + let candidate_entry = + store.load_candidate_entry(&candidate_receipt2.hash()).unwrap().unwrap(); + let our_assignment = + candidate_entry.approval_entry(&block_hash).unwrap().our_assignment().unwrap(); + assert!(our_assignment.triggered()); + virtual_overseer + }); +} + /// Ensure that when two assignments are imported, only one triggers the Approval Checking work async fn handle_double_assignment_import( virtual_overseer: &mut VirtualOverseer, @@ -2364,9 +2683,9 @@ async fn handle_double_assignment_import( overseer_recv(virtual_overseer).await, AllMessages::ApprovalDistribution(ApprovalDistributionMessage::DistributeAssignment( _, - c_index, + c_indices, )) => { - assert_eq!(candidate_index, c_index); + assert_eq!(Into::::into(candidate_index), c_indices); } ); @@ -2379,7 +2698,7 @@ async fn handle_double_assignment_import( _, c_index )) => { - assert_eq!(candidate_index, c_index); + assert_eq!(Into::::into(candidate_index), c_index); } ); @@ -2400,7 +2719,6 @@ async fn handle_double_assignment_import( overseer_recv(virtual_overseer).await, AllMessages::ApprovalDistribution(ApprovalDistributionMessage::DistributeApproval(_)) ); - // Assert that there are no more messages being sent by the subsystem assert!(overseer_recv(virtual_overseer).timeout(TIMEOUT / 2).await.is_none()); } @@ -2475,8 +2793,9 @@ where let mut assignments = HashMap::new(); let _ = assignments.insert( CoreIndex(0), - approval_db::v1::OurAssignment { - cert: garbage_assignment_cert(AssignmentCertKind::RelayVRFModulo { sample: 0 }), + approval_db::v2::OurAssignment { + cert: garbage_assignment_cert(AssignmentCertKind::RelayVRFModulo { sample: 0 }) + .into(), tranche: our_assigned_tranche, validator_index: ValidatorIndex(0), triggered: false, @@ -2610,14 +2929,12 @@ async fn step_until_done(clock: &MockClock) { futures_timer::Delay::new(Duration::from_millis(200)).await; let mut clock = clock.inner.lock(); if let Some(tick) = clock.next_wakeup() { - println!("TICK: {:?}", tick); relevant_ticks.push(tick); clock.set_tick(tick); } else { break } } - println!("relevant_ticks: {:?}", relevant_ticks); } #[test] diff --git a/polkadot/node/core/approval-voting/src/time.rs b/polkadot/node/core/approval-voting/src/time.rs index 34132dc22b23..a45866402c82 100644 --- a/polkadot/node/core/approval-voting/src/time.rs +++ b/polkadot/node/core/approval-voting/src/time.rs @@ -17,7 +17,7 @@ //! Time utilities for approval voting. use futures::prelude::*; -use polkadot_node_primitives::approval::DelayTranche; +use polkadot_node_primitives::approval::v1::DelayTranche; use sp_consensus_slots::Slot; use std::{ pin::Pin, diff --git a/polkadot/node/core/dispute-coordinator/src/import.rs b/polkadot/node/core/dispute-coordinator/src/import.rs index cf51ae4d3655..837ad7856e73 100644 --- a/polkadot/node/core/dispute-coordinator/src/import.rs +++ b/polkadot/node/core/dispute-coordinator/src/import.rs @@ -117,7 +117,7 @@ pub enum OwnVoteState { } impl OwnVoteState { - fn new(votes: &CandidateVotes, env: &CandidateEnvironment<'_>) -> Self { + fn new(votes: &CandidateVotes, env: &CandidateEnvironment) -> Self { let controlled_indices = env.controlled_indices(); if controlled_indices.is_empty() { return Self::CannotVote diff --git a/polkadot/node/network/approval-distribution/Cargo.toml b/polkadot/node/network/approval-distribution/Cargo.toml index e19a1b83a62f..32b7f489efa2 100644 --- a/polkadot/node/network/approval-distribution/Cargo.toml +++ b/polkadot/node/network/approval-distribution/Cargo.toml @@ -14,10 +14,12 @@ polkadot-node-subsystem-util = { path = "../../subsystem-util" } polkadot-primitives = { path = "../../../primitives" } polkadot-node-jaeger = { path = "../../jaeger" } rand = "0.8" +itertools = "0.10.5" futures = "0.3.21" futures-timer = "3.0.2" gum = { package = "tracing-gum", path = "../../gum" } +bitvec = { version = "1.0.0", default-features = false, features = ["alloc"] } [dev-dependencies] sp-authority-discovery = { path = "../../../../substrate/primitives/authority-discovery" } diff --git a/polkadot/node/network/approval-distribution/src/lib.rs b/polkadot/node/network/approval-distribution/src/lib.rs index d8949b1c1123..47482eef7640 100644 --- a/polkadot/node/network/approval-distribution/src/lib.rs +++ b/polkadot/node/network/approval-distribution/src/lib.rs @@ -23,17 +23,23 @@ #![warn(missing_docs)] +use self::metrics::Metrics; use futures::{channel::oneshot, select, FutureExt as _}; +use itertools::Itertools; +use net_protocol::peer_set::{ProtocolVersion, ValidationVersion}; use polkadot_node_jaeger as jaeger; use polkadot_node_network_protocol::{ - self as net_protocol, + self as net_protocol, filter_by_peer_version, grid_topology::{RandomRouting, RequiredRouting, SessionGridTopologies, SessionGridTopology}, - peer_set::{ValidationVersion, MAX_NOTIFICATION_SIZE}, - v1 as protocol_v1, v2 as protocol_v2, PeerId, UnifiedReputationChange as Rep, Versioned, - VersionedValidationProtocol, View, + peer_set::MAX_NOTIFICATION_SIZE, + v1 as protocol_v1, v2 as protocol_v2, vstaging as protocol_vstaging, PeerId, + UnifiedReputationChange as Rep, Versioned, View, }; use polkadot_node_primitives::approval::{ - AssignmentCert, BlockApprovalMeta, IndirectAssignmentCert, IndirectSignedApprovalVote, + v1::{ + AssignmentCertKind, BlockApprovalMeta, IndirectAssignmentCert, IndirectSignedApprovalVote, + }, + v2::{AsBitIndex, AssignmentCertKindV2, CandidateBitfield, IndirectAssignmentCertV2}, }; use polkadot_node_subsystem::{ messages::{ @@ -52,8 +58,6 @@ use std::{ time::Duration, }; -use self::metrics::Metrics; - mod metrics; #[cfg(test)] @@ -67,11 +71,15 @@ const COST_DUPLICATE_MESSAGE: Rep = Rep::CostMinorRepeated("Peer sent identical const COST_ASSIGNMENT_TOO_FAR_IN_THE_FUTURE: Rep = Rep::CostMinor("The vote was valid but too far in the future"); const COST_INVALID_MESSAGE: Rep = Rep::CostMajor("The vote was bad"); +const COST_OVERSIZED_BITFIELD: Rep = Rep::CostMajor("Oversized certificate or candidate bitfield"); const BENEFIT_VALID_MESSAGE: Rep = Rep::BenefitMinor("Peer sent a valid message"); const BENEFIT_VALID_MESSAGE_FIRST: Rep = Rep::BenefitMinorFirst("Valid message with new information"); +// Maximum valid size for the `CandidateBitfield` in the assignment messages. +const MAX_BITFIELD_SIZE: usize = 500; + /// The Approval Distribution subsystem. pub struct ApprovalDistribution { metrics: Metrics, @@ -100,7 +108,144 @@ impl RecentlyOutdated { } } -// In case the original gtid topology mechanisms don't work on their own, we need to trade bandwidth +// Contains topology routing information for assignments and approvals. +struct ApprovalRouting { + required_routing: RequiredRouting, + local: bool, + random_routing: RandomRouting, +} + +// This struct is responsible for tracking the full state of an assignment and grid routing +// information. +struct ApprovalEntry { + // The assignment certificate. + assignment: IndirectAssignmentCertV2, + // The candidates claimed by the certificate. A mapping between bit index and candidate index. + candidates: CandidateBitfield, + // The approval signatures for each `CandidateIndex` claimed by the assignment certificate. + approvals: HashMap, + // The validator index of the assignment signer. + validator_index: ValidatorIndex, + // Information required for gossiping to other peers using the grid topology. + routing_info: ApprovalRouting, +} + +#[derive(Debug)] +enum ApprovalEntryError { + InvalidValidatorIndex, + CandidateIndexOutOfBounds, + InvalidCandidateIndex, + DuplicateApproval, +} + +impl ApprovalEntry { + pub fn new( + assignment: IndirectAssignmentCertV2, + candidates: CandidateBitfield, + routing_info: ApprovalRouting, + ) -> ApprovalEntry { + Self { + validator_index: assignment.validator, + assignment, + approvals: HashMap::with_capacity(candidates.len()), + candidates, + routing_info, + } + } + + // Create a `MessageSubject` to reference the assignment. + pub fn create_assignment_knowledge(&self, block_hash: Hash) -> (MessageSubject, MessageKind) { + ( + MessageSubject(block_hash, self.candidates.clone(), self.validator_index), + MessageKind::Assignment, + ) + } + + // Create a `MessageSubject` to reference the approval. + pub fn create_approval_knowledge( + &self, + block_hash: Hash, + candidate_index: CandidateIndex, + ) -> (MessageSubject, MessageKind) { + ( + MessageSubject(block_hash, candidate_index.into(), self.validator_index), + MessageKind::Approval, + ) + } + + // Updates routing information and returns the previous information if any. + pub fn routing_info_mut(&mut self) -> &mut ApprovalRouting { + &mut self.routing_info + } + + // Get the routing information. + pub fn routing_info(&self) -> &ApprovalRouting { + &self.routing_info + } + + // Update routing information. + pub fn update_required_routing(&mut self, required_routing: RequiredRouting) { + self.routing_info.required_routing = required_routing; + } + + // Records a new approval. Returns error if the claimed candidate is not found or we already + // have received the approval. + pub fn note_approval( + &mut self, + approval: IndirectSignedApprovalVote, + ) -> Result<(), ApprovalEntryError> { + // First do some sanity checks: + // - check validator index matches + // - check claimed candidate + // - check for duplicate approval + if self.validator_index != approval.validator { + return Err(ApprovalEntryError::InvalidValidatorIndex) + } + + if self.candidates.len() <= approval.candidate_index as usize { + return Err(ApprovalEntryError::CandidateIndexOutOfBounds) + } + + if !self.candidates.bit_at(approval.candidate_index.as_bit_index()) { + return Err(ApprovalEntryError::InvalidCandidateIndex) + } + + if self.approvals.contains_key(&approval.candidate_index) { + return Err(ApprovalEntryError::DuplicateApproval) + } + + self.approvals.insert(approval.candidate_index, approval); + Ok(()) + } + + // Get the assignment certiticate and claimed candidates. + pub fn assignment(&self) -> (IndirectAssignmentCertV2, CandidateBitfield) { + (self.assignment.clone(), self.candidates.clone()) + } + + // Get all approvals for all candidates claimed by the assignment. + pub fn approvals(&self) -> Vec { + self.approvals.values().cloned().collect::>() + } + + // Get the approval for a specific candidate index. + pub fn approval(&self, candidate_index: CandidateIndex) -> Option { + self.approvals.get(&candidate_index).cloned() + } + + // Get validator index. + pub fn validator_index(&self) -> ValidatorIndex { + self.validator_index + } +} + +// We keep track of each peer view and protocol version using this struct. +struct PeerEntry { + pub view: View, + pub version: ProtocolVersion, +} + +// In case the original grid topology mechanisms don't work on their own, we need to trade bandwidth // for protocol liveliness by introducing aggression. // // Aggression has 3 levels: @@ -120,7 +265,6 @@ impl RecentlyOutdated { // not to all blocks older than the threshold. Most likely, a few assignments struggle to // be propagated in a single block and this holds up all of its descendants blocks. // Accordingly, we only step on the gas for the block which is most obviously holding up finality. - /// Aggression configuration representation #[derive(Clone)] struct AggressionConfig { @@ -163,15 +307,6 @@ enum Resend { No, } -/// Data stored on a per-peer basis. -#[derive(Debug)] -struct PeerData { - /// The peer's view. - view: View, - /// The peer's protocol version. - version: ValidationVersion, -} - /// The [`State`] struct is responsible for tracking the overall state of the subsystem. /// /// It tracks metadata about our view of the unfinalized chain, @@ -192,7 +327,7 @@ struct State { pending_known: HashMap>, /// Peer data is partially stored here, and partially inline within the [`BlockEntry`]s - peer_data: HashMap, + peer_views: HashMap, /// Keeps a topology for various different sessions. topologies: SessionGridTopologies, @@ -200,12 +335,12 @@ struct State { /// Tracks recently finalized blocks. recent_outdated_blocks: RecentlyOutdated, - /// Config for aggression. - aggression_config: AggressionConfig, - - /// `HashMap` from active leaves to spans + /// HashMap from active leaves to spans spans: HashMap, + /// Aggression configuration. + aggression_config: AggressionConfig, + /// Current approval checking finality lag. approval_checking_lag: BlockNumber, @@ -219,8 +354,11 @@ enum MessageKind { Approval, } +// Utility structure to identify assignments and approvals for specific candidates. +// Assignments can span multiple candidates, while approvals refer to only one candidate. +// #[derive(Debug, Clone, Hash, PartialEq, Eq)] -struct MessageSubject(Hash, CandidateIndex, ValidatorIndex); +struct MessageSubject(Hash, pub CandidateBitfield, ValidatorIndex); #[derive(Debug, Clone, Default)] struct Knowledge { @@ -241,9 +379,11 @@ impl Knowledge { } fn insert(&mut self, message: MessageSubject, kind: MessageKind) -> bool { - match self.known_messages.entry(message) { + let mut success = match self.known_messages.entry(message.clone()) { hash_map::Entry::Vacant(vacant) => { vacant.insert(kind); + // If there are multiple candidates assigned in the message, create + // separate entries for each one. true }, hash_map::Entry::Occupied(mut occupied) => match (*occupied.get(), kind) { @@ -255,7 +395,25 @@ impl Knowledge { true }, }, + }; + + // In case of succesful insertion of multiple candidate assignments create additional + // entries for each assigned candidate. This fakes knowledge of individual assignments, but + // we need to share the same `MessageSubject` with the followup approval candidate index. + if kind == MessageKind::Assignment && success && message.1.count_ones() > 1 { + for candidate_index in message.1.iter_ones() { + success = success && + self.insert( + MessageSubject( + message.0, + vec![candidate_index as u32].try_into().expect("Non-empty vec; qed"), + message.2, + ), + kind, + ); + } } + success } } @@ -289,47 +447,97 @@ struct BlockEntry { candidates: Vec, /// The session index of this block. session: SessionIndex, + /// Approval entries for whole block. These also contain all approvals in the case of multiple + /// candidates being claimed by assignments. + approval_entries: HashMap<(ValidatorIndex, CandidateBitfield), ApprovalEntry>, } -#[derive(Debug)] -enum ApprovalState { - Assigned(AssignmentCert), - Approved(AssignmentCert, ValidatorSignature), -} +impl BlockEntry { + // Returns the peer which currently know this block. + pub fn known_by(&self) -> Vec { + self.known_by.keys().cloned().collect::>() + } -impl ApprovalState { - fn assignment_cert(&self) -> &AssignmentCert { - match *self { - ApprovalState::Assigned(ref cert) => cert, - ApprovalState::Approved(ref cert, _) => cert, + pub fn insert_approval_entry(&mut self, entry: ApprovalEntry) -> &mut ApprovalEntry { + // First map one entry per candidate to the same key we will use in `approval_entries`. + // Key is (Validator_index, CandidateBitfield) that links the `ApprovalEntry` to the (K,V) + // entry in `candidate_entry.messages`. + for claimed_candidate_index in entry.candidates.iter_ones() { + match self.candidates.get_mut(claimed_candidate_index) { + Some(candidate_entry) => { + candidate_entry + .messages + .entry(entry.validator_index()) + .or_insert(entry.candidates.clone()); + }, + None => { + // This should never happen, but if it happens, it means the subsystem is + // broken. + gum::warn!( + target: LOG_TARGET, + hash = ?entry.assignment.block_hash, + ?claimed_candidate_index, + "Missing candidate entry on `import_and_circulate_assignment`", + ); + }, + }; } + + self.approval_entries + .entry((entry.validator_index, entry.candidates.clone())) + .or_insert(entry) } - fn approval_signature(&self) -> Option { - match *self { - ApprovalState::Assigned(_) => None, - ApprovalState::Approved(_, ref sig) => Some(sig.clone()), - } + // Returns a mutable reference of `ApprovalEntry` for `candidate_index` from validator + // `validator_index`. + pub fn approval_entry( + &mut self, + candidate_index: CandidateIndex, + validator_index: ValidatorIndex, + ) -> Option<&mut ApprovalEntry> { + self.candidates + .get(candidate_index as usize) + .map_or(None, |candidate_entry| candidate_entry.messages.get(&validator_index)) + .map_or(None, |candidate_indices| { + self.approval_entries.get_mut(&(validator_index, candidate_indices.clone())) + }) } -} -// routing state bundled with messages for the candidate. Corresponding assignments -// and approvals are stored together and should be routed in the same way, with -// assignments preceding approvals in all cases. -#[derive(Debug)] -struct MessageState { - required_routing: RequiredRouting, - local: bool, - random_routing: RandomRouting, - approval_state: ApprovalState, + // Get all approval entries for a given candidate. + pub fn approval_entries(&self, candidate_index: CandidateIndex) -> Vec<&ApprovalEntry> { + // Get the keys for fetching `ApprovalEntry` from `self.approval_entries`, + let approval_entry_keys = self + .candidates + .get(candidate_index as usize) + .map(|candidate_entry| &candidate_entry.messages); + + if let Some(approval_entry_keys) = approval_entry_keys { + // Ensure no duplicates. + let approval_entry_keys = approval_entry_keys.iter().unique().collect::>(); + + let mut entries = Vec::new(); + for (validator_index, candidate_indices) in approval_entry_keys { + if let Some(entry) = + self.approval_entries.get(&(*validator_index, candidate_indices.clone())) + { + entries.push(entry); + } + } + entries + } else { + vec![] + } + } } -/// Information about candidates in the context of a particular block they are included in. -/// In other words, multiple `CandidateEntry`s may exist for the same candidate, -/// if it is included by multiple blocks - this is likely the case when there are forks. +// Information about candidates in the context of a particular block they are included in. +// In other words, multiple `CandidateEntry`s may exist for the same candidate, +// if it is included by multiple blocks - this is likely the case when there are forks. #[derive(Debug, Default)] struct CandidateEntry { - messages: HashMap, + // The value represents part of the lookup key in `approval_entries` to fetch the assignment + // and existing votes. + messages: HashMap, } #[derive(Debug, Clone, PartialEq)] @@ -348,7 +556,7 @@ impl MessageSource { } enum PendingMessage { - Assignment(IndirectAssignmentCert, CandidateIndex), + Assignment(IndirectAssignmentCertV2, CandidateBitfield), Approval(IndirectSignedApprovalVote), } @@ -365,27 +573,13 @@ impl State { NetworkBridgeEvent::PeerConnected(peer_id, role, version, _) => { // insert a blank view if none already present gum::trace!(target: LOG_TARGET, ?peer_id, ?role, "Peer connected"); - let version = match ValidationVersion::try_from(version).ok() { - Some(v) => v, - None => { - // sanity: network bridge is supposed to detect this already. - gum::error!( - target: LOG_TARGET, - ?peer_id, - ?version, - "Unsupported protocol version" - ); - return - }, - }; - - self.peer_data + self.peer_views .entry(peer_id) - .or_insert_with(|| PeerData { version, view: Default::default() }); + .or_insert(PeerEntry { view: Default::default(), version }); }, NetworkBridgeEvent::PeerDisconnected(peer_id) => { gum::trace!(target: LOG_TARGET, ?peer_id, "Peer disconnected"); - self.peer_data.remove(&peer_id); + self.peer_views.remove(&peer_id); self.blocks.iter_mut().for_each(|(_hash, entry)| { entry.known_by.remove(&peer_id); }) @@ -422,8 +616,8 @@ impl State { live }); }, - NetworkBridgeEvent::PeerMessage(peer_id, msg) => { - self.process_incoming_peer_message(ctx, metrics, peer_id, msg, rng).await; + NetworkBridgeEvent::PeerMessage(peer_id, message) => { + self.process_incoming_peer_message(ctx, metrics, peer_id, message, rng).await; }, NetworkBridgeEvent::UpdatedAuthorityIds { .. } => { // The approval-distribution subsystem doesn't deal with `AuthorityDiscoveryId`s. @@ -462,6 +656,7 @@ impl State { knowledge: Knowledge::default(), candidates, session: meta.session, + approval_entries: HashMap::new(), }); self.topologies.inc_session_refs(meta.session); @@ -484,18 +679,17 @@ impl State { { let sender = ctx.sender(); - for (peer_id, data) in self.peer_data.iter() { - let intersection = data.view.iter().filter(|h| new_hashes.contains(h)); - let view_intersection = - View::new(intersection.cloned(), data.view.finalized_number); + for (peer_id, PeerEntry { view, version }) in self.peer_views.iter() { + let intersection = view.iter().filter(|h| new_hashes.contains(h)); + let view_intersection = View::new(intersection.cloned(), view.finalized_number); Self::unify_with_peer( sender, metrics, &mut self.blocks, &self.topologies, - self.peer_data.len(), + self.peer_views.len(), *peer_id, - data.version, + *version, view_intersection, rng, ) @@ -533,13 +727,13 @@ impl State { for (peer_id, message) in to_import { match message { - PendingMessage::Assignment(assignment, claimed_index) => { + PendingMessage::Assignment(assignment, claimed_indices) => { self.import_and_circulate_assignment( ctx, metrics, MessageSource::Peer(peer_id), assignment, - claimed_index, + claimed_indices, rng, ) .await; @@ -578,32 +772,100 @@ impl State { adjust_required_routing_and_propagate( ctx, - &self.peer_data, &mut self.blocks, &self.topologies, |block_entry| block_entry.session == session, |required_routing, local, validator_index| { - if *required_routing == RequiredRouting::PendingTopology { - *required_routing = topology + if required_routing == &RequiredRouting::PendingTopology { + topology .local_grid_neighbors() - .required_routing_by_index(*validator_index, local); + .required_routing_by_index(*validator_index, local) + } else { + *required_routing } }, + &self.peer_views, ) .await; } + async fn process_incoming_assignments( + &mut self, + ctx: &mut Context, + metrics: &Metrics, + peer_id: PeerId, + assignments: Vec<(IndirectAssignmentCertV2, CandidateBitfield)>, + rng: &mut R, + ) where + R: CryptoRng + Rng, + { + for (assignment, claimed_indices) in assignments { + if let Some(pending) = self.pending_known.get_mut(&assignment.block_hash) { + let block_hash = &assignment.block_hash; + let validator_index = assignment.validator; + + gum::trace!( + target: LOG_TARGET, + %peer_id, + ?block_hash, + ?claimed_indices, + ?validator_index, + "Pending assignment", + ); + + pending.push((peer_id, PendingMessage::Assignment(assignment, claimed_indices))); + + continue + } + + self.import_and_circulate_assignment( + ctx, + metrics, + MessageSource::Peer(peer_id), + assignment, + claimed_indices, + rng, + ) + .await; + } + } + async fn process_incoming_peer_message( &mut self, ctx: &mut Context, metrics: &Metrics, peer_id: PeerId, - msg: net_protocol::ApprovalDistributionMessage, + msg: Versioned< + protocol_v1::ApprovalDistributionMessage, + protocol_v2::ApprovalDistributionMessage, + protocol_vstaging::ApprovalDistributionMessage, + >, rng: &mut R, ) where R: CryptoRng + Rng, { match msg { + Versioned::VStaging(protocol_vstaging::ApprovalDistributionMessage::Assignments( + assignments, + )) => { + gum::trace!( + target: LOG_TARGET, + peer_id = %peer_id, + num = assignments.len(), + "Processing assignments from a peer", + ); + let sanitized_assignments = + self.sanitize_v2_assignments(peer_id, ctx.sender(), assignments).await; + + self.process_incoming_assignments( + ctx, + metrics, + peer_id, + sanitized_assignments, + rng, + ) + .await; + }, Versioned::V1(protocol_v1::ApprovalDistributionMessage::Assignments(assignments)) | Versioned::V2(protocol_v2::ApprovalDistributionMessage::Assignments(assignments)) => { gum::trace!( @@ -612,38 +874,22 @@ impl State { num = assignments.len(), "Processing assignments from a peer", ); - for (assignment, claimed_index) in assignments.into_iter() { - if let Some(pending) = self.pending_known.get_mut(&assignment.block_hash) { - let message_subject = MessageSubject( - assignment.block_hash, - claimed_index, - assignment.validator, - ); - gum::trace!( - target: LOG_TARGET, - %peer_id, - ?message_subject, - "Pending assignment", - ); - - pending - .push((peer_id, PendingMessage::Assignment(assignment, claimed_index))); - - continue - } + let sanitized_assignments = + self.sanitize_v1_assignments(peer_id, ctx.sender(), assignments).await; - self.import_and_circulate_assignment( - ctx, - metrics, - MessageSource::Peer(peer_id), - assignment, - claimed_index, - rng, - ) - .await; - } + self.process_incoming_assignments( + ctx, + metrics, + peer_id, + sanitized_assignments, + rng, + ) + .await; }, + Versioned::VStaging(protocol_vstaging::ApprovalDistributionMessage::Approvals( + approvals, + )) | Versioned::V1(protocol_v1::ApprovalDistributionMessage::Approvals(approvals)) | Versioned::V2(protocol_v2::ApprovalDistributionMessage::Approvals(approvals)) => { gum::trace!( @@ -654,17 +900,17 @@ impl State { ); for approval_vote in approvals.into_iter() { if let Some(pending) = self.pending_known.get_mut(&approval_vote.block_hash) { - let message_subject = MessageSubject( - approval_vote.block_hash, - approval_vote.candidate_index, - approval_vote.validator, - ); + let block_hash = approval_vote.block_hash; + let candidate_index = approval_vote.candidate_index; + let validator_index = approval_vote.validator; gum::trace!( target: LOG_TARGET, %peer_id, - ?message_subject, - "Pending approval", + ?block_hash, + ?candidate_index, + ?validator_index, + "Pending assignment", ); pending.push((peer_id, PendingMessage::Approval(approval_vote))); @@ -698,14 +944,23 @@ impl State { { gum::trace!(target: LOG_TARGET, ?view, "Peer view change"); let finalized_number = view.finalized_number; - let (peer_protocol_version, old_finalized_number) = match self - .peer_data - .get_mut(&peer_id) - .map(|d| (d.version, std::mem::replace(&mut d.view, view.clone()))) - { - Some((v, view)) => (v, view.finalized_number), - None => return, // unknown peer - }; + + let (old_view, protocol_version) = + if let Some(peer_entry) = self.peer_views.get_mut(&peer_id) { + (Some(std::mem::replace(&mut peer_entry.view, view.clone())), peer_entry.version) + } else { + // This shouldn't happen, but if it does we assume protocol version 1. + gum::warn!( + target: LOG_TARGET, + ?peer_id, + ?view, + "Peer view change for missing `peer_entry`" + ); + + (None, ValidationVersion::V1.into()) + }; + + let old_finalized_number = old_view.map(|v| v.finalized_number).unwrap_or(0); // we want to prune every block known_by peer up to (including) view.finalized_number let blocks = &mut self.blocks; @@ -730,9 +985,9 @@ impl State { metrics, &mut self.blocks, &self.topologies, - self.peer_data.len(), + self.peer_views.len(), peer_id, - peer_protocol_version, + protocol_version, view, rng, ) @@ -773,8 +1028,8 @@ impl State { ctx: &mut Context, metrics: &Metrics, source: MessageSource, - assignment: IndirectAssignmentCert, - claimed_candidate_index: CandidateIndex, + assignment: IndirectAssignmentCertV2, + claimed_candidate_indices: CandidateBitfield, rng: &mut R, ) where R: CryptoRng + Rng, @@ -822,9 +1077,11 @@ impl State { }, }; - // compute metadata on the assignment. - let message_subject = MessageSubject(block_hash, claimed_candidate_index, validator_index); - let message_kind = MessageKind::Assignment; + // Compute metadata on the assignment. + let (message_subject, message_kind) = ( + MessageSubject(block_hash, claimed_candidate_indices.clone(), validator_index), + MessageKind::Assignment, + ); if let Some(peer_id) = source.peer_id() { // check if our knowledge of the peer already contains this assignment @@ -840,6 +1097,7 @@ impl State { ?message_subject, "Duplicate assignment", ); + modify_reputation( &mut self.reputation, ctx.sender(), @@ -847,6 +1105,15 @@ impl State { COST_DUPLICATE_MESSAGE, ) .await; + } else { + gum::trace!( + target: LOG_TARGET, + ?peer_id, + hash = ?block_hash, + ?validator_index, + ?message_subject, + "We sent the message to the peer while peer was sending it to us. Known race condition.", + ); } return } @@ -888,7 +1155,7 @@ impl State { ctx.send_message(ApprovalVotingMessage::CheckAndImportAssignment( assignment.clone(), - claimed_candidate_index, + claimed_candidate_indices.clone(), tx, )) .await; @@ -919,7 +1186,7 @@ impl State { BENEFIT_VALID_MESSAGE_FIRST, ) .await; - entry.knowledge.known_messages.insert(message_subject.clone(), message_kind); + entry.knowledge.insert(message_subject.clone(), message_kind); if let Some(peer_knowledge) = entry.known_by.get_mut(&peer_id) { peer_knowledge.received.insert(message_subject.clone(), message_kind); } @@ -993,7 +1260,7 @@ impl State { // Invariant: to our knowledge, none of the peers except for the `source` know about the // assignment. - metrics.on_assignment_imported(); + metrics.on_assignment_imported(&assignment.cert.kind); let topology = self.topologies.get_topology(entry.session); let local = source == MessageSource::Local; @@ -1002,28 +1269,14 @@ impl State { t.local_grid_neighbors().required_routing_by_index(validator_index, local) }); - let message_state = match entry.candidates.get_mut(claimed_candidate_index as usize) { - Some(candidate_entry) => { - // set the approval state for validator_index to Assigned - // unless the approval state is set already - candidate_entry.messages.entry(validator_index).or_insert_with(|| MessageState { - required_routing, - local, - random_routing: Default::default(), - approval_state: ApprovalState::Assigned(assignment.cert.clone()), - }) - }, - None => { - gum::warn!( - target: LOG_TARGET, - hash = ?block_hash, - ?claimed_candidate_index, - "Expected a candidate entry on import_and_circulate_assignment", - ); + // All the peers that know the relay chain block. + let peers_to_filter = entry.known_by(); - return - }, - }; + let approval_entry = entry.insert_approval_entry(ApprovalEntry::new( + assignment.clone(), + claimed_candidate_indices.clone(), + ApprovalRouting { required_routing, local, random_routing: Default::default() }, + )); // Dispatch the message to all peers in the routing set which // know the block. @@ -1031,83 +1284,65 @@ impl State { // If the topology isn't known yet (race with networking subsystems) // then messages will be sent when we get it. - let assignments = vec![(assignment, claimed_candidate_index)]; - let n_peers_total = self.peer_data.len(); + let assignments = vec![(assignment, claimed_candidate_indices.clone())]; + let n_peers_total = self.peer_views.len(); let source_peer = source.peer_id(); - let mut peer_filter = move |peer| { - if Some(peer) == source_peer.as_ref() { - return false + // Peers that we will send the assignment to. + let mut peers = Vec::new(); + + // Filter destination peers + for peer in peers_to_filter.into_iter() { + if Some(peer) == source_peer { + continue } if let Some(true) = topology .as_ref() - .map(|t| t.local_grid_neighbors().route_to_peer(required_routing, peer)) + .map(|t| t.local_grid_neighbors().route_to_peer(required_routing, &peer)) { - return true + peers.push(peer); + continue } // Note: at this point, we haven't received the message from any peers // other than the source peer, and we just got it, so we haven't sent it // to any peers either. - let route_random = message_state.random_routing.sample(n_peers_total, rng); + let route_random = + approval_entry.routing_info().random_routing.sample(n_peers_total, rng); if route_random { - message_state.random_routing.inc_sent(); - } - - route_random - }; - - let (v1_peers, v2_peers) = { - let peer_data = &self.peer_data; - let peers = entry - .known_by - .keys() - .filter_map(|p| peer_data.get_key_value(p)) - .filter(|(p, _)| peer_filter(p)) - .map(|(p, peer_data)| (*p, peer_data.version)) - .collect::>(); - - // Add the metadata of the assignment to the knowledge of each peer. - for (peer, _) in peers.iter() { - // we already filtered peers above, so this should always be Some - if let Some(peer_knowledge) = entry.known_by.get_mut(peer) { - peer_knowledge.sent.insert(message_subject.clone(), message_kind); - } + approval_entry.routing_info_mut().random_routing.inc_sent(); + peers.push(peer); } + } - if !peers.is_empty() { - gum::trace!( - target: LOG_TARGET, - ?block_hash, - ?claimed_candidate_index, - local = source.peer_id().is_none(), - num_peers = peers.len(), - "Sending an assignment to peers", - ); + // Add the metadata of the assignment to the knowledge of each peer. + for peer in peers.iter() { + // we already filtered peers above, so this should always be Some + if let Some(peer_knowledge) = entry.known_by.get_mut(peer) { + peer_knowledge.sent.insert(message_subject.clone(), message_kind); } + } - let v1_peers = filter_peers_by_version(&peers, ValidationVersion::V1); - let v2_peers = filter_peers_by_version(&peers, ValidationVersion::V2); - - (v1_peers, v2_peers) - }; + if !peers.is_empty() { + gum::trace!( + target: LOG_TARGET, + ?block_hash, + ?claimed_candidate_indices, + local = source.peer_id().is_none(), + num_peers = peers.len(), + "Sending an assignment to peers", + ); - if !v1_peers.is_empty() { - ctx.send_message(NetworkBridgeTxMessage::SendValidationMessage( - v1_peers, - versioned_assignments_packet(ValidationVersion::V1, assignments.clone()), - )) - .await; - } + let peers = peers + .iter() + .filter_map(|peer_id| { + self.peer_views.get(peer_id).map(|peer_entry| (*peer_id, peer_entry.version)) + }) + .collect::>(); - if !v2_peers.is_empty() { - ctx.send_message(NetworkBridgeTxMessage::SendValidationMessage( - v2_peers, - versioned_assignments_packet(ValidationVersion::V2, assignments.clone()), - )) - .await; + send_assignments_batched(ctx.sender(), assignments, &peers).await; } } @@ -1142,6 +1377,14 @@ impl State { _ => { if let Some(peer_id) = source.peer_id() { if !self.recent_outdated_blocks.is_recent_outdated(&block_hash) { + gum::debug!( + target: LOG_TARGET, + ?peer_id, + ?block_hash, + ?validator_index, + ?candidate_index, + "Approval from a peer is out of view", + ); modify_reputation( &mut self.reputation, ctx.sender(), @@ -1156,7 +1399,7 @@ impl State { }; // compute metadata on the assignment. - let message_subject = MessageSubject(block_hash, candidate_index, validator_index); + let message_subject = MessageSubject(block_hash, candidate_index.into(), validator_index); let message_kind = MessageKind::Approval; if let Some(peer_id) = source.peer_id() { @@ -1306,71 +1549,48 @@ impl State { } } - // Invariant: to our knowledge, none of the peers except for the `source` know about the - // approval. - metrics.on_approval_imported(); - - let required_routing = match entry.candidates.get_mut(candidate_index as usize) { - Some(candidate_entry) => { - // set the approval state for validator_index to Approved - // it should be in assigned state already - match candidate_entry.messages.remove(&validator_index) { - Some(MessageState { - approval_state: ApprovalState::Assigned(cert), - required_routing, - local, - random_routing, - }) => { - candidate_entry.messages.insert( - validator_index, - MessageState { - approval_state: ApprovalState::Approved( - cert, - vote.signature.clone(), - ), - required_routing, - local, - random_routing, - }, - ); - - required_routing - }, - Some(_) => { - unreachable!( - "we only insert it after the metadata, checked the metadata above; qed" - ); - }, - None => { - // this would indicate a bug in approval-voting - gum::warn!( - target: LOG_TARGET, - hash = ?block_hash, - ?candidate_index, - ?validator_index, - "Importing an approval we don't have an assignment for", - ); + let required_routing = match entry.approval_entry(candidate_index, validator_index) { + Some(approval_entry) => { + // Invariant: to our knowledge, none of the peers except for the `source` know about + // the approval. + metrics.on_approval_imported(); + + if let Err(err) = approval_entry.note_approval(vote.clone()) { + // this would indicate a bug in approval-voting: + // - validator index mismatch + // - candidate index mismatch + // - duplicate approval + gum::warn!( + target: LOG_TARGET, + hash = ?block_hash, + ?candidate_index, + ?validator_index, + ?err, + "Possible bug: Vote import failed", + ); - return - }, + return } + + approval_entry.routing_info().required_routing }, None => { + let peer_id = source.peer_id(); + // This indicates a bug in approval-distribution, since we check the knowledge at + // the begining of the function. gum::warn!( target: LOG_TARGET, - hash = ?block_hash, - ?candidate_index, - ?validator_index, - "Expected a candidate entry on import_and_circulate_approval", + ?peer_id, + ?message_subject, + "Unknown approval assignment", ); - + // No rep change as this is caused by an issue return }, }; // Dispatch a ApprovalDistributionV1Message::Approval(vote) // to all peers required by the topology, with the exception of the source peer. - let topology = self.topologies.get_topology(entry.session); let source_peer = source.peer_id(); @@ -1394,57 +1614,32 @@ impl State { in_topology || knowledge.sent.contains(message_subject, MessageKind::Assignment) }; - let (v1_peers, v2_peers) = { - let peer_data = &self.peer_data; - let peers = entry - .known_by - .iter() - .filter_map(|(p, k)| peer_data.get(&p).map(|pd| (p, k, pd.version))) - .filter(|(p, k, _)| peer_filter(p, k)) - .map(|(p, _, v)| (*p, v)) - .collect::>(); - - // Add the metadata of the assignment to the knowledge of each peer. - for (peer, _) in peers.iter() { - // we already filtered peers above, so this should always be Some - if let Some(peer_knowledge) = entry.known_by.get_mut(peer) { - peer_knowledge.sent.insert(message_subject.clone(), message_kind); - } - } - - if !peers.is_empty() { - gum::trace!( - target: LOG_TARGET, - ?block_hash, - ?candidate_index, - local = source.peer_id().is_none(), - num_peers = peers.len(), - "Sending an approval to peers", - ); + let peers = entry + .known_by + .iter() + .filter(|(p, k)| peer_filter(p, k)) + .filter_map(|(p, _)| self.peer_views.get(p).map(|entry| (*p, entry.version))) + .collect::>(); + + // Add the metadata of the assignment to the knowledge of each peer. + for peer in peers.iter() { + // we already filtered peers above, so this should always be Some + if let Some(entry) = entry.known_by.get_mut(&peer.0) { + entry.sent.insert(message_subject.clone(), message_kind); } - - let v1_peers = filter_peers_by_version(&peers, ValidationVersion::V1); - let v2_peers = filter_peers_by_version(&peers, ValidationVersion::V2); - - (v1_peers, v2_peers) - }; - - let approvals = vec![vote]; - - if !v1_peers.is_empty() { - ctx.send_message(NetworkBridgeTxMessage::SendValidationMessage( - v1_peers, - versioned_approvals_packet(ValidationVersion::V1, approvals.clone()), - )) - .await; } - if !v2_peers.is_empty() { - ctx.send_message(NetworkBridgeTxMessage::SendValidationMessage( - v2_peers, - versioned_approvals_packet(ValidationVersion::V2, approvals), - )) - .await; + if !peers.is_empty() { + let approvals = vec![vote]; + gum::trace!( + target: LOG_TARGET, + ?block_hash, + ?candidate_index, + local = source.peer_id().is_none(), + num_peers = peers.len(), + "Sending an approval to peers", + ); + send_approvals_batched(ctx.sender(), approvals, &peers).await; } } @@ -1475,25 +1670,11 @@ impl State { Some(e) => e, }; - let candidate_entry = match block_entry.candidates.get(index as usize) { - None => { - gum::debug!( - target: LOG_TARGET, - ?hash, - ?index, - "`get_approval_signatures`: could not find candidate entry for given hash and index!" - ); - continue - }, - Some(e) => e, - }; - let sigs = - candidate_entry.messages.iter().filter_map(|(validator_index, message_state)| { - match &message_state.approval_state { - ApprovalState::Approved(_, sig) => Some((*validator_index, sig.clone())), - ApprovalState::Assigned(_) => None, - } - }); + let sigs = block_entry + .approval_entries(index) + .into_iter() + .filter_map(|approval_entry| approval_entry.approval(index)) + .map(|approval| (approval.validator, approval.signature)); all_sigs.extend(sigs); } all_sigs @@ -1506,7 +1687,7 @@ impl State { topologies: &SessionGridTopologies, total_peers: usize, peer_id: PeerId, - peer_protocol_version: ValidationVersion, + protocol_version: ProtocolVersion, view: View, rng: &mut (impl CryptoRng + Rng), ) { @@ -1519,6 +1700,8 @@ impl State { let view_finalized_number = view.finalized_number; for head in view.into_iter() { let mut block = head; + + // Walk the chain back to last finalized block of the peer view. loop { let entry = match entries.get_mut(&block) { Some(entry) if entry.number > view_finalized_number => entry, @@ -1533,19 +1716,16 @@ impl State { } let peer_knowledge = entry.known_by.entry(peer_id).or_default(); - let topology = topologies.get_topology(entry.session); - // Iterate all messages in all candidates. - for (candidate_index, validator, message_state) in - entry.candidates.iter_mut().enumerate().flat_map(|(c_i, c)| { - c.messages.iter_mut().map(move |(k, v)| (c_i as _, k, v)) - }) { + // We want to iterate the `approval_entries` of the block entry as these contain all + // assignments that also link all approval votes. + for approval_entry in entry.approval_entries.values_mut() { // Propagate the message to all peers in the required routing set OR // randomly sample peers. { - let random_routing = &mut message_state.random_routing; - let required_routing = message_state.required_routing; + let required_routing = approval_entry.routing_info().required_routing; + let random_routing = &mut approval_entry.routing_info_mut().random_routing; let rng = &mut *rng; let mut peer_filter = move |peer_id| { let in_topology = topology.as_ref().map_or(false, |t| { @@ -1566,39 +1746,24 @@ impl State { } } - let message_subject = MessageSubject(block, candidate_index, *validator); - - let assignment_message = ( - IndirectAssignmentCert { - block_hash: block, - validator: *validator, - cert: message_state.approval_state.assignment_cert().clone(), - }, - candidate_index, - ); + let assignment_message = approval_entry.assignment(); + let approval_messages = approval_entry.approvals(); + let (assignment_knowledge, message_kind) = + approval_entry.create_assignment_knowledge(block); - let approval_message = - message_state.approval_state.approval_signature().map(|signature| { - IndirectSignedApprovalVote { - block_hash: block, - validator: *validator, - candidate_index, - signature, - } - }); - - if !peer_knowledge.contains(&message_subject, MessageKind::Assignment) { - peer_knowledge - .sent - .insert(message_subject.clone(), MessageKind::Assignment); + // Only send stuff a peer doesn't know in the context of a relay chain block. + if !peer_knowledge.contains(&assignment_knowledge, message_kind) { + peer_knowledge.sent.insert(assignment_knowledge, message_kind); assignments_to_send.push(assignment_message); } - if let Some(approval_message) = approval_message { - if !peer_knowledge.contains(&message_subject, MessageKind::Approval) { - peer_knowledge - .sent - .insert(message_subject.clone(), MessageKind::Approval); + // Filter approval votes. + for approval_message in approval_messages { + let (approval_knowledge, message_kind) = approval_entry + .create_approval_knowledge(block, approval_message.candidate_index); + + if !peer_knowledge.contains(&approval_knowledge, message_kind) { + peer_knowledge.sent.insert(approval_knowledge, message_kind); approvals_to_send.push(approval_message); } } @@ -1612,23 +1777,30 @@ impl State { gum::trace!( target: LOG_TARGET, ?peer_id, + ?protocol_version, num = assignments_to_send.len(), "Sending assignments to unified peer", ); - send_assignments_batched(sender, assignments_to_send, peer_id, peer_protocol_version) - .await; + send_assignments_batched( + sender, + assignments_to_send, + &vec![(peer_id, protocol_version)], + ) + .await; } if !approvals_to_send.is_empty() { gum::trace!( target: LOG_TARGET, ?peer_id, + ?protocol_version, num = approvals_to_send.len(), "Sending approvals to unified peer", ); - send_approvals_batched(sender, approvals_to_send, peer_id, peer_protocol_version).await; + send_approvals_batched(sender, approvals_to_send, &vec![(peer_id, protocol_version)]) + .await; } } @@ -1664,7 +1836,6 @@ impl State { adjust_required_routing_and_propagate( ctx, - &self.peer_data, &mut self.blocks, &self.topologies, |block_entry| { @@ -1686,13 +1857,13 @@ impl State { false } }, - |_, _, _| {}, + |required_routing, _, _| *required_routing, + &self.peer_views, ) .await; adjust_required_routing_and_propagate( ctx, - &self.peer_data, &mut self.blocks, &self.topologies, |block_entry| { @@ -1711,30 +1882,110 @@ impl State { lag = ?self.approval_checking_lag, "Encountered old block pending gossip topology", ); - return + return *required_routing } + let mut new_required_routing = *required_routing; + if config.l1_threshold.as_ref().map_or(false, |t| &self.approval_checking_lag >= t) { // Message originator sends to everyone. - if local && *required_routing != RequiredRouting::All { + if local && new_required_routing != RequiredRouting::All { metrics.on_aggression_l1(); - *required_routing = RequiredRouting::All; + new_required_routing = RequiredRouting::All; } } if config.l2_threshold.as_ref().map_or(false, |t| &self.approval_checking_lag >= t) { // Message originator sends to everyone. Everyone else sends to XY. - if !local && *required_routing != RequiredRouting::GridXY { + if !local && new_required_routing != RequiredRouting::GridXY { metrics.on_aggression_l2(); - *required_routing = RequiredRouting::GridXY; + new_required_routing = RequiredRouting::GridXY; } } + new_required_routing }, + &self.peer_views, ) .await; } + + // Filter out invalid candidate index and certificate core bitfields. + // For each invalid assignment we also punish the peer. + async fn sanitize_v1_assignments( + &mut self, + peer_id: PeerId, + sender: &mut impl overseer::ApprovalDistributionSenderTrait, + assignments: Vec<(IndirectAssignmentCert, CandidateIndex)>, + ) -> Vec<(IndirectAssignmentCertV2, CandidateBitfield)> { + let mut sanitized_assignments = Vec::new(); + for (cert, candidate_index) in assignments.into_iter() { + let cert_bitfield_bits = match cert.cert.kind { + AssignmentCertKind::RelayVRFDelay { core_index } => core_index.0 as usize + 1, + // We don't want to run the VRF yet, but the output is always bounded by `n_cores`. + // We assume `candidate_bitfield` length for the core bitfield and we just check + // against `MAX_BITFIELD_SIZE` later. + AssignmentCertKind::RelayVRFModulo { .. } => candidate_index as usize + 1, + }; + + let candidate_bitfield_bits = candidate_index as usize + 1; + + // Ensure bitfields length under hard limit. + if cert_bitfield_bits > MAX_BITFIELD_SIZE || candidate_bitfield_bits > MAX_BITFIELD_SIZE + { + // Punish the peer for the invalid message. + modify_reputation(&mut self.reputation, sender, peer_id, COST_OVERSIZED_BITFIELD) + .await; + } else { + sanitized_assignments.push((cert.into(), candidate_index.into())) + } + } + + sanitized_assignments + } + + // Filter out oversized candidate and certificate core bitfields. + // For each invalid assignment we also punish the peer. + async fn sanitize_v2_assignments( + &mut self, + peer_id: PeerId, + sender: &mut impl overseer::ApprovalDistributionSenderTrait, + assignments: Vec<(IndirectAssignmentCertV2, CandidateBitfield)>, + ) -> Vec<(IndirectAssignmentCertV2, CandidateBitfield)> { + let mut sanitized_assignments = Vec::new(); + for (cert, candidate_bitfield) in assignments.into_iter() { + let cert_bitfield_bits = match &cert.cert.kind { + AssignmentCertKindV2::RelayVRFDelay { core_index } => core_index.0 as usize + 1, + // We don't want to run the VRF yet, but the output is always bounded by `n_cores`. + // We assume `candidate_bitfield` length for the core bitfield and we just check + // against `MAX_BITFIELD_SIZE` later. + AssignmentCertKindV2::RelayVRFModulo { .. } => candidate_bitfield.len(), + AssignmentCertKindV2::RelayVRFModuloCompact { core_bitfield } => + core_bitfield.len(), + }; + + let candidate_bitfield_bits = candidate_bitfield.len(); + + // Our bitfield has `Lsb0`. + let msb = candidate_bitfield_bits - 1; + + // Ensure bitfields length under hard limit. + if cert_bitfield_bits > MAX_BITFIELD_SIZE + || candidate_bitfield_bits > MAX_BITFIELD_SIZE + // Ensure minimum bitfield size - MSB needs to be one. + || !candidate_bitfield.bit_at(msb.as_bit_index()) + { + // Punish the peer for the invalid message. + modify_reputation(&mut self.reputation, sender, peer_id, COST_OVERSIZED_BITFIELD) + .await; + } else { + sanitized_assignments.push((cert, candidate_bitfield)) + } + } + + sanitized_assignments + } } // This adjusts the required routing of messages in blocks that pass the block filter @@ -1752,14 +2003,14 @@ impl State { #[overseer::contextbounds(ApprovalDistribution, prefix = self::overseer)] async fn adjust_required_routing_and_propagate( ctx: &mut Context, - peer_data: &HashMap, blocks: &mut HashMap, topologies: &SessionGridTopologies, block_filter: BlockFilter, routing_modifier: RoutingModifier, + peer_views: &HashMap, ) where BlockFilter: Fn(&mut BlockEntry) -> bool, - RoutingModifier: Fn(&mut RequiredRouting, bool, &ValidatorIndex), + RoutingModifier: Fn(&RequiredRouting, bool, &ValidatorIndex) -> RequiredRouting, { let mut peer_assignments = HashMap::new(); let mut peer_approvals = HashMap::new(); @@ -1771,64 +2022,55 @@ async fn adjust_required_routing_and_propagate t, + None => continue, + }; - if message_state.required_routing.is_empty() { - continue - } + // We just need to iterate the `approval_entries` of the block entry as these contain all + // assignments that also link all approval votes. + for approval_entry in block_entry.approval_entries.values_mut() { + let new_required_routing = routing_modifier( + &approval_entry.routing_info().required_routing, + approval_entry.routing_info().local, + &approval_entry.validator_index(), + ); - let topology = match topologies.get_topology(block_entry.session) { - Some(t) => t, - None => continue, - }; + approval_entry.update_required_routing(new_required_routing); - // Propagate the message to all peers in the required routing set. - let message_subject = MessageSubject(*block_hash, candidate_index, *validator); + if approval_entry.routing_info().required_routing.is_empty() { + continue + } - let assignment_message = ( - IndirectAssignmentCert { - block_hash: *block_hash, - validator: *validator, - cert: message_state.approval_state.assignment_cert().clone(), - }, - candidate_index, - ); - let approval_message = - message_state.approval_state.approval_signature().map(|signature| { - IndirectSignedApprovalVote { - block_hash: *block_hash, - validator: *validator, - candidate_index, - signature, - } - }); + let assignment_message = approval_entry.assignment(); + let approval_messages = approval_entry.approvals(); + let (assignment_knowledge, message_kind) = + approval_entry.create_assignment_knowledge(*block_hash); for (peer, peer_knowledge) in &mut block_entry.known_by { if !topology .local_grid_neighbors() - .route_to_peer(message_state.required_routing, peer) + .route_to_peer(approval_entry.routing_info().required_routing, peer) { continue } - if !peer_knowledge.contains(&message_subject, MessageKind::Assignment) { - peer_knowledge.sent.insert(message_subject.clone(), MessageKind::Assignment); + // Only send stuff a peer doesn't know in the context of a relay chain block. + if !peer_knowledge.contains(&assignment_knowledge, message_kind) { + peer_knowledge.sent.insert(assignment_knowledge.clone(), message_kind); peer_assignments .entry(*peer) .or_insert_with(Vec::new) .push(assignment_message.clone()); } - if let Some(approval_message) = approval_message.as_ref() { - if !peer_knowledge.contains(&message_subject, MessageKind::Approval) { - peer_knowledge.sent.insert(message_subject.clone(), MessageKind::Approval); + // Filter approval votes. + for approval_message in &approval_messages { + let (approval_knowledge, message_kind) = approval_entry + .create_approval_knowledge(*block_hash, approval_message.candidate_index); + + if !peer_knowledge.contains(&approval_knowledge, message_kind) { + peer_knowledge.sent.insert(approval_knowledge, message_kind); peer_approvals .entry(*peer) .or_insert_with(Vec::new) @@ -1840,24 +2082,32 @@ async fn adjust_required_routing_and_propagate continue, - Some(v) => v, - }; - - send_assignments_batched(ctx.sender(), assignments_packet, peer, peer_protocol_version) + if let Some(peer_view) = peer_views.get(&peer) { + send_assignments_batched( + ctx.sender(), + assignments_packet, + &vec![(peer, peer_view.version)], + ) .await; + } else { + // This should never happen. + gum::warn!(target: LOG_TARGET, ?peer, "Unknown protocol version for peer",); + } } for (peer, approvals_packet) in peer_approvals { - let peer_protocol_version = match peer_data.get(&peer).map(|pd| pd.version) { - None => continue, - Some(v) => v, - }; - - send_approvals_batched(ctx.sender(), approvals_packet, peer, peer_protocol_version).await; + if let Some(peer_view) = peer_views.get(&peer) { + send_approvals_batched( + ctx.sender(), + approvals_packet, + &vec![(peer, peer_view.version)], + ) + .await; + } else { + // This should never happen. + gum::warn!(target: LOG_TARGET, ?peer, "Unknown protocol version for peer",); + } } } @@ -1959,12 +2209,21 @@ impl ApprovalDistribution { ApprovalDistributionMessage::NewBlocks(metas) => { state.handle_new_blocks(ctx, metrics, metas, rng).await; }, - ApprovalDistributionMessage::DistributeAssignment(cert, candidate_index) => { + ApprovalDistributionMessage::DistributeAssignment(cert, candidate_indices) => { + let _span = state + .spans + .get(&cert.block_hash) + .map(|span| span.child("import-and-distribute-assignment")) + .unwrap_or_else(|| jaeger::Span::new(&cert.block_hash, "distribute-assignment")) + .with_string_tag("block-hash", format!("{:?}", cert.block_hash)) + .with_stage(jaeger::Stage::ApprovalDistribution); + gum::debug!( target: LOG_TARGET, - "Distributing our assignment on candidate (block={}, index={})", - cert.block_hash, - candidate_index, + ?candidate_indices, + block_hash = ?cert.block_hash, + assignment_kind = ?cert.cert.kind, + "Distributing our assignment on candidates", ); state @@ -1973,7 +2232,7 @@ impl ApprovalDistribution { &metrics, MessageSource::Local, cert, - candidate_index, + candidate_indices, rng, ) .await; @@ -2007,49 +2266,6 @@ impl ApprovalDistribution { } } -fn versioned_approvals_packet( - version: ValidationVersion, - approvals: Vec, -) -> VersionedValidationProtocol { - match version { - ValidationVersion::V1 => - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Approvals(approvals), - )), - ValidationVersion::V2 => - Versioned::V2(protocol_v2::ValidationProtocol::ApprovalDistribution( - protocol_v2::ApprovalDistributionMessage::Approvals(approvals), - )), - } -} - -fn versioned_assignments_packet( - version: ValidationVersion, - assignments: Vec<(IndirectAssignmentCert, CandidateIndex)>, -) -> VersionedValidationProtocol { - match version { - ValidationVersion::V1 => - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(assignments), - )), - ValidationVersion::V2 => - Versioned::V2(protocol_v2::ValidationProtocol::ApprovalDistribution( - protocol_v2::ApprovalDistributionMessage::Assignments(assignments), - )), - } -} - -fn filter_peers_by_version( - peers: &[(PeerId, ValidationVersion)], - version: ValidationVersion, -) -> Vec { - peers - .iter() - .filter(|(_, v)| v == &version) - .map(|(peer_id, _)| *peer_id) - .collect() -} - #[overseer::subsystem(ApprovalDistribution, error=SubsystemError, prefix=self::overseer)] impl ApprovalDistribution { fn start(self, ctx: Context) -> SpawnedSubsystem { @@ -2074,7 +2290,7 @@ const fn ensure_size_not_zero(size: usize) -> usize { /// the protocol configuration. pub const MAX_ASSIGNMENT_BATCH_SIZE: usize = ensure_size_not_zero( MAX_NOTIFICATION_SIZE as usize / - std::mem::size_of::<(IndirectAssignmentCert, CandidateIndex)>() / + std::mem::size_of::<(IndirectAssignmentCertV2, CandidateIndex)>() / 3, ); @@ -2083,6 +2299,57 @@ pub const MAX_APPROVAL_BATCH_SIZE: usize = ensure_size_not_zero( MAX_NOTIFICATION_SIZE as usize / std::mem::size_of::() / 3, ); +// Low level helper for sending assignments. +async fn send_assignments_batched_inner( + sender: &mut impl overseer::ApprovalDistributionSenderTrait, + batch: impl IntoIterator, + peers: Vec, + peer_version: ValidationVersion, +) { + if peer_version == ValidationVersion::VStaging { + sender + .send_message(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::VStaging(protocol_vstaging::ValidationProtocol::ApprovalDistribution( + protocol_vstaging::ApprovalDistributionMessage::Assignments( + batch.into_iter().collect(), + ), + )), + )) + .await; + } else { + // Create a batch of v1 assignments from v2 assignments that are compatible with v1. + // `IndirectAssignmentCertV2` -> `IndirectAssignmentCert` + let batch = batch + .into_iter() + .filter_map(|(cert, candidates)| { + cert.try_into().ok().map(|cert| { + ( + cert, + // First 1 bit index is the candidate index. + candidates + .first_one() + .map(|index| index as CandidateIndex) + .expect("Assignment was checked for not being empty; qed"), + ) + }) + }) + .collect(); + let message = if peer_version == ValidationVersion::V1 { + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(batch), + )) + } else { + Versioned::V2(protocol_v2::ValidationProtocol::ApprovalDistribution( + protocol_v2::ApprovalDistributionMessage::Assignments(batch), + )) + }; + sender + .send_message(NetworkBridgeTxMessage::SendValidationMessage(peers, message)) + .await; + } +} + /// Send assignments while honoring the `max_notification_size` of the protocol. /// /// Splitting the messages into multiple notifications allows more granular processing at the @@ -2090,37 +2357,121 @@ pub const MAX_APPROVAL_BATCH_SIZE: usize = ensure_size_not_zero( /// of assignments and can `select!` other tasks. pub(crate) async fn send_assignments_batched( sender: &mut impl overseer::ApprovalDistributionSenderTrait, - assignments: Vec<(IndirectAssignmentCert, CandidateIndex)>, - peer: PeerId, - protocol_version: ValidationVersion, + v2_assignments: impl IntoIterator + Clone, + peers: &[(PeerId, ProtocolVersion)], ) { - let mut batches = assignments.into_iter().peekable(); + let v1_peers = filter_by_peer_version(peers, ValidationVersion::V1.into()); + let v2_peers = filter_by_peer_version(peers, ValidationVersion::V2.into()); + let vstaging_peers = filter_by_peer_version(peers, ValidationVersion::VStaging.into()); + + // V1 and V2 validation protocol do not have any changes with regard to + // ApprovalDistributionMessage so they can be treated the same. + if !v1_peers.is_empty() || !v2_peers.is_empty() { + // Older peers(v1) do not understand `AssignmentsV2` messages, so we have to filter these + // out. + let v1_assignments = v2_assignments + .clone() + .into_iter() + .filter(|(_, candidates)| candidates.count_ones() == 1); + + let mut v1_batches = v1_assignments.peekable(); + + while v1_batches.peek().is_some() { + let batch: Vec<_> = v1_batches.by_ref().take(MAX_ASSIGNMENT_BATCH_SIZE).collect(); + if !v1_peers.is_empty() { + send_assignments_batched_inner( + sender, + batch.clone(), + v1_peers.clone(), + ValidationVersion::V1, + ) + .await; + } - while batches.peek().is_some() { - let batch: Vec<_> = batches.by_ref().take(MAX_ASSIGNMENT_BATCH_SIZE).collect(); - let versioned = versioned_assignments_packet(protocol_version, batch); + if !v2_peers.is_empty() { + send_assignments_batched_inner( + sender, + batch, + v2_peers.clone(), + ValidationVersion::V2, + ) + .await; + } + } + } - sender - .send_message(NetworkBridgeTxMessage::SendValidationMessage(vec![peer], versioned)) + if !vstaging_peers.is_empty() { + let mut vstaging = v2_assignments.into_iter().peekable(); + + while vstaging.peek().is_some() { + let batch = vstaging.by_ref().take(MAX_ASSIGNMENT_BATCH_SIZE).collect::>(); + send_assignments_batched_inner( + sender, + batch, + vstaging_peers.clone(), + ValidationVersion::VStaging, + ) .await; + } } } -/// Send approvals while honoring the `max_notification_size` of the protocol. +/// Send approvals while honoring the `max_notification_size` of the protocol and peer version. pub(crate) async fn send_approvals_batched( sender: &mut impl overseer::ApprovalDistributionSenderTrait, - approvals: Vec, - peer: PeerId, - protocol_version: ValidationVersion, + approvals: impl IntoIterator + Clone, + peers: &[(PeerId, ProtocolVersion)], ) { - let mut batches = approvals.into_iter().peekable(); + let v1_peers = filter_by_peer_version(peers, ValidationVersion::V1.into()); + let v2_peers = filter_by_peer_version(peers, ValidationVersion::V2.into()); + let vstaging_peers = filter_by_peer_version(peers, ValidationVersion::VStaging.into()); + + if !v1_peers.is_empty() || !v2_peers.is_empty() { + let mut batches = approvals.clone().into_iter().peekable(); + + while batches.peek().is_some() { + let batch: Vec<_> = batches.by_ref().take(MAX_APPROVAL_BATCH_SIZE).collect(); + + if !v1_peers.is_empty() { + sender + .send_message(NetworkBridgeTxMessage::SendValidationMessage( + v1_peers.clone(), + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Approvals(batch.clone()), + )), + )) + .await; + } - while batches.peek().is_some() { - let batch: Vec<_> = batches.by_ref().take(MAX_APPROVAL_BATCH_SIZE).collect(); - let versioned = versioned_approvals_packet(protocol_version, batch); + if !v2_peers.is_empty() { + sender + .send_message(NetworkBridgeTxMessage::SendValidationMessage( + v2_peers.clone(), + Versioned::V2(protocol_v2::ValidationProtocol::ApprovalDistribution( + protocol_v2::ApprovalDistributionMessage::Approvals(batch), + )), + )) + .await; + } + } + } - sender - .send_message(NetworkBridgeTxMessage::SendValidationMessage(vec![peer], versioned)) - .await; + if !vstaging_peers.is_empty() { + let mut batches = approvals.into_iter().peekable(); + + while batches.peek().is_some() { + let batch: Vec<_> = batches.by_ref().take(MAX_APPROVAL_BATCH_SIZE).collect(); + + sender + .send_message(NetworkBridgeTxMessage::SendValidationMessage( + vstaging_peers.clone(), + Versioned::VStaging( + protocol_vstaging::ValidationProtocol::ApprovalDistribution( + protocol_vstaging::ApprovalDistributionMessage::Approvals(batch), + ), + ), + )) + .await; + } } } diff --git a/polkadot/node/network/approval-distribution/src/metrics.rs b/polkadot/node/network/approval-distribution/src/metrics.rs index 896866ce099a..6864259e6fdb 100644 --- a/polkadot/node/network/approval-distribution/src/metrics.rs +++ b/polkadot/node/network/approval-distribution/src/metrics.rs @@ -15,6 +15,7 @@ // along with Polkadot. If not, see . use polkadot_node_metrics::metrics::{prometheus, Metrics as MetricsTrait}; +use polkadot_node_primitives::approval::v2::AssignmentCertKindV2; /// Approval Distribution metrics. #[derive(Default, Clone)] @@ -22,21 +23,34 @@ pub struct Metrics(Option); #[derive(Clone)] struct MetricsInner { - assignments_imported_total: prometheus::Counter, + assignments_imported_total: prometheus::CounterVec, approvals_imported_total: prometheus::Counter, unified_with_peer_total: prometheus::Counter, aggression_l1_messages_total: prometheus::Counter, aggression_l2_messages_total: prometheus::Counter, - time_unify_with_peer: prometheus::Histogram, time_import_pending_now_known: prometheus::Histogram, time_awaiting_approval_voting: prometheus::Histogram, } +trait AsLabel { + fn as_label(&self) -> &str; +} + +impl AsLabel for &AssignmentCertKindV2 { + fn as_label(&self) -> &str { + match self { + AssignmentCertKindV2::RelayVRFDelay { .. } => "VRF Delay", + AssignmentCertKindV2::RelayVRFModulo { .. } => "VRF Modulo", + AssignmentCertKindV2::RelayVRFModuloCompact { .. } => "VRF Modulo Compact", + } + } +} + impl Metrics { - pub(crate) fn on_assignment_imported(&self) { + pub(crate) fn on_assignment_imported(&self, kind: &AssignmentCertKindV2) { if let Some(metrics) = &self.0 { - metrics.assignments_imported_total.inc(); + metrics.assignments_imported_total.with_label_values(&[kind.as_label()]).inc(); } } @@ -89,9 +103,12 @@ impl MetricsTrait for Metrics { fn try_register(registry: &prometheus::Registry) -> Result { let metrics = MetricsInner { assignments_imported_total: prometheus::register( - prometheus::Counter::new( - "polkadot_parachain_assignments_imported_total", - "Number of valid assignments imported locally or from other peers.", + prometheus::CounterVec::new( + prometheus::Opts::new( + "polkadot_parachain_assignments_imported_total", + "Number of valid assignments imported locally or from other peers.", + ), + &["kind"], )?, registry, )?, @@ -124,10 +141,16 @@ impl MetricsTrait for Metrics { registry, )?, time_unify_with_peer: prometheus::register( - prometheus::Histogram::with_opts(prometheus::HistogramOpts::new( - "polkadot_parachain_time_unify_with_peer", - "Time spent within fn `unify_with_peer`.", - ).buckets(vec![0.000625, 0.00125,0.0025, 0.005, 0.0075, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0,]))?, + prometheus::Histogram::with_opts( + prometheus::HistogramOpts::new( + "polkadot_parachain_time_unify_with_peer", + "Time spent within fn `unify_with_peer`.", + ) + .buckets(vec![ + 0.000625, 0.00125, 0.0025, 0.005, 0.0075, 0.01, 0.025, 0.05, 0.1, 0.25, + 0.5, 1.0, 2.5, 5.0, 10.0, + ]), + )?, registry, )?, time_import_pending_now_known: prometheus::register( diff --git a/polkadot/node/network/approval-distribution/src/tests.rs b/polkadot/node/network/approval-distribution/src/tests.rs index 29c7d8aa45da..33c38c7c5dfa 100644 --- a/polkadot/node/network/approval-distribution/src/tests.rs +++ b/polkadot/node/network/approval-distribution/src/tests.rs @@ -24,20 +24,26 @@ use polkadot_node_network_protocol::{ view, ObservedRole, }; use polkadot_node_primitives::approval::{ - AssignmentCertKind, VrfOutput, VrfProof, VrfSignature, RELAY_VRF_MODULO_CONTEXT, + v1::{ + AssignmentCert, AssignmentCertKind, IndirectAssignmentCert, VrfOutput, VrfProof, + VrfSignature, + }, + v2::{ + AssignmentCertKindV2, AssignmentCertV2, CoreBitfield, IndirectAssignmentCertV2, + RELAY_VRF_MODULO_CONTEXT, + }, }; use polkadot_node_subsystem::messages::{ network_bridge_event, AllMessages, ApprovalCheckError, ReportPeerMessage, }; use polkadot_node_subsystem_test_helpers as test_helpers; use polkadot_node_subsystem_util::{reputation::add_reputation, TimeoutExt as _}; -use polkadot_primitives::{AuthorityDiscoveryId, BlakeTwo256, HashT}; +use polkadot_primitives::{AuthorityDiscoveryId, BlakeTwo256, CoreIndex, HashT}; use polkadot_primitives_test_helpers::dummy_signature; use rand::SeedableRng; use sp_authority_discovery::AuthorityPair as AuthorityDiscoveryPair; use sp_core::crypto::Pair as PairT; use std::time::Duration; - type VirtualOverseer = test_helpers::TestSubsystemContextHandle; fn test_harness>( @@ -219,15 +225,15 @@ async fn setup_gossip_topology( async fn setup_peer_with_view( virtual_overseer: &mut VirtualOverseer, peer_id: &PeerId, - validation_version: ValidationVersion, view: View, + version: ValidationVersion, ) { overseer_send( virtual_overseer, ApprovalDistributionMessage::NetworkBridgeUpdate(NetworkBridgeEvent::PeerConnected( *peer_id, ObservedRole::Full, - validation_version.into(), + version.into(), None, )), ) @@ -244,12 +250,43 @@ async fn setup_peer_with_view( async fn send_message_from_peer( virtual_overseer: &mut VirtualOverseer, peer_id: &PeerId, - msg: net_protocol::ApprovalDistributionMessage, + msg: protocol_v1::ApprovalDistributionMessage, +) { + overseer_send( + virtual_overseer, + ApprovalDistributionMessage::NetworkBridgeUpdate(NetworkBridgeEvent::PeerMessage( + *peer_id, + Versioned::V1(msg), + )), + ) + .await; +} + +async fn send_message_from_peer_v2( + virtual_overseer: &mut VirtualOverseer, + peer_id: &PeerId, + msg: protocol_v2::ApprovalDistributionMessage, ) { overseer_send( virtual_overseer, ApprovalDistributionMessage::NetworkBridgeUpdate(NetworkBridgeEvent::PeerMessage( - *peer_id, msg, + *peer_id, + Versioned::V2(msg), + )), + ) + .await; +} + +async fn send_message_from_peer_vstaging( + virtual_overseer: &mut VirtualOverseer, + peer_id: &PeerId, + msg: protocol_vstaging::ApprovalDistributionMessage, +) { + overseer_send( + virtual_overseer, + ApprovalDistributionMessage::NetworkBridgeUpdate(NetworkBridgeEvent::PeerMessage( + *peer_id, + Versioned::VStaging(msg), )), ) .await; @@ -273,6 +310,28 @@ fn fake_assignment_cert(block_hash: Hash, validator: ValidatorIndex) -> Indirect } } +fn fake_assignment_cert_v2( + block_hash: Hash, + validator: ValidatorIndex, + core_bitfield: CoreBitfield, +) -> IndirectAssignmentCertV2 { + let ctx = schnorrkel::signing_context(RELAY_VRF_MODULO_CONTEXT); + let msg = b"WhenParachains?"; + let mut prng = rand_core::OsRng; + let keypair = schnorrkel::Keypair::generate_with(&mut prng); + let (inout, proof, _) = keypair.vrf_sign(ctx.bytes(msg)); + let out = inout.to_output(); + + IndirectAssignmentCertV2 { + block_hash, + validator, + cert: AssignmentCertV2 { + kind: AssignmentCertKindV2::RelayVRFModuloCompact { core_bitfield }, + vrf: VrfSignature { output: VrfOutput(out), proof: VrfProof(proof) }, + }, + } +} + async fn expect_reputation_change( virtual_overseer: &mut VirtualOverseer, peer_id: &PeerId, @@ -331,9 +390,9 @@ fn try_import_the_same_assignment() { let _ = test_harness(state_without_reputation_delay(), |mut virtual_overseer| async move { let overseer = &mut virtual_overseer; // setup peers - setup_peer_with_view(overseer, &peer_a, ValidationVersion::V1, view![]).await; - setup_peer_with_view(overseer, &peer_b, ValidationVersion::V1, view![hash]).await; - setup_peer_with_view(overseer, &peer_c, ValidationVersion::V1, view![hash]).await; + setup_peer_with_view(overseer, &peer_a, view![], ValidationVersion::V1).await; + setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V1).await; + setup_peer_with_view(overseer, &peer_c, view![hash], ValidationVersion::V1).await; // new block `hash_a` with 1 candidates let meta = BlockApprovalMeta { @@ -353,7 +412,7 @@ fn try_import_the_same_assignment() { let assignments = vec![(cert.clone(), 0u32)]; let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); - send_message_from_peer(overseer, &peer_a, Versioned::V1(msg)).await; + send_message_from_peer(overseer, &peer_a, msg).await; expect_reputation_change(overseer, &peer_a, COST_UNEXPECTED_MESSAGE).await; @@ -362,10 +421,11 @@ fn try_import_the_same_assignment() { overseer_recv(overseer).await, AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( assignment, - 0u32, + claimed_indices, tx, )) => { - assert_eq!(assignment, cert); + assert_eq!(claimed_indices, 0u32.into()); + assert_eq!(assignment, cert.into()); tx.send(AssignmentCheckResult::Accepted).unwrap(); } ); @@ -385,12 +445,104 @@ fn try_import_the_same_assignment() { } ); - // setup new peer - setup_peer_with_view(overseer, &peer_d, ValidationVersion::V1, view![]).await; + // setup new peer with V2 + setup_peer_with_view(overseer, &peer_d, view![], ValidationVersion::VStaging).await; // send the same assignment from peer_d let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments); - send_message_from_peer(overseer, &peer_d, Versioned::V1(msg)).await; + send_message_from_peer(overseer, &peer_d, msg).await; + + expect_reputation_change(overseer, &peer_d, COST_UNEXPECTED_MESSAGE).await; + expect_reputation_change(overseer, &peer_d, BENEFIT_VALID_MESSAGE).await; + + assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); + virtual_overseer + }); +} + +/// Just like `try_import_the_same_assignment` but use `VRFModuloCompact` assignments for multiple +/// cores. +#[test] +fn try_import_the_same_assignment_v2() { + let peer_a = PeerId::random(); + let peer_b = PeerId::random(); + let peer_c = PeerId::random(); + let peer_d = PeerId::random(); + let parent_hash = Hash::repeat_byte(0xFF); + let hash = Hash::repeat_byte(0xAA); + + let _ = test_harness(state_without_reputation_delay(), |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // setup peers + setup_peer_with_view(overseer, &peer_a, view![], ValidationVersion::VStaging).await; + setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::VStaging).await; + setup_peer_with_view(overseer, &peer_c, view![hash], ValidationVersion::VStaging).await; + + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 2, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + // send the assignment related to `hash` + let validator_index = ValidatorIndex(0); + let cores = vec![1, 2, 3, 4]; + let core_bitfield: CoreBitfield = cores + .iter() + .map(|index| CoreIndex(*index)) + .collect::>() + .try_into() + .unwrap(); + + let cert = fake_assignment_cert_v2(hash, validator_index, core_bitfield.clone()); + let assignments = vec![(cert.clone(), cores.clone().try_into().unwrap())]; + + let msg = protocol_vstaging::ApprovalDistributionMessage::Assignments(assignments.clone()); + send_message_from_peer_vstaging(overseer, &peer_a, msg).await; + + expect_reputation_change(overseer, &peer_a, COST_UNEXPECTED_MESSAGE).await; + + // send an `Accept` message from the Approval Voting subsystem + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( + assignment, + claimed_indices, + tx, + )) => { + assert_eq!(claimed_indices, cores.try_into().unwrap()); + assert_eq!(assignment, cert.into()); + tx.send(AssignmentCheckResult::Accepted).unwrap(); + } + ); + + expect_reputation_change(overseer, &peer_a, BENEFIT_VALID_MESSAGE_FIRST).await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::VStaging(protocol_vstaging::ValidationProtocol::ApprovalDistribution( + protocol_vstaging::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert_eq!(peers.len(), 2); + assert_eq!(assignments.len(), 1); + } + ); + + // setup new peer + setup_peer_with_view(overseer, &peer_d, view![], ValidationVersion::VStaging).await; + + // send the same assignment from peer_d + let msg = protocol_vstaging::ApprovalDistributionMessage::Assignments(assignments); + send_message_from_peer_vstaging(overseer, &peer_d, msg).await; expect_reputation_change(overseer, &peer_d, COST_UNEXPECTED_MESSAGE).await; expect_reputation_change(overseer, &peer_d, BENEFIT_VALID_MESSAGE).await; @@ -413,7 +565,7 @@ fn delay_reputation_change() { let overseer = &mut virtual_overseer; // Setup peers - setup_peer_with_view(overseer, &peer, ValidationVersion::V1, view![]).await; + setup_peer_with_view(overseer, &peer, view![], ValidationVersion::V1).await; // new block `hash_a` with 1 candidates let meta = BlockApprovalMeta { @@ -433,17 +585,18 @@ fn delay_reputation_change() { let assignments = vec![(cert.clone(), 0u32)]; let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); - send_message_from_peer(overseer, &peer, Versioned::V1(msg)).await; + send_message_from_peer(overseer, &peer, msg).await; // send an `Accept` message from the Approval Voting subsystem assert_matches!( overseer_recv(overseer).await, AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( assignment, - 0u32, + claimed_candidates, tx, )) => { - assert_eq!(assignment, cert); + assert_eq!(assignment.cert, cert.cert.into()); + assert_eq!(claimed_candidates, vec![0u32].try_into().unwrap()); tx.send(AssignmentCheckResult::Accepted).unwrap(); } ); @@ -474,7 +627,7 @@ fn spam_attack_results_in_negative_reputation_change() { let _ = test_harness(state_without_reputation_delay(), |mut virtual_overseer| async move { let overseer = &mut virtual_overseer; let peer = &peer_a; - setup_peer_with_view(overseer, peer, ValidationVersion::V1, view![]).await; + setup_peer_with_view(overseer, peer, view![], ValidationVersion::V1).await; // new block `hash_b` with 20 candidates let candidates_count = 20; @@ -501,7 +654,7 @@ fn spam_attack_results_in_negative_reputation_change() { .collect(); let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); - send_message_from_peer(overseer, peer, Versioned::V1(msg.clone())).await; + send_message_from_peer(overseer, peer, msg.clone()).await; for i in 0..candidates_count { expect_reputation_change(overseer, peer, COST_UNEXPECTED_MESSAGE).await; @@ -513,8 +666,8 @@ fn spam_attack_results_in_negative_reputation_change() { claimed_candidate_index, tx, )) => { - assert_eq!(assignment, assignments[i].0); - assert_eq!(claimed_candidate_index, assignments[i].1); + assert_eq!(assignment, assignments[i].0.clone().into()); + assert_eq!(claimed_candidate_index, assignments[i].1.into()); tx.send(AssignmentCheckResult::Accepted).unwrap(); } ); @@ -533,7 +686,7 @@ fn spam_attack_results_in_negative_reputation_change() { .await; // send the assignments again - send_message_from_peer(overseer, peer, Versioned::V1(msg.clone())).await; + send_message_from_peer(overseer, peer, msg.clone()).await; // each of them will incur `COST_UNEXPECTED_MESSAGE`, not only the first one for _ in 0..candidates_count { @@ -558,7 +711,7 @@ fn peer_sending_us_the_same_we_just_sent_them_is_ok() { let _ = test_harness(state_without_reputation_delay(), |mut virtual_overseer| async move { let overseer = &mut virtual_overseer; let peer = &peer_a; - setup_peer_with_view(overseer, peer, ValidationVersion::V1, view![]).await; + setup_peer_with_view(overseer, peer, view![], ValidationVersion::V1).await; // new block `hash` with 1 candidates let meta = BlockApprovalMeta { @@ -578,7 +731,10 @@ fn peer_sending_us_the_same_we_just_sent_them_is_ok() { let cert = fake_assignment_cert(hash, validator_index); overseer_send( overseer, - ApprovalDistributionMessage::DistributeAssignment(cert.clone(), candidate_index), + ApprovalDistributionMessage::DistributeAssignment( + cert.clone().into(), + candidate_index.into(), + ), ) .await; @@ -610,12 +766,12 @@ fn peer_sending_us_the_same_we_just_sent_them_is_ok() { // the peer could send us it as well let assignments = vec![(cert, candidate_index)]; let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments); - send_message_from_peer(overseer, peer, Versioned::V1(msg.clone())).await; + send_message_from_peer(overseer, peer, msg.clone()).await; assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "we should not punish the peer"); // send the assignments again - send_message_from_peer(overseer, peer, Versioned::V1(msg)).await; + send_message_from_peer(overseer, peer, msg).await; // now we should expect_reputation_change(overseer, peer, COST_DUPLICATE_MESSAGE).await; @@ -633,10 +789,10 @@ fn import_approval_happy_path() { let _ = test_harness(state_without_reputation_delay(), |mut virtual_overseer| async move { let overseer = &mut virtual_overseer; - // setup peers - setup_peer_with_view(overseer, &peer_a, ValidationVersion::V1, view![]).await; - setup_peer_with_view(overseer, &peer_b, ValidationVersion::V1, view![hash]).await; - setup_peer_with_view(overseer, &peer_c, ValidationVersion::V1, view![hash]).await; + // setup peers with V1 and V2 protocol versions + setup_peer_with_view(overseer, &peer_a, view![], ValidationVersion::V1).await; + setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::VStaging).await; + setup_peer_with_view(overseer, &peer_c, view![hash], ValidationVersion::V1).await; // new block `hash_a` with 1 candidates let meta = BlockApprovalMeta { @@ -656,10 +812,14 @@ fn import_approval_happy_path() { let cert = fake_assignment_cert(hash, validator_index); overseer_send( overseer, - ApprovalDistributionMessage::DistributeAssignment(cert, candidate_index), + ApprovalDistributionMessage::DistributeAssignment( + cert.clone().into(), + candidate_index.into(), + ), ) .await; + // 1 peer is v1 assert_matches!( overseer_recv(overseer).await, AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( @@ -668,7 +828,21 @@ fn import_approval_happy_path() { protocol_v1::ApprovalDistributionMessage::Assignments(assignments) )) )) => { - assert_eq!(peers.len(), 2); + assert_eq!(peers.len(), 1); + assert_eq!(assignments.len(), 1); + } + ); + + // 1 peer is v2 + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::VStaging(protocol_vstaging::ValidationProtocol::ApprovalDistribution( + protocol_vstaging::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert_eq!(peers.len(), 1); assert_eq!(assignments.len(), 1); } ); @@ -681,7 +855,7 @@ fn import_approval_happy_path() { signature: dummy_signature(), }; let msg = protocol_v1::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); - send_message_from_peer(overseer, &peer_b, Versioned::V1(msg)).await; + send_message_from_peer(overseer, &peer_b, msg).await; assert_matches!( overseer_recv(overseer).await, @@ -722,8 +896,8 @@ fn import_approval_bad() { let _ = test_harness(state_without_reputation_delay(), |mut virtual_overseer| async move { let overseer = &mut virtual_overseer; // setup peers - setup_peer_with_view(overseer, &peer_a, ValidationVersion::V1, view![]).await; - setup_peer_with_view(overseer, &peer_b, ValidationVersion::V1, view![hash]).await; + setup_peer_with_view(overseer, &peer_a, view![], ValidationVersion::V1).await; + setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V1).await; // new block `hash_a` with 1 candidates let meta = BlockApprovalMeta { @@ -749,14 +923,14 @@ fn import_approval_bad() { signature: dummy_signature(), }; let msg = protocol_v1::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); - send_message_from_peer(overseer, &peer_b, Versioned::V1(msg)).await; + send_message_from_peer(overseer, &peer_b, msg).await; expect_reputation_change(overseer, &peer_b, COST_UNEXPECTED_MESSAGE).await; // now import an assignment from peer_b let assignments = vec![(cert.clone(), candidate_index)]; let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments); - send_message_from_peer(overseer, &peer_b, Versioned::V1(msg)).await; + send_message_from_peer(overseer, &peer_b, msg).await; assert_matches!( overseer_recv(overseer).await, @@ -765,8 +939,8 @@ fn import_approval_bad() { i, tx, )) => { - assert_eq!(assignment, cert); - assert_eq!(i, candidate_index); + assert_eq!(assignment, cert.into()); + assert_eq!(i, candidate_index.into()); tx.send(AssignmentCheckResult::Accepted).unwrap(); } ); @@ -775,7 +949,7 @@ fn import_approval_bad() { // and try again let msg = protocol_v1::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); - send_message_from_peer(overseer, &peer_b, Versioned::V1(msg)).await; + send_message_from_peer(overseer, &peer_b, msg).await; assert_matches!( overseer_recv(overseer).await, @@ -911,12 +1085,20 @@ fn update_peer_view() { let cert_a = fake_assignment_cert(hash_a, ValidatorIndex(0)); let cert_b = fake_assignment_cert(hash_b, ValidatorIndex(0)); - overseer_send(overseer, ApprovalDistributionMessage::DistributeAssignment(cert_a, 0)).await; + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeAssignment(cert_a.into(), 0.into()), + ) + .await; - overseer_send(overseer, ApprovalDistributionMessage::DistributeAssignment(cert_b, 0)).await; + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeAssignment(cert_b.into(), 0.into()), + ) + .await; // connect a peer - setup_peer_with_view(overseer, peer, ValidationVersion::V1, view![hash_a]).await; + setup_peer_with_view(overseer, peer, view![hash_a], ValidationVersion::V1).await; // we should send relevant assignments to the peer assert_matches!( @@ -934,7 +1116,7 @@ fn update_peer_view() { virtual_overseer }); - assert_eq!(state.peer_data.get(peer).map(|data| data.view.finalized_number), Some(0)); + assert_eq!(state.peer_views.get(peer).map(|v| v.view.finalized_number), Some(0)); assert_eq!( state .blocks @@ -965,7 +1147,7 @@ fn update_peer_view() { overseer_send( overseer, - ApprovalDistributionMessage::DistributeAssignment(cert_c.clone(), 0), + ApprovalDistributionMessage::DistributeAssignment(cert_c.clone().into(), 0.into()), ) .await; @@ -986,7 +1168,7 @@ fn update_peer_view() { virtual_overseer }); - assert_eq!(state.peer_data.get(peer).map(|data| data.view.finalized_number), Some(2)); + assert_eq!(state.peer_views.get(peer).map(|v| v.view.finalized_number), Some(2)); assert_eq!( state .blocks @@ -1016,10 +1198,7 @@ fn update_peer_view() { virtual_overseer }); - assert_eq!( - state.peer_data.get(peer).map(|data| data.view.finalized_number), - Some(finalized_number) - ); + assert_eq!(state.peer_views.get(peer).map(|v| v.view.finalized_number), Some(finalized_number)); assert!(state.blocks.get(&hash_c).unwrap().known_by.get(peer).is_none()); } @@ -1034,7 +1213,7 @@ fn import_remotely_then_locally() { let _ = test_harness(state_without_reputation_delay(), |mut virtual_overseer| async move { let overseer = &mut virtual_overseer; // setup the peer - setup_peer_with_view(overseer, peer, ValidationVersion::V1, view![hash]).await; + setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; // new block `hash_a` with 1 candidates let meta = BlockApprovalMeta { @@ -1054,7 +1233,7 @@ fn import_remotely_then_locally() { let cert = fake_assignment_cert(hash, validator_index); let assignments = vec![(cert.clone(), candidate_index)]; let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); - send_message_from_peer(overseer, peer, Versioned::V1(msg)).await; + send_message_from_peer(overseer, peer, msg).await; // send an `Accept` message from the Approval Voting subsystem assert_matches!( @@ -1064,8 +1243,8 @@ fn import_remotely_then_locally() { i, tx, )) => { - assert_eq!(assignment, cert); - assert_eq!(i, candidate_index); + assert_eq!(assignment, cert.clone().into()); + assert_eq!(i, candidate_index.into()); tx.send(AssignmentCheckResult::Accepted).unwrap(); } ); @@ -1075,7 +1254,10 @@ fn import_remotely_then_locally() { // import the same assignment locally overseer_send( overseer, - ApprovalDistributionMessage::DistributeAssignment(cert, candidate_index), + ApprovalDistributionMessage::DistributeAssignment( + cert.clone().into(), + candidate_index.into(), + ), ) .await; @@ -1089,7 +1271,7 @@ fn import_remotely_then_locally() { signature: dummy_signature(), }; let msg = protocol_v1::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); - send_message_from_peer(overseer, peer, Versioned::V1(msg)).await; + send_message_from_peer(overseer, peer, msg).await; assert_matches!( overseer_recv(overseer).await, @@ -1147,7 +1329,10 @@ fn sends_assignments_even_when_state_is_approved() { overseer_send( overseer, - ApprovalDistributionMessage::DistributeAssignment(cert.clone(), candidate_index), + ApprovalDistributionMessage::DistributeAssignment( + cert.clone().into(), + candidate_index.into(), + ), ) .await; @@ -1155,7 +1340,7 @@ fn sends_assignments_even_when_state_is_approved() { .await; // connect the peer. - setup_peer_with_view(overseer, peer, ValidationVersion::V1, view![hash]).await; + setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; let assignments = vec![(cert.clone(), candidate_index)]; let approvals = vec![approval.clone()]; @@ -1191,6 +1376,112 @@ fn sends_assignments_even_when_state_is_approved() { }); } +/// Same as `sends_assignments_even_when_state_is_approved_v2` but with `VRFModuloCompact` +/// assignemnts. +#[test] +fn sends_assignments_even_when_state_is_approved_v2() { + let peer_a = PeerId::random(); + let parent_hash = Hash::repeat_byte(0xFF); + let hash = Hash::repeat_byte(0xAA); + let peer = &peer_a; + + let _ = test_harness(State::default(), |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![Default::default(); 4], + slot: 1.into(), + session: 1, + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + let validator_index = ValidatorIndex(0); + let cores = vec![0, 1, 2, 3]; + let candidate_bitfield: CandidateBitfield = cores.clone().try_into().unwrap(); + + let core_bitfield: CoreBitfield = cores + .iter() + .map(|index| CoreIndex(*index)) + .collect::>() + .try_into() + .unwrap(); + + let cert = fake_assignment_cert_v2(hash, validator_index, core_bitfield.clone()); + + // Assumes candidate index == core index. + let approvals = cores + .iter() + .map(|core| IndirectSignedApprovalVote { + block_hash: hash, + candidate_index: *core, + validator: validator_index, + signature: dummy_signature(), + }) + .collect::>(); + + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeAssignment( + cert.clone().into(), + candidate_bitfield.clone(), + ), + ) + .await; + + for approval in &approvals { + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeApproval(approval.clone()), + ) + .await; + } + + // connect the peer. + setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::VStaging).await; + + let assignments = vec![(cert.clone(), candidate_bitfield.clone())]; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::VStaging(protocol_vstaging::ValidationProtocol::ApprovalDistribution( + protocol_vstaging::ApprovalDistributionMessage::Assignments(sent_assignments) + )) + )) => { + assert_eq!(peers, vec![*peer]); + assert_eq!(sent_assignments, assignments); + } + ); + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::VStaging(protocol_vstaging::ValidationProtocol::ApprovalDistribution( + protocol_vstaging::ApprovalDistributionMessage::Approvals(sent_approvals) + )) + )) => { + // Construct a hashmaps of approvals for comparison. Approval distribution reorders messages because they are kept in a + // hashmap as well. + let sent_approvals = sent_approvals.into_iter().map(|approval| (approval.candidate_index, approval)).collect::>(); + let approvals = approvals.into_iter().map(|approval| (approval.candidate_index, approval)).collect::>(); + + assert_eq!(peers, vec![*peer]); + assert_eq!(sent_approvals, approvals); + } + ); + + assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); + virtual_overseer + }); +} + /// /// /// 1. Receive remote peer view update with an unknown head @@ -1219,7 +1510,7 @@ fn race_condition_in_local_vs_remote_view_update() { }; // This will send a peer view that is ahead of our view - setup_peer_with_view(overseer, peer, ValidationVersion::V1, view![hash_b]).await; + setup_peer_with_view(overseer, peer, view![hash_b], ValidationVersion::V1).await; // Send our view update to include a new head overseer_send( @@ -1240,7 +1531,7 @@ fn race_condition_in_local_vs_remote_view_update() { .collect(); let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); - send_message_from_peer(overseer, peer, Versioned::V1(msg.clone())).await; + send_message_from_peer(overseer, peer, msg.clone()).await; // This will handle pending messages being processed let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); @@ -1257,8 +1548,8 @@ fn race_condition_in_local_vs_remote_view_update() { claimed_candidate_index, tx, )) => { - assert_eq!(assignment, assignments[i].0); - assert_eq!(claimed_candidate_index, assignments[i].1); + assert_eq!(assignment, assignments[i].0.clone().into()); + assert_eq!(claimed_candidate_index, assignments[i].1.into()); tx.send(AssignmentCheckResult::Accepted).unwrap(); } ); @@ -1283,7 +1574,7 @@ fn propagates_locally_generated_assignment_to_both_dimensions() { // Connect all peers. for (peer, _) in &peers { - setup_peer_with_view(overseer, peer, ValidationVersion::V1, view![hash]).await; + setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; } // Set up a gossip topology. @@ -1325,7 +1616,10 @@ fn propagates_locally_generated_assignment_to_both_dimensions() { overseer_send( overseer, - ApprovalDistributionMessage::DistributeAssignment(cert.clone(), candidate_index), + ApprovalDistributionMessage::DistributeAssignment( + cert.clone().into(), + candidate_index.into(), + ), ) .await; @@ -1388,7 +1682,7 @@ fn propagates_assignments_along_unshared_dimension() { // Connect all peers. for (peer, _) in &peers { - setup_peer_with_view(overseer, peer, ValidationVersion::V1, view![hash]).await; + setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; } // Set up a gossip topology. @@ -1424,7 +1718,7 @@ fn propagates_assignments_along_unshared_dimension() { // Issuer of the message is important, not the peer we receive from. // 99 deliberately chosen because it's not in X or Y. - send_message_from_peer(overseer, &peers[99].0, Versioned::V1(msg)).await; + send_message_from_peer(overseer, &peers[99].0, msg).await; assert_matches!( overseer_recv(overseer).await, AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( @@ -1473,7 +1767,7 @@ fn propagates_assignments_along_unshared_dimension() { // Issuer of the message is important, not the peer we receive from. // 99 deliberately chosen because it's not in X or Y. - send_message_from_peer(overseer, &peers[99].0, Versioned::V1(msg)).await; + send_message_from_peer(overseer, &peers[99].0, msg).await; assert_matches!( overseer_recv(overseer).await, AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( @@ -1530,7 +1824,7 @@ fn propagates_to_required_after_connect() { // Connect all peers except omitted. for (i, (peer, _)) in peers.iter().enumerate() { if !omitted.contains(&i) { - setup_peer_with_view(overseer, peer, ValidationVersion::V1, view![hash]).await; + setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; } } @@ -1573,7 +1867,10 @@ fn propagates_to_required_after_connect() { overseer_send( overseer, - ApprovalDistributionMessage::DistributeAssignment(cert.clone(), candidate_index), + ApprovalDistributionMessage::DistributeAssignment( + cert.clone().into(), + candidate_index.into(), + ), ) .await; @@ -1619,7 +1916,7 @@ fn propagates_to_required_after_connect() { ); for i in omitted.iter().copied() { - setup_peer_with_view(overseer, &peers[i].0, ValidationVersion::V1, view![hash]).await; + setup_peer_with_view(overseer, &peers[i].0, view![hash], ValidationVersion::V1).await; assert_matches!( overseer_recv(overseer).await, @@ -1668,7 +1965,7 @@ fn sends_to_more_peers_after_getting_topology() { // Connect all peers except omitted. for (peer, _) in &peers { - setup_peer_with_view(overseer, peer, ValidationVersion::V1, view![hash]).await; + setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; } // new block `hash_a` with 1 candidates @@ -1698,7 +1995,10 @@ fn sends_to_more_peers_after_getting_topology() { overseer_send( overseer, - ApprovalDistributionMessage::DistributeAssignment(cert.clone(), candidate_index), + ApprovalDistributionMessage::DistributeAssignment( + cert.clone().into(), + candidate_index.into(), + ), ) .await; @@ -1820,7 +2120,7 @@ fn originator_aggression_l1() { // Connect all peers except omitted. for (peer, _) in &peers { - setup_peer_with_view(overseer, peer, ValidationVersion::V1, view![hash]).await; + setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; } // new block `hash_a` with 1 candidates @@ -1857,7 +2157,10 @@ fn originator_aggression_l1() { overseer_send( overseer, - ApprovalDistributionMessage::DistributeAssignment(cert.clone(), candidate_index), + ApprovalDistributionMessage::DistributeAssignment( + cert.clone().into(), + candidate_index.into(), + ), ) .await; @@ -1979,7 +2282,7 @@ fn non_originator_aggression_l1() { // Connect all peers except omitted. for (peer, _) in &peers { - setup_peer_with_view(overseer, peer, ValidationVersion::V1, view![hash]).await; + setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; } // new block `hash_a` with 1 candidates @@ -2008,12 +2311,12 @@ fn non_originator_aggression_l1() { ) .await; - let assignments = vec![(cert.clone(), candidate_index)]; + let assignments = vec![(cert.clone().into(), candidate_index)]; let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); // Issuer of the message is important, not the peer we receive from. // 99 deliberately chosen because it's not in X or Y. - send_message_from_peer(overseer, &peers[99].0, Versioned::V1(msg)).await; + send_message_from_peer(overseer, &peers[99].0, msg).await; assert_matches!( overseer_recv(overseer).await, AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( @@ -2084,7 +2387,7 @@ fn non_originator_aggression_l2() { // Connect all peers except omitted. for (peer, _) in &peers { - setup_peer_with_view(overseer, peer, ValidationVersion::V1, view![hash]).await; + setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; } // new block `hash_a` with 1 candidates @@ -2118,7 +2421,7 @@ fn non_originator_aggression_l2() { // Issuer of the message is important, not the peer we receive from. // 99 deliberately chosen because it's not in X or Y. - send_message_from_peer(overseer, &peers[99].0, Versioned::V1(msg)).await; + send_message_from_peer(overseer, &peers[99].0, msg).await; assert_matches!( overseer_recv(overseer).await, AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( @@ -2249,7 +2552,7 @@ fn resends_messages_periodically() { // Connect all peers. for (peer, _) in &peers { - setup_peer_with_view(overseer, peer, ValidationVersion::V1, view![hash]).await; + setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; } // Set up a gossip topology. @@ -2284,7 +2587,7 @@ fn resends_messages_periodically() { // Issuer of the message is important, not the peer we receive from. // 99 deliberately chosen because it's not in X or Y. - send_message_from_peer(overseer, &peers[99].0, Versioned::V1(msg)).await; + send_message_from_peer(overseer, &peers[99].0, msg).await; assert_matches!( overseer_recv(overseer).await, AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( @@ -2388,9 +2691,9 @@ fn import_versioned_approval() { let _ = test_harness(state, |mut virtual_overseer| async move { let overseer = &mut virtual_overseer; // All peers are aware of relay parent. - setup_peer_with_view(overseer, &peer_a, ValidationVersion::V2, view![hash]).await; - setup_peer_with_view(overseer, &peer_b, ValidationVersion::V1, view![hash]).await; - setup_peer_with_view(overseer, &peer_c, ValidationVersion::V2, view![hash]).await; + setup_peer_with_view(overseer, &peer_a, view![hash], ValidationVersion::V2).await; + setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V1).await; + setup_peer_with_view(overseer, &peer_c, view![hash], ValidationVersion::V2).await; // new block `hash_a` with 1 candidates let meta = BlockApprovalMeta { @@ -2410,7 +2713,7 @@ fn import_versioned_approval() { let cert = fake_assignment_cert(hash, validator_index); overseer_send( overseer, - ApprovalDistributionMessage::DistributeAssignment(cert, candidate_index), + ApprovalDistributionMessage::DistributeAssignment(cert.into(), candidate_index.into()), ) .await; @@ -2451,7 +2754,7 @@ fn import_versioned_approval() { signature: dummy_signature(), }; let msg = protocol_v2::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); - send_message_from_peer(overseer, &peer_a, Versioned::V2(msg)).await; + send_message_from_peer_v2(overseer, &peer_a, msg).await; assert_matches!( overseer_recv(overseer).await, @@ -2512,7 +2815,9 @@ fn batch_test_round(message_count: usize) { let validators = 0..message_count; let assignments: Vec<_> = validators .clone() - .map(|index| (fake_assignment_cert(Hash::zero(), ValidatorIndex(index as u32)), 0)) + .map(|index| { + (fake_assignment_cert(Hash::zero(), ValidatorIndex(index as u32)).into(), 0.into()) + }) .collect(); let approvals: Vec<_> = validators @@ -2525,9 +2830,18 @@ fn batch_test_round(message_count: usize) { .collect(); let peer = PeerId::random(); - send_assignments_batched(&mut sender, assignments.clone(), peer, ValidationVersion::V1) - .await; - send_approvals_batched(&mut sender, approvals.clone(), peer, ValidationVersion::V1).await; + send_assignments_batched( + &mut sender, + assignments.clone(), + &vec![(peer, ValidationVersion::V1.into())], + ) + .await; + send_approvals_batched( + &mut sender, + approvals.clone(), + &vec![(peer, ValidationVersion::V1.into())], + ) + .await; // Check expected assignments batches. for assignment_index in (0..assignments.len()).step_by(super::MAX_ASSIGNMENT_BATCH_SIZE) { @@ -2549,7 +2863,7 @@ fn batch_test_round(message_count: usize) { assert_eq!(peers.len(), 1); for (message_index, assignment) in sent_assignments.iter().enumerate() { - assert_eq!(assignment.0, assignments[assignment_index + message_index].0); + assert_eq!(assignment.0, assignments[assignment_index + message_index].0.clone().try_into().unwrap()); assert_eq!(assignment.1, 0); } } diff --git a/polkadot/node/network/bitfield-distribution/src/lib.rs b/polkadot/node/network/bitfield-distribution/src/lib.rs index 68e381ab6be5..9cc79aee8490 100644 --- a/polkadot/node/network/bitfield-distribution/src/lib.rs +++ b/polkadot/node/network/bitfield-distribution/src/lib.rs @@ -25,14 +25,15 @@ use always_assert::never; use futures::{channel::oneshot, FutureExt}; +use net_protocol::filter_by_peer_version; use polkadot_node_network_protocol::{ self as net_protocol, grid_topology::{ GridNeighbors, RandomRouting, RequiredRouting, SessionBoundGridTopologyStorage, }, peer_set::{ProtocolVersion, ValidationVersion}, - v1 as protocol_v1, v2 as protocol_v2, OurView, PeerId, UnifiedReputationChange as Rep, - Versioned, View, + v1 as protocol_v1, v2 as protocol_v2, vstaging as protocol_vstaging, OurView, PeerId, + UnifiedReputationChange as Rep, Versioned, View, }; use polkadot_node_subsystem::{ jaeger, messages::*, overseer, ActiveLeavesUpdate, FromOrchestra, OverseerSignal, PerLeafSpan, @@ -101,6 +102,11 @@ impl BitfieldGossipMessage { self.relay_parent, self.signed_availability.into(), )), + Some(ValidationVersion::VStaging) => + Versioned::VStaging(protocol_vstaging::BitfieldDistributionMessage::Bitfield( + self.relay_parent, + self.signed_availability.into(), + )), None => { never!("Peers should only have supported protocol versions."); @@ -131,9 +137,9 @@ pub struct PeerData { /// Data used to track information of peers and relay parents the /// overseer ordered us to work on. -#[derive(Default, Debug)] +#[derive(Default)] struct ProtocolState { - /// Track all active peers and their views + /// Track all active peer views and protocol versions /// to determine what is relevant to them. peer_data: HashMap, @@ -492,17 +498,13 @@ async fn relay_message( } else { let _span = span.child("gossip"); - let filter_by_version = |peers: &[(PeerId, ProtocolVersion)], - version: ValidationVersion| { - peers - .iter() - .filter(|(_, v)| v == &version.into()) - .map(|(peer_id, _)| *peer_id) - .collect::>() - }; + let v1_interested_peers = + filter_by_peer_version(&interested_peers, ValidationVersion::V1.into()); + let v2_interested_peers = + filter_by_peer_version(&interested_peers, ValidationVersion::V2.into()); - let v1_interested_peers = filter_by_version(&interested_peers, ValidationVersion::V1); - let v2_interested_peers = filter_by_version(&interested_peers, ValidationVersion::V2); + let vstaging_interested_peers = + filter_by_peer_version(&interested_peers, ValidationVersion::VStaging.into()); if !v1_interested_peers.is_empty() { ctx.send_message(NetworkBridgeTxMessage::SendValidationMessage( @@ -515,7 +517,15 @@ async fn relay_message( if !v2_interested_peers.is_empty() { ctx.send_message(NetworkBridgeTxMessage::SendValidationMessage( v2_interested_peers, - message.into_validation_protocol(ValidationVersion::V2.into()), + message.clone().into_validation_protocol(ValidationVersion::V2.into()), + )) + .await + } + + if !vstaging_interested_peers.is_empty() { + ctx.send_message(NetworkBridgeTxMessage::SendValidationMessage( + vstaging_interested_peers, + message.into_validation_protocol(ValidationVersion::VStaging.into()), )) .await } @@ -540,6 +550,10 @@ async fn process_incoming_peer_message( Versioned::V2(protocol_v2::BitfieldDistributionMessage::Bitfield( relay_parent, bitfield, + )) | + Versioned::VStaging(protocol_vstaging::BitfieldDistributionMessage::Bitfield( + relay_parent, + bitfield, )) => (relay_parent, bitfield), }; @@ -774,9 +788,11 @@ async fn handle_network_msg( handle_peer_view_change(ctx, state, new_peer, old_view, rng).await; } }, - NetworkBridgeEvent::PeerViewChange(peerid, new_view) => { - gum::trace!(target: LOG_TARGET, ?peerid, ?new_view, "Peer view change"); - handle_peer_view_change(ctx, state, peerid, new_view, rng).await; + NetworkBridgeEvent::PeerViewChange(peer_id, new_view) => { + gum::trace!(target: LOG_TARGET, ?peer_id, ?new_view, "Peer view change"); + if state.peer_data.get(&peer_id).is_some() { + handle_peer_view_change(ctx, state, peer_id, new_view, rng).await; + } }, NetworkBridgeEvent::OurViewChange(new_view) => { gum::trace!(target: LOG_TARGET, ?new_view, "Our view change"); diff --git a/polkadot/node/network/bridge/src/network.rs b/polkadot/node/network/bridge/src/network.rs index 823e1254612f..c264c94cc19b 100644 --- a/polkadot/node/network/bridge/src/network.rs +++ b/polkadot/node/network/bridge/src/network.rs @@ -28,23 +28,129 @@ use sc_network::{ }; use polkadot_node_network_protocol::{ - peer_set::{PeerSet, PeerSetProtocolNames, ProtocolVersion}, + peer_set::{ + CollationVersion, PeerSet, PeerSetProtocolNames, ProtocolVersion, ValidationVersion, + }, request_response::{OutgoingRequest, Recipient, ReqProtocolNames, Requests}, - PeerId, + v1 as protocol_v1, v2 as protocol_v2, vstaging as protocol_vstaging, PeerId, }; use polkadot_primitives::{AuthorityDiscoveryId, Block, Hash}; -use crate::validator_discovery::AuthorityDiscovery; +use crate::{metrics::Metrics, validator_discovery::AuthorityDiscovery, WireMessage}; // network bridge network abstraction log target const LOG_TARGET: &'static str = "parachain::network-bridge-net"; -/// Send a message to the network. +// Helper function to send a validation v1 message to a list of peers. +// Messages are always sent via the main protocol, even legacy protocol messages. +pub(crate) fn send_validation_message_v1( + net: &mut impl Network, + peers: Vec, + peerset_protocol_names: &PeerSetProtocolNames, + message: WireMessage, + metrics: &Metrics, +) { + gum::trace!(target: LOG_TARGET, ?peers, ?message, "Sending validation v1 message to peers",); + + send_message( + net, + peers, + PeerSet::Validation, + ValidationVersion::V1.into(), + peerset_protocol_names, + message, + metrics, + ); +} + +// Helper function to send a validation vstaging message to a list of peers. +// Messages are always sent via the main protocol, even legacy protocol messages. +pub(crate) fn send_validation_message_vstaging( + net: &mut impl Network, + peers: Vec, + peerset_protocol_names: &PeerSetProtocolNames, + message: WireMessage, + metrics: &Metrics, +) { + gum::trace!(target: LOG_TARGET, ?peers, ?message, "Sending validation vstaging message to peers",); + + send_message( + net, + peers, + PeerSet::Validation, + ValidationVersion::VStaging.into(), + peerset_protocol_names, + message, + metrics, + ); +} + +// Helper function to send a validation v2 message to a list of peers. +// Messages are always sent via the main protocol, even legacy protocol messages. +pub(crate) fn send_validation_message_v2( + net: &mut impl Network, + peers: Vec, + protocol_names: &PeerSetProtocolNames, + message: WireMessage, + metrics: &Metrics, +) { + send_message( + net, + peers, + PeerSet::Validation, + ValidationVersion::V2.into(), + protocol_names, + message, + metrics, + ); +} + +// Helper function to send a collation v1 message to a list of peers. +// Messages are always sent via the main protocol, even legacy protocol messages. +pub(crate) fn send_collation_message_v1( + net: &mut impl Network, + peers: Vec, + peerset_protocol_names: &PeerSetProtocolNames, + message: WireMessage, + metrics: &Metrics, +) { + send_message( + net, + peers, + PeerSet::Collation, + CollationVersion::V1.into(), + peerset_protocol_names, + message, + metrics, + ); +} + +// Helper function to send a collation v2 message to a list of peers. +// Messages are always sent via the main protocol, even legacy protocol messages. +pub(crate) fn send_collation_message_v2( + net: &mut impl Network, + peers: Vec, + peerset_protocol_names: &PeerSetProtocolNames, + message: WireMessage, + metrics: &Metrics, +) { + send_message( + net, + peers, + PeerSet::Collation, + CollationVersion::V2.into(), + peerset_protocol_names, + message, + metrics, + ); +} + +/// Lower level function that sends a message to the network using the main protocol version. /// /// This function is only used internally by the network-bridge, which is responsible to only send /// messages that are compatible with the passed peer set, as that is currently not enforced by /// this function. These are messages of type `WireMessage` parameterized on the matching type. -pub(crate) fn send_message( +fn send_message( net: &mut impl Network, mut peers: Vec, peer_set: PeerSet, @@ -65,6 +171,17 @@ pub(crate) fn send_message( encoded }; + // optimization: generate the protocol name once. + let protocol_name = protocol_names.get_name(peer_set, version); + gum::trace!( + target: LOG_TARGET, + ?peers, + ?version, + ?protocol_name, + ?message, + "Sending message to peers", + ); + // optimization: avoid cloning the message for the last peer in the // list. The message payload can be quite large. If the underlying // network used `Bytes` this would not be necessary. diff --git a/polkadot/node/network/bridge/src/rx/mod.rs b/polkadot/node/network/bridge/src/rx/mod.rs index f64410c1353d..06be57ead006 100644 --- a/polkadot/node/network/bridge/src/rx/mod.rs +++ b/polkadot/node/network/bridge/src/rx/mod.rs @@ -21,6 +21,7 @@ use super::*; use always_assert::never; use bytes::Bytes; use futures::stream::{BoxStream, StreamExt}; +use net_protocol::filter_by_peer_version; use parity_scale_codec::{Decode, DecodeAll}; use sc_network::Event as NetworkEvent; @@ -33,8 +34,8 @@ use polkadot_node_network_protocol::{ CollationVersion, PeerSet, PeerSetProtocolNames, PerPeerSet, ProtocolVersion, ValidationVersion, }, - v1 as protocol_v1, v2 as protocol_v2, ObservedRole, OurView, PeerId, - UnifiedReputationChange as Rep, View, + v1 as protocol_v1, v2 as protocol_v2, vstaging as protocol_vstaging, ObservedRole, OurView, + PeerId, UnifiedReputationChange as Rep, View, }; use polkadot_node_subsystem::{ @@ -64,9 +65,11 @@ use super::validator_discovery; /// Actual interfacing to the network based on the `Network` trait. /// /// Defines the `Network` trait with an implementation for an `Arc`. -use crate::network::{send_message, Network}; - -use crate::network::get_peer_id_by_authority_id; +use crate::network::{ + send_collation_message_v1, send_collation_message_v2, send_validation_message_v1, + send_validation_message_v2, send_validation_message_vstaging, Network, +}; +use crate::{network::get_peer_id_by_authority_id, WireMessage}; use super::metrics::Metrics; @@ -251,22 +254,27 @@ where match ValidationVersion::try_from(version) .expect("try_get_protocol has already checked version is known; qed") { - ValidationVersion::V1 => send_message( + ValidationVersion::V1 => send_validation_message_v1( &mut network_service, vec![peer], - PeerSet::Validation, - version, &peerset_protocol_names, WireMessage::::ViewUpdate( local_view, ), &metrics, ), - ValidationVersion::V2 => send_message( + ValidationVersion::VStaging => send_validation_message_vstaging( + &mut network_service, + vec![peer], + &peerset_protocol_names, + WireMessage::::ViewUpdate( + local_view, + ), + &metrics, + ), + ValidationVersion::V2 => send_validation_message_v2( &mut network_service, vec![peer], - PeerSet::Validation, - version, &peerset_protocol_names, WireMessage::::ViewUpdate( local_view, @@ -293,22 +301,18 @@ where match CollationVersion::try_from(version) .expect("try_get_protocol has already checked version is known; qed") { - CollationVersion::V1 => send_message( + CollationVersion::V1 => send_collation_message_v1( &mut network_service, vec![peer], - PeerSet::Collation, - version, &peerset_protocol_names, WireMessage::::ViewUpdate( local_view, ), &metrics, ), - CollationVersion::V2 => send_message( + CollationVersion::V2 => send_collation_message_v2( &mut network_service, vec![peer], - PeerSet::Collation, - version, &peerset_protocol_names, WireMessage::::ViewUpdate( local_view, @@ -386,8 +390,16 @@ where .filter_map(|(protocol, msg_bytes)| { // version doesn't matter because we always receive on the 'correct' // protocol name, not the negotiated fallback. - let (peer_set, _version) = + let (peer_set, version) = peerset_protocol_names.try_get_protocol(protocol)?; + gum::trace!( + target: LOG_TARGET, + ?peer_set, + ?protocol, + ?version, + "Received notification" + ); + if peer_set == PeerSet::Validation { if expected_versions[PeerSet::Validation].is_none() { return Some(Err(UNCONNECTED_PEERSET_COST)) @@ -474,6 +486,16 @@ where v_messages, &metrics, ) + } else if expected_versions[PeerSet::Validation] == + Some(ValidationVersion::VStaging.into()) + { + handle_peer_messages::( + remote, + PeerSet::Validation, + &mut shared.0.lock().validation_peers, + v_messages, + &metrics, + ) } else { gum::warn!( target: LOG_TARGET, @@ -815,15 +837,16 @@ fn update_our_view( ) }; - let filter_by_version = |peers: &[(PeerId, ProtocolVersion)], version| { - peers.iter().filter(|(_, v)| v == &version).map(|(p, _)| *p).collect::>() - }; + let v1_validation_peers = + filter_by_peer_version(&validation_peers, ValidationVersion::V1.into()); + let v1_collation_peers = filter_by_peer_version(&collation_peers, CollationVersion::V1.into()); - let v1_validation_peers = filter_by_version(&validation_peers, ValidationVersion::V1.into()); - let v1_collation_peers = filter_by_version(&collation_peers, CollationVersion::V1.into()); + let v2_validation_peers = + filter_by_peer_version(&validation_peers, ValidationVersion::V2.into()); + let v2_collation_peers = filter_by_peer_version(&collation_peers, CollationVersion::V2.into()); - let v2_validation_peers = filter_by_version(&validation_peers, ValidationVersion::V2.into()); - let v2_collation_peers = filter_by_version(&collation_peers, ValidationVersion::V2.into()); + let vstaging_validation_peers = + filter_by_peer_version(&validation_peers, ValidationVersion::VStaging.into()); send_validation_message_v1( net, @@ -853,7 +876,15 @@ fn update_our_view( net, v2_collation_peers, peerset_protocol_names, - WireMessage::ViewUpdate(new_view), + WireMessage::ViewUpdate(new_view.clone()), + metrics, + ); + + send_validation_message_vstaging( + net, + vstaging_validation_peers, + peerset_protocol_names, + WireMessage::ViewUpdate(new_view.clone()), metrics, ); @@ -926,78 +957,6 @@ fn handle_peer_messages>( (outgoing_events, reports) } -fn send_validation_message_v1( - net: &mut impl Network, - peers: Vec, - peerset_protocol_names: &PeerSetProtocolNames, - message: WireMessage, - metrics: &Metrics, -) { - send_message( - net, - peers, - PeerSet::Validation, - ValidationVersion::V1.into(), - peerset_protocol_names, - message, - metrics, - ); -} - -fn send_collation_message_v1( - net: &mut impl Network, - peers: Vec, - peerset_protocol_names: &PeerSetProtocolNames, - message: WireMessage, - metrics: &Metrics, -) { - send_message( - net, - peers, - PeerSet::Collation, - CollationVersion::V1.into(), - peerset_protocol_names, - message, - metrics, - ); -} - -fn send_validation_message_v2( - net: &mut impl Network, - peers: Vec, - protocol_names: &PeerSetProtocolNames, - message: WireMessage, - metrics: &Metrics, -) { - send_message( - net, - peers, - PeerSet::Validation, - ValidationVersion::V2.into(), - protocol_names, - message, - metrics, - ); -} - -fn send_collation_message_v2( - net: &mut impl Network, - peers: Vec, - protocol_names: &PeerSetProtocolNames, - message: WireMessage, - metrics: &Metrics, -) { - send_message( - net, - peers, - PeerSet::Collation, - CollationVersion::V2.into(), - protocol_names, - message, - metrics, - ); -} - async fn dispatch_validation_event_to_all( event: NetworkBridgeEvent, ctx: &mut impl overseer::NetworkBridgeRxSenderTrait, diff --git a/polkadot/node/network/bridge/src/rx/tests.rs b/polkadot/node/network/bridge/src/rx/tests.rs index 7c69cce48391..f784e78a7f21 100644 --- a/polkadot/node/network/bridge/src/rx/tests.rs +++ b/polkadot/node/network/bridge/src/rx/tests.rs @@ -1248,6 +1248,9 @@ fn network_protocol_versioning_view_update() { ValidationVersion::V2 => WireMessage::::ViewUpdate(view.clone()) .encode(), + ValidationVersion::VStaging => + WireMessage::::ViewUpdate(view.clone()) + .encode(), }; assert_network_actions_contains( &actions, diff --git a/polkadot/node/network/bridge/src/tx/mod.rs b/polkadot/node/network/bridge/src/tx/mod.rs index f15635f1f41c..5f222ad59c75 100644 --- a/polkadot/node/network/bridge/src/tx/mod.rs +++ b/polkadot/node/network/bridge/src/tx/mod.rs @@ -18,9 +18,7 @@ use super::*; use polkadot_node_network_protocol::{ - peer_set::{CollationVersion, PeerSet, PeerSetProtocolNames, ValidationVersion}, - request_response::ReqProtocolNames, - v1 as protocol_v1, v2 as protocol_v2, PeerId, Versioned, + peer_set::PeerSetProtocolNames, request_response::ReqProtocolNames, Versioned, }; use polkadot_node_subsystem::{ @@ -41,7 +39,10 @@ use crate::validator_discovery; /// Actual interfacing to the network based on the `Network` trait. /// /// Defines the `Network` trait with an implementation for an `Arc`. -use crate::network::{send_message, Network}; +use crate::network::{ + send_collation_message_v1, send_collation_message_v2, send_validation_message_v1, + send_validation_message_v2, send_validation_message_vstaging, Network, +}; use crate::metrics::Metrics; @@ -187,6 +188,7 @@ where gum::trace!( target: LOG_TARGET, action = "SendValidationMessages", + ?msg, num_messages = 1usize, ); @@ -198,6 +200,13 @@ where WireMessage::ProtocolMessage(msg), &metrics, ), + Versioned::VStaging(msg) => send_validation_message_vstaging( + &mut network_service, + peers, + peerset_protocol_names, + WireMessage::ProtocolMessage(msg), + &metrics, + ), Versioned::V2(msg) => send_validation_message_v2( &mut network_service, peers, @@ -212,6 +221,7 @@ where target: LOG_TARGET, action = "SendValidationMessages", num_messages = %msgs.len(), + ?msgs, ); for (peers, msg) in msgs { @@ -223,6 +233,13 @@ where WireMessage::ProtocolMessage(msg), &metrics, ), + Versioned::VStaging(msg) => send_validation_message_vstaging( + &mut network_service, + peers, + peerset_protocol_names, + WireMessage::ProtocolMessage(msg), + &metrics, + ), Versioned::V2(msg) => send_validation_message_v2( &mut network_service, peers, @@ -248,7 +265,7 @@ where WireMessage::ProtocolMessage(msg), &metrics, ), - Versioned::V2(msg) => send_collation_message_v2( + Versioned::V2(msg) | Versioned::VStaging(msg) => send_collation_message_v2( &mut network_service, peers, peerset_protocol_names, @@ -273,7 +290,7 @@ where WireMessage::ProtocolMessage(msg), &metrics, ), - Versioned::V2(msg) => send_collation_message_v2( + Versioned::V2(msg) | Versioned::VStaging(msg) => send_collation_message_v2( &mut network_service, peers, peerset_protocol_names, @@ -386,75 +403,3 @@ where Ok(()) } - -fn send_validation_message_v1( - net: &mut impl Network, - peers: Vec, - protocol_names: &PeerSetProtocolNames, - message: WireMessage, - metrics: &Metrics, -) { - send_message( - net, - peers, - PeerSet::Validation, - ValidationVersion::V1.into(), - protocol_names, - message, - metrics, - ); -} - -fn send_collation_message_v1( - net: &mut impl Network, - peers: Vec, - protocol_names: &PeerSetProtocolNames, - message: WireMessage, - metrics: &Metrics, -) { - send_message( - net, - peers, - PeerSet::Collation, - CollationVersion::V1.into(), - protocol_names, - message, - metrics, - ); -} - -fn send_validation_message_v2( - net: &mut impl Network, - peers: Vec, - protocol_names: &PeerSetProtocolNames, - message: WireMessage, - metrics: &Metrics, -) { - send_message( - net, - peers, - PeerSet::Validation, - ValidationVersion::V2.into(), - protocol_names, - message, - metrics, - ); -} - -fn send_collation_message_v2( - net: &mut impl Network, - peers: Vec, - protocol_names: &PeerSetProtocolNames, - message: WireMessage, - metrics: &Metrics, -) { - send_message( - net, - peers, - PeerSet::Collation, - CollationVersion::V2.into(), - protocol_names, - message, - metrics, - ); -} diff --git a/polkadot/node/network/bridge/src/tx/tests.rs b/polkadot/node/network/bridge/src/tx/tests.rs index 48287f8b74c9..1a2d9a7a4240 100644 --- a/polkadot/node/network/bridge/src/tx/tests.rs +++ b/polkadot/node/network/bridge/src/tx/tests.rs @@ -25,9 +25,9 @@ use std::collections::HashSet; use sc_network::{Event as NetworkEvent, IfDisconnected, ProtocolName, ReputationChange}; use polkadot_node_network_protocol::{ - peer_set::PeerSetProtocolNames, + peer_set::{PeerSetProtocolNames, ValidationVersion}, request_response::{outgoing::Requests, ReqProtocolNames}, - ObservedRole, Versioned, + v1 as protocol_v1, v2 as protocol_v2, ObservedRole, Versioned, }; use polkadot_node_subsystem::{FromOrchestra, OverseerSignal}; use polkadot_node_subsystem_test_helpers::TestSubsystemContextHandle; @@ -356,7 +356,6 @@ fn network_protocol_versioning_send() { } // send a validation protocol message. - { let approval_distribution_message = protocol_v2::ApprovalDistributionMessage::Approvals(Vec::new()); diff --git a/polkadot/node/network/collator-protocol/src/collator_side/mod.rs b/polkadot/node/network/collator-protocol/src/collator_side/mod.rs index e4b95f24d5a3..b3a396e1be34 100644 --- a/polkadot/node/network/collator-protocol/src/collator_side/mod.rs +++ b/polkadot/node/network/collator-protocol/src/collator_side/mod.rs @@ -880,7 +880,9 @@ async fn handle_incoming_peer_message( use protocol_v2::CollatorProtocolMessage as V2; match msg { - Versioned::V1(V1::Declare(..)) | Versioned::V2(V2::Declare(..)) => { + Versioned::V1(V1::Declare(..)) | + Versioned::V2(V2::Declare(..)) | + Versioned::VStaging(V2::Declare(..)) => { gum::trace!( target: LOG_TARGET, ?origin, @@ -891,7 +893,9 @@ async fn handle_incoming_peer_message( ctx.send_message(NetworkBridgeTxMessage::DisconnectPeer(origin, PeerSet::Collation)) .await; }, - Versioned::V1(V1::AdvertiseCollation(_)) | Versioned::V2(V2::AdvertiseCollation { .. }) => { + Versioned::V1(V1::AdvertiseCollation(_)) | + Versioned::V2(V2::AdvertiseCollation { .. }) | + Versioned::VStaging(V2::AdvertiseCollation { .. }) => { gum::trace!( target: LOG_TARGET, ?origin, @@ -906,7 +910,8 @@ async fn handle_incoming_peer_message( .await; }, Versioned::V1(V1::CollationSeconded(relay_parent, statement)) | - Versioned::V2(V2::CollationSeconded(relay_parent, statement)) => { + Versioned::V2(V2::CollationSeconded(relay_parent, statement)) | + Versioned::VStaging(V2::CollationSeconded(relay_parent, statement)) => { if !matches!(statement.unchecked_payload(), Statement::Seconded(_)) { gum::warn!( target: LOG_TARGET, diff --git a/polkadot/node/network/collator-protocol/src/validator_side/mod.rs b/polkadot/node/network/collator-protocol/src/validator_side/mod.rs index 00bf50013a53..20b3b9ea1d26 100644 --- a/polkadot/node/network/collator-protocol/src/validator_side/mod.rs +++ b/polkadot/node/network/collator-protocol/src/validator_side/mod.rs @@ -776,7 +776,8 @@ async fn process_incoming_peer_message( match msg { Versioned::V1(V1::Declare(collator_id, para_id, signature)) | - Versioned::V2(V2::Declare(collator_id, para_id, signature)) => { + Versioned::V2(V2::Declare(collator_id, para_id, signature)) | + Versioned::VStaging(V2::Declare(collator_id, para_id, signature)) => { if collator_peer_id(&state.peer_data, &collator_id).is_some() { modify_reputation( &mut state.reputation, @@ -892,6 +893,11 @@ async fn process_incoming_peer_message( relay_parent, candidate_hash, parent_head_data_hash, + }) | + Versioned::VStaging(V2::AdvertiseCollation { + relay_parent, + candidate_hash, + parent_head_data_hash, }) => if let Err(err) = handle_advertisement( ctx.sender(), @@ -915,7 +921,9 @@ async fn process_incoming_peer_message( modify_reputation(&mut state.reputation, ctx.sender(), origin, rep).await; } }, - Versioned::V1(V1::CollationSeconded(..)) | Versioned::V2(V2::CollationSeconded(..)) => { + Versioned::V1(V1::CollationSeconded(..)) | + Versioned::V2(V2::CollationSeconded(..)) | + Versioned::VStaging(V2::CollationSeconded(..)) => { gum::warn!( target: LOG_TARGET, peer_id = ?origin, diff --git a/polkadot/node/network/gossip-support/src/lib.rs b/polkadot/node/network/gossip-support/src/lib.rs index 2b2b2d0933e6..674c86e5ce27 100644 --- a/polkadot/node/network/gossip-support/src/lib.rs +++ b/polkadot/node/network/gossip-support/src/lib.rs @@ -477,6 +477,7 @@ where match message { Versioned::V1(m) => match m {}, Versioned::V2(m) => match m {}, + Versioned::VStaging(m) => match m {}, } }, } diff --git a/polkadot/node/network/protocol/Cargo.toml b/polkadot/node/network/protocol/Cargo.toml index 379334ded24a..c33b9eae3252 100644 --- a/polkadot/node/network/protocol/Cargo.toml +++ b/polkadot/node/network/protocol/Cargo.toml @@ -27,3 +27,6 @@ bitvec = "1" [dev-dependencies] rand_chacha = "0.3.1" + +[features] +network-protocol-staging = [] diff --git a/polkadot/node/network/protocol/src/lib.rs b/polkadot/node/network/protocol/src/lib.rs index 901ac99b6693..9aeeb98ea9d6 100644 --- a/polkadot/node/network/protocol/src/lib.rs +++ b/polkadot/node/network/protocol/src/lib.rs @@ -253,25 +253,29 @@ impl View { /// A protocol-versioned type. #[derive(Debug, Clone, PartialEq, Eq)] -pub enum Versioned { +pub enum Versioned { /// V1 type. V1(V1), /// V2 type. V2(V2), + /// VStaging type + VStaging(VStaging), } -impl Versioned<&'_ V1, &'_ V2> { +impl Versioned<&'_ V1, &'_ V2, &'_ VStaging> { /// Convert to a fully-owned version of the message. - pub fn clone_inner(&self) -> Versioned { + pub fn clone_inner(&self) -> Versioned { match *self { Versioned::V1(inner) => Versioned::V1(inner.clone()), Versioned::V2(inner) => Versioned::V2(inner.clone()), + Versioned::VStaging(inner) => Versioned::VStaging(inner.clone()), } } } /// All supported versions of the validation protocol message. -pub type VersionedValidationProtocol = Versioned; +pub type VersionedValidationProtocol = + Versioned; impl From for VersionedValidationProtocol { fn from(v1: v1::ValidationProtocol) -> Self { @@ -285,6 +289,12 @@ impl From for VersionedValidationProtocol { } } +impl From for VersionedValidationProtocol { + fn from(vstaging: vstaging::ValidationProtocol) -> Self { + VersionedValidationProtocol::VStaging(vstaging) + } +} + /// All supported versions of the collation protocol message. pub type VersionedCollationProtocol = Versioned; @@ -307,12 +317,12 @@ macro_rules! impl_versioned_full_protocol_from { match versioned_from { Versioned::V1(x) => Versioned::V1(x.into()), Versioned::V2(x) => Versioned::V2(x.into()), + Versioned::VStaging(x) => Versioned::VStaging(x.into()), } } } }; } - /// Implement `TryFrom` for one versioned enum variant into the inner type. /// `$m_ty::$variant(inner) -> Ok(inner)` macro_rules! impl_versioned_try_from { @@ -320,7 +330,8 @@ macro_rules! impl_versioned_try_from { $from:ty, $out:ty, $v1_pat:pat => $v1_out:expr, - $v2_pat:pat => $v2_out:expr + $v2_pat:pat => $v2_out:expr, + $vstaging_pat:pat => $vstaging_out:expr ) => { impl TryFrom<$from> for $out { type Error = crate::WrongVariant; @@ -330,6 +341,7 @@ macro_rules! impl_versioned_try_from { match x { Versioned::V1($v1_pat) => Ok(Versioned::V1($v1_out)), Versioned::V2($v2_pat) => Ok(Versioned::V2($v2_out)), + Versioned::VStaging($vstaging_pat) => Ok(Versioned::VStaging($vstaging_out)), _ => Err(crate::WrongVariant), } } @@ -343,6 +355,8 @@ macro_rules! impl_versioned_try_from { match x { Versioned::V1($v1_pat) => Ok(Versioned::V1($v1_out.clone())), Versioned::V2($v2_pat) => Ok(Versioned::V2($v2_out.clone())), + Versioned::VStaging($vstaging_pat) => + Ok(Versioned::VStaging($vstaging_out.clone())), _ => Err(crate::WrongVariant), } } @@ -351,8 +365,11 @@ macro_rules! impl_versioned_try_from { } /// Version-annotated messages used by the bitfield distribution subsystem. -pub type BitfieldDistributionMessage = - Versioned; +pub type BitfieldDistributionMessage = Versioned< + v1::BitfieldDistributionMessage, + v2::BitfieldDistributionMessage, + vstaging::BitfieldDistributionMessage, +>; impl_versioned_full_protocol_from!( BitfieldDistributionMessage, VersionedValidationProtocol, @@ -362,12 +379,16 @@ impl_versioned_try_from!( VersionedValidationProtocol, BitfieldDistributionMessage, v1::ValidationProtocol::BitfieldDistribution(x) => x, - v2::ValidationProtocol::BitfieldDistribution(x) => x + v2::ValidationProtocol::BitfieldDistribution(x) => x, + vstaging::ValidationProtocol::BitfieldDistribution(x) => x ); /// Version-annotated messages used by the statement distribution subsystem. -pub type StatementDistributionMessage = - Versioned; +pub type StatementDistributionMessage = Versioned< + v1::StatementDistributionMessage, + v2::StatementDistributionMessage, + vstaging::StatementDistributionMessage, +>; impl_versioned_full_protocol_from!( StatementDistributionMessage, VersionedValidationProtocol, @@ -377,12 +398,16 @@ impl_versioned_try_from!( VersionedValidationProtocol, StatementDistributionMessage, v1::ValidationProtocol::StatementDistribution(x) => x, - v2::ValidationProtocol::StatementDistribution(x) => x + v2::ValidationProtocol::StatementDistribution(x) => x, + vstaging::ValidationProtocol::StatementDistribution(x) => x ); /// Version-annotated messages used by the approval distribution subsystem. -pub type ApprovalDistributionMessage = - Versioned; +pub type ApprovalDistributionMessage = Versioned< + v1::ApprovalDistributionMessage, + v2::ApprovalDistributionMessage, + vstaging::ApprovalDistributionMessage, +>; impl_versioned_full_protocol_from!( ApprovalDistributionMessage, VersionedValidationProtocol, @@ -392,13 +417,18 @@ impl_versioned_try_from!( VersionedValidationProtocol, ApprovalDistributionMessage, v1::ValidationProtocol::ApprovalDistribution(x) => x, - v2::ValidationProtocol::ApprovalDistribution(x) => x + v2::ValidationProtocol::ApprovalDistribution(x) => x, + vstaging::ValidationProtocol::ApprovalDistribution(x) => x ); /// Version-annotated messages used by the gossip-support subsystem (this is void). -pub type GossipSupportNetworkMessage = - Versioned; +pub type GossipSupportNetworkMessage = Versioned< + v1::GossipSupportNetworkMessage, + v2::GossipSupportNetworkMessage, + vstaging::GossipSupportNetworkMessage, +>; + // This is a void enum placeholder, so never gets sent over the wire. impl TryFrom for GossipSupportNetworkMessage { type Error = WrongVariant; @@ -426,6 +456,7 @@ impl_versioned_try_from!( VersionedCollationProtocol, CollatorProtocolMessage, v1::CollationProtocol::CollatorProtocol(x) => x, + v2::CollationProtocol::CollatorProtocol(x) => x, v2::CollationProtocol::CollatorProtocol(x) => x ); @@ -439,7 +470,7 @@ pub mod v1 { }; use polkadot_node_primitives::{ - approval::{IndirectAssignmentCert, IndirectSignedApprovalVote}, + approval::v1::{IndirectAssignmentCert, IndirectSignedApprovalVote}, UncheckedSignedFullStatement, }; @@ -598,7 +629,7 @@ pub mod v2 { }; use polkadot_node_primitives::{ - approval::{IndirectAssignmentCert, IndirectSignedApprovalVote}, + approval::v1::{IndirectAssignmentCert, IndirectSignedApprovalVote}, UncheckedSignedFullStatement, }; @@ -839,3 +870,64 @@ pub mod v2 { payload } } + +/// vstaging network protocol types, intended to become v3. +/// Initial purpose is for chaning ApprovalDistributionMessage to +/// include more than one assignment in the message. +pub mod vstaging { + use parity_scale_codec::{Decode, Encode}; + + use polkadot_node_primitives::approval::{ + v1::IndirectSignedApprovalVote, + v2::{CandidateBitfield, IndirectAssignmentCertV2}, + }; + + /// This parts of the protocol did not change from v2, so just alias them in vstaging, + /// no reason why they can't be change untill vstaging becomes v3 and is released. + pub use super::v2::{ + declare_signature_payload, BackedCandidateAcknowledgement, BackedCandidateManifest, + BitfieldDistributionMessage, GossipSupportNetworkMessage, StatementDistributionMessage, + StatementFilter, + }; + + /// Network messages used by the approval distribution subsystem. + #[derive(Debug, Clone, Encode, Decode, PartialEq, Eq)] + pub enum ApprovalDistributionMessage { + /// Assignments for candidates in recent, unfinalized blocks. + /// We use a bitfield to reference claimed candidates, where the bit index is equal to + /// candidate index. + /// + /// Actually checking the assignment may yield a different result. + /// TODO: Look at getting rid of bitfield in the future. + #[codec(index = 0)] + Assignments(Vec<(IndirectAssignmentCertV2, CandidateBitfield)>), + /// Approvals for candidates in some recent, unfinalized block. + #[codec(index = 1)] + Approvals(Vec), + } + + /// All network messages on the validation peer-set. + #[derive(Debug, Clone, Encode, Decode, PartialEq, Eq, derive_more::From)] + pub enum ValidationProtocol { + /// Bitfield distribution messages + #[codec(index = 1)] + #[from] + BitfieldDistribution(BitfieldDistributionMessage), + /// Statement distribution messages + #[codec(index = 3)] + #[from] + StatementDistribution(StatementDistributionMessage), + /// Approval distribution messages + #[codec(index = 4)] + #[from] + ApprovalDistribution(ApprovalDistributionMessage), + } +} + +/// Returns the subset of `peers` with the specified `version`. +pub fn filter_by_peer_version( + peers: &[(PeerId, peer_set::ProtocolVersion)], + version: peer_set::ProtocolVersion, +) -> Vec { + peers.iter().filter(|(_, v)| v == &version).map(|(p, _)| *p).collect::>() +} diff --git a/polkadot/node/network/protocol/src/peer_set.rs b/polkadot/node/network/protocol/src/peer_set.rs index 8dd68b297e30..eb483dec9709 100644 --- a/polkadot/node/network/protocol/src/peer_set.rs +++ b/polkadot/node/network/protocol/src/peer_set.rs @@ -118,10 +118,17 @@ impl PeerSet { /// Networking layer relies on `get_main_version()` being the version /// of the main protocol name reported by [`PeerSetProtocolNames::get_main_name()`]. pub fn get_main_version(self) -> ProtocolVersion { + #[cfg(not(feature = "network-protocol-staging"))] match self { PeerSet::Validation => ValidationVersion::V2.into(), PeerSet::Collation => CollationVersion::V2.into(), } + + #[cfg(feature = "network-protocol-staging")] + match self { + PeerSet::Validation => ValidationVersion::VStaging.into(), + PeerSet::Collation => CollationVersion::V2.into(), + } } /// Get the max notification size for this peer set. @@ -147,6 +154,8 @@ impl PeerSet { Some("validation/1") } else if version == ValidationVersion::V2.into() { Some("validation/2") + } else if version == ValidationVersion::VStaging.into() { + Some("validation/3") } else { None }, @@ -218,6 +227,9 @@ pub enum ValidationVersion { V1 = 1, /// The second version. V2 = 2, + /// The staging version to gather changes + /// that before the release become v3. + VStaging = 3, } /// Supported collation protocol versions. Only versions defined here must be used in the codebase. diff --git a/polkadot/node/network/statement-distribution/src/legacy_v1/mod.rs b/polkadot/node/network/statement-distribution/src/legacy_v1/mod.rs index fc2aff0da305..d9866af1ee23 100644 --- a/polkadot/node/network/statement-distribution/src/legacy_v1/mod.rs +++ b/polkadot/node/network/statement-distribution/src/legacy_v1/mod.rs @@ -14,6 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . +use net_protocol::{filter_by_peer_version, peer_set::ProtocolVersion}; use parity_scale_codec::Encode; use polkadot_node_network_protocol::{ @@ -21,7 +22,8 @@ use polkadot_node_network_protocol::{ grid_topology::{GridNeighbors, RequiredRouting, SessionBoundGridTopologyStorage}, peer_set::{IsAuthority, PeerSet, ValidationVersion}, v1::{self as protocol_v1, StatementMetadata}, - v2 as protocol_v2, IfDisconnected, PeerId, UnifiedReputationChange as Rep, Versioned, View, + v2 as protocol_v2, vstaging as protocol_vstaging, IfDisconnected, PeerId, + UnifiedReputationChange as Rep, Versioned, View, }; use polkadot_node_primitives::{ SignedFullStatement, Statement, StatementWithPVD, UncheckedSignedFullStatement, @@ -1061,7 +1063,7 @@ async fn circulate_statement<'a, Context>( "We filter out duplicates above. qed.", ); - let (v1_peers_to_send, v2_peers_to_send) = peers_to_send + let (v1_peers_to_send, non_v1_peers_to_send) = peers_to_send .into_iter() .map(|peer_id| { let peer_data = @@ -1073,7 +1075,7 @@ async fn circulate_statement<'a, Context>( }) .partition::, _>(|(_, _, version)| match version { ValidationVersion::V1 => true, - ValidationVersion::V2 => false, + ValidationVersion::V2 | ValidationVersion::VStaging => false, }); // partition is handy here but not if we add more protocol versions let payload = v1_statement_message(relay_parent, stored.statement.clone(), metrics); @@ -1093,6 +1095,22 @@ async fn circulate_statement<'a, Context>( )) .await; } + + let peers_to_send: Vec<(PeerId, ProtocolVersion)> = non_v1_peers_to_send + .iter() + .map(|(p, _, version)| (*p, (*version).into())) + .collect(); + + let peer_needs_dependent_statement = v1_peers_to_send + .into_iter() + .chain(non_v1_peers_to_send) + .filter_map(|(peer, needs_dependent, _)| if needs_dependent { Some(peer) } else { None }) + .collect(); + + let v2_peers_to_send = filter_by_peer_version(&peers_to_send, ValidationVersion::V2.into()); + let vstaging_to_send = + filter_by_peer_version(&peers_to_send, ValidationVersion::VStaging.into()); + if !v2_peers_to_send.is_empty() { gum::trace!( target: LOG_TARGET, @@ -1102,17 +1120,28 @@ async fn circulate_statement<'a, Context>( "Sending statement to v2 peers", ); ctx.send_message(NetworkBridgeTxMessage::SendValidationMessage( - v2_peers_to_send.iter().map(|(p, _, _)| *p).collect(), + v2_peers_to_send, compatible_v1_message(ValidationVersion::V2, payload.clone()).into(), )) .await; } - v1_peers_to_send - .into_iter() - .chain(v2_peers_to_send) - .filter_map(|(peer, needs_dependent, _)| if needs_dependent { Some(peer) } else { None }) - .collect() + if !vstaging_to_send.is_empty() { + gum::trace!( + target: LOG_TARGET, + ?vstaging_to_send, + ?relay_parent, + statement = ?stored.statement, + "Sending statement to vstaging peers", + ); + ctx.send_message(NetworkBridgeTxMessage::SendValidationMessage( + vstaging_to_send, + compatible_v1_message(ValidationVersion::VStaging, payload.clone()).into(), + )) + .await; + } + + peer_needs_dependent_statement } /// Send all statements about a given candidate hash to a peer. @@ -1442,8 +1471,11 @@ async fn handle_incoming_message<'a, Context>( let message = match message { Versioned::V1(m) => m, - Versioned::V2(protocol_v2::StatementDistributionMessage::V1Compatibility(m)) => m, - Versioned::V2(_) => { + Versioned::V2(protocol_v2::StatementDistributionMessage::V1Compatibility(m)) | + Versioned::VStaging(protocol_vstaging::StatementDistributionMessage::V1Compatibility( + m, + )) => m, + Versioned::V2(_) | Versioned::VStaging(_) => { // The higher-level subsystem code is supposed to filter out // all non v1 messages. gum::debug!( @@ -2169,5 +2201,8 @@ fn compatible_v1_message( ValidationVersion::V1 => Versioned::V1(message), ValidationVersion::V2 => Versioned::V2(protocol_v2::StatementDistributionMessage::V1Compatibility(message)), + ValidationVersion::VStaging => Versioned::VStaging( + protocol_vstaging::StatementDistributionMessage::V1Compatibility(message), + ), } } diff --git a/polkadot/node/network/statement-distribution/src/lib.rs b/polkadot/node/network/statement-distribution/src/lib.rs index 259c8f6a3601..0a80c1491a90 100644 --- a/polkadot/node/network/statement-distribution/src/lib.rs +++ b/polkadot/node/network/statement-distribution/src/lib.rs @@ -27,7 +27,7 @@ use std::time::Duration; use polkadot_node_network_protocol::{ request_response::{v1 as request_v1, v2::AttestedCandidateRequest, IncomingRequestReceiver}, - v2 as protocol_v2, Versioned, + v2 as protocol_v2, vstaging as protocol_vstaging, Versioned, }; use polkadot_node_primitives::StatementWithPVD; use polkadot_node_subsystem::{ @@ -399,9 +399,12 @@ impl StatementDistributionSubsystem { NetworkBridgeEvent::PeerMessage(_, message) => match message { Versioned::V2( protocol_v2::StatementDistributionMessage::V1Compatibility(_), + ) | + Versioned::VStaging( + protocol_vstaging::StatementDistributionMessage::V1Compatibility(_), ) => VersionTarget::Legacy, Versioned::V1(_) => VersionTarget::Legacy, - Versioned::V2(_) => VersionTarget::Current, + Versioned::V2(_) | Versioned::VStaging(_) => VersionTarget::Current, }, _ => VersionTarget::Both, }; diff --git a/polkadot/node/network/statement-distribution/src/v2/mod.rs b/polkadot/node/network/statement-distribution/src/v2/mod.rs index 95104732df02..6f39a5c504d0 100644 --- a/polkadot/node/network/statement-distribution/src/v2/mod.rs +++ b/polkadot/node/network/statement-distribution/src/v2/mod.rs @@ -17,6 +17,7 @@ //! Implementation of the v2 statement distribution protocol, //! designed for asynchronous backing. +use net_protocol::{filter_by_peer_version, peer_set::ProtocolVersion}; use polkadot_node_network_protocol::{ self as net_protocol, grid_topology::SessionGridTopology, @@ -28,7 +29,8 @@ use polkadot_node_network_protocol::{ MAX_PARALLEL_ATTESTED_CANDIDATE_REQUESTS, }, v2::{self as protocol_v2, StatementFilter}, - IfDisconnected, PeerId, UnifiedReputationChange as Rep, Versioned, View, + vstaging as protocol_vstaging, IfDisconnected, PeerId, UnifiedReputationChange as Rep, + Versioned, View, }; use polkadot_node_primitives::{ SignedFullStatementWithPVD, StatementWithPVD as FullStatementWithPVD, @@ -260,6 +262,7 @@ fn connected_validator_peer( struct PeerState { view: View, + protocol_version: ValidationVersion, implicit_view: HashSet, discovery_ids: Option>, } @@ -332,9 +335,13 @@ pub(crate) async fn handle_network_update( NetworkBridgeEvent::PeerConnected(peer_id, role, protocol_version, mut authority_ids) => { gum::trace!(target: LOG_TARGET, ?peer_id, ?role, ?protocol_version, "Peer connected"); - if protocol_version != ValidationVersion::V2.into() { + let versioned_protocol = if protocol_version != ValidationVersion::V2.into() && + protocol_version != ValidationVersion::VStaging.into() + { return - } + } else { + protocol_version.try_into().expect("Qed, we checked above") + }; if let Some(ref mut authority_ids) = authority_ids { authority_ids.retain(|a| match state.authorities.entry(a.clone()) { @@ -361,6 +368,7 @@ pub(crate) async fn handle_network_update( PeerState { view: View::default(), implicit_view: HashSet::new(), + protocol_version: versioned_protocol, discovery_ids: authority_ids, }, ); @@ -393,17 +401,29 @@ pub(crate) async fn handle_network_update( net_protocol::StatementDistributionMessage::V1(_) => return, net_protocol::StatementDistributionMessage::V2( protocol_v2::StatementDistributionMessage::V1Compatibility(_), + ) | + net_protocol::StatementDistributionMessage::VStaging( + protocol_vstaging::StatementDistributionMessage::V1Compatibility(_), ) => return, net_protocol::StatementDistributionMessage::V2( protocol_v2::StatementDistributionMessage::Statement(relay_parent, statement), + ) | + net_protocol::StatementDistributionMessage::VStaging( + protocol_vstaging::StatementDistributionMessage::Statement(relay_parent, statement), ) => handle_incoming_statement(ctx, state, peer_id, relay_parent, statement, reputation) .await, net_protocol::StatementDistributionMessage::V2( protocol_v2::StatementDistributionMessage::BackedCandidateManifest(inner), + ) | + net_protocol::StatementDistributionMessage::VStaging( + protocol_vstaging::StatementDistributionMessage::BackedCandidateManifest(inner), ) => handle_incoming_manifest(ctx, state, peer_id, inner, reputation).await, net_protocol::StatementDistributionMessage::V2( protocol_v2::StatementDistributionMessage::BackedCandidateKnown(inner), + ) | + net_protocol::StatementDistributionMessage::VStaging( + protocol_vstaging::StatementDistributionMessage::BackedCandidateKnown(inner), ) => handle_incoming_acknowledgement(ctx, state, peer_id, inner, reputation).await, }, NetworkBridgeEvent::PeerViewChange(peer_id, view) => @@ -709,7 +729,7 @@ async fn send_peer_messages_for_relay_parent( send_pending_cluster_statements( ctx, relay_parent, - &peer, + &(peer, peer_data.protocol_version), validator_id, &mut local_validator_state.cluster_tracker, &state.candidates, @@ -721,7 +741,7 @@ async fn send_peer_messages_for_relay_parent( send_pending_grid_messages( ctx, relay_parent, - &peer, + &(peer, peer_data.protocol_version), validator_id, &per_session_state.groups, relay_parent_state, @@ -734,15 +754,34 @@ async fn send_peer_messages_for_relay_parent( fn pending_statement_network_message( statement_store: &StatementStore, relay_parent: Hash, - peer: &PeerId, + peer: &(PeerId, ValidationVersion), originator: ValidatorIndex, compact: CompactStatement, ) -> Option<(Vec, net_protocol::VersionedValidationProtocol)> { - statement_store - .validator_statement(originator, compact) - .map(|s| s.as_unchecked().clone()) - .map(|signed| protocol_v2::StatementDistributionMessage::Statement(relay_parent, signed)) - .map(|msg| (vec![*peer], Versioned::V2(msg).into())) + match peer.1 { + ValidationVersion::V2 => statement_store + .validator_statement(originator, compact) + .map(|s| s.as_unchecked().clone()) + .map(|signed| { + protocol_v2::StatementDistributionMessage::Statement(relay_parent, signed) + }) + .map(|msg| (vec![peer.0], Versioned::V2(msg).into())), + ValidationVersion::VStaging => statement_store + .validator_statement(originator, compact) + .map(|s| s.as_unchecked().clone()) + .map(|signed| { + protocol_vstaging::StatementDistributionMessage::Statement(relay_parent, signed) + }) + .map(|msg| (vec![peer.0], Versioned::VStaging(msg).into())), + ValidationVersion::V1 => { + gum::error!( + target: LOG_TARGET, + "Bug ValidationVersion::V1 should not be used in statement-distribution v2, + legacy should have handled this" + ); + None + }, + } } /// Send a peer all pending cluster statements for a relay parent. @@ -750,7 +789,7 @@ fn pending_statement_network_message( async fn send_pending_cluster_statements( ctx: &mut Context, relay_parent: Hash, - peer_id: &PeerId, + peer_id: &(PeerId, ValidationVersion), peer_validator_id: ValidatorIndex, cluster_tracker: &mut ClusterTracker, candidates: &Candidates, @@ -794,7 +833,7 @@ async fn send_pending_cluster_statements( async fn send_pending_grid_messages( ctx: &mut Context, relay_parent: Hash, - peer_id: &PeerId, + peer_id: &(PeerId, ValidationVersion), peer_validator_id: ValidatorIndex, groups: &Groups, relay_parent_state: &mut PerRelayParentState, @@ -856,20 +895,37 @@ async fn send_pending_grid_messages( candidate_hash, local_knowledge.clone(), ); - - messages.push(( - vec![*peer_id], - Versioned::V2( - protocol_v2::StatementDistributionMessage::BackedCandidateManifest( - manifest, - ), - ) - .into(), - )); + match peer_id.1 { + ValidationVersion::V2 => messages.push(( + vec![peer_id.0], + Versioned::V2( + protocol_v2::StatementDistributionMessage::BackedCandidateManifest( + manifest, + ), + ) + .into(), + )), + ValidationVersion::VStaging => messages.push(( + vec![peer_id.0], + Versioned::VStaging( + protocol_vstaging::StatementDistributionMessage::BackedCandidateManifest( + manifest, + ), + ) + .into(), + )), + ValidationVersion::V1 => { + gum::error!( + target: LOG_TARGET, + "Bug ValidationVersion::V1 should not be used in statement-distribution v2, + legacy should have handled this" + ); + } + }; }, grid::ManifestKind::Acknowledgement => { messages.extend(acknowledgement_and_statement_messages( - *peer_id, + peer_id, peer_validator_id, groups, relay_parent_state, @@ -1156,11 +1212,18 @@ async fn circulate_statement( (local_validator, targets) }; - let mut statement_to = Vec::new(); + let mut statement_to_peers: Vec<(PeerId, ProtocolVersion)> = Vec::new(); for (target, authority_id, kind) in targets { // Find peer ID based on authority ID, and also filter to connected. - let peer_id: PeerId = match authorities.get(&authority_id) { - Some(p) if peers.get(p).map_or(false, |p| p.knows_relay_parent(&relay_parent)) => *p, + let peer_id: (PeerId, ProtocolVersion) = match authorities.get(&authority_id) { + Some(p) if peers.get(p).map_or(false, |p| p.knows_relay_parent(&relay_parent)) => ( + *p, + peers + .get(p) + .expect("Qed, can't fail because it was checked above") + .protocol_version + .into(), + ), None | Some(_) => continue, }; @@ -1178,11 +1241,11 @@ async fn circulate_statement( originator, compact_statement.clone(), ); - statement_to.push(peer_id); + statement_to_peers.push(peer_id); } }, DirectTargetKind::Grid => { - statement_to.push(peer_id); + statement_to_peers.push(peer_id); local_validator.grid_tracker.sent_or_received_direct_statement( &per_session.groups, originator, @@ -1193,17 +1256,23 @@ async fn circulate_statement( } } + let statement_to_v2_peers = + filter_by_peer_version(&statement_to_peers, ValidationVersion::V2.into()); + + let statement_to_vstaging_peers = + filter_by_peer_version(&statement_to_peers, ValidationVersion::VStaging.into()); + // ship off the network messages to the network bridge. - if !statement_to.is_empty() { + if !statement_to_v2_peers.is_empty() { gum::debug!( target: LOG_TARGET, ?compact_statement, - n_peers = ?statement_to.len(), - "Sending statement to peers", + n_peers = ?statement_to_v2_peers.len(), + "Sending statement to v2 peers", ); ctx.send_message(NetworkBridgeTxMessage::SendValidationMessage( - statement_to, + statement_to_v2_peers, Versioned::V2(protocol_v2::StatementDistributionMessage::Statement( relay_parent, statement.as_unchecked().clone(), @@ -1212,6 +1281,25 @@ async fn circulate_statement( )) .await; } + + if !statement_to_vstaging_peers.is_empty() { + gum::debug!( + target: LOG_TARGET, + ?compact_statement, + n_peers = ?statement_to_peers.len(), + "Sending statement to vstaging peers", + ); + + ctx.send_message(NetworkBridgeTxMessage::SendValidationMessage( + statement_to_vstaging_peers, + Versioned::VStaging(protocol_vstaging::StatementDistributionMessage::Statement( + relay_parent, + statement.as_unchecked().clone(), + )) + .into(), + )) + .await; + } } /// Check a statement signature under this parent hash. fn check_statement_signature( @@ -1697,14 +1785,8 @@ async fn provide_candidate_to_grid( statement_knowledge: filter.clone(), }; - let manifest_message = - Versioned::V2(protocol_v2::StatementDistributionMessage::BackedCandidateManifest(manifest)); - let ack_message = Versioned::V2( - protocol_v2::StatementDistributionMessage::BackedCandidateKnown(acknowledgement), - ); - - let mut manifest_peers = Vec::new(); - let mut ack_peers = Vec::new(); + let mut manifest_peers: Vec<(PeerId, ProtocolVersion)> = Vec::new(); + let mut ack_peers: Vec<(PeerId, ProtocolVersion)> = Vec::new(); let mut post_statements = Vec::new(); for (v, action) in actions { @@ -1712,7 +1794,7 @@ async fn provide_candidate_to_grid( None => continue, Some(p) => if peers.get(&p).map_or(false, |d| d.knows_relay_parent(&relay_parent)) { - p + (p, peers.get(&p).expect("Qed, was checked above").protocol_version.into()) } else { continue }, @@ -1738,44 +1820,95 @@ async fn provide_candidate_to_grid( &per_session.groups, group_index, candidate_hash, + &(p.0, p.1.try_into().expect("Qed, can not fail was checked above")), ) .into_iter() - .map(|m| (vec![p], m)), + .map(|m| (vec![p.0], m)), ); } - if !manifest_peers.is_empty() { + let manifest_peers_v2 = filter_by_peer_version(&manifest_peers, ValidationVersion::V2.into()); + let manifest_peers_vstaging = + filter_by_peer_version(&manifest_peers, ValidationVersion::VStaging.into()); + if !manifest_peers_v2.is_empty() { gum::debug!( target: LOG_TARGET, ?candidate_hash, local_validator = ?local_validator.index, - n_peers = manifest_peers.len(), - "Sending manifest to peers" + n_peers = manifest_peers_v2.len(), + "Sending manifest to v2 peers" ); ctx.send_message(NetworkBridgeTxMessage::SendValidationMessage( - manifest_peers, - manifest_message.into(), + manifest_peers_v2, + Versioned::V2(protocol_v2::StatementDistributionMessage::BackedCandidateManifest( + manifest.clone(), + )) + .into(), )) .await; } - if !ack_peers.is_empty() { + if !manifest_peers_vstaging.is_empty() { gum::debug!( target: LOG_TARGET, ?candidate_hash, local_validator = ?local_validator.index, - n_peers = ack_peers.len(), - "Sending acknowledgement to peers" + n_peers = manifest_peers_vstaging.len(), + "Sending manifest to vstaging peers" ); ctx.send_message(NetworkBridgeTxMessage::SendValidationMessage( - ack_peers, - ack_message.into(), + manifest_peers_vstaging, + Versioned::VStaging( + protocol_vstaging::StatementDistributionMessage::BackedCandidateManifest(manifest), + ) + .into(), )) .await; } + let ack_peers_v2 = filter_by_peer_version(&ack_peers, ValidationVersion::V2.into()); + let ack_peers_vstaging = filter_by_peer_version(&ack_peers, ValidationVersion::VStaging.into()); + if !ack_peers_v2.is_empty() { + gum::debug!( + target: LOG_TARGET, + ?candidate_hash, + local_validator = ?local_validator.index, + n_peers = ack_peers_v2.len(), + "Sending acknowledgement to v2 peers" + ); + + ctx.send_message(NetworkBridgeTxMessage::SendValidationMessage( + ack_peers_v2, + Versioned::V2(protocol_v2::StatementDistributionMessage::BackedCandidateKnown( + acknowledgement.clone(), + )) + .into(), + )) + .await; + } + + if !ack_peers_vstaging.is_empty() { + gum::debug!( + target: LOG_TARGET, + ?candidate_hash, + local_validator = ?local_validator.index, + n_peers = ack_peers_vstaging.len(), + "Sending acknowledgement to vstaging peers" + ); + + ctx.send_message(NetworkBridgeTxMessage::SendValidationMessage( + ack_peers_vstaging, + Versioned::VStaging( + protocol_vstaging::StatementDistributionMessage::BackedCandidateKnown( + acknowledgement, + ), + ) + .into(), + )) + .await; + } if !post_statements.is_empty() { ctx.send_message(NetworkBridgeTxMessage::SendValidationMessages(post_statements)) .await; @@ -2074,6 +2207,7 @@ fn post_acknowledgement_statement_messages( groups: &Groups, group_index: GroupIndex, candidate_hash: CandidateHash, + peer: &(PeerId, ValidationVersion), ) -> Vec { let sending_filter = match grid_tracker.pending_statements_for(recipient, candidate_hash) { None => return Vec::new(), @@ -2090,14 +2224,29 @@ fn post_acknowledgement_statement_messages( recipient, statement.payload(), ); - - messages.push(Versioned::V2( - protocol_v2::StatementDistributionMessage::Statement( - relay_parent, - statement.as_unchecked().clone(), - ) - .into(), - )); + match peer.1.into() { + ValidationVersion::V2 => messages.push(Versioned::V2( + protocol_v2::StatementDistributionMessage::Statement( + relay_parent, + statement.as_unchecked().clone(), + ) + .into(), + )), + ValidationVersion::VStaging => messages.push(Versioned::VStaging( + protocol_vstaging::StatementDistributionMessage::Statement( + relay_parent, + statement.as_unchecked().clone(), + ) + .into(), + )), + ValidationVersion::V1 => { + gum::error!( + target: LOG_TARGET, + "Bug ValidationVersion::V1 should not be used in statement-distribution v2, + legacy should have handled this" + ); + }, + }; } messages @@ -2167,7 +2316,15 @@ async fn handle_incoming_manifest( }; let messages = acknowledgement_and_statement_messages( - peer, + &( + peer, + state + .peers + .get(&peer) + .map(|val| val.protocol_version) + // Assume the latest stable version, if we don't have info about peer version. + .unwrap_or(ValidationVersion::V2), + ), sender_index, &per_session.groups, relay_parent_state, @@ -2198,7 +2355,7 @@ async fn handle_incoming_manifest( /// Produces acknowledgement and statement messages to be sent over the network, /// noting that they have been sent within the grid topology tracker as well. fn acknowledgement_and_statement_messages( - peer: PeerId, + peer: &(PeerId, ValidationVersion), validator_index: ValidatorIndex, groups: &Groups, relay_parent_state: &mut PerRelayParentState, @@ -2217,11 +2374,28 @@ fn acknowledgement_and_statement_messages( statement_knowledge: local_knowledge.clone(), }; - let msg = Versioned::V2(protocol_v2::StatementDistributionMessage::BackedCandidateKnown( - acknowledgement, + let msg_v2 = Versioned::V2(protocol_v2::StatementDistributionMessage::BackedCandidateKnown( + acknowledgement.clone(), )); - let mut messages = vec![(vec![peer], msg.into())]; + let mut messages = match peer.1 { + ValidationVersion::V2 => vec![(vec![peer.0], msg_v2.into())], + ValidationVersion::VStaging => vec![( + vec![peer.0], + Versioned::VStaging(protocol_v2::StatementDistributionMessage::BackedCandidateKnown( + acknowledgement, + )) + .into(), + )], + ValidationVersion::V1 => { + gum::error!( + target: LOG_TARGET, + "Bug ValidationVersion::V1 should not be used in statement-distribution v2, + legacy should have handled this" + ); + return Vec::new() + }, + }; local_validator.grid_tracker.manifest_sent_to( groups, @@ -2238,9 +2412,10 @@ fn acknowledgement_and_statement_messages( &groups, group_index, candidate_hash, + peer, ); - messages.extend(statement_messages.into_iter().map(|m| (vec![peer], m))); + messages.extend(statement_messages.into_iter().map(|m| (vec![peer.0], m))); messages } @@ -2320,6 +2495,15 @@ async fn handle_incoming_acknowledgement( &per_session.groups, group_index, candidate_hash, + &( + peer, + state + .peers + .get(&peer) + .map(|val| val.protocol_version) + // Assume the latest stable version, if we don't have info about peer version. + .unwrap_or(ValidationVersion::V2), + ), ); if !messages.is_empty() { diff --git a/polkadot/node/primitives/Cargo.toml b/polkadot/node/primitives/Cargo.toml index 55dfa6738709..c39fd5947b0b 100644 --- a/polkadot/node/primitives/Cargo.toml +++ b/polkadot/node/primitives/Cargo.toml @@ -20,6 +20,7 @@ sp-runtime = { path = "../../../substrate/primitives/runtime" } polkadot-parachain-primitives = { path = "../../parachain", default-features = false } schnorrkel = "0.9.1" thiserror = "1.0.48" +bitvec = { version = "1.0.0", default-features = false, features = ["alloc"] } serde = { version = "1.0.188", features = ["derive"] } [target.'cfg(not(target_os = "unknown"))'.dependencies] diff --git a/polkadot/node/primitives/src/approval.rs b/polkadot/node/primitives/src/approval.rs index 01f45a207874..e5ae24f7a51e 100644 --- a/polkadot/node/primitives/src/approval.rs +++ b/polkadot/node/primitives/src/approval.rs @@ -16,190 +16,515 @@ //! Types relevant for approval. -pub use sp_consensus_babe::{Randomness, Slot, VrfOutput, VrfProof, VrfSignature, VrfTranscript}; - -use parity_scale_codec::{Decode, Encode}; -use polkadot_primitives::{ - BlockNumber, CandidateHash, CandidateIndex, CoreIndex, Hash, Header, SessionIndex, - ValidatorIndex, ValidatorSignature, -}; -use sp_application_crypto::ByteArray; -use sp_consensus_babe as babe_primitives; - -/// Validators assigning to check a particular candidate are split up into tranches. -/// Earlier tranches of validators check first, with later tranches serving as backup. -pub type DelayTranche = u32; - -/// A static context used to compute the Relay VRF story based on the -/// VRF output included in the header-chain. -pub const RELAY_VRF_STORY_CONTEXT: &[u8] = b"A&V RC-VRF"; - -/// A static context used for all relay-vrf-modulo VRFs. -pub const RELAY_VRF_MODULO_CONTEXT: &[u8] = b"A&V MOD"; - -/// A static context used for all relay-vrf-modulo VRFs. -pub const RELAY_VRF_DELAY_CONTEXT: &[u8] = b"A&V DELAY"; - -/// A static context used for transcripts indicating assigned availability core. -pub const ASSIGNED_CORE_CONTEXT: &[u8] = b"A&V ASSIGNED"; - -/// A static context associated with producing randomness for a core. -pub const CORE_RANDOMNESS_CONTEXT: &[u8] = b"A&V CORE"; - -/// A static context associated with producing randomness for a tranche. -pub const TRANCHE_RANDOMNESS_CONTEXT: &[u8] = b"A&V TRANCHE"; - -/// random bytes derived from the VRF submitted within the block by the -/// block author as a credential and used as input to approval assignment criteria. -#[derive(Debug, Clone, Encode, Decode, PartialEq)] -pub struct RelayVRFStory(pub [u8; 32]); - -/// Different kinds of input data or criteria that can prove a validator's assignment -/// to check a particular parachain. -#[derive(Debug, Clone, Encode, Decode, PartialEq, Eq)] -pub enum AssignmentCertKind { - /// An assignment story based on the VRF that authorized the relay-chain block where the - /// candidate was included combined with a sample number. +/// A list of primitives introduced in v1. +pub mod v1 { + use sp_consensus_babe as babe_primitives; + pub use sp_consensus_babe::{ + Randomness, Slot, VrfOutput, VrfProof, VrfSignature, VrfTranscript, + }; + + use parity_scale_codec::{Decode, Encode}; + use polkadot_primitives::{ + BlockNumber, CandidateHash, CandidateIndex, CoreIndex, Hash, Header, SessionIndex, + ValidatorIndex, ValidatorSignature, + }; + use sp_application_crypto::ByteArray; + + /// Validators assigning to check a particular candidate are split up into tranches. + /// Earlier tranches of validators check first, with later tranches serving as backup. + pub type DelayTranche = u32; + + /// A static context used to compute the Relay VRF story based on the + /// VRF output included in the header-chain. + pub const RELAY_VRF_STORY_CONTEXT: &[u8] = b"A&V RC-VRF"; + + /// A static context used for all relay-vrf-modulo VRFs. + pub const RELAY_VRF_MODULO_CONTEXT: &[u8] = b"A&V MOD"; + + /// A static context used for all relay-vrf-modulo VRFs. + pub const RELAY_VRF_DELAY_CONTEXT: &[u8] = b"A&V DELAY"; + + /// A static context used for transcripts indicating assigned availability core. + pub const ASSIGNED_CORE_CONTEXT: &[u8] = b"A&V ASSIGNED"; + + /// A static context associated with producing randomness for a core. + pub const CORE_RANDOMNESS_CONTEXT: &[u8] = b"A&V CORE"; + + /// A static context associated with producing randomness for a tranche. + pub const TRANCHE_RANDOMNESS_CONTEXT: &[u8] = b"A&V TRANCHE"; + + /// random bytes derived from the VRF submitted within the block by the + /// block author as a credential and used as input to approval assignment criteria. + #[derive(Debug, Clone, Encode, Decode, PartialEq)] + pub struct RelayVRFStory(pub [u8; 32]); + + /// Different kinds of input data or criteria that can prove a validator's assignment + /// to check a particular parachain. + #[derive(Debug, Clone, Encode, Decode, PartialEq, Eq)] + pub enum AssignmentCertKind { + /// An assignment story based on the VRF that authorized the relay-chain block where the + /// candidate was included combined with a sample number. + /// + /// The context used to produce bytes is [`RELAY_VRF_MODULO_CONTEXT`] + RelayVRFModulo { + /// The sample number used in this cert. + sample: u32, + }, + /// An assignment story based on the VRF that authorized the relay-chain block where the + /// candidate was included combined with the index of a particular core. + /// + /// The context is [`RELAY_VRF_DELAY_CONTEXT`] + RelayVRFDelay { + /// The core index chosen in this cert. + core_index: CoreIndex, + }, + } + + /// A certification of assignment. + #[derive(Debug, Clone, Encode, Decode, PartialEq, Eq)] + pub struct AssignmentCert { + /// The criterion which is claimed to be met by this cert. + pub kind: AssignmentCertKind, + /// The VRF signature showing the criterion is met. + pub vrf: VrfSignature, + } + + /// An assignment criterion which refers to the candidate under which the assignment is + /// relevant by block hash. + #[derive(Debug, Clone, Encode, Decode, PartialEq, Eq)] + pub struct IndirectAssignmentCert { + /// A block hash where the candidate appears. + pub block_hash: Hash, + /// The validator index. + pub validator: ValidatorIndex, + /// The cert itself. + pub cert: AssignmentCert, + } + + /// A signed approval vote which references the candidate indirectly via the block. /// - /// The context used to produce bytes is [`RELAY_VRF_MODULO_CONTEXT`] - RelayVRFModulo { - /// The sample number used in this cert. - sample: u32, - }, - /// An assignment story based on the VRF that authorized the relay-chain block where the - /// candidate was included combined with the index of a particular core. + /// In practice, we have a look-up from block hash and candidate index to candidate hash, + /// so this can be transformed into a `SignedApprovalVote`. + #[derive(Debug, Clone, Encode, Decode, PartialEq, Eq)] + pub struct IndirectSignedApprovalVote { + /// A block hash where the candidate appears. + pub block_hash: Hash, + /// The index of the candidate in the list of candidates fully included as-of the block. + pub candidate_index: CandidateIndex, + /// The validator index. + pub validator: ValidatorIndex, + /// The signature by the validator. + pub signature: ValidatorSignature, + } + + /// Metadata about a block which is now live in the approval protocol. + #[derive(Debug)] + pub struct BlockApprovalMeta { + /// The hash of the block. + pub hash: Hash, + /// The number of the block. + pub number: BlockNumber, + /// The hash of the parent block. + pub parent_hash: Hash, + /// The candidates included by the block. + /// Note that these are not the same as the candidates that appear within the block body. + pub candidates: Vec, + /// The consensus slot of the block. + pub slot: Slot, + /// The session of the block. + pub session: SessionIndex, + } + + /// Errors that can occur during the approvals protocol. + #[derive(Debug, thiserror::Error)] + #[allow(missing_docs)] + pub enum ApprovalError { + #[error("Schnorrkel signature error")] + SchnorrkelSignature(schnorrkel::errors::SignatureError), + #[error("Authority index {0} out of bounds")] + AuthorityOutOfBounds(usize), + } + + /// An unsafe VRF output. Provide BABE Epoch info to create a `RelayVRFStory`. + pub struct UnsafeVRFOutput { + vrf_output: VrfOutput, + slot: Slot, + authority_index: u32, + } + + impl UnsafeVRFOutput { + /// Get the slot. + pub fn slot(&self) -> Slot { + self.slot + } + + /// Compute the randomness associated with this VRF output. + pub fn compute_randomness( + self, + authorities: &[(babe_primitives::AuthorityId, babe_primitives::BabeAuthorityWeight)], + randomness: &babe_primitives::Randomness, + epoch_index: u64, + ) -> Result { + let author = match authorities.get(self.authority_index as usize) { + None => return Err(ApprovalError::AuthorityOutOfBounds(self.authority_index as _)), + Some(x) => &x.0, + }; + + let pubkey = schnorrkel::PublicKey::from_bytes(author.as_slice()) + .map_err(ApprovalError::SchnorrkelSignature)?; + + let transcript = + sp_consensus_babe::make_vrf_transcript(randomness, self.slot, epoch_index); + + let inout = self + .vrf_output + .0 + .attach_input_hash(&pubkey, transcript.0) + .map_err(ApprovalError::SchnorrkelSignature)?; + Ok(RelayVRFStory(inout.make_bytes(super::v1::RELAY_VRF_STORY_CONTEXT))) + } + } + + /// Extract the slot number and relay VRF from a header. /// - /// The context is [`RELAY_VRF_DELAY_CONTEXT`] - RelayVRFDelay { - /// The core index chosen in this cert. - core_index: CoreIndex, - }, -} + /// This fails if either there is no BABE `PreRuntime` digest or + /// the digest has type `SecondaryPlain`, which Substrate nodes do + /// not produce or accept anymore. + pub fn babe_unsafe_vrf_info(header: &Header) -> Option { + use babe_primitives::digests::CompatibleDigestItem; -/// A certification of assignment. -#[derive(Debug, Clone, Encode, Decode, PartialEq, Eq)] -pub struct AssignmentCert { - /// The criterion which is claimed to be met by this cert. - pub kind: AssignmentCertKind, - /// The VRF signature showing the criterion is met. - pub vrf: VrfSignature, -} + for digest in &header.digest.logs { + if let Some(pre) = digest.as_babe_pre_digest() { + let slot = pre.slot(); + let authority_index = pre.authority_index(); -/// An assignment criterion which refers to the candidate under which the assignment is -/// relevant by block hash. -#[derive(Debug, Clone, Encode, Decode, PartialEq, Eq)] -pub struct IndirectAssignmentCert { - /// A block hash where the candidate appears. - pub block_hash: Hash, - /// The validator index. - pub validator: ValidatorIndex, - /// The cert itself. - pub cert: AssignmentCert, -} + return pre.vrf_signature().map(|sig| UnsafeVRFOutput { + vrf_output: sig.output.clone(), + slot, + authority_index, + }) + } + } -/// A signed approval vote which references the candidate indirectly via the block. -/// -/// In practice, we have a look-up from block hash and candidate index to candidate hash, -/// so this can be transformed into a `SignedApprovalVote`. -#[derive(Debug, Clone, Encode, Decode, PartialEq, Eq)] -pub struct IndirectSignedApprovalVote { - /// A block hash where the candidate appears. - pub block_hash: Hash, - /// The index of the candidate in the list of candidates fully included as-of the block. - pub candidate_index: CandidateIndex, - /// The validator index. - pub validator: ValidatorIndex, - /// The signature by the validator. - pub signature: ValidatorSignature, + None + } } -/// Metadata about a block which is now live in the approval protocol. -#[derive(Debug)] -pub struct BlockApprovalMeta { - /// The hash of the block. - pub hash: Hash, - /// The number of the block. - pub number: BlockNumber, - /// The hash of the parent block. - pub parent_hash: Hash, - /// The candidates included by the block. - /// Note that these are not the same as the candidates that appear within the block body. - pub candidates: Vec, - /// The consensus slot of the block. - pub slot: Slot, - /// The session of the block. - pub session: SessionIndex, -} +/// A list of primitives introduced by v2. +pub mod v2 { + use parity_scale_codec::{Decode, Encode}; + pub use sp_consensus_babe::{ + Randomness, Slot, VrfOutput, VrfProof, VrfSignature, VrfTranscript, + }; + use std::ops::BitOr; -/// Errors that can occur during the approvals protocol. -#[derive(Debug, thiserror::Error)] -#[allow(missing_docs)] -pub enum ApprovalError { - #[error("Schnorrkel signature error")] - SchnorrkelSignature(schnorrkel::errors::SignatureError), - #[error("Authority index {0} out of bounds")] - AuthorityOutOfBounds(usize), -} + use bitvec::{prelude::Lsb0, vec::BitVec}; + use polkadot_primitives::{CandidateIndex, CoreIndex, Hash, ValidatorIndex}; -/// An unsafe VRF output. Provide BABE Epoch info to create a `RelayVRFStory`. -pub struct UnsafeVRFOutput { - vrf_output: VrfOutput, - slot: Slot, - authority_index: u32, -} + /// A static context associated with producing randomness for a core. + pub const CORE_RANDOMNESS_CONTEXT: &[u8] = b"A&V CORE v2"; + /// A static context associated with producing randomness for v2 multi-core assignments. + pub const ASSIGNED_CORE_CONTEXT: &[u8] = b"A&V ASSIGNED v2"; + /// A static context used for all relay-vrf-modulo VRFs for v2 multi-core assignments. + pub const RELAY_VRF_MODULO_CONTEXT: &[u8] = b"A&V MOD v2"; + /// A read-only bitvec wrapper + #[derive(Clone, Debug, Encode, Decode, Hash, PartialEq, Eq)] + pub struct Bitfield(BitVec, std::marker::PhantomData); + + /// A `read-only`, `non-zero` bitfield. + /// Each 1 bit identifies a candidate by the bitfield bit index. + pub type CandidateBitfield = Bitfield; + /// A bitfield of core assignments. + pub type CoreBitfield = Bitfield; + + /// Errors that can occur when creating and manipulating bitfields. + #[derive(Debug)] + pub enum BitfieldError { + /// All bits are zero. + NullAssignment, + } + + /// A bit index in `Bitfield`. + #[cfg_attr(test, derive(PartialEq, Clone))] + pub struct BitIndex(pub usize); + + /// Helper trait to convert primitives to `BitIndex`. + pub trait AsBitIndex { + /// Returns the index of the corresponding bit in `Bitfield`. + fn as_bit_index(&self) -> BitIndex; + } + + impl Bitfield { + /// Returns the bit value at specified `index`. If `index` is greater than bitfield size, + /// returns `false`. + pub fn bit_at(&self, index: BitIndex) -> bool { + if self.0.len() <= index.0 { + false + } else { + self.0[index.0] + } + } + + /// Returns number of bits. + pub fn len(&self) -> usize { + self.0.len() + } + + /// Returns the number of 1 bits. + pub fn count_ones(&self) -> usize { + self.0.count_ones() + } + + /// Returns the index of the first 1 bit. + pub fn first_one(&self) -> Option { + self.0.first_one() + } + + /// Returns an iterator over inner bits. + pub fn iter_ones(&self) -> bitvec::slice::IterOnes { + self.0.iter_ones() + } -impl UnsafeVRFOutput { - /// Get the slot. - pub fn slot(&self) -> Slot { - self.slot + /// For testing purpose, we want a inner mutable ref. + #[cfg(test)] + pub fn inner_mut(&mut self) -> &mut BitVec { + &mut self.0 + } + + /// Returns the inner bitfield and consumes `self`. + pub fn into_inner(self) -> BitVec { + self.0 + } } - /// Compute the randomness associated with this VRF output. - pub fn compute_randomness( - self, - authorities: &[(babe_primitives::AuthorityId, babe_primitives::BabeAuthorityWeight)], - randomness: &babe_primitives::Randomness, - epoch_index: u64, - ) -> Result { - let author = match authorities.get(self.authority_index as usize) { - None => return Err(ApprovalError::AuthorityOutOfBounds(self.authority_index as _)), - Some(x) => &x.0, - }; + impl AsBitIndex for CandidateIndex { + fn as_bit_index(&self) -> BitIndex { + BitIndex(*self as usize) + } + } - let pubkey = schnorrkel::PublicKey::from_bytes(author.as_slice()) - .map_err(ApprovalError::SchnorrkelSignature)?; + impl AsBitIndex for CoreIndex { + fn as_bit_index(&self) -> BitIndex { + BitIndex(self.0 as usize) + } + } - let transcript = sp_consensus_babe::make_vrf_transcript(randomness, self.slot, epoch_index); + impl AsBitIndex for usize { + fn as_bit_index(&self) -> BitIndex { + BitIndex(*self) + } + } - let inout = self - .vrf_output - .0 - .attach_input_hash(&pubkey, transcript.0) - .map_err(ApprovalError::SchnorrkelSignature)?; - Ok(RelayVRFStory(inout.make_bytes(RELAY_VRF_STORY_CONTEXT))) + impl From for Bitfield + where + T: AsBitIndex, + { + fn from(value: T) -> Self { + Self( + { + let mut bv = bitvec::bitvec![u8, Lsb0; 0; value.as_bit_index().0 + 1]; + bv.set(value.as_bit_index().0, true); + bv + }, + Default::default(), + ) + } + } + + impl TryFrom> for Bitfield + where + T: Into>, + { + type Error = BitfieldError; + + fn try_from(mut value: Vec) -> Result { + if value.is_empty() { + return Err(BitfieldError::NullAssignment) + } + + let initial_bitfield = + value.pop().expect("Just checked above it's not empty; qed").into(); + + Ok(Self( + value.into_iter().fold(initial_bitfield.0, |initial_bitfield, element| { + let mut bitfield: Bitfield = element.into(); + bitfield + .0 + .resize(std::cmp::max(initial_bitfield.len(), bitfield.0.len()), false); + bitfield.0.bitor(initial_bitfield) + }), + Default::default(), + )) + } + } + + /// Certificate is changed compared to `AssignmentCertKind`: + /// - introduced RelayVRFModuloCompact + #[derive(Debug, Clone, Encode, Decode, PartialEq, Eq)] + pub enum AssignmentCertKindV2 { + /// Multiple assignment stories based on the VRF that authorized the relay-chain block + /// where the candidates were included. + /// + /// The context is [`super::v2::RELAY_VRF_MODULO_CONTEXT`] + #[codec(index = 0)] + RelayVRFModuloCompact { + /// A bitfield representing the core indices claimed by this assignment. + core_bitfield: CoreBitfield, + }, + /// An assignment story based on the VRF that authorized the relay-chain block where the + /// candidate was included combined with the index of a particular core. + /// + /// The context is [`super::v1::RELAY_VRF_DELAY_CONTEXT`] + #[codec(index = 1)] + RelayVRFDelay { + /// The core index chosen in this cert. + core_index: CoreIndex, + }, + /// Deprectated assignment. Soon to be removed. + /// An assignment story based on the VRF that authorized the relay-chain block where the + /// candidate was included combined with a sample number. + /// + /// The context used to produce bytes is [`super::v1::RELAY_VRF_MODULO_CONTEXT`] + #[codec(index = 2)] + RelayVRFModulo { + /// The sample number used in this cert. + sample: u32, + }, + } + + /// A certification of assignment. + #[derive(Debug, Clone, Encode, Decode, PartialEq, Eq)] + pub struct AssignmentCertV2 { + /// The criterion which is claimed to be met by this cert. + pub kind: AssignmentCertKindV2, + /// The VRF showing the criterion is met. + pub vrf: VrfSignature, + } + + impl From for AssignmentCertV2 { + fn from(cert: super::v1::AssignmentCert) -> Self { + Self { + kind: match cert.kind { + super::v1::AssignmentCertKind::RelayVRFDelay { core_index } => + AssignmentCertKindV2::RelayVRFDelay { core_index }, + super::v1::AssignmentCertKind::RelayVRFModulo { sample } => + AssignmentCertKindV2::RelayVRFModulo { sample }, + }, + vrf: cert.vrf, + } + } } -} -/// Extract the slot number and relay VRF from a header. -/// -/// This fails if either there is no BABE `PreRuntime` digest or -/// the digest has type `SecondaryPlain`, which Substrate nodes do -/// not produce or accept anymore. -pub fn babe_unsafe_vrf_info(header: &Header) -> Option { - use babe_primitives::digests::CompatibleDigestItem; - - for digest in &header.digest.logs { - if let Some(pre) = digest.as_babe_pre_digest() { - let slot = pre.slot(); - let authority_index = pre.authority_index(); - - return pre.vrf_signature().map(|sig| UnsafeVRFOutput { - vrf_output: sig.output.clone(), - slot, - authority_index, + /// Errors that can occur when trying to convert to/from assignment v1/v2 + #[derive(Debug)] + pub enum AssignmentConversionError { + /// Assignment certificate is not supported in v1. + CertificateNotSupported, + } + + impl TryFrom for super::v1::AssignmentCert { + type Error = AssignmentConversionError; + fn try_from(cert: AssignmentCertV2) -> Result { + Ok(Self { + kind: match cert.kind { + AssignmentCertKindV2::RelayVRFDelay { core_index } => + super::v1::AssignmentCertKind::RelayVRFDelay { core_index }, + AssignmentCertKindV2::RelayVRFModulo { sample } => + super::v1::AssignmentCertKind::RelayVRFModulo { sample }, + // Not supported + _ => return Err(AssignmentConversionError::CertificateNotSupported), + }, + vrf: cert.vrf, }) } } - None + /// An assignment criterion which refers to the candidate under which the assignment is + /// relevant by block hash. + #[derive(Debug, Clone, Encode, Decode, PartialEq, Eq)] + pub struct IndirectAssignmentCertV2 { + /// A block hash where the candidate appears. + pub block_hash: Hash, + /// The validator index. + pub validator: ValidatorIndex, + /// The cert itself. + pub cert: AssignmentCertV2, + } + + impl From for IndirectAssignmentCertV2 { + fn from(indirect_cert: super::v1::IndirectAssignmentCert) -> Self { + Self { + block_hash: indirect_cert.block_hash, + validator: indirect_cert.validator, + cert: indirect_cert.cert.into(), + } + } + } + + impl TryFrom for super::v1::IndirectAssignmentCert { + type Error = AssignmentConversionError; + fn try_from( + indirect_cert: IndirectAssignmentCertV2, + ) -> Result { + Ok(Self { + block_hash: indirect_cert.block_hash, + validator: indirect_cert.validator, + cert: indirect_cert.cert.try_into()?, + }) + } + } +} + +#[cfg(test)] +mod test { + use super::v2::{BitIndex, Bitfield}; + + use polkadot_primitives::{CandidateIndex, CoreIndex}; + + #[test] + fn test_assignment_bitfield_from_vec() { + let candidate_indices = vec![1u32, 7, 3, 10, 45, 8, 200, 2]; + let max_index = *candidate_indices.iter().max().unwrap(); + let bitfield = Bitfield::try_from(candidate_indices.clone()).unwrap(); + let candidate_indices = + candidate_indices.into_iter().map(|i| BitIndex(i as usize)).collect::>(); + + // Test 1 bits. + for index in candidate_indices.clone() { + assert!(bitfield.bit_at(index)); + } + + // Test 0 bits. + for index in 0..max_index { + if candidate_indices.contains(&BitIndex(index as usize)) { + continue + } + assert!(!bitfield.bit_at(BitIndex(index as usize))); + } + } + + #[test] + fn test_assignment_bitfield_invariant_msb() { + let core_indices = vec![CoreIndex(1), CoreIndex(3), CoreIndex(10), CoreIndex(20)]; + let mut bitfield = Bitfield::try_from(core_indices.clone()).unwrap(); + assert!(bitfield.inner_mut().pop().unwrap()); + + for i in 0..1024 { + assert!(Bitfield::try_from(CoreIndex(i)).unwrap().inner_mut().pop().unwrap()); + assert!(Bitfield::try_from(i).unwrap().inner_mut().pop().unwrap()); + } + } + + #[test] + fn test_assignment_bitfield_basic() { + let bitfield = Bitfield::try_from(CoreIndex(0)).unwrap(); + assert!(bitfield.bit_at(BitIndex(0))); + assert!(!bitfield.bit_at(BitIndex(1))); + assert_eq!(bitfield.len(), 1); + + let mut bitfield = Bitfield::try_from(20 as CandidateIndex).unwrap(); + assert!(bitfield.bit_at(BitIndex(20))); + assert_eq!(bitfield.inner_mut().count_ones(), 1); + assert_eq!(bitfield.len(), 21); + } } diff --git a/polkadot/node/service/Cargo.toml b/polkadot/node/service/Cargo.toml index 41b86f2151e9..0ed50086f86f 100644 --- a/polkadot/node/service/Cargo.toml +++ b/polkadot/node/service/Cargo.toml @@ -139,6 +139,7 @@ polkadot-statement-distribution = { path = "../network/statement-distribution", [dev-dependencies] polkadot-test-client = { path = "../test/client" } polkadot-node-subsystem-test-helpers = { path = "../subsystem-test-helpers" } +test-helpers = { package = "polkadot-primitives-test-helpers", path = "../../primitives/test-helpers" } env_logger = "0.9.0" assert_matches = "1.5.0" serial_test = "2.0.0" @@ -222,3 +223,7 @@ runtime-metrics = [ "rococo-runtime?/runtime-metrics", "westend-runtime?/runtime-metrics", ] + +network-protocol-staging = [ + "polkadot-node-network-protocol/network-protocol-staging", +] diff --git a/polkadot/node/service/src/parachains_db/mod.rs b/polkadot/node/service/src/parachains_db/mod.rs index 519afbe0ccd1..92f3f167f22f 100644 --- a/polkadot/node/service/src/parachains_db/mod.rs +++ b/polkadot/node/service/src/parachains_db/mod.rs @@ -41,7 +41,15 @@ pub(crate) mod columns { pub const COL_SESSION_WINDOW_DATA: u32 = 5; } + // Version 4 only changed structures in approval voting, so we can re-export the v4 definitions. pub mod v3 { + pub use super::v4::{ + COL_APPROVAL_DATA, COL_AVAILABILITY_DATA, COL_AVAILABILITY_META, + COL_CHAIN_SELECTION_DATA, COL_DISPUTE_COORDINATOR_DATA, NUM_COLUMNS, ORDERED_COL, + }; + } + + pub mod v4 { pub const NUM_COLUMNS: u32 = 5; pub const COL_AVAILABILITY_DATA: u32 = 0; pub const COL_AVAILABILITY_META: u32 = 1; @@ -73,14 +81,14 @@ pub struct ColumnsConfig { /// The real columns used by the parachains DB. #[cfg(any(test, feature = "full-node"))] pub const REAL_COLUMNS: ColumnsConfig = ColumnsConfig { - col_availability_data: columns::v3::COL_AVAILABILITY_DATA, - col_availability_meta: columns::v3::COL_AVAILABILITY_META, - col_approval_data: columns::v3::COL_APPROVAL_DATA, - col_chain_selection_data: columns::v3::COL_CHAIN_SELECTION_DATA, - col_dispute_coordinator_data: columns::v3::COL_DISPUTE_COORDINATOR_DATA, + col_availability_data: columns::v4::COL_AVAILABILITY_DATA, + col_availability_meta: columns::v4::COL_AVAILABILITY_META, + col_approval_data: columns::v4::COL_APPROVAL_DATA, + col_chain_selection_data: columns::v4::COL_CHAIN_SELECTION_DATA, + col_dispute_coordinator_data: columns::v4::COL_DISPUTE_COORDINATOR_DATA, }; -#[derive(PartialEq)] +#[derive(PartialEq, Copy, Clone)] pub(crate) enum DatabaseKind { ParityDB, RocksDB, @@ -125,28 +133,28 @@ pub fn open_creating_rocksdb( let path = root.join("parachains").join("db"); - let mut db_config = DatabaseConfig::with_columns(columns::v3::NUM_COLUMNS); + let mut db_config = DatabaseConfig::with_columns(columns::v4::NUM_COLUMNS); let _ = db_config .memory_budget - .insert(columns::v3::COL_AVAILABILITY_DATA, cache_sizes.availability_data); + .insert(columns::v4::COL_AVAILABILITY_DATA, cache_sizes.availability_data); let _ = db_config .memory_budget - .insert(columns::v3::COL_AVAILABILITY_META, cache_sizes.availability_meta); + .insert(columns::v4::COL_AVAILABILITY_META, cache_sizes.availability_meta); let _ = db_config .memory_budget - .insert(columns::v3::COL_APPROVAL_DATA, cache_sizes.approval_data); + .insert(columns::v4::COL_APPROVAL_DATA, cache_sizes.approval_data); let path_str = path .to_str() .ok_or_else(|| other_io_error(format!("Bad database path: {:?}", path)))?; std::fs::create_dir_all(&path_str)?; - upgrade::try_upgrade_db(&path, DatabaseKind::RocksDB)?; + upgrade::try_upgrade_db(&path, DatabaseKind::RocksDB, upgrade::CURRENT_VERSION)?; let db = Database::open(&db_config, &path_str)?; let db = polkadot_node_subsystem_util::database::kvdb_impl::DbAdapter::new( db, - columns::v3::ORDERED_COL, + columns::v4::ORDERED_COL, ); Ok(Arc::new(db)) @@ -164,14 +172,14 @@ pub fn open_creating_paritydb( .ok_or_else(|| other_io_error(format!("Bad database path: {:?}", path)))?; std::fs::create_dir_all(&path_str)?; - upgrade::try_upgrade_db(&path, DatabaseKind::ParityDB)?; + upgrade::try_upgrade_db(&path, DatabaseKind::ParityDB, upgrade::CURRENT_VERSION)?; let db = parity_db::Db::open_or_create(&upgrade::paritydb_version_3_config(&path)) .map_err(|err| io::Error::new(io::ErrorKind::Other, format!("{:?}", err)))?; let db = polkadot_node_subsystem_util::database::paritydb_impl::DbAdapter::new( db, - columns::v3::ORDERED_COL, + columns::v4::ORDERED_COL, ); Ok(Arc::new(db)) } diff --git a/polkadot/node/service/src/parachains_db/upgrade.rs b/polkadot/node/service/src/parachains_db/upgrade.rs index 54ef97afd71c..1d76c79d3e32 100644 --- a/polkadot/node/service/src/parachains_db/upgrade.rs +++ b/polkadot/node/service/src/parachains_db/upgrade.rs @@ -22,13 +22,17 @@ use std::{ str::FromStr, }; +use polkadot_node_core_approval_voting::approval_db::v2::{ + migration_helpers::v1_to_v2, Config as ApprovalDbConfig, +}; type Version = u32; /// Version file name. const VERSION_FILE_NAME: &'static str = "parachain_db_version"; /// Current db version. -const CURRENT_VERSION: Version = 3; +/// Version 4 changes approval db format for `OurAssignment`. +pub(crate) const CURRENT_VERSION: Version = 4; #[derive(thiserror::Error, Debug)] pub enum Error { @@ -38,6 +42,10 @@ pub enum Error { CorruptedVersionFile, #[error("Parachains DB has a future version (expected {current:?}, found {got:?})")] FutureVersion { current: Version, got: Version }, + #[error("Parachain DB migration failed")] + MigrationFailed, + #[error("Parachain DB migration would take forever")] + MigrationLoop, } impl From for io::Error { @@ -49,10 +57,42 @@ impl From for io::Error { } } -/// Try upgrading parachain's database to the current version. -pub(crate) fn try_upgrade_db(db_path: &Path, db_kind: DatabaseKind) -> Result<(), Error> { +/// Try upgrading parachain's database to a target version. +pub(crate) fn try_upgrade_db( + db_path: &Path, + db_kind: DatabaseKind, + target_version: Version, +) -> Result<(), Error> { + // Ensure we don't loop forever below because of a bug. + const MAX_MIGRATIONS: u32 = 30; + + #[cfg(test)] + remove_file_lock(&db_path); + + // Loop migrations until we reach the target version. + for _ in 0..MAX_MIGRATIONS { + let version = try_upgrade_db_to_next_version(db_path, db_kind)?; + + #[cfg(test)] + remove_file_lock(&db_path); + + if version == target_version { + return Ok(()) + } + } + + Err(Error::MigrationLoop) +} + +/// Try upgrading parachain's database to the next version. +/// If successfull, it returns the current version. +pub(crate) fn try_upgrade_db_to_next_version( + db_path: &Path, + db_kind: DatabaseKind, +) -> Result { let is_empty = db_path.read_dir().map_or(true, |mut d| d.next().is_none()); - if !is_empty { + + let new_version = if !is_empty { match get_db_version(db_path)? { // 0 -> 1 migration Some(0) => migrate_from_version_0_to_1(db_path, db_kind)?, @@ -60,21 +100,26 @@ pub(crate) fn try_upgrade_db(db_path: &Path, db_kind: DatabaseKind) -> Result<() Some(1) => migrate_from_version_1_to_2(db_path, db_kind)?, // 2 -> 3 migration Some(2) => migrate_from_version_2_to_3(db_path, db_kind)?, + // 3 -> 4 migration + Some(3) => migrate_from_version_3_to_4(db_path, db_kind)?, // Already at current version, do nothing. - Some(CURRENT_VERSION) => (), + Some(CURRENT_VERSION) => CURRENT_VERSION, // This is an arbitrary future version, we don't handle it. Some(v) => return Err(Error::FutureVersion { current: CURRENT_VERSION, got: v }), // No version file. For `RocksDB` we dont need to do anything. - None if db_kind == DatabaseKind::RocksDB => (), + None if db_kind == DatabaseKind::RocksDB => CURRENT_VERSION, // No version file. `ParityDB` did not previously have a version defined. // We handle this as a `0 -> 1` migration. None if db_kind == DatabaseKind::ParityDB => migrate_from_version_0_to_1(db_path, db_kind)?, None => unreachable!(), } - } + } else { + CURRENT_VERSION + }; - update_version(db_path) + update_version(db_path, new_version)?; + Ok(new_version) } /// Reads current database version from the file at given path. @@ -91,9 +136,9 @@ fn get_db_version(path: &Path) -> Result, Error> { /// Writes current database version to the file. /// Creates a new file if the version file does not exist yet. -fn update_version(path: &Path) -> Result<(), Error> { +fn update_version(path: &Path, new_version: Version) -> Result<(), Error> { fs::create_dir_all(path)?; - fs::write(version_file_path(path), CURRENT_VERSION.to_string()).map_err(Into::into) + fs::write(version_file_path(path), new_version.to_string()).map_err(Into::into) } /// Returns the version file path. @@ -103,7 +148,7 @@ fn version_file_path(path: &Path) -> PathBuf { file_path } -fn migrate_from_version_0_to_1(path: &Path, db_kind: DatabaseKind) -> Result<(), Error> { +fn migrate_from_version_0_to_1(path: &Path, db_kind: DatabaseKind) -> Result { gum::info!(target: LOG_TARGET, "Migrating parachains db from version 0 to version 1 ..."); match db_kind { @@ -116,7 +161,7 @@ fn migrate_from_version_0_to_1(path: &Path, db_kind: DatabaseKind) -> Result<(), }) } -fn migrate_from_version_1_to_2(path: &Path, db_kind: DatabaseKind) -> Result<(), Error> { +fn migrate_from_version_1_to_2(path: &Path, db_kind: DatabaseKind) -> Result { gum::info!(target: LOG_TARGET, "Migrating parachains db from version 1 to version 2 ..."); match db_kind { @@ -129,7 +174,48 @@ fn migrate_from_version_1_to_2(path: &Path, db_kind: DatabaseKind) -> Result<(), }) } -fn migrate_from_version_2_to_3(path: &Path, db_kind: DatabaseKind) -> Result<(), Error> { +// Migrade approval voting database. `OurAssignment` has been changed to support the v2 assignments. +// As these are backwards compatible, we'll convert the old entries in the new format. +fn migrate_from_version_3_to_4(path: &Path, db_kind: DatabaseKind) -> Result { + gum::info!(target: LOG_TARGET, "Migrating parachains db from version 3 to version 4 ..."); + use polkadot_node_subsystem_util::database::{ + kvdb_impl::DbAdapter as RocksDbAdapter, paritydb_impl::DbAdapter as ParityDbAdapter, + }; + use std::sync::Arc; + + let approval_db_config = + ApprovalDbConfig { col_approval_data: super::REAL_COLUMNS.col_approval_data }; + + let _result = match db_kind { + DatabaseKind::ParityDB => { + let db = ParityDbAdapter::new( + parity_db::Db::open(&paritydb_version_3_config(path)) + .map_err(|e| other_io_error(format!("Error opening db {:?}", e)))?, + super::columns::v3::ORDERED_COL, + ); + + v1_to_v2(Arc::new(db), approval_db_config).map_err(|_| Error::MigrationFailed)?; + }, + DatabaseKind::RocksDB => { + let db_path = path + .to_str() + .ok_or_else(|| super::other_io_error("Invalid database path".into()))?; + let db_cfg = + kvdb_rocksdb::DatabaseConfig::with_columns(super::columns::v3::NUM_COLUMNS); + let db = RocksDbAdapter::new( + kvdb_rocksdb::Database::open(&db_cfg, db_path)?, + &super::columns::v3::ORDERED_COL, + ); + + v1_to_v2(Arc::new(db), approval_db_config).map_err(|_| Error::MigrationFailed)?; + }, + }; + + gum::info!(target: LOG_TARGET, "Migration complete! "); + Ok(CURRENT_VERSION) +} + +fn migrate_from_version_2_to_3(path: &Path, db_kind: DatabaseKind) -> Result { gum::info!(target: LOG_TARGET, "Migrating parachains db from version 2 to version 3 ..."); match db_kind { DatabaseKind::ParityDB => paritydb_migrate_from_version_2_to_3(path), @@ -143,7 +229,7 @@ fn migrate_from_version_2_to_3(path: &Path, db_kind: DatabaseKind) -> Result<(), /// Migration from version 0 to version 1: /// * the number of columns has changed from 3 to 5; -fn rocksdb_migrate_from_version_0_to_1(path: &Path) -> Result<(), Error> { +fn rocksdb_migrate_from_version_0_to_1(path: &Path) -> Result { use kvdb_rocksdb::{Database, DatabaseConfig}; let db_path = path @@ -155,12 +241,12 @@ fn rocksdb_migrate_from_version_0_to_1(path: &Path) -> Result<(), Error> { db.add_column()?; db.add_column()?; - Ok(()) + Ok(1) } /// Migration from version 1 to version 2: /// * the number of columns has changed from 5 to 6; -fn rocksdb_migrate_from_version_1_to_2(path: &Path) -> Result<(), Error> { +fn rocksdb_migrate_from_version_1_to_2(path: &Path) -> Result { use kvdb_rocksdb::{Database, DatabaseConfig}; let db_path = path @@ -171,10 +257,10 @@ fn rocksdb_migrate_from_version_1_to_2(path: &Path) -> Result<(), Error> { db.add_column()?; - Ok(()) + Ok(2) } -fn rocksdb_migrate_from_version_2_to_3(path: &Path) -> Result<(), Error> { +fn rocksdb_migrate_from_version_2_to_3(path: &Path) -> Result { use kvdb_rocksdb::{Database, DatabaseConfig}; let db_path = path @@ -185,7 +271,7 @@ fn rocksdb_migrate_from_version_2_to_3(path: &Path) -> Result<(), Error> { db.remove_last_column()?; - Ok(()) + Ok(3) } // This currently clears columns which had their configs altered between versions. @@ -249,7 +335,7 @@ fn paritydb_fix_columns( pub(crate) fn paritydb_version_1_config(path: &Path) -> parity_db::Options { let mut options = parity_db::Options::with_columns(&path, super::columns::v1::NUM_COLUMNS as u8); - for i in columns::v3::ORDERED_COL { + for i in columns::v4::ORDERED_COL { options.columns[*i as usize].btree_index = true; } @@ -260,7 +346,7 @@ pub(crate) fn paritydb_version_1_config(path: &Path) -> parity_db::Options { pub(crate) fn paritydb_version_2_config(path: &Path) -> parity_db::Options { let mut options = parity_db::Options::with_columns(&path, super::columns::v2::NUM_COLUMNS as u8); - for i in columns::v3::ORDERED_COL { + for i in columns::v4::ORDERED_COL { options.columns[*i as usize].btree_index = true; } @@ -278,48 +364,161 @@ pub(crate) fn paritydb_version_3_config(path: &Path) -> parity_db::Options { options } +/// Database configuration for version 0. This is useful just for testing. +#[cfg(test)] +pub(crate) fn paritydb_version_0_config(path: &Path) -> parity_db::Options { + let mut options = + parity_db::Options::with_columns(&path, super::columns::v0::NUM_COLUMNS as u8); + options.columns[super::columns::v4::COL_AVAILABILITY_META as usize].btree_index = true; + + options +} + /// Migration from version 0 to version 1. /// Cases covered: /// - upgrading from v0.9.23 or earlier -> the `dispute coordinator column` was changed /// - upgrading from v0.9.24+ -> this is a no op assuming the DB has been manually fixed as per /// release notes -fn paritydb_migrate_from_version_0_to_1(path: &Path) -> Result<(), Error> { +fn paritydb_migrate_from_version_0_to_1(path: &Path) -> Result { // Delete the `dispute coordinator` column if needed (if column configuration is changed). paritydb_fix_columns( path, paritydb_version_1_config(path), - vec![super::columns::v3::COL_DISPUTE_COORDINATOR_DATA], + vec![super::columns::v4::COL_DISPUTE_COORDINATOR_DATA], )?; - Ok(()) + Ok(1) } /// Migration from version 1 to version 2: /// - add a new column for session information storage -fn paritydb_migrate_from_version_1_to_2(path: &Path) -> Result<(), Error> { +fn paritydb_migrate_from_version_1_to_2(path: &Path) -> Result { let mut options = paritydb_version_1_config(path); // Adds the session info column. parity_db::Db::add_column(&mut options, Default::default()) .map_err(|e| other_io_error(format!("Error adding column {:?}", e)))?; - Ok(()) + Ok(2) } /// Migration from version 2 to version 3: /// - drop the column used by `RollingSessionWindow` -fn paritydb_migrate_from_version_2_to_3(path: &Path) -> Result<(), Error> { +fn paritydb_migrate_from_version_2_to_3(path: &Path) -> Result { parity_db::Db::drop_last_column(&mut paritydb_version_2_config(path)) .map_err(|e| other_io_error(format!("Error removing COL_SESSION_WINDOW_DATA {:?}", e)))?; - Ok(()) + Ok(3) +} + +/// Remove the lock file. If file is locked, it will wait up to 1s. +#[cfg(test)] +pub fn remove_file_lock(path: &std::path::Path) { + use std::{io::ErrorKind, thread::sleep, time::Duration}; + + let mut lock_path = std::path::PathBuf::from(path); + lock_path.push("lock"); + + for _ in 0..10 { + let result = std::fs::remove_file(lock_path.as_path()); + match result { + Err(error) => match error.kind() { + ErrorKind::WouldBlock => { + sleep(Duration::from_millis(100)); + continue + }, + _ => return, + }, + Ok(_) => {}, + } + } + + unreachable!("Database is locked, waited 1s for lock file: {:?}", lock_path); } #[cfg(test)] mod tests { use super::{ - columns::{v2::COL_SESSION_WINDOW_DATA, v3::*}, + columns::{v2::COL_SESSION_WINDOW_DATA, v4::*}, *, }; + use polkadot_node_core_approval_voting::approval_db::v2::migration_helpers::v1_to_v2_fill_test_data; + use test_helpers::dummy_candidate_receipt; + + #[test] + fn test_paritydb_migrate_0_to_1() { + use parity_db::Db; + + let db_dir = tempfile::tempdir().unwrap(); + let path = db_dir.path(); + { + let db = Db::open_or_create(&paritydb_version_0_config(&path)).unwrap(); + + db.commit(vec![( + COL_AVAILABILITY_META as u8, + b"5678".to_vec(), + Some(b"somevalue".to_vec()), + )]) + .unwrap(); + } + + try_upgrade_db(&path, DatabaseKind::ParityDB, 1).unwrap(); + + let db = Db::open(&paritydb_version_1_config(&path)).unwrap(); + assert_eq!( + db.get(COL_AVAILABILITY_META as u8, b"5678").unwrap(), + Some("somevalue".as_bytes().to_vec()) + ); + } + + #[test] + fn test_paritydb_migrate_1_to_2() { + use parity_db::Db; + + let db_dir = tempfile::tempdir().unwrap(); + let path = db_dir.path(); + + // We need to properly set db version for upgrade to work. + fs::write(version_file_path(path), "1").expect("Failed to write DB version"); + + { + let db = Db::open_or_create(&paritydb_version_1_config(&path)).unwrap(); + + // Write some dummy data + db.commit(vec![( + COL_DISPUTE_COORDINATOR_DATA as u8, + b"1234".to_vec(), + Some(b"somevalue".to_vec()), + )]) + .unwrap(); + + assert_eq!(db.num_columns(), columns::v1::NUM_COLUMNS as u8); + } + + try_upgrade_db(&path, DatabaseKind::ParityDB, 2).unwrap(); + + let db = Db::open(&paritydb_version_2_config(&path)).unwrap(); + + assert_eq!(db.num_columns(), columns::v2::NUM_COLUMNS as u8); + + assert_eq!( + db.get(COL_DISPUTE_COORDINATOR_DATA as u8, b"1234").unwrap(), + Some("somevalue".as_bytes().to_vec()) + ); + + // Test we can write the new column. + db.commit(vec![( + COL_SESSION_WINDOW_DATA as u8, + b"1337".to_vec(), + Some(b"0xdeadb00b".to_vec()), + )]) + .unwrap(); + + // Read back data from new column. + assert_eq!( + db.get(COL_SESSION_WINDOW_DATA as u8, b"1337").unwrap(), + Some("0xdeadb00b".as_bytes().to_vec()) + ); + } #[test] fn test_rocksdb_migrate_1_to_2() { @@ -338,7 +537,7 @@ mod tests { // We need to properly set db version for upgrade to work. fs::write(version_file_path(db_dir.path()), "1").expect("Failed to write DB version"); { - let db = DbAdapter::new(db, columns::v3::ORDERED_COL); + let db = DbAdapter::new(db, columns::v4::ORDERED_COL); db.write(DBTransaction { ops: vec![DBOp::Insert { col: COL_DISPUTE_COORDINATOR_DATA, @@ -349,14 +548,14 @@ mod tests { .unwrap(); } - try_upgrade_db(&db_dir.path(), DatabaseKind::RocksDB).unwrap(); + try_upgrade_db(&db_dir.path(), DatabaseKind::RocksDB, 2).unwrap(); let db_cfg = DatabaseConfig::with_columns(super::columns::v2::NUM_COLUMNS); let db = Database::open(&db_cfg, db_path).unwrap(); assert_eq!(db.num_columns(), super::columns::v2::NUM_COLUMNS); - let db = DbAdapter::new(db, columns::v3::ORDERED_COL); + let db = DbAdapter::new(db, columns::v4::ORDERED_COL); assert_eq!( db.get(COL_DISPUTE_COORDINATOR_DATA, b"1234").unwrap(), @@ -380,6 +579,109 @@ mod tests { ); } + #[test] + fn test_migrate_3_to_4() { + use kvdb_rocksdb::{Database, DatabaseConfig}; + use polkadot_node_core_approval_voting::approval_db::v2::migration_helpers::v1_to_v2_sanity_check; + use polkadot_node_subsystem_util::database::kvdb_impl::DbAdapter; + + let db_dir = tempfile::tempdir().unwrap(); + let db_path = db_dir.path().to_str().unwrap(); + let db_cfg: DatabaseConfig = DatabaseConfig::with_columns(super::columns::v3::NUM_COLUMNS); + + let approval_cfg = ApprovalDbConfig { + col_approval_data: crate::parachains_db::REAL_COLUMNS.col_approval_data, + }; + + // We need to properly set db version for upgrade to work. + fs::write(version_file_path(db_dir.path()), "3").expect("Failed to write DB version"); + let expected_candidates = { + let db = Database::open(&db_cfg, db_path).unwrap(); + assert_eq!(db.num_columns(), super::columns::v3::NUM_COLUMNS as u32); + let db = DbAdapter::new(db, columns::v3::ORDERED_COL); + // Fill the approval voting column with test data. + v1_to_v2_fill_test_data(std::sync::Arc::new(db), approval_cfg, dummy_candidate_receipt) + .unwrap() + }; + + try_upgrade_db(&db_dir.path(), DatabaseKind::RocksDB, 4).unwrap(); + + let db_cfg = DatabaseConfig::with_columns(super::columns::v4::NUM_COLUMNS); + let db = Database::open(&db_cfg, db_path).unwrap(); + let db = DbAdapter::new(db, columns::v4::ORDERED_COL); + + v1_to_v2_sanity_check(std::sync::Arc::new(db), approval_cfg, expected_candidates).unwrap(); + } + + #[test] + fn test_rocksdb_migrate_0_to_4() { + use kvdb_rocksdb::{Database, DatabaseConfig}; + + let db_dir = tempfile::tempdir().unwrap(); + let db_path = db_dir.path().to_str().unwrap(); + + fs::write(version_file_path(db_dir.path()), "0").expect("Failed to write DB version"); + try_upgrade_db(&db_dir.path(), DatabaseKind::RocksDB, 4).unwrap(); + + let db_cfg = DatabaseConfig::with_columns(super::columns::v4::NUM_COLUMNS); + let db = Database::open(&db_cfg, db_path).unwrap(); + + assert_eq!(db.num_columns(), columns::v4::NUM_COLUMNS); + } + + #[test] + fn test_paritydb_migrate_0_to_4() { + use parity_db::Db; + + let db_dir = tempfile::tempdir().unwrap(); + let path = db_dir.path(); + + // We need to properly set db version for upgrade to work. + fs::write(version_file_path(path), "0").expect("Failed to write DB version"); + + { + let db = Db::open_or_create(&paritydb_version_0_config(&path)).unwrap(); + assert_eq!(db.num_columns(), columns::v0::NUM_COLUMNS as u8); + } + + try_upgrade_db(&path, DatabaseKind::ParityDB, 4).unwrap(); + + let db = Db::open(&paritydb_version_3_config(&path)).unwrap(); + assert_eq!(db.num_columns(), columns::v4::NUM_COLUMNS as u8); + } + + #[test] + fn test_paritydb_migrate_2_to_3() { + use parity_db::Db; + + let db_dir = tempfile::tempdir().unwrap(); + let path = db_dir.path(); + let test_key = b"1337"; + + // We need to properly set db version for upgrade to work. + fs::write(version_file_path(path), "2").expect("Failed to write DB version"); + + { + let db = Db::open_or_create(&paritydb_version_2_config(&path)).unwrap(); + + // Write some dummy data + db.commit(vec![( + COL_SESSION_WINDOW_DATA as u8, + test_key.to_vec(), + Some(b"0xdeadb00b".to_vec()), + )]) + .unwrap(); + + assert_eq!(db.num_columns(), columns::v2::NUM_COLUMNS as u8); + } + + try_upgrade_db(&path, DatabaseKind::ParityDB, 3).unwrap(); + + let db = Db::open(&paritydb_version_3_config(&path)).unwrap(); + + assert_eq!(db.num_columns(), columns::v3::NUM_COLUMNS as u8); + } + #[test] fn test_rocksdb_migrate_2_to_3() { use kvdb_rocksdb::{Database, DatabaseConfig}; @@ -387,6 +689,7 @@ mod tests { let db_dir = tempfile::tempdir().unwrap(); let db_path = db_dir.path().to_str().unwrap(); let db_cfg = DatabaseConfig::with_columns(super::columns::v2::NUM_COLUMNS); + { let db = Database::open(&db_cfg, db_path).unwrap(); assert_eq!(db.num_columns(), super::columns::v2::NUM_COLUMNS as u32); @@ -395,7 +698,7 @@ mod tests { // We need to properly set db version for upgrade to work. fs::write(version_file_path(db_dir.path()), "2").expect("Failed to write DB version"); - try_upgrade_db(&db_dir.path(), DatabaseKind::RocksDB).unwrap(); + try_upgrade_db(&db_dir.path(), DatabaseKind::RocksDB, 3).unwrap(); let db_cfg = DatabaseConfig::with_columns(super::columns::v3::NUM_COLUMNS); let db = Database::open(&db_cfg, db_path).unwrap(); diff --git a/polkadot/node/service/src/tests.rs b/polkadot/node/service/src/tests.rs index 86119662d9bc..26c8083185d8 100644 --- a/polkadot/node/service/src/tests.rs +++ b/polkadot/node/service/src/tests.rs @@ -17,7 +17,7 @@ use super::{relay_chain_selection::*, *}; use futures::channel::oneshot::Receiver; -use polkadot_node_primitives::approval::VrfSignature; +use polkadot_node_primitives::approval::v2::VrfSignature; use polkadot_node_subsystem::messages::{AllMessages, BlockDescription}; use polkadot_node_subsystem_test_helpers as test_helpers; use polkadot_node_subsystem_util::TimeoutExt; diff --git a/polkadot/node/subsystem-types/Cargo.toml b/polkadot/node/subsystem-types/Cargo.toml index a1c00cb0652e..9fd3775da591 100644 --- a/polkadot/node/subsystem-types/Cargo.toml +++ b/polkadot/node/subsystem-types/Cargo.toml @@ -25,3 +25,4 @@ smallvec = "1.8.0" substrate-prometheus-endpoint = { path = "../../../substrate/utils/prometheus" } thiserror = "1.0.48" async-trait = "0.1.57" +bitvec = { version = "1.0.0", default-features = false, features = ["alloc"] } diff --git a/polkadot/node/subsystem-types/src/messages.rs b/polkadot/node/subsystem-types/src/messages.rs index 6bc874384594..ce4fa6633dc8 100644 --- a/polkadot/node/subsystem-types/src/messages.rs +++ b/polkadot/node/subsystem-types/src/messages.rs @@ -32,7 +32,10 @@ use polkadot_node_network_protocol::{ self as net_protocol, peer_set::PeerSet, request_response::Requests, PeerId, }; use polkadot_node_primitives::{ - approval::{BlockApprovalMeta, IndirectAssignmentCert, IndirectSignedApprovalVote}, + approval::{ + v1::{BlockApprovalMeta, IndirectSignedApprovalVote}, + v2::{CandidateBitfield, IndirectAssignmentCertV2}, + }, AvailableData, BabeEpoch, BlockWeight, CandidateVotes, CollationGenerationConfig, CollationSecondedSignal, DisputeMessage, DisputeStatus, ErasureChunk, PoV, SignedDisputeStatement, SignedFullStatement, SignedFullStatementWithPVD, SubmitCollationParams, @@ -841,6 +844,8 @@ pub enum AssignmentCheckError { InvalidCert(ValidatorIndex, String), #[error("Internal state mismatch: {0:?}, {1:?}")] Internal(Hash, CandidateHash), + #[error("Oversized candidate or core bitfield >= {0}")] + InvalidBitfield(usize), } /// The result type of [`ApprovalVotingMessage::CheckAndImportApproval`] request. @@ -906,8 +911,8 @@ pub enum ApprovalVotingMessage { /// Check if the assignment is valid and can be accepted by our view of the protocol. /// Should not be sent unless the block hash is known. CheckAndImportAssignment( - IndirectAssignmentCert, - CandidateIndex, + IndirectAssignmentCertV2, + CandidateBitfield, oneshot::Sender, ), /// Check if the approval vote is valid and can be accepted by our view of the @@ -942,7 +947,7 @@ pub enum ApprovalDistributionMessage { NewBlocks(Vec), /// Distribute an assignment cert from the local validator. The cert is assumed /// to be valid, relevant, and for the given relay-parent and validator index. - DistributeAssignment(IndirectAssignmentCert, CandidateIndex), + DistributeAssignment(IndirectAssignmentCertV2, CandidateBitfield), /// Distribute an approval vote for the local validator. The approval vote is assumed to be /// valid, relevant, and the corresponding approval already issued. /// If not, the subsystem is free to drop the message. diff --git a/polkadot/parachain/test-parachains/undying/collator/Cargo.toml b/polkadot/parachain/test-parachains/undying/collator/Cargo.toml index 4569d4e153b1..578c3d6715dc 100644 --- a/polkadot/parachain/test-parachains/undying/collator/Cargo.toml +++ b/polkadot/parachain/test-parachains/undying/collator/Cargo.toml @@ -39,3 +39,6 @@ sc-service = { path = "../../../../../substrate/client/service" } sp-keyring = { path = "../../../../../substrate/primitives/keyring" } tokio = { version = "1.24.2", features = ["macros"] } + +[features] +network-protocol-staging = [ "polkadot-cli/network-protocol-staging" ] diff --git a/polkadot/roadmap/implementers-guide/src/node/approval/approval-voting.md b/polkadot/roadmap/implementers-guide/src/node/approval/approval-voting.md index 6da2f5d9b883..1a17f90d9ba3 100644 --- a/polkadot/roadmap/implementers-guide/src/node/approval/approval-voting.md +++ b/polkadot/roadmap/implementers-guide/src/node/approval/approval-voting.md @@ -75,22 +75,27 @@ struct TrancheEntry { assignments: Vec<(ValidatorIndex, Tick)>, } -struct OurAssignment { - cert: AssignmentCert, - tranche: DelayTranche, - validator_index: ValidatorIndex, - triggered: bool, +pub struct OurAssignment { + /// Our assignment certificate. + cert: AssignmentCertV2, + /// The tranche for which the assignment refers to. + tranche: DelayTranche, + /// Our validator index for the session in which the candidates were included. + validator_index: ValidatorIndex, + /// Whether the assignment has been triggered already. + triggered: bool, } -struct ApprovalEntry { - tranches: Vec, // sorted ascending by tranche number. - backing_group: GroupIndex, - our_assignment: Option, - our_approval_sig: Option, - assignments: Bitfield, // n_validators bits - approved: bool, +pub struct ApprovalEntry { + tranches: Vec, // sorted ascending by tranche number. + backing_group: GroupIndex, + our_assignment: Option, + our_approval_sig: Option, + assigned_validators: Bitfield, // `n_validators` bits. + approved: bool, } + struct CandidateEntry { candidate: CandidateReceipt, session: SessionIndex, @@ -264,19 +269,25 @@ entry. The cert itself contains information necessary to determine the candidate * Check the assignment cert * If the cert kind is `RelayVRFModulo`, then the certificate is valid as long as `sample < session_info.relay_vrf_samples` and the VRF is valid for the validator's key with the input - `block_entry.relay_vrf_story ++ sample.encode()` as described with [the approvals protocol - section](../../protocol-approval.md#assignment-criteria). We set `core_index = vrf.make_bytes().to_u32() % - session_info.n_cores`. If the `BlockEntry` causes inclusion of a candidate at `core_index`, then this is a valid - assignment for the candidate at `core_index` and has delay tranche 0. Otherwise, it can be ignored. - * If the cert kind is `RelayVRFDelay`, then we check if the VRF is valid for the validator's key with the input - `block_entry.relay_vrf_story ++ cert.core_index.encode()` as described in [the approvals protocol - section](../../protocol-approval.md#assignment-criteria). The cert can be ignored if the block did not cause - inclusion of a candidate on that core index. Otherwise, this is a valid assignment for the included candidate. The - delay tranche for the assignment is determined by reducing `(vrf.make_bytes().to_u64() % - (session_info.n_delay_tranches + - session_info.zeroth_delay_tranche_width)).saturating_sub(session_info.zeroth_delay_tranche_width)`. - * We also check that the core index derived by the output is covered by the `VRFProof` by means of an auxiliary - signature. + `block_entry.relay_vrf_story ++ sample.encode()` as described with + [the approvals protocol section](../../protocol-approval.md#assignment-criteria). We set + `core_index = vrf.make_bytes().to_u32() % session_info.n_cores`. If the `BlockEntry` causes + inclusion of a candidate at `core_index`, then this is a valid assignment for the candidate + at `core_index` and has delay tranche 0. Otherwise, it can be ignored. + * If the cert kind is `RelayVRFModuloCompact`, then the certificate is valid as long as the VRF + is valid for the validator's key with the input `block_entry.relay_vrf_story ++ relay_vrf_samples.encode()` + as described with [the approvals protocol section](../../protocol-approval.md#assignment-criteria). + We enforce that all `core_bitfield` indices are included in the set of the core indices sampled from the + VRF Output. The assignment is considered a valid tranche0 assignment for all claimed candidates if all + `core_bitfield` indices match the core indices where the claimed candidates were included at. + + * If the cert kind is `RelayVRFDelay`, then we check if the VRF is valid for the validator's key with the + input `block_entry.relay_vrf_story ++ cert.core_index.encode()` as described in [the approvals protocol + section](../../protocol-approval.md#assignment-criteria). The cert can be ignored if the block did not + cause inclusion of a candidate on that core index. Otherwise, this is a valid assignment for the included + candidate. The delay tranche for the assignment is determined by reducing + `(vrf.make_bytes().to_u64() % (session_info.n_delay_tranches + session_info.zeroth_delay_tranche_width)).saturating_sub(session_info.zeroth_delay_tranche_width)`. + * We also check that the core index derived by the output is covered by the `VRFProof` by means of an auxiliary signature. * If the delay tranche is too far in the future, return `AssignmentCheckResult::TooFarInFuture`. * Import the assignment. * Load the candidate in question and access the `approval_entry` for the block hash the cert references. diff --git a/polkadot/roadmap/implementers-guide/src/protocol-approval.md b/polkadot/roadmap/implementers-guide/src/protocol-approval.md index 9ba7f6da1734..4fac84591456 100644 --- a/polkadot/roadmap/implementers-guide/src/protocol-approval.md +++ b/polkadot/roadmap/implementers-guide/src/protocol-approval.md @@ -189,9 +189,10 @@ Assignment criteria compute actual assignments using stories and the validators' Assignment criteria output a `Position` consisting of both a `ParaId` to be checked, as well as a precedence `DelayTranche` for when the assignment becomes valid. -Assignment criteria come in three flavors, `RelayVRFModulo`, `RelayVRFDelay` and `RelayEquivocation`. Among these, both -`RelayVRFModulo` and `RelayVRFDelay` run a VRF whose input is the output of a `RelayVRFStory`, while `RelayEquivocation` -runs a VRF whose input is the output of a `RelayEquivocationStory`. +Assignment criteria come in four flavors, `RelayVRFModuloCompact`, `RelayVRFDelay`, `RelayEquivocation` and the +deprecated `RelayVRFModulo`. Among these, `RelayVRFModulo`, `RelayVRFModuloCompact` and `RelayVRFDelay` run a +VRF whose input is the output of a `RelayVRFStory`, while `RelayEquivocation` runs a VRF whose input is the +output of a `RelayEquivocationStory`. Among these, we have two distinct VRF output computations: @@ -203,6 +204,12 @@ sampled availability core in this relay chain block. We choose three samples in secure and efficient by increasing this to four or five, and reducing the backing checks accordingly. All successful `RelayVRFModulo` samples are assigned delay tranche zero. +`RelayVRFModuloCompact` runs a single samples whose VRF input is the `RelayVRFStory` and the sample count. Similar +to `RelayVRFModulo` introduces multiple core assignments for tranche zero. It computes the VRF output with +`schnorrkel::vrf::VRFInOut::make_bytes` using the context "A&V Core v2" and samples up to 160 bytes of the output +as an array of `u32`. Then reduces each `u32` modulo the number of availability cores, and outputs up +to `relay_vrf_modulo_samples` availability core indices. + There is no sampling process for `RelayVRFDelay` and `RelayEquivocation`. We instead run them on specific candidates and they compute a delay from their VRF output. `RelayVRFDelay` runs for all candidates included under, aka declared available by, a relay chain block, and inputs the associated VRF output via `RelayVRFStory`. `RelayEquivocation` runs @@ -223,14 +230,14 @@ We track all validators' announced approval assignments for each candidate assoc tells us which validators were assigned to which candidates. We permit at most one assignment per candidate per story per validator, so one validator could be assigned under both -the `RelayVRFDelay` and `RelayEquivocation` criteria, but not under both `RelayVRFModulo` and `RelayVRFDelay` criteria, -since those both use the same story. We permit only one approval vote per candidate per validator, which counts for any -applicable criteria. +the `RelayVRFDelay` and `RelayEquivocation` criteria, but not under both `RelayVRFModulo/RelayVRFModuloCompact` +and `RelayVRFDelay` criteria, since those both use the same story. We permit only one approval vote per candidate per +validator, which counts for any applicable criteria. We announce, and start checking for, our own assignments when the delay of their tranche is reached, but only if the -tracker says the assignee candidate requires more approval checkers. We never announce an assignment we believe -unnecessary because early announcements gives an adversary information. All delay tranche zero assignments always get -announced, which includes all `RelayVRFModulo` assignments. +tracker says the assignee candidate requires more approval checkers. We never announce an assignment we believe unnecessary +because early announcements gives an adversary information. All delay tranche zero assignments always get announced, +which includes all `RelayVRFModulo` and `RelayVRFModuloCompact` assignments. In other words, if some candidate `C` needs more approval checkers by the time we reach round `t` then any validators with an assignment to `C` in delay tranche `t` gossip their send assignment notice for `C`, and begin reconstruction and @@ -318,10 +325,10 @@ finality. We might explore limits on postponement too, but this sounds much har ## Parameters -We prefer doing approval checkers assignments under `RelayVRFModulo` as opposed to `RelayVRFDelay` because -`RelayVRFModulo` avoids giving individual checkers too many assignments and tranche zero assignments benefit security -the most. We suggest assigning at least 16 checkers under `RelayVRFModulo` although assignment levels have never been -properly analyzed. +We prefer doing approval checkers assignments under `RelayVRFModulo` or `RelayVRFModuloCompact` as opposed to +`RelayVRFDelay` because `RelayVRFModulo` avoids giving individual checkers too many assignments and tranche zero +assignments benefit security the most. We suggest assigning at least 16 checkers under `RelayVRFModulo` or +`RelayVRFModuloCompact` although assignment levels have never been properly analyzed. Our delay criteria `RelayVRFDelay` and `RelayEquivocation` both have two primary paramaters, expected checkers per tranche and the zeroth delay tranche width. diff --git a/polkadot/roadmap/implementers-guide/src/types/approval.md b/polkadot/roadmap/implementers-guide/src/types/approval.md index bc33f024426f..c19ffa53762a 100644 --- a/polkadot/roadmap/implementers-guide/src/types/approval.md +++ b/polkadot/roadmap/implementers-guide/src/types/approval.md @@ -22,6 +22,35 @@ enum AssignmentCertKind { } } +enum AssignmentCertKindV2 { + /// Multiple assignment stories based on the VRF that authorized the relay-chain block where the + /// candidates were included. + /// + /// The context is [`v2::RELAY_VRF_MODULO_CONTEXT`] + RelayVRFModuloCompact { + /// A bitfield representing the core indices claimed by this assignment. + core_bitfield: CoreBitfield, + }, + /// An assignment story based on the VRF that authorized the relay-chain block where the + /// candidate was included combined with the index of a particular core. + /// + /// The context is [`v2::RELAY_VRF_DELAY_CONTEXT`] + RelayVRFDelay { + /// The core index chosen in this cert. + core_index: CoreIndex, + }, + /// Deprectated assignment. Soon to be removed. + /// + /// An assignment story based on the VRF that authorized the relay-chain block where the + /// candidate was included combined with a sample number. + /// + /// The context used to produce bytes is [`v1::RELAY_VRF_MODULO_CONTEXT`] + RelayVRFModulo { + /// The sample number used in this cert. + sample: u32, + }, +} + struct AssignmentCert { // The criterion which is claimed to be met by this cert. kind: AssignmentCertKind, diff --git a/polkadot/roadmap/implementers-guide/src/types/runtime.md b/polkadot/roadmap/implementers-guide/src/types/runtime.md index 79da899bd35e..4b97409f8df3 100644 --- a/polkadot/roadmap/implementers-guide/src/types/runtime.md +++ b/polkadot/roadmap/implementers-guide/src/types/runtime.md @@ -55,7 +55,7 @@ struct HostConfiguration { pub zeroth_delay_tranche_width: u32, /// The number of validators needed to approve a block. pub needed_approvals: u32, - /// The number of samples to do of the RelayVRFModulo approval assignment criterion. + /// The number of samples to use in `RelayVRFModulo` or `RelayVRFModuloCompact` approval assignment criterions. pub relay_vrf_modulo_samples: u32, /// Total number of individual messages allowed in the parachain -> relay-chain message queue. pub max_upward_queue_count: u32, diff --git a/polkadot/zombienet_tests/functional/0006-parachains-max-tranche0.toml b/polkadot/zombienet_tests/functional/0006-parachains-max-tranche0.toml new file mode 100644 index 000000000000..68ac90f5a7e1 --- /dev/null +++ b/polkadot/zombienet_tests/functional/0006-parachains-max-tranche0.toml @@ -0,0 +1,39 @@ +[settings] +timeout = 1000 +bootnode = true + +[relaychain.genesis.runtime.configuration.config] + max_validators_per_core = 1 + needed_approvals = 7 + relay_vrf_modulo_samples = 5 + +[relaychain] +default_image = "{{ZOMBIENET_INTEGRATION_TEST_IMAGE}}" +chain = "rococo-local" +default_command = "polkadot" + +[relaychain.default_resources] +limits = { memory = "4G", cpu = "2" } +requests = { memory = "2G", cpu = "1" } + + [[relaychain.node_groups]] + name = "some-validator" + count = 8 + args = ["-lparachain=debug,runtime=debug"] + +{% for id in range(2000,2005) %} +[[parachains]] +id = {{id}} +addToGenesis = true +genesis_state_generator = "undying-collator export-genesis-state --pov-size={{10000*(id-1999)}} --pvf-complexity={{id - 1999}}" + [parachains.collator] + image = "{{COL_IMAGE}}" + name = "collator" + command = "undying-collator" + args = ["-lparachain=debug", "--pov-size={{10000*(id-1999)}}", "--parachain-id={{id}}", "--pvf-complexity={{id - 1999}}"] +{% endfor %} + +[types.Header] +number = "u64" +parent_hash = "Hash" +post_state = "Hash" diff --git a/polkadot/zombienet_tests/functional/0006-parachains-max-tranche0.zndsl b/polkadot/zombienet_tests/functional/0006-parachains-max-tranche0.zndsl new file mode 100644 index 000000000000..6cec6c66797a --- /dev/null +++ b/polkadot/zombienet_tests/functional/0006-parachains-max-tranche0.zndsl @@ -0,0 +1,27 @@ +Description: Test if parachains make progress with most of approvals being tranch0 +Network: ./0006-parachains-max-tranche0.toml +Creds: config + +# Check authority status. +some-validator-0: reports node_roles is 4 +some-validator-1: reports node_roles is 4 +some-validator-3: reports node_roles is 4 +some-validator-4: reports node_roles is 4 +some-validator-5: reports node_roles is 4 +some-validator-6: reports node_roles is 4 +some-validator-7: reports node_roles is 4 + +some-validator-0: parachain 2000 block height is at least 5 within 180 seconds +some-validator-1: parachain 2001 block height is at least 5 within 180 seconds +some-validator-2: parachain 2002 block height is at least 5 within 180 seconds +some-validator-3: parachain 2003 block height is at least 5 within 180 seconds +some-validator-4: parachain 2004 block height is at least 5 within 180 seconds + +some-validator-0: reports polkadot_parachain_approval_checking_finality_lag is lower than 2 +some-validator-1: reports polkadot_parachain_approval_checking_finality_lag is lower than 2 +some-validator-2: reports polkadot_parachain_approval_checking_finality_lag is lower than 2 +some-validator-3: reports polkadot_parachain_approval_checking_finality_lag is lower than 2 +some-validator-4: reports polkadot_parachain_approval_checking_finality_lag is lower than 2 +some-validator-5: reports polkadot_parachain_approval_checking_finality_lag is lower than 2 +some-validator-6: reports polkadot_parachain_approval_checking_finality_lag is lower than 2 +some-validator-7: reports polkadot_parachain_approval_checking_finality_lag is lower than 2 diff --git a/prdoc/pr_1178.prdoc b/prdoc/pr_1178.prdoc new file mode 100644 index 000000000000..36c3b05c7a3f --- /dev/null +++ b/prdoc/pr_1178.prdoc @@ -0,0 +1,23 @@ +title: tranche0 assignments in one certificate part1 + +doc: + - audience: Node Operator + description: | + Changed approval-voting, approval-distribution to send all messages tranche0 assignments in one message. + This required: + * A new parachains_db version. + * A new validation protocol to support the new message types. + The new logic will be disabled and will be enabled at a later date after all validators have upgraded. + +migrations: + db: + - name: Parachains database change from v3 to v4. + description: | + Approval-voting column format has been updated with several new fields. All existing data will be automatically + be migrated to the new values. + +crates: + - name: "polkadot" + semver: patch + +host_functions: [] From 15df7f54d2c32a94bb91f73f19552c8bcde6a9fe Mon Sep 17 00:00:00 2001 From: Alin Dima Date: Mon, 6 Nov 2023 18:30:09 +0200 Subject: [PATCH 26/31] minor: overseer availability-distribution message declaration update (#2179) availability-distribution subsystem is not sending availability-recovery messages. Update the overseer declaration to reflect this --- polkadot/node/overseer/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/polkadot/node/overseer/src/lib.rs b/polkadot/node/overseer/src/lib.rs index 6802426d3c70..5207bb830d8c 100644 --- a/polkadot/node/overseer/src/lib.rs +++ b/polkadot/node/overseer/src/lib.rs @@ -498,7 +498,6 @@ pub struct Overseer { #[subsystem(AvailabilityDistributionMessage, sends: [ AvailabilityStoreMessage, - AvailabilityRecoveryMessage, ChainApiMessage, RuntimeApiMessage, NetworkBridgeTxMessage, From 32a974088c43d9abf849b15349268e0a8426845a Mon Sep 17 00:00:00 2001 From: Piet <75956460+PieWol@users.noreply.github.com> Date: Mon, 6 Nov 2023 19:40:14 +0100 Subject: [PATCH 27/31] TryDecodeEntireState check for storage types and pallets (#1805) ### This PR is a port of this [PR for substrate](https://github.com/paritytech/substrate/pull/13013) by @kianenigma Add infrastructure needed to have a Pallet::decode_entire_state(), which makes sure all "typed" storage items defined in the pallet are decode-able. This is not enforced in any way at the moment. Teams who wish to integrate/use this in the try-runtime feature flag should add frame_support::storage::migration::EnsureStateDecodes as the LAST ITEM of the runtime's custom migrations, and pass it to frame-executive. This will make it usable in try-runtime on-runtime-upgrade. This now catches cases like https://github.com/paritytech/polkadot-sdk/pull/1969: ```pre ERROR runtime::executive] failed to decode the value at key: Failed to decode value at key: 0x94eadf0156a8ad5156507773d0471e4ab8ebad86f546c7e0b135a4212aace339. Storage info StorageInfo { pallet_name: Ok("ParaScheduler"), storage_name: Ok("AvailabilityCores"), prefix: Err(Utf8Error { valid_up_to: 0, error_len: Some(1) }), max_values: Some(1), max_size: None }. Raw value: Some("0x0c010101010101") ``` ... or: ![image](https://github.com/paritytech/polkadot-sdk/assets/10380170/73052d4f-4da5-4b21-a8dd-b17004e5965e) Closes #241 --------- Signed-off-by: Oliver Tale-Yazdi Co-authored-by: Oliver Tale-Yazdi Co-authored-by: Liam Aharon --- prdoc/pr_1805.prdoc | 19 + .../bin/node-template/runtime/src/lib.rs | 7 + substrate/frame/executive/src/lib.rs | 72 ++- substrate/frame/glutton/src/lib.rs | 5 +- substrate/frame/support/Cargo.toml | 2 +- .../procedural/src/pallet/expand/event.rs | 3 +- .../procedural/src/pallet/expand/storage.rs | 59 +++ .../procedural/src/pallet/parse/storage.rs | 2 +- .../support/src/storage/generator/mod.rs | 6 +- .../support/src/storage/types/counted_map.rs | 14 +- .../support/src/storage/types/counted_nmap.rs | 6 +- .../frame/support/src/storage/types/mod.rs | 2 +- .../frame/support/src/storage/types/value.rs | 4 +- substrate/frame/support/src/traits.rs | 5 +- substrate/frame/support/src/traits/storage.rs | 4 +- .../traits/try_runtime/decode_entire_state.rs | 498 ++++++++++++++++++ .../{try_runtime.rs => try_runtime/mod.rs} | 17 + ...age_ensure_span_are_ok_on_wrong_gen.stderr | 59 +++ ...re_span_are_ok_on_wrong_gen_unnamed.stderr | 59 +++ 19 files changed, 807 insertions(+), 36 deletions(-) create mode 100644 prdoc/pr_1805.prdoc create mode 100644 substrate/frame/support/src/traits/try_runtime/decode_entire_state.rs rename substrate/frame/support/src/traits/{try_runtime.rs => try_runtime/mod.rs} (94%) diff --git a/prdoc/pr_1805.prdoc b/prdoc/pr_1805.prdoc new file mode 100644 index 000000000000..8a8e6c2fde26 --- /dev/null +++ b/prdoc/pr_1805.prdoc @@ -0,0 +1,19 @@ +title: Introduce state decoding check after runtime upgrades. + +doc: + - audience: Core Dev + description: | + Adds a check to the try-runtime logic that will verify that all pallet on-chain storage still decodes. This can help to spot missing migrations before they become a problem. The check is enabled as soon as the `--checks` option of the `try-runtime` CLI is not `None`. + +migrations: + db: [] + + runtime: [] + +crates: + - name: frame-support + semver: minor + - name: frame-support-procedural + semver: minor + +host_functions: [] diff --git a/substrate/bin/node-template/runtime/src/lib.rs b/substrate/bin/node-template/runtime/src/lib.rs index 62c24081cbea..6aa4cb70fde1 100644 --- a/substrate/bin/node-template/runtime/src/lib.rs +++ b/substrate/bin/node-template/runtime/src/lib.rs @@ -313,6 +313,12 @@ pub type SignedExtra = ( pallet_transaction_payment::ChargeTransactionPayment, ); +/// All migrations of the runtime, aside from the ones declared in the pallets. +/// +/// This can be a tuple of types, each implementing `OnRuntimeUpgrade`. +#[allow(unused_parens)] +type Migrations = (); + /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; @@ -325,6 +331,7 @@ pub type Executive = frame_executive::Executive< frame_system::ChainContext, Runtime, AllPalletsWithSystem, + Migrations, >; #[cfg(feature = "runtime-benchmarks")] diff --git a/substrate/frame/executive/src/lib.rs b/substrate/frame/executive/src/lib.rs index 1ca9629fd420..5f390c77baff 100644 --- a/substrate/frame/executive/src/lib.rs +++ b/substrate/frame/executive/src/lib.rs @@ -139,9 +139,15 @@ use sp_runtime::{ use sp_std::{marker::PhantomData, prelude::*}; #[cfg(feature = "try-runtime")] -use log; -#[cfg(feature = "try-runtime")] -use sp_runtime::TryRuntimeError; +use ::{ + frame_support::{ + traits::{TryDecodeEntireStorage, TryDecodeEntireStorageError, TryState}, + StorageNoopGuard, + }, + frame_try_runtime::{TryStateSelect, UpgradeCheckSelect}, + log, + sp_runtime::TryRuntimeError, +}; #[allow(dead_code)] const LOG_TARGET: &str = "runtime::executive"; @@ -229,7 +235,8 @@ impl< + OnIdle> + OnFinalize> + OffchainWorker> - + frame_support::traits::TryState>, + + TryState> + + TryDecodeEntireStorage, COnRuntimeUpgrade: OnRuntimeUpgrade, > Executive where @@ -308,11 +315,15 @@ where let _guard = frame_support::StorageNoopGuard::default(); , - >>::try_state(*header.number(), select) + >>::try_state(*header.number(), select.clone()) .map_err(|e| { log::error!(target: LOG_TARGET, "failure: {:?}", e); e })?; + if select.any() { + let res = AllPalletsWithSystem::try_decode_entire_state(); + Self::log_decode_result(res)?; + } drop(_guard); // do some of the checks that would normally happen in `final_checks`, but perhaps skip @@ -352,26 +363,61 @@ where /// Execute all `OnRuntimeUpgrade` of this runtime. /// /// The `checks` param determines whether to execute `pre/post_upgrade` and `try_state` hooks. - pub fn try_runtime_upgrade( - checks: frame_try_runtime::UpgradeCheckSelect, - ) -> Result { + pub fn try_runtime_upgrade(checks: UpgradeCheckSelect) -> Result { let weight = <(COnRuntimeUpgrade, AllPalletsWithSystem) as OnRuntimeUpgrade>::try_on_runtime_upgrade( checks.pre_and_post(), )?; + // Nothing should modify the state after the migrations ran: + let _guard = StorageNoopGuard::default(); + // The state must be decodable: + if checks.any() { + let res = AllPalletsWithSystem::try_decode_entire_state(); + Self::log_decode_result(res)?; + } + + // Check all storage invariants: if checks.try_state() { - let _guard = frame_support::StorageNoopGuard::default(); - , - >>::try_state( + AllPalletsWithSystem::try_state( frame_system::Pallet::::block_number(), - frame_try_runtime::TryStateSelect::All, + TryStateSelect::All, )?; } Ok(weight) } + + /// Logs the result of trying to decode the entire state. + fn log_decode_result( + res: Result>, + ) -> Result<(), TryRuntimeError> { + match res { + Ok(bytes) => { + log::debug!( + target: LOG_TARGET, + "decoded the entire state ({bytes} bytes)", + ); + + Ok(()) + }, + Err(errors) => { + log::error!( + target: LOG_TARGET, + "`try_decode_entire_state` failed with {} errors", + errors.len(), + ); + + for (i, err) in errors.iter().enumerate() { + // We log the short version to `error` and then the full debug info to `debug`: + log::error!(target: LOG_TARGET, "- {i}. error: {err}"); + log::debug!(target: LOG_TARGET, "- {i}. error: {err:?}"); + } + + Err("`try_decode_entire_state` failed".into()) + }, + } + } } impl< diff --git a/substrate/frame/glutton/src/lib.rs b/substrate/frame/glutton/src/lib.rs index f5e90a17c735..344a70becaeb 100644 --- a/substrate/frame/glutton/src/lib.rs +++ b/substrate/frame/glutton/src/lib.rs @@ -21,9 +21,8 @@ //! //! # Glutton Pallet //! -//! Pallet that consumes `ref_time` and `proof_size` of a block. Based on the -//! `Compute` and `Storage` parameters the pallet consumes the adequate amount -//! of weight. +//! Pallet that consumes `ref_time` and `proof_size` of a block. Based on the `Compute` and +//! `Storage` parameters the pallet consumes the adequate amount of weight. #![deny(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] diff --git a/substrate/frame/support/Cargo.toml b/substrate/frame/support/Cargo.toml index f8c8edc05104..b8e21e60761a 100644 --- a/substrate/frame/support/Cargo.toml +++ b/substrate/frame/support/Cargo.toml @@ -13,6 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] +array-bytes = { version = "6.1", default-features = false } serde = { version = "1.0.188", default-features = false, features = ["alloc", "derive"] } codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive", "max-encoded-len"] } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } @@ -52,7 +53,6 @@ aquamarine = { version = "0.3.2" } assert_matches = "1.3.0" pretty_assertions = "1.2.1" frame-system = { path = "../system" } -array-bytes = "6.1" [features] default = [ "std" ] diff --git a/substrate/frame/support/procedural/src/pallet/expand/event.rs b/substrate/frame/support/procedural/src/pallet/expand/event.rs index fbb699b4d41c..2713f45fc3d5 100644 --- a/substrate/frame/support/procedural/src/pallet/expand/event.rs +++ b/substrate/frame/support/procedural/src/pallet/expand/event.rs @@ -127,11 +127,12 @@ pub fn expand_event(def: &mut Def) -> proc_macro2::TokenStream { let trait_use_gen = &def.trait_use_generics(event.attr_span); let type_impl_gen = &def.type_impl_generics(event.attr_span); let type_use_gen = &def.type_use_generics(event.attr_span); + let pallet_ident = &def.pallet_struct.pallet; let PalletEventDepositAttr { fn_vis, fn_span, .. } = deposit_event; quote::quote_spanned!(*fn_span => - impl<#type_impl_gen> Pallet<#type_use_gen> #completed_where_clause { + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { #fn_vis fn deposit_event(event: Event<#event_use_gen>) { let event = < ::RuntimeEvent as diff --git a/substrate/frame/support/procedural/src/pallet/expand/storage.rs b/substrate/frame/support/procedural/src/pallet/expand/storage.rs index e7f7cf548f0e..96c2c8e3120b 100644 --- a/substrate/frame/support/procedural/src/pallet/expand/storage.rs +++ b/substrate/frame/support/procedural/src/pallet/expand/storage.rs @@ -822,12 +822,69 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream { ) }); + // aggregated where clause of all storage types and the whole pallet. let mut where_clauses = vec![&def.config.where_clause]; where_clauses.extend(def.storages.iter().map(|storage| &storage.where_clause)); let completed_where_clause = super::merge_where_clauses(&where_clauses); let type_impl_gen = &def.type_impl_generics(proc_macro2::Span::call_site()); let type_use_gen = &def.type_use_generics(proc_macro2::Span::call_site()); + let try_decode_entire_state = { + let mut storage_names = def + .storages + .iter() + .filter_map(|storage| { + if storage.cfg_attrs.is_empty() { + let ident = &storage.ident; + let gen = &def.type_use_generics(storage.attr_span); + Some(quote::quote_spanned!(storage.attr_span => #ident<#gen> )) + } else { + None + } + }) + .collect::>(); + storage_names.sort_by_cached_key(|ident| ident.to_string()); + + quote::quote!( + #[cfg(feature = "try-runtime")] + impl<#type_impl_gen> #frame_support::traits::TryDecodeEntireStorage + for #pallet_ident<#type_use_gen> #completed_where_clause + { + fn try_decode_entire_state() -> Result> { + let pallet_name = <::PalletInfo as frame_support::traits::PalletInfo> + ::name::<#pallet_ident<#type_use_gen>>() + .expect("Every active pallet has a name in the runtime; qed"); + + #frame_support::__private::log::debug!(target: "runtime::try-decode-state", "trying to decode pallet: {pallet_name}"); + + // NOTE: for now, we have to exclude storage items that are feature gated. + let mut errors = #frame_support::__private::sp_std::vec::Vec::new(); + let mut decoded = 0usize; + + #( + #frame_support::__private::log::debug!(target: "runtime::try-decode-state", "trying to decode storage: \ + {pallet_name}::{}", stringify!(#storage_names)); + + match <#storage_names as #frame_support::traits::TryDecodeEntireStorage>::try_decode_entire_state() { + Ok(count) => { + decoded += count; + }, + Err(err) => { + errors.extend(err); + }, + } + )* + + if errors.is_empty() { + Ok(decoded) + } else { + Err(errors) + } + } + } + ) + }; + quote::quote!( impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause @@ -853,5 +910,7 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream { #( #getters )* #( #prefix_structs )* #( #on_empty_structs )* + + #try_decode_entire_state ) } diff --git a/substrate/frame/support/procedural/src/pallet/parse/storage.rs b/substrate/frame/support/procedural/src/pallet/parse/storage.rs index 3a0ec4747153..d1c7ba2e5e3c 100644 --- a/substrate/frame/support/procedural/src/pallet/parse/storage.rs +++ b/substrate/frame/support/procedural/src/pallet/parse/storage.rs @@ -151,7 +151,7 @@ pub enum QueryKind { /// `type MyStorage = StorageValue` /// The keys and values types are parsed in order to get metadata pub struct StorageDef { - /// The index of error item in pallet module. + /// The index of storage item in pallet module. pub index: usize, /// Visibility of the storage type. pub vis: syn::Visibility, diff --git a/substrate/frame/support/src/storage/generator/mod.rs b/substrate/frame/support/src/storage/generator/mod.rs index bac9f642e37d..2b2abdc2e830 100644 --- a/substrate/frame/support/src/storage/generator/mod.rs +++ b/substrate/frame/support/src/storage/generator/mod.rs @@ -24,10 +24,10 @@ //! //! This is internal api and is subject to change. -mod double_map; +pub(crate) mod double_map; pub(crate) mod map; -mod nmap; -mod value; +pub(crate) mod nmap; +pub(crate) mod value; pub use double_map::StorageDoubleMap; pub use map::StorageMap; diff --git a/substrate/frame/support/src/storage/types/counted_map.rs b/substrate/frame/support/src/storage/types/counted_map.rs index 75fbdf2617d1..04e69751c16a 100644 --- a/substrate/frame/support/src/storage/types/counted_map.rs +++ b/substrate/frame/support/src/storage/types/counted_map.rs @@ -119,7 +119,11 @@ impl MapWrapper type Map = StorageMap; } -type CounterFor

= StorageValue<

::CounterPrefix, u32, ValueQuery>; +/// The numeric counter type. +pub type Counter = u32; + +type CounterFor

= + StorageValue<

::CounterPrefix, Counter, ValueQuery>; /// On removal logic for updating counter while draining upon some prefix with /// [`crate::storage::PrefixIterator`]. @@ -423,14 +427,14 @@ where /// can be very heavy, so use with caution. /// /// Returns the number of items in the map which is used to set the counter. - pub fn initialize_counter() -> u32 { - let count = Self::iter_values().count() as u32; + pub fn initialize_counter() -> Counter { + let count = Self::iter_values().count() as Counter; CounterFor::::set(count); count } /// Return the count. - pub fn count() -> u32 { + pub fn count() -> Counter { CounterFor::::get() } } @@ -1207,7 +1211,7 @@ mod test { StorageEntryMetadataIR { name: "counter_for_foo", modifier: StorageEntryModifierIR::Default, - ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), + ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), default: vec![0, 0, 0, 0], docs: if cfg!(feature = "no-metadata-docs") { vec![] diff --git a/substrate/frame/support/src/storage/types/counted_nmap.rs b/substrate/frame/support/src/storage/types/counted_nmap.rs index c2c2197aceee..279894ee9736 100644 --- a/substrate/frame/support/src/storage/types/counted_nmap.rs +++ b/substrate/frame/support/src/storage/types/counted_nmap.rs @@ -114,8 +114,10 @@ impl MapWrapper type Map = StorageNMap; } +type Counter = super::counted_map::Counter; + type CounterFor

= - StorageValue<

::CounterPrefix, u32, ValueQuery>; + StorageValue<

::CounterPrefix, Counter, ValueQuery>; /// On removal logic for updating counter while draining upon some prefix with /// [`crate::storage::PrefixIterator`]. @@ -472,7 +474,7 @@ where } /// Return the count. - pub fn count() -> u32 { + pub fn count() -> Counter { CounterFor::::get() } } diff --git a/substrate/frame/support/src/storage/types/mod.rs b/substrate/frame/support/src/storage/types/mod.rs index 1d995d93e882..9dd6f4066e43 100644 --- a/substrate/frame/support/src/storage/types/mod.rs +++ b/substrate/frame/support/src/storage/types/mod.rs @@ -30,7 +30,7 @@ mod map; mod nmap; mod value; -pub use counted_map::{CountedStorageMap, CountedStorageMapInstance}; +pub use counted_map::{CountedStorageMap, CountedStorageMapInstance, Counter}; pub use counted_nmap::{CountedStorageNMap, CountedStorageNMapInstance}; pub use double_map::StorageDoubleMap; pub use key::{ diff --git a/substrate/frame/support/src/storage/types/value.rs b/substrate/frame/support/src/storage/types/value.rs index 34cc3e24956b..263091dd2523 100644 --- a/substrate/frame/support/src/storage/types/value.rs +++ b/substrate/frame/support/src/storage/types/value.rs @@ -23,7 +23,7 @@ use crate::{ types::{OptionQuery, QueryKindTrait, StorageEntryMetadataBuilder}, StorageAppend, StorageDecodeLength, StorageTryAppend, }, - traits::{GetDefault, StorageInfo, StorageInstance}, + traits::{Get, GetDefault, StorageInfo, StorageInstance}, }; use codec::{Decode, Encode, EncodeLike, FullCodec, MaxEncodedLen}; use frame_support::storage::StorageDecodeNonDedupLength; @@ -72,7 +72,7 @@ where Prefix: StorageInstance, Value: FullCodec, QueryKind: QueryKindTrait, - OnEmpty: crate::traits::Get + 'static, + OnEmpty: Get + 'static, { type Query = QueryKind::Query; fn pallet_prefix() -> &'static [u8] { diff --git a/substrate/frame/support/src/traits.rs b/substrate/frame/support/src/traits.rs index 4c692f848fde..12cc84027c48 100644 --- a/substrate/frame/support/src/traits.rs +++ b/substrate/frame/support/src/traits.rs @@ -126,4 +126,7 @@ pub use tx_pause::{TransactionPause, TransactionPauseError}; #[cfg(feature = "try-runtime")] mod try_runtime; #[cfg(feature = "try-runtime")] -pub use try_runtime::{Select as TryStateSelect, TryState, UpgradeCheckSelect}; +pub use try_runtime::{ + Select as TryStateSelect, TryDecodeEntireStorage, TryDecodeEntireStorageError, TryState, + UpgradeCheckSelect, +}; diff --git a/substrate/frame/support/src/traits/storage.rs b/substrate/frame/support/src/traits/storage.rs index fe1b9bf13bb0..9e467aea4220 100644 --- a/substrate/frame/support/src/traits/storage.rs +++ b/substrate/frame/support/src/traits/storage.rs @@ -93,9 +93,7 @@ pub trait StorageInstance { } /// Metadata about storage from the runtime. -#[derive( - codec::Encode, codec::Decode, RuntimeDebug, Eq, PartialEq, Clone, scale_info::TypeInfo, -)] +#[derive(Debug, codec::Encode, codec::Decode, Eq, PartialEq, Clone, scale_info::TypeInfo)] pub struct StorageInfo { /// Encoded string of pallet name. pub pallet_name: Vec, diff --git a/substrate/frame/support/src/traits/try_runtime/decode_entire_state.rs b/substrate/frame/support/src/traits/try_runtime/decode_entire_state.rs new file mode 100644 index 000000000000..afbf291a0bbb --- /dev/null +++ b/substrate/frame/support/src/traits/try_runtime/decode_entire_state.rs @@ -0,0 +1,498 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Types to check that the entire storage can be decoded. + +use super::StorageInstance; +use crate::{ + storage::types::{ + CountedStorageMapInstance, CountedStorageNMapInstance, Counter, KeyGenerator, + QueryKindTrait, + }, + traits::{PartialStorageInfoTrait, StorageInfo}, + StorageHasher, +}; +use codec::{Decode, DecodeAll, FullCodec}; +use impl_trait_for_tuples::impl_for_tuples; +use sp_core::Get; +use sp_std::prelude::*; + +/// Decode the entire data under the given storage type. +/// +/// For values, this is trivial. For all kinds of maps, it should decode all the values associated +/// with all keys existing in the map. +/// +/// Tuple implementations are provided and simply decode each type in the tuple, summing up the +/// decoded bytes if `Ok(_)`. +pub trait TryDecodeEntireStorage { + /// Decode the entire data under the given storage, returning `Ok(bytes_decoded)` if success. + fn try_decode_entire_state() -> Result>; +} + +#[cfg_attr(all(not(feature = "tuples-96"), not(feature = "tuples-128")), impl_for_tuples(64))] +#[cfg_attr(all(feature = "tuples-96", not(feature = "tuples-128")), impl_for_tuples(96))] +#[cfg_attr(feature = "tuples-128", impl_for_tuples(128))] +impl TryDecodeEntireStorage for Tuple { + fn try_decode_entire_state() -> Result> { + let mut errors = Vec::new(); + let mut len = 0usize; + + for_tuples!(#( + match Tuple::try_decode_entire_state() { + Ok(bytes) => len += bytes, + Err(errs) => errors.extend(errs), + } + )*); + + if errors.is_empty() { + Ok(len) + } else { + Err(errors) + } + } +} + +/// A value could not be decoded. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct TryDecodeEntireStorageError { + /// The key of the undecodable value. + pub key: Vec, + /// The raw value. + pub raw: Option>, + /// The storage info of the key. + pub info: StorageInfo, +} + +impl core::fmt::Display for TryDecodeEntireStorageError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!( + f, + "Failed to decode storage item `{}::{}`", + &sp_std::str::from_utf8(&self.info.pallet_name).unwrap_or(""), + &sp_std::str::from_utf8(&self.info.storage_name).unwrap_or(""), + ) + } +} + +/// Decode all the values based on the prefix of `info` to `V`. +/// +/// Basically, it decodes and sums up all the values who's key start with `info.prefix`. For values, +/// this would be the value itself. For all sorts of maps, this should be all map items in the +/// absence of key collision. +fn decode_storage_info( + info: StorageInfo, +) -> Result> { + let mut next_key = info.prefix.clone(); + let mut decoded = 0; + + let decode_key = |key: &[u8]| match sp_io::storage::get(key) { + None => Ok(0), + Some(bytes) => { + let len = bytes.len(); + let _ = ::decode_all(&mut bytes.as_ref()).map_err(|_| { + vec![TryDecodeEntireStorageError { + key: key.to_vec(), + raw: Some(bytes.to_vec()), + info: info.clone(), + }] + })?; + + Ok::>(len) + }, + }; + + decoded += decode_key(&next_key)?; + loop { + match sp_io::storage::next_key(&next_key) { + Some(key) if key.starts_with(&info.prefix) => { + decoded += decode_key(&key)?; + next_key = key; + }, + _ => break, + } + } + + Ok(decoded) +} + +impl TryDecodeEntireStorage + for crate::storage::types::StorageValue +where + Prefix: StorageInstance, + Value: FullCodec, + QueryKind: QueryKindTrait, + OnEmpty: Get + 'static, +{ + fn try_decode_entire_state() -> Result> { + let info = Self::partial_storage_info() + .first() + .cloned() + .expect("Value has only one storage info; qed"); + decode_storage_info::(info) + } +} + +impl TryDecodeEntireStorage + for crate::storage::types::StorageMap +where + Prefix: StorageInstance, + Hasher: StorageHasher, + Key: FullCodec, + Value: FullCodec, + QueryKind: QueryKindTrait, + OnEmpty: Get + 'static, + MaxValues: Get>, +{ + fn try_decode_entire_state() -> Result> { + let info = Self::partial_storage_info() + .first() + .cloned() + .expect("Map has only one storage info; qed"); + decode_storage_info::(info) + } +} + +impl TryDecodeEntireStorage + for crate::storage::types::CountedStorageMap< + Prefix, + Hasher, + Key, + Value, + QueryKind, + OnEmpty, + MaxValues, + > where + Prefix: CountedStorageMapInstance, + Hasher: StorageHasher, + Key: FullCodec, + Value: FullCodec, + QueryKind: QueryKindTrait, + OnEmpty: Get + 'static, + MaxValues: Get>, +{ + fn try_decode_entire_state() -> Result> { + let (map_info, counter_info) = match &Self::partial_storage_info()[..] { + [a, b] => (a.clone(), b.clone()), + _ => panic!("Counted map has two storage info items; qed"), + }; + let mut decoded = decode_storage_info::(counter_info)?; + decoded += decode_storage_info::(map_info)?; + Ok(decoded) + } +} + +impl + TryDecodeEntireStorage + for crate::storage::types::StorageDoubleMap< + Prefix, + Hasher1, + Key1, + Hasher2, + Key2, + Value, + QueryKind, + OnEmpty, + MaxValues, + > where + Prefix: StorageInstance, + Hasher1: StorageHasher, + Key1: FullCodec, + Hasher2: StorageHasher, + Key2: FullCodec, + Value: FullCodec, + QueryKind: QueryKindTrait, + OnEmpty: Get + 'static, + MaxValues: Get>, +{ + fn try_decode_entire_state() -> Result> { + let info = Self::partial_storage_info() + .first() + .cloned() + .expect("Double-map has only one storage info; qed"); + decode_storage_info::(info) + } +} + +impl TryDecodeEntireStorage + for crate::storage::types::StorageNMap +where + Prefix: StorageInstance, + Key: KeyGenerator, + Value: FullCodec, + QueryKind: QueryKindTrait, + OnEmpty: Get + 'static, + MaxValues: Get>, +{ + fn try_decode_entire_state() -> Result> { + let info = Self::partial_storage_info() + .first() + .cloned() + .expect("N-map has only one storage info; qed"); + decode_storage_info::(info) + } +} + +impl TryDecodeEntireStorage + for crate::storage::types::CountedStorageNMap +where + Prefix: CountedStorageNMapInstance, + Key: KeyGenerator, + Value: FullCodec, + QueryKind: QueryKindTrait, + OnEmpty: Get + 'static, + MaxValues: Get>, +{ + fn try_decode_entire_state() -> Result> { + let (map_info, counter_info) = match &Self::partial_storage_info()[..] { + [a, b] => (a.clone(), b.clone()), + _ => panic!("Counted NMap has two storage info items; qed"), + }; + + let mut decoded = decode_storage_info::(counter_info)?; + decoded += decode_storage_info::(map_info)?; + Ok(decoded) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + storage::types::{self, CountedStorageMapInstance, CountedStorageNMapInstance, Key}, + Blake2_128Concat, + }; + + type H = Blake2_128Concat; + + macro_rules! build_prefix { + ($name:ident) => { + struct $name; + impl StorageInstance for $name { + fn pallet_prefix() -> &'static str { + "test_pallet" + } + const STORAGE_PREFIX: &'static str = stringify!($name); + } + }; + } + + build_prefix!(ValuePrefix); + type Value = types::StorageValue; + + build_prefix!(MapPrefix); + type Map = types::StorageMap; + + build_prefix!(CMapCounterPrefix); + build_prefix!(CMapPrefix); + impl CountedStorageMapInstance for CMapPrefix { + type CounterPrefix = CMapCounterPrefix; + } + type CMap = types::CountedStorageMap; + + build_prefix!(DMapPrefix); + type DMap = types::StorageDoubleMap; + + build_prefix!(NMapPrefix); + type NMap = types::StorageNMap, Key), u128>; + + build_prefix!(CountedNMapCounterPrefix); + build_prefix!(CountedNMapPrefix); + impl CountedStorageNMapInstance for CountedNMapPrefix { + type CounterPrefix = CountedNMapCounterPrefix; + } + type CNMap = types::CountedStorageNMap, Key), u128>; + + #[test] + fn try_decode_entire_state_value_works() { + sp_io::TestExternalities::new_empty().execute_with(|| { + assert_eq!(Value::try_decode_entire_state(), Ok(0)); + + Value::put(42); + assert_eq!(Value::try_decode_entire_state(), Ok(4)); + + Value::kill(); + assert_eq!(Value::try_decode_entire_state(), Ok(0)); + + // two bytes, cannot be decoded into u32. + sp_io::storage::set(&Value::hashed_key(), &[0u8, 1]); + assert!(Value::try_decode_entire_state().is_err()); + }) + } + + #[test] + fn try_decode_entire_state_map_works() { + sp_io::TestExternalities::new_empty().execute_with(|| { + assert_eq!(Map::try_decode_entire_state(), Ok(0)); + + Map::insert(0, 42); + assert_eq!(Map::try_decode_entire_state(), Ok(4)); + + Map::insert(0, 42); + assert_eq!(Map::try_decode_entire_state(), Ok(4)); + + Map::insert(1, 42); + assert_eq!(Map::try_decode_entire_state(), Ok(8)); + + Map::remove(0); + assert_eq!(Map::try_decode_entire_state(), Ok(4)); + + // two bytes, cannot be decoded into u32. + sp_io::storage::set(&Map::hashed_key_for(2), &[0u8, 1]); + assert!(Map::try_decode_entire_state().is_err()); + }) + } + + #[test] + fn try_decode_entire_state_counted_map_works() { + sp_io::TestExternalities::new_empty().execute_with(|| { + // counter is not even initialized; + assert_eq!(CMap::try_decode_entire_state(), Ok(0 + 0)); + + let counter = 4; + let value_size = std::mem::size_of::(); + + CMap::insert(0, 42); + assert_eq!(CMap::try_decode_entire_state(), Ok(value_size + counter)); + + CMap::insert(0, 42); + assert_eq!(CMap::try_decode_entire_state(), Ok(value_size + counter)); + + CMap::insert(1, 42); + assert_eq!(CMap::try_decode_entire_state(), Ok(value_size * 2 + counter)); + + CMap::remove(0); + assert_eq!(CMap::try_decode_entire_state(), Ok(value_size + counter)); + + // counter is cleared again. + let _ = CMap::clear(u32::MAX, None); + assert_eq!(CMap::try_decode_entire_state(), Ok(0 + 0)); + + // 1 bytes, cannot be decoded into u16. + sp_io::storage::set(&CMap::hashed_key_for(2), &[0u8]); + assert!(CMap::try_decode_entire_state().is_err()); + }) + } + + #[test] + fn try_decode_entire_state_double_works() { + sp_io::TestExternalities::new_empty().execute_with(|| { + assert_eq!(DMap::try_decode_entire_state(), Ok(0)); + + DMap::insert(0, 0, 42); + assert_eq!(DMap::try_decode_entire_state(), Ok(4)); + + DMap::insert(0, 0, 42); + assert_eq!(DMap::try_decode_entire_state(), Ok(4)); + + DMap::insert(0, 1, 42); + assert_eq!(DMap::try_decode_entire_state(), Ok(8)); + + DMap::insert(1, 0, 42); + assert_eq!(DMap::try_decode_entire_state(), Ok(12)); + + DMap::remove(0, 0); + assert_eq!(DMap::try_decode_entire_state(), Ok(8)); + + // two bytes, cannot be decoded into u32. + sp_io::storage::set(&DMap::hashed_key_for(1, 1), &[0u8, 1]); + assert!(DMap::try_decode_entire_state().is_err()); + }) + } + + #[test] + fn try_decode_entire_state_n_map_works() { + sp_io::TestExternalities::new_empty().execute_with(|| { + assert_eq!(NMap::try_decode_entire_state(), Ok(0)); + + let value_size = std::mem::size_of::(); + + NMap::insert((0u8, 0), 42); + assert_eq!(NMap::try_decode_entire_state(), Ok(value_size)); + + NMap::insert((0, 0), 42); + assert_eq!(NMap::try_decode_entire_state(), Ok(value_size)); + + NMap::insert((0, 1), 42); + assert_eq!(NMap::try_decode_entire_state(), Ok(value_size * 2)); + + NMap::insert((1, 0), 42); + assert_eq!(NMap::try_decode_entire_state(), Ok(value_size * 3)); + + NMap::remove((0, 0)); + assert_eq!(NMap::try_decode_entire_state(), Ok(value_size * 2)); + + // two bytes, cannot be decoded into u128. + sp_io::storage::set(&NMap::hashed_key_for((1, 1)), &[0u8, 1]); + assert!(NMap::try_decode_entire_state().is_err()); + }) + } + + #[test] + fn try_decode_entire_state_counted_n_map_works() { + sp_io::TestExternalities::new_empty().execute_with(|| { + sp_io::TestExternalities::new_empty().execute_with(|| { + assert_eq!(NMap::try_decode_entire_state(), Ok(0)); + + let value_size = std::mem::size_of::(); + let counter = 4; + + CNMap::insert((0u8, 0), 42); + assert_eq!(CNMap::try_decode_entire_state(), Ok(value_size + counter)); + + CNMap::insert((0, 0), 42); + assert_eq!(CNMap::try_decode_entire_state(), Ok(value_size + counter)); + + CNMap::insert((0, 1), 42); + assert_eq!(CNMap::try_decode_entire_state(), Ok(value_size * 2 + counter)); + + CNMap::insert((1, 0), 42); + assert_eq!(CNMap::try_decode_entire_state(), Ok(value_size * 3 + counter)); + + CNMap::remove((0, 0)); + assert_eq!(CNMap::try_decode_entire_state(), Ok(value_size * 2 + counter)); + + // two bytes, cannot be decoded into u128. + sp_io::storage::set(&CNMap::hashed_key_for((1, 1)), &[0u8, 1]); + assert!(CNMap::try_decode_entire_state().is_err()); + }) + }) + } + + #[test] + fn extra_bytes_are_rejected() { + sp_io::TestExternalities::new_empty().execute_with(|| { + assert_eq!(Map::try_decode_entire_state(), Ok(0)); + + // 6bytes, too many to fit in u32, should be rejected. + sp_io::storage::set(&Map::hashed_key_for(2), &[0u8, 1, 3, 4, 5, 6]); + assert!(Map::try_decode_entire_state().is_err()); + }) + } + + #[test] + fn try_decode_entire_state_tuple_of_storage_works() { + sp_io::TestExternalities::new_empty().execute_with(|| { + assert_eq!(<(Value, Map) as TryDecodeEntireStorage>::try_decode_entire_state(), Ok(0)); + + Value::put(42); + assert_eq!(<(Value, Map) as TryDecodeEntireStorage>::try_decode_entire_state(), Ok(4)); + + Map::insert(0, 42); + assert_eq!(<(Value, Map) as TryDecodeEntireStorage>::try_decode_entire_state(), Ok(8)); + }); + } +} diff --git a/substrate/frame/support/src/traits/try_runtime.rs b/substrate/frame/support/src/traits/try_runtime/mod.rs similarity index 94% rename from substrate/frame/support/src/traits/try_runtime.rs rename to substrate/frame/support/src/traits/try_runtime/mod.rs index e7a1fe109fc2..bec2dbf549a1 100644 --- a/substrate/frame/support/src/traits/try_runtime.rs +++ b/substrate/frame/support/src/traits/try_runtime/mod.rs @@ -17,6 +17,11 @@ //! Try-runtime specific traits and types. +pub mod decode_entire_state; +pub use decode_entire_state::{TryDecodeEntireStorage, TryDecodeEntireStorageError}; + +use super::StorageInstance; + use impl_trait_for_tuples::impl_for_tuples; use sp_arithmetic::traits::AtLeast32BitUnsigned; use sp_runtime::TryRuntimeError; @@ -37,6 +42,13 @@ pub enum Select { Only(Vec>), } +impl Select { + /// Whether to run any checks at all. + pub fn any(&self) -> bool { + !matches!(self, Select::None) + } +} + impl Default for Select { fn default() -> Self { Select::None @@ -105,6 +117,11 @@ impl UpgradeCheckSelect { pub fn try_state(&self) -> bool { matches!(self, Self::All | Self::TryState) } + + /// Whether to run any checks at all. + pub fn any(&self) -> bool { + !matches!(self, Self::None) + } } #[cfg(feature = "std")] diff --git a/substrate/frame/support/test/tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen.stderr b/substrate/frame/support/test/tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen.stderr index 930af1d7fcb3..7375bcd2f16a 100644 --- a/substrate/frame/support/test/tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen.stderr +++ b/substrate/frame/support/test/tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen.stderr @@ -128,3 +128,62 @@ error[E0277]: the trait bound `Bar: WrapperTypeEncode` is not satisfied = note: required for `Bar` to implement `FullEncode` = note: required for `Bar` to implement `FullCodec` = note: required for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo, Bar>` to implement `StorageEntryMetadataBuilder` + +error[E0277]: the trait bound `Bar: WrapperTypeDecode` is not satisfied + --> tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen.rs:18:1 + | +18 | #[frame_support::pallet] + | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `WrapperTypeDecode` is not implemented for `Bar` + | + = help: the following other types implement trait `WrapperTypeDecode`: + Box + frame_support::sp_runtime::sp_application_crypto::sp_core::Bytes + Rc + Arc + = note: required for `Bar` to implement `Decode` + = note: required for `Bar` to implement `FullCodec` + = note: required for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo, Bar>` to implement `TryDecodeEntireStorage` + = note: this error originates in the attribute macro `frame_support::pallet` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `Bar: EncodeLike` is not satisfied + --> tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen.rs:18:1 + | +18 | #[frame_support::pallet] + | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `EncodeLike` is not implemented for `Bar` + | + = help: the following other types implement trait `EncodeLike`: + + + + + + + + + and $N others + = note: required for `Bar` to implement `FullEncode` + = note: required for `Bar` to implement `FullCodec` + = note: required for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo, Bar>` to implement `TryDecodeEntireStorage` + = note: this error originates in the attribute macro `frame_support::pallet` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `Bar: WrapperTypeEncode` is not satisfied + --> tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen.rs:18:1 + | +18 | #[frame_support::pallet] + | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `WrapperTypeEncode` is not implemented for `Bar` + | + = help: the following other types implement trait `WrapperTypeEncode`: + Box + bytes::bytes::Bytes + Cow<'a, T> + parity_scale_codec::Ref<'a, T, U> + frame_support::sp_runtime::sp_application_crypto::sp_core::Bytes + Rc + Arc + Vec + and $N others + = note: required for `Bar` to implement `Encode` + = note: required for `Bar` to implement `FullEncode` + = note: required for `Bar` to implement `FullCodec` + = note: required for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo, Bar>` to implement `TryDecodeEntireStorage` + = note: this error originates in the attribute macro `frame_support::pallet` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/substrate/frame/support/test/tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen_unnamed.stderr b/substrate/frame/support/test/tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen_unnamed.stderr index 79798963c8b1..3a0a25712aaf 100644 --- a/substrate/frame/support/test/tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen_unnamed.stderr +++ b/substrate/frame/support/test/tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen_unnamed.stderr @@ -128,3 +128,62 @@ error[E0277]: the trait bound `Bar: WrapperTypeEncode` is not satisfied = note: required for `Bar` to implement `FullEncode` = note: required for `Bar` to implement `FullCodec` = note: required for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo, Bar>` to implement `StorageEntryMetadataBuilder` + +error[E0277]: the trait bound `Bar: WrapperTypeDecode` is not satisfied + --> tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen_unnamed.rs:18:1 + | +18 | #[frame_support::pallet] + | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `WrapperTypeDecode` is not implemented for `Bar` + | + = help: the following other types implement trait `WrapperTypeDecode`: + Box + frame_support::sp_runtime::sp_application_crypto::sp_core::Bytes + Rc + Arc + = note: required for `Bar` to implement `Decode` + = note: required for `Bar` to implement `FullCodec` + = note: required for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo, Bar>` to implement `TryDecodeEntireStorage` + = note: this error originates in the attribute macro `frame_support::pallet` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `Bar: EncodeLike` is not satisfied + --> tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen_unnamed.rs:18:1 + | +18 | #[frame_support::pallet] + | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `EncodeLike` is not implemented for `Bar` + | + = help: the following other types implement trait `EncodeLike`: + + + + + + + + + and $N others + = note: required for `Bar` to implement `FullEncode` + = note: required for `Bar` to implement `FullCodec` + = note: required for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo, Bar>` to implement `TryDecodeEntireStorage` + = note: this error originates in the attribute macro `frame_support::pallet` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `Bar: WrapperTypeEncode` is not satisfied + --> tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen_unnamed.rs:18:1 + | +18 | #[frame_support::pallet] + | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `WrapperTypeEncode` is not implemented for `Bar` + | + = help: the following other types implement trait `WrapperTypeEncode`: + Box + bytes::bytes::Bytes + Cow<'a, T> + parity_scale_codec::Ref<'a, T, U> + frame_support::sp_runtime::sp_application_crypto::sp_core::Bytes + Rc + Arc + Vec + and $N others + = note: required for `Bar` to implement `Encode` + = note: required for `Bar` to implement `FullEncode` + = note: required for `Bar` to implement `FullCodec` + = note: required for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo, Bar>` to implement `TryDecodeEntireStorage` + = note: this error originates in the attribute macro `frame_support::pallet` (in Nightly builds, run with -Z macro-backtrace for more info) From c4211b6533bd4dbb5a77b861c1ea35d9885b4f4f Mon Sep 17 00:00:00 2001 From: Liam Aharon Date: Tue, 7 Nov 2023 15:12:40 +1100 Subject: [PATCH 28/31] Initialise on-chain `StorageVersion` for pallets added after genesis (#1297) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Original PR https://github.com/paritytech/substrate/pull/14641 --- Closes https://github.com/paritytech/polkadot-sdk/issues/109 ### Problem Quoting from the above issue: > When adding a pallet to chain after genesis we currently don't set the StorageVersion. So, when calling on_chain_storage_version it returns 0 while the pallet is maybe already at storage version 9 when it was added to the chain. This could lead to issues when running migrations. ### Solution - Create a new trait `BeforeAllRuntimeMigrations` with a single method `fn before_all_runtime_migrations() -> Weight` trait with a noop default implementation - Modify `Executive` to call `BeforeAllRuntimeMigrations::before_all_runtime_migrations` for all pallets before running any other hooks - Implement `BeforeAllRuntimeMigrations` in the pallet proc macro to initialize the on-chain version to the current pallet version if the pallet has no storage set (indicating it has been recently added to the runtime and needs to have its version initialised). ### Other changes in this PR - Abstracted repeated boilerplate to access the `pallet_name` in the pallet expand proc macro. ### FAQ #### Why create a new hook instead of adding this logic to the pallet `pre_upgrade`? `Executive` currently runs `COnRuntimeUpgrade` (custom migrations) before `AllPalletsWithSystem` migrations. We need versions to be initialized before the `COnRuntimeUpgrade` migrations are run, because `COnRuntimeUpgrade` migrations may use the on-chain version for critical logic. e.g. `VersionedRuntimeUpgrade` uses it to decide whether or not to execute. We cannot reorder `COnRuntimeUpgrade` and `AllPalletsWithSystem` so `AllPalletsWithSystem` runs first, because `AllPalletsWithSystem` have some logic in their `post_upgrade` hooks to verify that the on-chain version and current pallet version match. A common use case of `COnRuntimeUpgrade` migrations is to perform a migration which will result in the versions matching, so if they were reordered these `post_upgrade` checks would fail. #### Why init the on-chain version for pallets without a current storage version? We must init the on-chain version for pallets even if they don't have a defined storage version so if there is a future version bump, the on-chain version is not automatically set to that new version without a proper migration. e.g. bad scenario: 1. A pallet with no 'current version' is added to the runtime 2. Later, the pallet is upgraded with the 'current version' getting set to 1 and a migration is added to Executive Migrations to migrate the storage from 0 to 1 a. Runtime upgrade occurs b. `before_all` hook initializes the on-chain version to 1 c. `on_runtime_upgrade` of the migration executes, and sees the on-chain version is already 1 therefore think storage is already migrated and does not execute the storage migration Now, on-chain version is 1 but storage is still at version 0. By always initializing the on-chain version when the pallet is added to the runtime we avoid that scenario. --------- Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Co-authored-by: Bastian KΓΆcher --- substrate/frame/executive/src/lib.rs | 16 +++- .../procedural/src/pallet/expand/hooks.rs | 93 +++++++++++++------ substrate/frame/support/src/lib.rs | 2 +- substrate/frame/support/src/migrations.rs | 4 +- substrate/frame/support/src/traits.rs | 4 +- substrate/frame/support/src/traits/hooks.rs | 27 ++++++ .../frame/support/src/traits/metadata.rs | 17 ++++ substrate/frame/support/test/tests/pallet.rs | 60 +++++++++++- 8 files changed, 182 insertions(+), 41 deletions(-) diff --git a/substrate/frame/executive/src/lib.rs b/substrate/frame/executive/src/lib.rs index 5f390c77baff..e2c906c1bf6c 100644 --- a/substrate/frame/executive/src/lib.rs +++ b/substrate/frame/executive/src/lib.rs @@ -121,8 +121,8 @@ use frame_support::{ dispatch::{DispatchClass, DispatchInfo, GetDispatchInfo, PostDispatchInfo}, pallet_prelude::InvalidTransaction, traits::{ - EnsureInherentsAreFirst, ExecuteBlock, OffchainWorker, OnFinalize, OnIdle, OnInitialize, - OnRuntimeUpgrade, + BeforeAllRuntimeMigrations, EnsureInherentsAreFirst, ExecuteBlock, OffchainWorker, + OnFinalize, OnIdle, OnInitialize, OnRuntimeUpgrade, }, weights::Weight, }; @@ -194,6 +194,7 @@ impl< Context: Default, UnsignedValidator, AllPalletsWithSystem: OnRuntimeUpgrade + + BeforeAllRuntimeMigrations + OnInitialize> + OnIdle> + OnFinalize> @@ -231,6 +232,7 @@ impl< Context: Default, UnsignedValidator, AllPalletsWithSystem: OnRuntimeUpgrade + + BeforeAllRuntimeMigrations + OnInitialize> + OnIdle> + OnFinalize> @@ -364,7 +366,9 @@ where /// /// The `checks` param determines whether to execute `pre/post_upgrade` and `try_state` hooks. pub fn try_runtime_upgrade(checks: UpgradeCheckSelect) -> Result { - let weight = + let before_all_weight = + ::before_all_runtime_migrations(); + let try_on_runtime_upgrade_weight = <(COnRuntimeUpgrade, AllPalletsWithSystem) as OnRuntimeUpgrade>::try_on_runtime_upgrade( checks.pre_and_post(), )?; @@ -385,7 +389,7 @@ where )?; } - Ok(weight) + Ok(before_all_weight.saturating_add(try_on_runtime_upgrade_weight)) } /// Logs the result of trying to decode the entire state. @@ -429,6 +433,7 @@ impl< Context: Default, UnsignedValidator, AllPalletsWithSystem: OnRuntimeUpgrade + + BeforeAllRuntimeMigrations + OnInitialize> + OnIdle> + OnFinalize> @@ -445,7 +450,10 @@ where { /// Execute all `OnRuntimeUpgrade` of this runtime, and return the aggregate weight. pub fn execute_on_runtime_upgrade() -> Weight { + let before_all_weight = + ::before_all_runtime_migrations(); <(COnRuntimeUpgrade, AllPalletsWithSystem) as OnRuntimeUpgrade>::on_runtime_upgrade() + .saturating_add(before_all_weight) } /// Start the execution of a particular block. diff --git a/substrate/frame/support/procedural/src/pallet/expand/hooks.rs b/substrate/frame/support/procedural/src/pallet/expand/hooks.rs index aaad4dd2be0e..5044d4285bb6 100644 --- a/substrate/frame/support/procedural/src/pallet/expand/hooks.rs +++ b/substrate/frame/support/procedural/src/pallet/expand/hooks.rs @@ -34,6 +34,38 @@ pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream { let type_use_gen = &def.type_use_generics(span); let pallet_ident = &def.pallet_struct.pallet; let frame_system = &def.frame_system; + let pallet_name = quote::quote! { + < + ::PalletInfo + as + #frame_support::traits::PalletInfo + >::name::().unwrap_or("") + }; + + let initialize_on_chain_storage_version = if let Some(current_version) = + &def.pallet_struct.storage_version + { + quote::quote! { + #frame_support::__private::log::info!( + target: #frame_support::LOG_TARGET, + "πŸ₯ New pallet {:?} detected in the runtime. Initializing the on-chain storage version to match the storage version defined in the pallet: {:?}", + #pallet_name, + #current_version + ); + #current_version.put::(); + } + } else { + quote::quote! { + let default_version = #frame_support::traits::StorageVersion::new(0); + #frame_support::__private::log::info!( + target: #frame_support::LOG_TARGET, + "πŸ₯ New pallet {:?} detected in the runtime. The pallet has no defined storage version, so the on-chain version is being initialized to {:?}.", + #pallet_name, + default_version + ); + default_version.put::(); + } + }; let log_runtime_upgrade = if has_runtime_upgrade { // a migration is defined here. @@ -42,7 +74,7 @@ pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream { target: #frame_support::LOG_TARGET, "⚠️ {} declares internal migrations (which *might* execute). \ On-chain `{:?}` vs current storage version `{:?}`", - pallet_name, + #pallet_name, ::on_chain_storage_version(), ::current_storage_version(), ); @@ -53,7 +85,7 @@ pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream { #frame_support::__private::log::debug!( target: #frame_support::LOG_TARGET, "βœ… no migration for {}", - pallet_name, + #pallet_name, ); } }; @@ -78,16 +110,10 @@ pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream { let current_version = ::current_storage_version(); if on_chain_version != current_version { - let pallet_name = < - ::PalletInfo - as - #frame_support::traits::PalletInfo - >::name::().unwrap_or(""); - #frame_support::__private::log::error!( target: #frame_support::LOG_TARGET, "{}: On chain storage version {:?} doesn't match current storage version {:?}.", - pallet_name, + #pallet_name, on_chain_version, current_version, ); @@ -100,17 +126,11 @@ pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream { let on_chain_version = ::on_chain_storage_version(); if on_chain_version != #frame_support::traits::StorageVersion::new(0) { - let pallet_name = < - ::PalletInfo - as - #frame_support::traits::PalletInfo - >::name::().unwrap_or(""); - #frame_support::__private::log::error!( target: #frame_support::LOG_TARGET, "{}: On chain storage version {:?} is set to non zero, \ while the pallet is missing the `#[pallet::storage_version(VERSION)]` attribute.", - pallet_name, + #pallet_name, on_chain_version, ); @@ -173,6 +193,32 @@ pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream { } } + impl<#type_impl_gen> + #frame_support::traits::BeforeAllRuntimeMigrations + for #pallet_ident<#type_use_gen> #where_clause + { + fn before_all_runtime_migrations() -> #frame_support::weights::Weight { + use #frame_support::traits::{Get, PalletInfoAccess}; + use #frame_support::__private::hashing::twox_128; + use #frame_support::storage::unhashed::contains_prefixed_key; + #frame_support::__private::sp_tracing::enter_span!( + #frame_support::__private::sp_tracing::trace_span!("before_all") + ); + + // Check if the pallet has any keys set, including the storage version. If there are + // no keys set, the pallet was just added to the runtime and needs to have its + // version initialized. + let pallet_hashed_prefix = ::name_hash(); + let exists = contains_prefixed_key(&pallet_hashed_prefix); + if !exists { + #initialize_on_chain_storage_version + ::DbWeight::get().reads_writes(1, 1) + } else { + ::DbWeight::get().reads(1) + } + } + } + impl<#type_impl_gen> #frame_support::traits::OnRuntimeUpgrade for #pallet_ident<#type_use_gen> #where_clause @@ -183,11 +229,6 @@ pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream { ); // log info about the upgrade. - let pallet_name = < - ::PalletInfo - as - #frame_support::traits::PalletInfo - >::name::().unwrap_or(""); #log_runtime_upgrade < @@ -258,16 +299,10 @@ pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream { n: #frame_system::pallet_prelude::BlockNumberFor::, _s: #frame_support::traits::TryStateSelect ) -> Result<(), #frame_support::sp_runtime::TryRuntimeError> { - let pallet_name = < - ::PalletInfo - as - #frame_support::traits::PalletInfo - >::name::().expect("No name found for the pallet! This usually means that the pallet wasn't added to `construct_runtime!`."); - #frame_support::__private::log::info!( target: #frame_support::LOG_TARGET, "🩺 Running {:?} try-state checks", - pallet_name, + #pallet_name, ); < Self as #frame_support::traits::Hooks< @@ -277,7 +312,7 @@ pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream { #frame_support::__private::log::error!( target: #frame_support::LOG_TARGET, "❌ {:?} try_state checks failed: {:?}", - pallet_name, + #pallet_name, err ); diff --git a/substrate/frame/support/src/lib.rs b/substrate/frame/support/src/lib.rs index d49588686851..a01f3a01593a 100644 --- a/substrate/frame/support/src/lib.rs +++ b/substrate/frame/support/src/lib.rs @@ -47,7 +47,7 @@ pub mod __private { pub use sp_core::{OpaqueMetadata, Void}; pub use sp_core_hashing_proc_macro; pub use sp_inherents; - pub use sp_io::{self, storage::root as storage_root}; + pub use sp_io::{self, hashing, storage::root as storage_root}; pub use sp_metadata_ir as metadata_ir; #[cfg(feature = "std")] pub use sp_runtime::{bounded_btree_map, bounded_vec}; diff --git a/substrate/frame/support/src/migrations.rs b/substrate/frame/support/src/migrations.rs index d6ae6c6291b1..22471e883a70 100644 --- a/substrate/frame/support/src/migrations.rs +++ b/substrate/frame/support/src/migrations.rs @@ -119,7 +119,7 @@ impl< let on_chain_version = Pallet::on_chain_storage_version(); if on_chain_version == FROM { log::info!( - "🚚 Pallet {:?} migrating storage version from {:?} to {:?}.", + "🚚 Pallet {:?} VersionedMigration migrating storage version from {:?} to {:?}.", Pallet::name(), FROM, TO @@ -134,7 +134,7 @@ impl< weight.saturating_add(DbWeight::get().reads_writes(1, 1)) } else { log::warn!( - "🚚 Pallet {:?} migration {}->{} can be removed; on-chain is already at {:?}.", + "🚚 Pallet {:?} VersionedMigration migration {}->{} can be removed; on-chain is already at {:?}.", Pallet::name(), FROM, TO, diff --git a/substrate/frame/support/src/traits.rs b/substrate/frame/support/src/traits.rs index 12cc84027c48..6362e750d2ab 100644 --- a/substrate/frame/support/src/traits.rs +++ b/substrate/frame/support/src/traits.rs @@ -84,8 +84,8 @@ mod hooks; #[allow(deprecated)] pub use hooks::GenesisBuild; pub use hooks::{ - BuildGenesisConfig, Hooks, IntegrityTest, OnFinalize, OnGenesis, OnIdle, OnInitialize, - OnRuntimeUpgrade, OnTimestampSet, + BeforeAllRuntimeMigrations, BuildGenesisConfig, Hooks, IntegrityTest, OnFinalize, OnGenesis, + OnIdle, OnInitialize, OnRuntimeUpgrade, OnTimestampSet, }; pub mod schedule; diff --git a/substrate/frame/support/src/traits/hooks.rs b/substrate/frame/support/src/traits/hooks.rs index 6acecb8928d8..20788ce932d8 100644 --- a/substrate/frame/support/src/traits/hooks.rs +++ b/substrate/frame/support/src/traits/hooks.rs @@ -99,6 +99,19 @@ pub trait OnGenesis { fn on_genesis() {} } +/// Implemented by pallets, allows defining logic to run prior to any [`OnRuntimeUpgrade`] logic. +/// +/// This hook is intended to be used internally in FRAME and not be exposed to FRAME developers. +/// +/// It is defined as a seperate trait from [`OnRuntimeUpgrade`] precisely to not pollute the public +/// API. +pub trait BeforeAllRuntimeMigrations { + /// Something that should happen before runtime migrations are executed. + fn before_all_runtime_migrations() -> Weight { + Weight::zero() + } +} + /// See [`Hooks::on_runtime_upgrade`]. pub trait OnRuntimeUpgrade { /// See [`Hooks::on_runtime_upgrade`]. @@ -150,10 +163,24 @@ pub trait OnRuntimeUpgrade { } } +#[cfg_attr(all(not(feature = "tuples-96"), not(feature = "tuples-128")), impl_for_tuples(64))] +#[cfg_attr(all(feature = "tuples-96", not(feature = "tuples-128")), impl_for_tuples(96))] +#[cfg_attr(feature = "tuples-128", impl_for_tuples(128))] +impl BeforeAllRuntimeMigrations for Tuple { + /// Implements the default behavior of + /// [`BeforeAllRuntimeMigrations::before_all_runtime_migrations`] for tuples. + fn before_all_runtime_migrations() -> Weight { + let mut weight = Weight::zero(); + for_tuples!( #( weight = weight.saturating_add(Tuple::before_all_runtime_migrations()); )* ); + weight + } +} + #[cfg_attr(all(not(feature = "tuples-96"), not(feature = "tuples-128")), impl_for_tuples(64))] #[cfg_attr(all(feature = "tuples-96", not(feature = "tuples-128")), impl_for_tuples(96))] #[cfg_attr(feature = "tuples-128", impl_for_tuples(128))] impl OnRuntimeUpgrade for Tuple { + /// Implements the default behavior of [`OnRuntimeUpgrade::on_runtime_upgrade`] for tuples. fn on_runtime_upgrade() -> Weight { let mut weight = Weight::zero(); for_tuples!( #( weight = weight.saturating_add(Tuple::on_runtime_upgrade()); )* ); diff --git a/substrate/frame/support/src/traits/metadata.rs b/substrate/frame/support/src/traits/metadata.rs index bd29b6009161..0af8d06719fe 100644 --- a/substrate/frame/support/src/traits/metadata.rs +++ b/substrate/frame/support/src/traits/metadata.rs @@ -222,6 +222,23 @@ impl StorageVersion { crate::storage::unhashed::get_or_default(&key) } + + /// Returns if the storage version key for the given pallet exists in storage. + /// + /// See [`STORAGE_VERSION_STORAGE_KEY_POSTFIX`] on how this key is built. + /// + /// # Panics + /// + /// This function will panic iff `Pallet` can not be found by `PalletInfo`. + /// In a runtime that is put together using + /// [`construct_runtime!`](crate::construct_runtime) this should never happen. + /// + /// It will also panic if this function isn't executed in an externalities + /// provided environment. + pub fn exists() -> bool { + let key = Self::storage_key::

(); + crate::storage::unhashed::exists(&key) + } } impl PartialEq for StorageVersion { diff --git a/substrate/frame/support/test/tests/pallet.rs b/substrate/frame/support/test/tests/pallet.rs index 83ae5b9253ce..00e7adafb0b7 100644 --- a/substrate/frame/support/test/tests/pallet.rs +++ b/substrate/frame/support/test/tests/pallet.rs @@ -21,7 +21,7 @@ use frame_support::{ dispatch_context::with_context, pallet_prelude::{StorageInfoTrait, ValueQuery}, parameter_types, - storage::unhashed, + storage::{unhashed, unhashed::contains_prefixed_key}, traits::{ ConstU32, GetCallIndex, GetCallName, GetStorageVersion, OnFinalize, OnGenesis, OnInitialize, OnRuntimeUpgrade, PalletError, PalletInfoAccess, StorageVersion, @@ -2330,6 +2330,58 @@ fn test_storage_alias() { }) } +#[test] +fn pallet_on_chain_storage_version_initializes_correctly() { + type Executive = frame_executive::Executive< + Runtime, + Block, + frame_system::ChainContext, + Runtime, + AllPalletsWithSystem, + >; + + // Simple example of a pallet with current version 10 being added to the runtime for the first + // time. + TestExternalities::default().execute_with(|| { + let current_version = Example::current_storage_version(); + + // Check the pallet has no storage items set. + let pallet_hashed_prefix = twox_128(Example::name().as_bytes()); + let exists = contains_prefixed_key(&pallet_hashed_prefix); + assert_eq!(exists, false); + + // [`frame_support::traits::BeforeAllRuntimeMigrations`] hook should initialize the storage + // version. + Executive::execute_on_runtime_upgrade(); + + // Check that the storage version was initialized to the current version + let on_chain_version_after = StorageVersion::get::(); + assert_eq!(on_chain_version_after, current_version); + }); + + // Pallet with no current storage version should have the on-chain version initialized to 0. + TestExternalities::default().execute_with(|| { + // Example4 current_storage_version is NoStorageVersionSet. + + // Check the pallet has no storage items set. + let pallet_hashed_prefix = twox_128(Example4::name().as_bytes()); + let exists = contains_prefixed_key(&pallet_hashed_prefix); + assert_eq!(exists, false); + + // Confirm the storage version is implicitly 0. + let on_chain_version_before = StorageVersion::get::(); + assert_eq!(on_chain_version_before, StorageVersion::new(0)); + + // [`frame_support::traits::BeforeAllRuntimeMigrations`] initializes the storage version. + Executive::execute_on_runtime_upgrade(); + + // Check that the storage version now exists and was initialized to 0. + let on_chain_version_after = StorageVersion::get::(); + assert_eq!(StorageVersion::exists::(), true); + assert_eq!(on_chain_version_after, StorageVersion::new(0)); + }); +} + #[cfg(feature = "try-runtime")] #[test] fn post_runtime_upgrade_detects_storage_version_issues() { @@ -2382,8 +2434,10 @@ fn post_runtime_upgrade_detects_storage_version_issues() { >; TestExternalities::default().execute_with(|| { - // Call `on_genesis` to put the storage version of `Example` into the storage. - Example::on_genesis(); + // Set the on-chain version to one less than the current version for `Example`, simulating a + // forgotten migration + StorageVersion::new(9).put::(); + // The version isn't changed, we should detect it. assert!( Executive::try_runtime_upgrade(UpgradeCheckSelect::PreAndPost).unwrap_err() == From f5e9827fdaca12a1bb4223c7848ca3423dcc4c3a Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghe <49718502+alexggh@users.noreply.github.com> Date: Tue, 7 Nov 2023 12:10:19 +0200 Subject: [PATCH 29/31] zombienet_tests: Fix genesis error in 0006-parachains-max-tranche0.toml (#2191) There was a race in merging between https://github.com/paritytech/polkadot-sdk/pull/1256 and https://github.com/paritytech/polkadot-sdk/pull/1178, so this newly added tests wasn't updated with the new path for the configuration, so fix that. Signed-off-by: Alexandru Gheorghe --- .../functional/0006-parachains-max-tranche0.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/polkadot/zombienet_tests/functional/0006-parachains-max-tranche0.toml b/polkadot/zombienet_tests/functional/0006-parachains-max-tranche0.toml index 68ac90f5a7e1..bef54cb8ca41 100644 --- a/polkadot/zombienet_tests/functional/0006-parachains-max-tranche0.toml +++ b/polkadot/zombienet_tests/functional/0006-parachains-max-tranche0.toml @@ -2,7 +2,7 @@ timeout = 1000 bootnode = true -[relaychain.genesis.runtime.configuration.config] +[relaychain.genesis.runtimeGenesis.patch.configuration.config] max_validators_per_core = 1 needed_approvals = 7 relay_vrf_modulo_samples = 5 From 295a8483017da3a556ee7bf2b126aa2436467ab9 Mon Sep 17 00:00:00 2001 From: Xiliang Chen Date: Tue, 7 Nov 2023 23:46:57 +1300 Subject: [PATCH 30/31] mark pallet-asset-rate optional in polkadot-runtime-common (#2187) Part of #2186 The only usage of pallet-asset-rate is guarded by `runtime-benchmarks` feature. I don't want ORML to be forced to include this pallet in deps for no good reason. --- polkadot/runtime/common/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/polkadot/runtime/common/Cargo.toml b/polkadot/runtime/common/Cargo.toml index a6642f9c9c77..0882e555aafe 100644 --- a/polkadot/runtime/common/Cargo.toml +++ b/polkadot/runtime/common/Cargo.toml @@ -39,7 +39,7 @@ pallet-timestamp = { path = "../../../substrate/frame/timestamp", default-featur pallet-vesting = { path = "../../../substrate/frame/vesting", default-features = false } pallet-transaction-payment = { path = "../../../substrate/frame/transaction-payment", default-features = false } pallet-treasury = { path = "../../../substrate/frame/treasury", default-features = false } -pallet-asset-rate = { path = "../../../substrate/frame/asset-rate", default-features = false } +pallet-asset-rate = { path = "../../../substrate/frame/asset-rate", default-features = false, optional = true } pallet-election-provider-multi-phase = { path = "../../../substrate/frame/election-provider-multi-phase", default-features = false } frame-election-provider-support = { path = "../../../substrate/frame/election-provider-support", default-features = false } @@ -80,7 +80,7 @@ std = [ "inherents/std", "libsecp256k1/std", "log/std", - "pallet-asset-rate/std", + "pallet-asset-rate?/std", "pallet-authorship/std", "pallet-balances/std", "pallet-election-provider-multi-phase/std", From 4caa3d8d8e6f90b2689cd2666c94044394712abf Mon Sep 17 00:00:00 2001 From: vuittont60 <81072379+vuittont60@users.noreply.github.com> Date: Tue, 7 Nov 2023 20:11:06 +0800 Subject: [PATCH 31/31] docs: fix typos (#2193) --- bridges/zombienet/README.md | 2 +- cumulus/parachains/runtimes/assets/README.md | 2 +- polkadot/roadmap/implementers-guide/src/disputes-flow.md | 2 +- .../src/node/backing/statement-distribution-legacy.md | 2 +- .../src/node/backing/statement-distribution.md | 2 +- .../src/node/disputes/dispute-distribution.md | 2 +- .../src/node/utility/candidate-validation.md | 2 +- .../src/node/utility/pvf-host-and-workers.md | 2 +- .../implementers-guide/src/node/utility/pvf-prechecker.md | 2 +- polkadot/roadmap/implementers-guide/src/protocol-approval.md | 2 +- polkadot/roadmap/implementers-guide/src/runtime/hrmp.md | 2 +- polkadot/roadmap/implementers-guide/src/runtime/inclusion.md | 2 +- polkadot/roadmap/implementers-guide/src/runtime/paras.md | 2 +- polkadot/roadmap/implementers-guide/src/runtime/scheduler.md | 2 +- polkadot/roadmap/implementers-guide/src/types/backing.md | 2 +- substrate/bin/utils/subkey/README.md | 4 ++-- substrate/client/consensus/beefy/README.md | 4 ++-- substrate/client/transaction-pool/README.md | 2 +- substrate/docs/CHANGELOG.md | 2 +- substrate/docs/Upgrading-2.0-to-3.0.md | 2 +- substrate/frame/root-testing/README.md | 2 +- substrate/primitives/maybe-compressed-blob/README.md | 2 +- substrate/test-utils/runtime/res/README.md | 2 +- 23 files changed, 25 insertions(+), 25 deletions(-) diff --git a/bridges/zombienet/README.md b/bridges/zombienet/README.md index 2da3093f4f01..7f7de770814b 100644 --- a/bridges/zombienet/README.md +++ b/bridges/zombienet/README.md @@ -2,7 +2,7 @@ This folder contains [zombienet](https://github.com/paritytech/zombienet/) based integration tests for both onchain and offchain bridges code. Due to some -[technical diffuculties](https://github.com/paritytech/parity-bridges-common/pull/2649#issue-1965339051), we +[technical difficulties](https://github.com/paritytech/parity-bridges-common/pull/2649#issue-1965339051), we are using native zombienet provider, which means that you need to build some binaries locally. To start those tests, you need to: diff --git a/cumulus/parachains/runtimes/assets/README.md b/cumulus/parachains/runtimes/assets/README.md index 78145395cbf9..05466e537f42 100644 --- a/cumulus/parachains/runtimes/assets/README.md +++ b/cumulus/parachains/runtimes/assets/README.md @@ -8,7 +8,7 @@ Asset Hub allows users to: - Deploy promise-backed assets, both fungible and non-fungible, with a DOT/KSM deposit. - Set admin roles to manage assets and asset classes. - Register assets as "self-sufficient" if the Relay Chain agrees, i.e. gain the ability for an - asset to justify the existance of accounts sans DOT/KSM. + asset to justify the existence of accounts sans DOT/KSM. - Pay transaction fees using sufficient assets. - Transfer (and approve transfer) assets. - Interact with the chain via its transactional API or XCM. diff --git a/polkadot/roadmap/implementers-guide/src/disputes-flow.md b/polkadot/roadmap/implementers-guide/src/disputes-flow.md index f9fd8dcce351..b5cc5611c6ff 100644 --- a/polkadot/roadmap/implementers-guide/src/disputes-flow.md +++ b/polkadot/roadmap/implementers-guide/src/disputes-flow.md @@ -52,7 +52,7 @@ stateDiagram-v2 IncomingRequestDisputeAvailabilityData --> RespondUnavailable IncomingRequestDisputeAvailabilityData --> DisputeDataAvail DisputeDataAvail --> RespondWithDisputeAvailabilityData: Send - VoteGossipReceived --> Track: implies source peer has
dispute availablity data + VoteGossipReceived --> Track: implies source peer has
dispute availability data ``` --- diff --git a/polkadot/roadmap/implementers-guide/src/node/backing/statement-distribution-legacy.md b/polkadot/roadmap/implementers-guide/src/node/backing/statement-distribution-legacy.md index e10a55010b91..710055144665 100644 --- a/polkadot/roadmap/implementers-guide/src/node/backing/statement-distribution-legacy.md +++ b/polkadot/roadmap/implementers-guide/src/node/backing/statement-distribution-legacy.md @@ -149,7 +149,7 @@ The receiver of such a message needs to request the actual payload via request/r This is necessary as distribution of a large payload (mega bytes) via gossip would make the network collapse and timely distribution of statements would no longer be possible. By using request/response it is ensured that each peer only -transferes large data once. We only take good care to detect an overloaded peer early and immediately move on to a +transfers large data once. We only take good care to detect an overloaded peer early and immediately move on to a different peer for fetching the data. This mechanism should result in a good load distribution and therefore a rather optimal distribution path. diff --git a/polkadot/roadmap/implementers-guide/src/node/backing/statement-distribution.md b/polkadot/roadmap/implementers-guide/src/node/backing/statement-distribution.md index 24f2785f8294..86a1bf121413 100644 --- a/polkadot/roadmap/implementers-guide/src/node/backing/statement-distribution.md +++ b/polkadot/roadmap/implementers-guide/src/node/backing/statement-distribution.md @@ -3,7 +3,7 @@ This subsystem is responsible for distributing signed statements that we have generated and forwarding statements generated by our peers. Received candidate receipts and statements are passed to the [Candidate Backing subsystem](candidate-backing.md) to handle producing local statements. On receiving -`StatementDistributionMessage::Share`, this subsystem distributes the message across the network with redundency to +`StatementDistributionMessage::Share`, this subsystem distributes the message across the network with redundancy to ensure a fast backing process. ## Overview diff --git a/polkadot/roadmap/implementers-guide/src/node/disputes/dispute-distribution.md b/polkadot/roadmap/implementers-guide/src/node/disputes/dispute-distribution.md index 4547e02352ec..e916d246060e 100644 --- a/polkadot/roadmap/implementers-guide/src/node/disputes/dispute-distribution.md +++ b/polkadot/roadmap/implementers-guide/src/node/disputes/dispute-distribution.md @@ -297,7 +297,7 @@ is `500ms` and above `RATE_LIMIT` is `100ms`. 1/3 of validators are malicious, so for 1000 this means around 330 malicious actors worst case. All those actors can send a message every `100ms`, that is 10 per second. This -means at the begining of an attack they can open up around 3300 batches. Each +means at the beginning of an attack they can open up around 3300 batches. Each containing two votes. So memory usage is still negligible. In reality it is even less, as we also demand 10 new votes to trickle in per batch in order to keep it alive, every `500ms`. Hence for the first second, each batch requires 20 votes diff --git a/polkadot/roadmap/implementers-guide/src/node/utility/candidate-validation.md b/polkadot/roadmap/implementers-guide/src/node/utility/candidate-validation.md index 376fa187de1b..e252ec237b79 100644 --- a/polkadot/roadmap/implementers-guide/src/node/utility/candidate-validation.md +++ b/polkadot/roadmap/implementers-guide/src/node/utility/candidate-validation.md @@ -31,7 +31,7 @@ all the necessary parameters to the validation function. These are: * The [`ValidationData`](../../types/candidate.md#validationdata). * The [`PoV`](../../types/availability.md#proofofvalidity). -The second category is for PVF pre-checking. This is primarly used by the [PVF pre-checker](pvf-prechecker.md) +The second category is for PVF pre-checking. This is primarily used by the [PVF pre-checker](pvf-prechecker.md) subsystem. ### Determining Parameters diff --git a/polkadot/roadmap/implementers-guide/src/node/utility/pvf-host-and-workers.md b/polkadot/roadmap/implementers-guide/src/node/utility/pvf-host-and-workers.md index 0cefeb1f77ca..52129f9eb80a 100644 --- a/polkadot/roadmap/implementers-guide/src/node/utility/pvf-host-and-workers.md +++ b/polkadot/roadmap/implementers-guide/src/node/utility/pvf-host-and-workers.md @@ -44,7 +44,7 @@ execution request: We use timeouts for both preparation and execution jobs to limit the amount of time they can take. As the time for a job can vary depending on the machine and load on the machine, this can potentially lead to disputes where some validators -successfuly execute a PVF and others don't. +successfully execute a PVF and others don't. One dispute mitigation we have in place is a more lenient timeout for preparation during execution than during pre-checking. The rationale is that the diff --git a/polkadot/roadmap/implementers-guide/src/node/utility/pvf-prechecker.md b/polkadot/roadmap/implementers-guide/src/node/utility/pvf-prechecker.md index b722bdc6539a..f0de50f2267b 100644 --- a/polkadot/roadmap/implementers-guide/src/node/utility/pvf-prechecker.md +++ b/polkadot/roadmap/implementers-guide/src/node/utility/pvf-prechecker.md @@ -24,7 +24,7 @@ relevant. When a PVF just becomes relevant, the subsystem will send a message to the [Candidate Validation] subsystem asking for the pre-check. -Upon receving a message from the candidate-validation subsystem, the pre-checker will note down that the PVF has its +Upon receiving a message from the candidate-validation subsystem, the pre-checker will note down that the PVF has its judgement and will also sign and submit a [`PvfCheckStatement`][PvfCheckStatement] via the [`submit_pvf_check_statement` runtime API][PVF pre-checking runtime API]. In case, a judgement was received for a PVF that is no longer in view it is ignored. diff --git a/polkadot/roadmap/implementers-guide/src/protocol-approval.md b/polkadot/roadmap/implementers-guide/src/protocol-approval.md index 4fac84591456..70bc0233d65a 100644 --- a/polkadot/roadmap/implementers-guide/src/protocol-approval.md +++ b/polkadot/roadmap/implementers-guide/src/protocol-approval.md @@ -330,7 +330,7 @@ We prefer doing approval checkers assignments under `RelayVRFModulo` or `RelayVR assignments benefit security the most. We suggest assigning at least 16 checkers under `RelayVRFModulo` or `RelayVRFModuloCompact` although assignment levels have never been properly analyzed. -Our delay criteria `RelayVRFDelay` and `RelayEquivocation` both have two primary paramaters, expected checkers per +Our delay criteria `RelayVRFDelay` and `RelayEquivocation` both have two primary parameters, expected checkers per tranche and the zeroth delay tranche width. We require expected checkers per tranche to be less than three because otherwise an adversary with 1/3 stake could force diff --git a/polkadot/roadmap/implementers-guide/src/runtime/hrmp.md b/polkadot/roadmap/implementers-guide/src/runtime/hrmp.md index aa31491d72f6..69d33ca8670d 100644 --- a/polkadot/roadmap/implementers-guide/src/runtime/hrmp.md +++ b/polkadot/roadmap/implementers-guide/src/runtime/hrmp.md @@ -108,7 +108,7 @@ HrmpEgressChannelsIndex: map ParaId => Vec; /// Invariant: cannot be non-empty if the corresponding channel in `HrmpChannels` is `None`. HrmpChannelContents: map HrmpChannelId => Vec; /// Maintains a mapping that can be used to answer the question: -/// What paras sent a message at the given block number for a given reciever. +/// What paras sent a message at the given block number for a given receiver. /// Invariants: /// - The inner `Vec` is never empty. /// - The inner `Vec` cannot store two same `ParaId`. diff --git a/polkadot/roadmap/implementers-guide/src/runtime/inclusion.md b/polkadot/roadmap/implementers-guide/src/runtime/inclusion.md index f73df209981a..f6a32a01d502 100644 --- a/polkadot/roadmap/implementers-guide/src/runtime/inclusion.md +++ b/polkadot/roadmap/implementers-guide/src/runtime/inclusion.md @@ -133,7 +133,7 @@ All failed checks should lead to an unrecoverable error making the block invalid [`UpwardMessage`s](../types/messages.md#upward-message) from the [`CandidateCommitments`](../types/candidate.md#candidate-commitments). 1. call `Dmp::prune_dmq` with the para id of the candidate and the candidate's `processed_downward_messages`. - 1. call `Hrmp::prune_hrmp` with the para id of the candiate and the candidate's `hrmp_watermark`. + 1. call `Hrmp::prune_hrmp` with the para id of the candidate and the candidate's `hrmp_watermark`. 1. call `Hrmp::queue_outbound_hrmp` with the para id of the candidate and the list of horizontal messages taken from the commitment, 1. Call `Paras::note_new_head` using the `HeadData` from the receipt and `relay_parent_number`. diff --git a/polkadot/roadmap/implementers-guide/src/runtime/paras.md b/polkadot/roadmap/implementers-guide/src/runtime/paras.md index ac9b1520c3df..e6e0b53512fe 100644 --- a/polkadot/roadmap/implementers-guide/src/runtime/paras.md +++ b/polkadot/roadmap/implementers-guide/src/runtime/paras.md @@ -189,7 +189,7 @@ UpgradeGoAheadSignal: map hasher(twox_64_concat) ParaId => Option)`: indicate previosuly-occupied cores which are to be considered returned +- `free_cores(Vec<(CoreIndex, FreedReason)>)`: indicate previously-occupied cores which are to be considered returned and why they are being returned. - All freed lease holding parachain cores should be assigned to their respective parachain - All freed on-demand parachain cores whose reason for freeing was `FreedReason::Concluded` should have the claim diff --git a/polkadot/roadmap/implementers-guide/src/types/backing.md b/polkadot/roadmap/implementers-guide/src/types/backing.md index 7e43325ec5cd..bfe182383fd5 100644 --- a/polkadot/roadmap/implementers-guide/src/types/backing.md +++ b/polkadot/roadmap/implementers-guide/src/types/backing.md @@ -30,7 +30,7 @@ work, we extract a signed wrapper. ```rust,ignore /// A signed type which encapsulates the common desire to sign some data and validate a signature. /// -/// Note that the internal fields are not public; they are all accessable by immutable getters. +/// Note that the internal fields are not public; they are all accessible by immutable getters. /// This reduces the chance that they are accidentally mutated, invalidating the signature. struct Signed { /// The payload is part of the signed data. The rest is the signing context, diff --git a/substrate/bin/utils/subkey/README.md b/substrate/bin/utils/subkey/README.md index 60e5a9ca9350..3232c8295872 100644 --- a/substrate/bin/utils/subkey/README.md +++ b/substrate/bin/utils/subkey/README.md @@ -74,7 +74,7 @@ The output above shows a **secret phrase** (also called **mnemonic phrase**) and **Private Key**). Those 2 secrets are the pieces of information you MUST keep safe and secret. All the other information below can be derived from those secrets. -The output above also show the **public key** and the **Account ID**. Those are the independant from the network where +The output above also show the **public key** and the **Account ID**. Those are the independent from the network where you will use the key. The **SS58 address** (or **Public Address**) of a new account is a reprensentation of the public keys of an account for @@ -163,7 +163,7 @@ This time, we properly recovered `5He5pZpc7AJ8evPuab37vJF6KkFDqq9uDq2WXh877Qw6ia ### Inspecting a key -If you have *some data* about a key, `subkey inpsect` will help you discover more information about it. +If you have *some data* about a key, `subkey inspect` will help you discover more information about it. If you have **secrets** that you would like to verify for instance, you can use: diff --git a/substrate/client/consensus/beefy/README.md b/substrate/client/consensus/beefy/README.md index 1a5a9667fdbb..13f88303a972 100644 --- a/substrate/client/consensus/beefy/README.md +++ b/substrate/client/consensus/beefy/README.md @@ -104,7 +104,7 @@ shortcuts: ## Mental Model BEEFY should be considered as an extra voting round done by GRANDPA validators for the current -best finalized block. Similarily to how GRANDPA is lagging behind best produced (non-finalized) +best finalized block. Similarly to how GRANDPA is lagging behind best produced (non-finalized) block, BEEFY is going to lag behind best GRANDPA (finalized) block. ``` @@ -302,7 +302,7 @@ periodically on the global topic. Let's now dive into description of the message ## Misbehavior -Similarily to other PoS protocols, BEEFY considers casting two different votes in the same round a +Similarly to other PoS protocols, BEEFY considers casting two different votes in the same round a misbehavior. I.e. for a particular `round_number`, the validator produces signatures for 2 different `Commitment`s and broadcasts them. This is called **equivocation**. diff --git a/substrate/client/transaction-pool/README.md b/substrate/client/transaction-pool/README.md index b34b623b26e0..b55dc6482d64 100644 --- a/substrate/client/transaction-pool/README.md +++ b/substrate/client/transaction-pool/README.md @@ -227,7 +227,7 @@ transactions that are prepared for block inclusion. Propagation is best effort, especially for block authors and is not directly incentivised. However the networking protocol might penalise peers that send invalid or useless transactions so we should be nice to others. Also see below a proposal -to instead of gossiping everyting have other peers request transactions they +to instead of gossiping everything have other peers request transactions they are interested in. Since the pool is expected to store more transactions than what can fit diff --git a/substrate/docs/CHANGELOG.md b/substrate/docs/CHANGELOG.md index 8a1b245a5b0c..042b7b8a2f01 100644 --- a/substrate/docs/CHANGELOG.md +++ b/substrate/docs/CHANGELOG.md @@ -45,7 +45,7 @@ board](https://github.com/paritytech/substrate/discussions). * Allow validators to block and kick their nominator set. (#7930) * Decouple Staking and Election - Part1: Support traits (#7908) * Introduces account existence providers reference counting (#7363) -* contracts: Cap the surcharge reward by the amount of rent that way payed by a contract (#7870) +* contracts: Cap the surcharge reward by the amount of rent that way paid by a contract (#7870) * Use checked math when calculating storage size (#7885) * Fix clear prefix check to avoid erasing child trie roots. (#7848) * contracts: Collect rent for the first block during deployment (#7847) diff --git a/substrate/docs/Upgrading-2.0-to-3.0.md b/substrate/docs/Upgrading-2.0-to-3.0.md index 3f2a3e7c5be2..1be41a34ef34 100644 --- a/substrate/docs/Upgrading-2.0-to-3.0.md +++ b/substrate/docs/Upgrading-2.0-to-3.0.md @@ -113,7 +113,7 @@ And update the overall definition for weights on frame and a few related types a ```diff= -const AVERAGE_ON_INITIALIZE_WEIGHT: Perbill = Perbill::from_percent(10); -+/// We assume that ~10% of the block weight is consumed by `on_initalize` handlers. ++/// We assume that ~10% of the block weight is consumed by `on_initialize` handlers. +/// This is used to limit the maximal weight of a single extrinsic. +const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(10); +/// We allow `Normal` extrinsics to fill up the block up to 75%, the rest can be used diff --git a/substrate/frame/root-testing/README.md b/substrate/frame/root-testing/README.md index aa231e3ef20a..a0eeda5cc6aa 100644 --- a/substrate/frame/root-testing/README.md +++ b/substrate/frame/root-testing/README.md @@ -1,5 +1,5 @@ # Root Testing Pallet -Pallet that contains extrinsics that can be usefull in testing. +Pallet that contains extrinsics that can be useful in testing. NOTE: This pallet should only be used for testing purposes and should not be used in production runtimes! diff --git a/substrate/primitives/maybe-compressed-blob/README.md b/substrate/primitives/maybe-compressed-blob/README.md index b5bb869c30e4..a2f6a1c53432 100644 --- a/substrate/primitives/maybe-compressed-blob/README.md +++ b/substrate/primitives/maybe-compressed-blob/README.md @@ -1,3 +1,3 @@ -Handling of blobs, typicaly validation code, which may be compressed. +Handling of blobs, typically validation code, which may be compressed. License: Apache-2.0 diff --git a/substrate/test-utils/runtime/res/README.md b/substrate/test-utils/runtime/res/README.md index 6d6ae55c3463..fdf94fd9d876 100644 --- a/substrate/test-utils/runtime/res/README.md +++ b/substrate/test-utils/runtime/res/README.md @@ -20,5 +20,5 @@ comparing keys, not the values. `renamed_authorities`. -`default_genesis_config_invalid.json` is just an imcomplete copy of +`default_genesis_config_invalid.json` is just an incomplete copy of `default_genesis_config.json` with `babe::authorities` field removed.