Follow-up on #1068 (#1567)

* BestParaHead small changes

Signed-off-by: Serban Iorga <serban@parity.io>

* Renamings

Signed-off-by: Serban Iorga <serban@parity.io>

* Use ParaInfo in parachains loop

Signed-off-by: Serban Iorga <serban@parity.io>

* Define StorageMapKeyProvider

Signed-off-by: Serban Iorga <serban@parity.io>

* CR fixes

Signed-off-by: Serban Iorga <serban@parity.io>
This commit is contained in:
Serban Iorga
2022-09-05 13:13:31 +03:00
committed by Bastian Köcher
parent 1c94fbeafc
commit ad38cdb873
6 changed files with 190 additions and 113 deletions
+7 -4
View File
@@ -56,7 +56,7 @@ where
_ => return Ok(ValidTransaction::default()),
};
let maybe_stored_best_head = crate::BestParaHeads::<T, I>::get(parachain);
let maybe_stored_best_head = crate::ParasInfo::<T, I>::get(parachain);
Self::validate_updated_parachain_head(
parachain,
&maybe_stored_best_head,
@@ -72,8 +72,9 @@ mod tests {
use crate::{
extension::FilterCall,
mock::{run_test, Call, TestRuntime},
BestParaHead, BestParaHeads, RelayBlockNumber,
ParaInfo, ParasInfo, RelayBlockNumber,
};
use bp_parachains::BestParaHeadHash;
use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId};
fn validate_submit_parachain_heads(
@@ -91,11 +92,13 @@ mod tests {
}
fn sync_to_relay_header_10() {
BestParaHeads::<TestRuntime, ()>::insert(
ParasInfo::<TestRuntime, ()>::insert(
ParaId(1),
BestParaHead {
ParaInfo {
best_head_hash: BestParaHeadHash {
at_relay_block_number: 10,
head_hash: [1u8; 32].into(),
},
next_imported_hash_position: 0,
},
);
+92 -66
View File
@@ -26,12 +26,10 @@
pub use weights::WeightInfo;
pub use weights_ext::WeightInfoExt;
use bp_parachains::parachain_head_storage_key_at_source;
use bp_parachains::{parachain_head_storage_key_at_source, ParaInfo};
use bp_polkadot_core::parachains::{ParaHash, ParaHasher, ParaHead, ParaHeadsProof, ParaId};
use bp_runtime::StorageProofError;
use codec::{Decode, Encode};
use frame_support::{traits::Contains, weights::PostDispatchInfo, RuntimeDebug};
use scale_info::TypeInfo;
use frame_support::{traits::Contains, weights::PostDispatchInfo};
use sp_runtime::traits::Header as HeaderT;
use sp_std::vec::Vec;
@@ -58,21 +56,10 @@ pub type RelayBlockNumber = bp_polkadot_core::BlockNumber;
/// Hasher of the bridged relay chain.
pub type RelayBlockHasher = bp_polkadot_core::Hasher;
/// Best known parachain head as it is stored in the runtime storage.
#[derive(Decode, Encode, PartialEq, RuntimeDebug, TypeInfo)]
pub struct BestParaHead {
/// Number of relay block where this head has been updated.
pub at_relay_block_number: RelayBlockNumber,
/// Hash of parachain head.
pub head_hash: ParaHash,
/// Current ring buffer position for this parachain.
pub next_imported_hash_position: u32,
}
/// Artifacts of the parachains head update.
struct UpdateParachainHeadArtifacts {
/// New best head of the parachain.
pub best_head: BestParaHead,
pub best_head: ParaInfo,
/// If `true`, some old parachain head has been pruned during update.
pub prune_happened: bool,
}
@@ -80,8 +67,10 @@ struct UpdateParachainHeadArtifacts {
#[frame_support::pallet]
pub mod pallet {
use super::*;
use bp_parachains::ImportedParaHeadsKeyProvider;
use bp_runtime::{BasicOperatingMode, OwnedBridgeModule, StorageDoubleMapKeyProvider};
use bp_parachains::{BestParaHeadHash, ImportedParaHeadsKeyProvider, ParasInfoKeyProvider};
use bp_runtime::{
BasicOperatingMode, OwnedBridgeModule, StorageDoubleMapKeyProvider, StorageMapKeyProvider,
};
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
@@ -159,10 +148,18 @@ pub mod pallet {
pub type PalletOperatingMode<T: Config<I>, I: 'static = ()> =
StorageValue<_, BasicOperatingMode, ValueQuery>;
/// Best parachain heads.
/// Parachains info.
///
/// Contains the following info:
/// - best parachain head hash
/// - the head of the `ImportedParaHashes` ring buffer
#[pallet::storage]
pub type BestParaHeads<T: Config<I>, I: 'static = ()> =
StorageMap<_, Blake2_128Concat, ParaId, BestParaHead>;
pub type ParasInfo<T: Config<I>, I: 'static = ()> = StorageMap<
_,
<ParasInfoKeyProvider as StorageMapKeyProvider>::Hasher,
<ParasInfoKeyProvider as StorageMapKeyProvider>::Key,
<ParasInfoKeyProvider as StorageMapKeyProvider>::Value,
>;
/// Parachain heads which have been imported into the pallet.
#[pallet::storage]
@@ -261,7 +258,7 @@ pub mod pallet {
target: LOG_TARGET,
"The head of parachain {:?} is None. {}",
parachain,
if BestParaHeads::<T, I>::contains_key(&parachain) {
if ParasInfo::<T, I>::contains_key(&parachain) {
"Looks like it is not yet registered at the source relay chain"
} else {
"Looks like it has been deregistered from the source relay chain"
@@ -294,7 +291,7 @@ pub mod pallet {
continue;
}
let update_result: Result<_, ()> = BestParaHeads::<T, I>::try_mutate(parachain, |stored_best_head| {
let update_result: Result<_, ()> = ParasInfo::<T, I>::try_mutate(parachain, |stored_best_head| {
let artifacts = Pallet::<T, I>::update_parachain_head(
parachain,
stored_best_head.take(),
@@ -348,7 +345,7 @@ pub mod pallet {
impl<T: Config<I>, I: 'static> Pallet<T, I> {
/// Get best finalized header of the given parachain.
pub fn best_parachain_head(parachain: ParaId) -> Option<ParaHead> {
let best_para_head_hash = BestParaHeads::<T, I>::get(parachain)?.head_hash;
let best_para_head_hash = ParasInfo::<T, I>::get(parachain)?.best_head_hash.head_hash;
ImportedParaHeads::<T, I>::get(parachain, best_para_head_hash)
}
@@ -392,7 +389,7 @@ pub mod pallet {
/// Without this check, we may import heads in random order.
pub fn validate_updated_parachain_head(
parachain: ParaId,
maybe_stored_best_head: &Option<BestParaHead>,
maybe_stored_best_head: &Option<ParaInfo>,
updated_at_relay_block_number: RelayBlockNumber,
updated_head_hash: ParaHash,
err_log_prefix: &str,
@@ -402,26 +399,28 @@ pub mod pallet {
None => return Ok(ValidTransaction::default()),
};
if stored_best_head.at_relay_block_number >= updated_at_relay_block_number {
if stored_best_head.best_head_hash.at_relay_block_number >=
updated_at_relay_block_number
{
log::trace!(
target: LOG_TARGET,
"{}. The parachain head for {:?} was already updated at better relay chain block {} >= {}.",
err_log_prefix,
parachain,
stored_best_head.at_relay_block_number,
stored_best_head.best_head_hash.at_relay_block_number,
updated_at_relay_block_number
);
return InvalidTransaction::Stale.into()
}
if stored_best_head.head_hash == updated_head_hash {
if stored_best_head.best_head_hash.head_hash == updated_head_hash {
log::trace!(
target: LOG_TARGET,
"{}. The parachain head hash for {:?} was already updated to {} at block {} < {}.",
err_log_prefix,
parachain,
updated_head_hash,
stored_best_head.at_relay_block_number,
stored_best_head.best_head_hash.at_relay_block_number,
updated_at_relay_block_number
);
return InvalidTransaction::Stale.into()
@@ -433,7 +432,7 @@ pub mod pallet {
/// Try to update parachain head.
pub(super) fn update_parachain_head(
parachain: ParaId,
stored_best_head: Option<BestParaHead>,
stored_best_head: Option<ParaInfo>,
updated_at_relay_block_number: RelayBlockNumber,
updated_head: ParaHead,
updated_head_hash: ParaHash,
@@ -454,9 +453,11 @@ pub mod pallet {
// insert updated best parachain head
let head_hash_to_prune =
ImportedParaHashes::<T, I>::try_get(parachain, next_imported_hash_position);
let updated_best_para_head = BestParaHead {
let updated_best_para_head = ParaInfo {
best_head_hash: BestParaHeadHash {
at_relay_block_number: updated_at_relay_block_number,
head_hash: updated_head_hash,
},
next_imported_hash_position: (next_imported_hash_position + 1) %
T::HeadsToKeep::get(),
};
@@ -527,9 +528,13 @@ mod tests {
use crate::mock::{
run_test, test_relay_header, Origin, TestRuntime, PARAS_PALLET_NAME, UNTRACKED_PARACHAIN_ID,
};
use codec::Encode;
use bp_parachains::ImportedParaHeadsKeyProvider;
use bp_runtime::{BasicOperatingMode, OwnedBridgeModuleError, StorageDoubleMapKeyProvider};
use bp_parachains::{BestParaHeadHash, ImportedParaHeadsKeyProvider, ParasInfoKeyProvider};
use bp_runtime::{
BasicOperatingMode, OwnedBridgeModuleError, StorageDoubleMapKeyProvider,
StorageMapKeyProvider,
};
use bp_test_utils::{
authority_list, generate_owned_bridge_module_tests, make_default_justification,
};
@@ -606,10 +611,12 @@ mod tests {
(root, ParaHeadsProof(storage_proof), parachains)
}
fn initial_best_head(parachain: u32) -> BestParaHead {
BestParaHead {
fn initial_best_head(parachain: u32) -> ParaInfo {
ParaInfo {
best_head_hash: BestParaHeadHash {
at_relay_block_number: 0,
head_hash: head_data(parachain, 0).hash(),
},
next_imported_hash_position: 1,
}
}
@@ -697,23 +704,31 @@ mod tests {
assert_eq!(result.expect("checked above").actual_weight, Some(expected_weight));
// but only 1 and 2 are updated, because proof is missing head of parachain#2
assert_eq!(BestParaHeads::<TestRuntime>::get(ParaId(1)), Some(initial_best_head(1)));
assert_eq!(BestParaHeads::<TestRuntime>::get(ParaId(2)), None);
assert_eq!(ParasInfo::<TestRuntime>::get(ParaId(1)), Some(initial_best_head(1)));
assert_eq!(ParasInfo::<TestRuntime>::get(ParaId(2)), None);
assert_eq!(
BestParaHeads::<TestRuntime>::get(ParaId(3)),
Some(BestParaHead {
ParasInfo::<TestRuntime>::get(ParaId(3)),
Some(ParaInfo {
best_head_hash: BestParaHeadHash {
at_relay_block_number: 0,
head_hash: head_data(3, 10).hash(),
head_hash: head_data(3, 10).hash()
},
next_imported_hash_position: 1,
})
);
assert_eq!(
ImportedParaHeads::<TestRuntime>::get(ParaId(1), initial_best_head(1).head_hash),
ImportedParaHeads::<TestRuntime>::get(
ParaId(1),
initial_best_head(1).best_head_hash.head_hash
),
Some(head_data(1, 0))
);
assert_eq!(
ImportedParaHeads::<TestRuntime>::get(ParaId(2), initial_best_head(2).head_hash),
ImportedParaHeads::<TestRuntime>::get(
ParaId(2),
initial_best_head(2).best_head_hash.head_hash
),
None
);
assert_eq!(
@@ -734,10 +749,12 @@ mod tests {
initialize(state_root_5);
assert_ok!(import_parachain_1_head(0, state_root_5, parachains_5, proof_5));
assert_eq!(
BestParaHeads::<TestRuntime>::get(ParaId(1)),
Some(BestParaHead {
ParasInfo::<TestRuntime>::get(ParaId(1)),
Some(ParaInfo {
best_head_hash: BestParaHeadHash {
at_relay_block_number: 0,
head_hash: head_data(1, 5).hash(),
head_hash: head_data(1, 5).hash()
},
next_imported_hash_position: 1,
})
);
@@ -754,10 +771,12 @@ mod tests {
proceed(1, state_root_10);
assert_ok!(import_parachain_1_head(1, state_root_10, parachains_10, proof_10));
assert_eq!(
BestParaHeads::<TestRuntime>::get(ParaId(1)),
Some(BestParaHead {
ParasInfo::<TestRuntime>::get(ParaId(1)),
Some(ParaInfo {
best_head_hash: BestParaHeadHash {
at_relay_block_number: 1,
head_hash: head_data(1, 10).hash(),
head_hash: head_data(1, 10).hash()
},
next_imported_hash_position: 2,
})
);
@@ -797,19 +816,23 @@ mod tests {
assert_ok!(result);
assert_eq!(result.expect("checked above").actual_weight, Some(expected_weight));
assert_eq!(
BestParaHeads::<TestRuntime>::get(ParaId(1)),
Some(BestParaHead {
ParasInfo::<TestRuntime>::get(ParaId(1)),
Some(ParaInfo {
best_head_hash: BestParaHeadHash {
at_relay_block_number: 0,
head_hash: head_data(1, 5).hash(),
head_hash: head_data(1, 5).hash()
},
next_imported_hash_position: 1,
})
);
assert_eq!(BestParaHeads::<TestRuntime>::get(ParaId(UNTRACKED_PARACHAIN_ID)), None,);
assert_eq!(ParasInfo::<TestRuntime>::get(ParaId(UNTRACKED_PARACHAIN_ID)), None,);
assert_eq!(
BestParaHeads::<TestRuntime>::get(ParaId(2)),
Some(BestParaHead {
ParasInfo::<TestRuntime>::get(ParaId(2)),
Some(ParaInfo {
best_head_hash: BestParaHeadHash {
at_relay_block_number: 0,
head_hash: head_data(1, 5).hash(),
head_hash: head_data(1, 5).hash()
},
next_imported_hash_position: 1,
})
);
@@ -824,13 +847,13 @@ mod tests {
// import head#0 of parachain#1 at relay block#0
initialize(state_root);
assert_ok!(import_parachain_1_head(0, state_root, parachains.clone(), proof.clone()));
assert_eq!(BestParaHeads::<TestRuntime>::get(ParaId(1)), Some(initial_best_head(1)));
assert_eq!(ParasInfo::<TestRuntime>::get(ParaId(1)), Some(initial_best_head(1)));
// try to import head#0 of parachain#1 at relay block#1
// => call succeeds, but nothing is changed
proceed(1, state_root);
assert_ok!(import_parachain_1_head(1, state_root, parachains, proof));
assert_eq!(BestParaHeads::<TestRuntime>::get(ParaId(1)), Some(initial_best_head(1)));
assert_eq!(ParasInfo::<TestRuntime>::get(ParaId(1)), Some(initial_best_head(1)));
});
}
@@ -848,10 +871,12 @@ mod tests {
proceed(1, state_root_10);
assert_ok!(import_parachain_1_head(1, state_root_10, parachains_10, proof_10));
assert_eq!(
BestParaHeads::<TestRuntime>::get(ParaId(1)),
Some(BestParaHead {
ParasInfo::<TestRuntime>::get(ParaId(1)),
Some(ParaInfo {
best_head_hash: BestParaHeadHash {
at_relay_block_number: 1,
head_hash: head_data(1, 10).hash(),
head_hash: head_data(1, 10).hash()
},
next_imported_hash_position: 1,
})
);
@@ -860,10 +885,12 @@ mod tests {
// => nothing is changed, because better head has already been imported
assert_ok!(import_parachain_1_head(0, state_root_5, parachains_5, proof_5));
assert_eq!(
BestParaHeads::<TestRuntime>::get(ParaId(1)),
Some(BestParaHead {
ParasInfo::<TestRuntime>::get(ParaId(1)),
Some(ParaInfo {
best_head_hash: BestParaHeadHash {
at_relay_block_number: 1,
head_hash: head_data(1, 10).hash(),
head_hash: head_data(1, 10).hash()
},
next_imported_hash_position: 1,
})
);
@@ -1003,9 +1030,8 @@ mod tests {
#[test]
fn storage_keys_computed_properly() {
assert_eq!(
BestParaHeads::<TestRuntime>::storage_map_final_key(ParaId(42)).to_vec(),
bp_parachains::best_parachain_head_hash_storage_key_at_target("Parachains", ParaId(42))
.0,
ParasInfo::<TestRuntime>::storage_map_final_key(ParaId(42)).to_vec(),
ParasInfoKeyProvider::final_key("Parachains", &ParaId(42)).0
);
assert_eq!(
+19 -12
View File
@@ -22,7 +22,7 @@ use bp_polkadot_core::{
parachains::{ParaHash, ParaHead, ParaId},
BlockNumber as RelayBlockNumber,
};
use bp_runtime::StorageDoubleMapKeyProvider;
use bp_runtime::{StorageDoubleMapKeyProvider, StorageMapKeyProvider};
use codec::{Decode, Encode};
use frame_support::{Blake2_128Concat, RuntimeDebug, Twox64Concat};
use scale_info::TypeInfo;
@@ -44,6 +44,15 @@ pub struct BestParaHeadHash {
pub head_hash: ParaHash,
}
/// Best known parachain head as it is stored in the runtime storage.
#[derive(Decode, Encode, PartialEq, RuntimeDebug, TypeInfo)]
pub struct ParaInfo {
/// Best known parachain head hash.
pub best_head_hash: BestParaHeadHash,
/// Current ring buffer position for this parachain.
pub next_imported_hash_position: u32,
}
/// Returns runtime storage key of given parachain head at the source chain.
///
/// The head is stored by the `paras` pallet in the `Heads` map.
@@ -54,18 +63,16 @@ pub fn parachain_head_storage_key_at_source(
bp_runtime::storage_map_final_key::<Twox64Concat>(paras_pallet_name, "Heads", &para_id.encode())
}
/// Returns runtime storage key of best known parachain head at the target chain.
/// Can be use to access the runtime storage key of the parachains info at the target chain.
///
/// The head is stored by the `pallet-bridge-parachains` pallet in the `BestParaHeads` map.
pub fn best_parachain_head_hash_storage_key_at_target(
bridge_parachains_pallet_name: &str,
para_id: ParaId,
) -> StorageKey {
bp_runtime::storage_map_final_key::<Blake2_128Concat>(
bridge_parachains_pallet_name,
"BestParaHeads",
&para_id.encode(),
)
/// The info is stored by the `pallet-bridge-parachains` pallet in the `ParasInfo` map.
pub struct ParasInfoKeyProvider;
impl StorageMapKeyProvider for ParasInfoKeyProvider {
const MAP_NAME: &'static str = "ParasInfo";
type Hasher = Blake2_128Concat;
type Key = ParaId;
type Value = ParaInfo;
}
/// Can be use to access the runtime storage key of the parachain head at the target chain.
+30 -8
View File
@@ -285,20 +285,42 @@ pub fn storage_value_key(pallet_prefix: &str, value_name: &str) -> StorageKey {
StorageKey(final_key)
}
/// Can be use to access the runtime storage key of a `StorageDoubleMap`.
pub trait StorageDoubleMapKeyProvider {
// The name of the variable that holds the `StorageDoubleMap`
/// Can be use to access the runtime storage key of a `StorageMap`.
pub trait StorageMapKeyProvider {
/// The name of the variable that holds the `StorageMap`.
const MAP_NAME: &'static str;
// The same as `StorageDoubleMap::Hasher1`
/// The same as `StorageMap::Hasher1`.
type Hasher: StorageHasher;
/// The same as `StorageMap::Key1`.
type Key: FullCodec;
/// The same as `StorageMap::Value`.
type Value: FullCodec;
/// This is a copy of the
/// `frame_support::storage::generator::StorageMap::storage_map_final_key`.
///
/// We're using it because to call `storage_map_final_key` directly, we need access
/// to the runtime and pallet instance, which (sometimes) is impossible.
fn final_key(pallet_prefix: &str, key: &Self::Key) -> StorageKey {
storage_map_final_key::<Self::Hasher>(pallet_prefix, Self::MAP_NAME, &key.encode())
}
}
/// Can be use to access the runtime storage key of a `StorageDoubleMap`.
pub trait StorageDoubleMapKeyProvider {
/// The name of the variable that holds the `StorageDoubleMap`.
const MAP_NAME: &'static str;
/// The same as `StorageDoubleMap::Hasher1`.
type Hasher1: StorageHasher;
// The same as `StorageDoubleMap::Key1`
/// The same as `StorageDoubleMap::Key1`.
type Key1: FullCodec;
// The same as `StorageDoubleMap::Hasher2`
/// The same as `StorageDoubleMap::Hasher2`.
type Hasher2: StorageHasher;
// The same as `StorageDoubleMap::Key2`
/// The same as `StorageDoubleMap::Key2`.
type Key2: FullCodec;
// The same as `StorageDoubleMap::Value`
/// The same as `StorageDoubleMap::Value`.
type Value: FullCodec;
/// This is a copy of the
+18 -1
View File
@@ -29,7 +29,7 @@ use crate::{
use async_std::sync::{Arc, Mutex};
use async_trait::async_trait;
use bp_runtime::{HeaderIdProvider, StorageDoubleMapKeyProvider};
use bp_runtime::{HeaderIdProvider, StorageDoubleMapKeyProvider, StorageMapKeyProvider};
use codec::{Decode, Encode};
use frame_system::AccountInfo;
use futures::{SinkExt, StreamExt};
@@ -320,6 +320,23 @@ impl<C: Chain> Client<C> {
.transpose()
}
/// Read `MapStorage` value from runtime storage.
pub async fn storage_map_value<T: StorageMapKeyProvider>(
&self,
pallet_prefix: &str,
key: &T::Key,
block_hash: Option<C::Hash>,
) -> Result<Option<T::Value>> {
let storage_key = T::final_key(pallet_prefix, key);
self.raw_storage_value(storage_key, block_hash)
.await?
.map(|encoded_value| {
T::Value::decode(&mut &encoded_value.0[..]).map_err(Error::ResponseParseFailed)
})
.transpose()
}
/// Read `DoubleMapStorage` value from runtime storage.
pub async fn storage_double_map_value<T: StorageDoubleMapKeyProvider>(
&self,
@@ -24,9 +24,7 @@ use crate::{
};
use async_trait::async_trait;
use bp_parachains::{
best_parachain_head_hash_storage_key_at_target, BestParaHeadHash, ImportedParaHeadsKeyProvider,
};
use bp_parachains::{BestParaHeadHash, ImportedParaHeadsKeyProvider, ParasInfoKeyProvider};
use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId};
use bp_runtime::HeaderIdProvider;
use codec::Decode;
@@ -122,12 +120,16 @@ where
metrics: Option<&ParachainsLoopMetrics>,
para_id: ParaId,
) -> Result<Option<BestParaHeadHash>, Self::Error> {
let best_para_head_hash_key = best_parachain_head_hash_storage_key_at_target(
let best_para_head_hash: Option<BestParaHeadHash> = self
.client
.storage_map_value::<ParasInfoKeyProvider>(
P::SourceRelayChain::PARACHAINS_FINALITY_PALLET_NAME,
para_id,
);
let best_para_head_hash: Option<BestParaHeadHash> =
self.client.storage_value(best_para_head_hash_key, Some(at_block.1)).await?;
&para_id,
Some(at_block.1),
)
.await?
.map(|para_info| para_info.best_head_hash);
if let (Some(metrics), &Some(ref best_para_head_hash)) = (metrics, &best_para_head_hash) {
let imported_para_head = self
.client