mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-31 00:31:02 +00:00
Allow Substrate Pallet to be Initialized via Dipatchable (#481)
* Add dispatchable for intializing pallet * Add Polkadot JS types for Substrate bridge pallet * Ensure Root is the only one that can initialize the pallet * Add some tests * Pack initialization data into struct * Only allow pallet to be initialized once * Use new initialization config in nodes * Rename ScheduledChange in Ethereum pallet We're renaming it to prevent clashes with Substrate bridge pallet type of the same name. This is relevant when importing types to Polkadot JS Apps. * Move all Polkadot JS types into one file * Appease Clippy
This commit is contained in:
committed by
Bastian Köcher
parent
d4fc7bebdc
commit
cac8319480
@@ -155,7 +155,10 @@ fn testnet_genesis(
|
|||||||
pallet_grandpa: Some(GrandpaConfig {
|
pallet_grandpa: Some(GrandpaConfig {
|
||||||
authorities: Vec::new(),
|
authorities: Vec::new(),
|
||||||
}),
|
}),
|
||||||
pallet_substrate_bridge: load_rialto_bridge_config(),
|
pallet_substrate_bridge: Some(BridgeRialtoConfig {
|
||||||
|
// We'll initialize the pallet with a dispatchable instead.
|
||||||
|
init_data: None,
|
||||||
|
}),
|
||||||
pallet_sudo: Some(SudoConfig { key: root_key }),
|
pallet_sudo: Some(SudoConfig { key: root_key }),
|
||||||
pallet_session: Some(SessionConfig {
|
pallet_session: Some(SessionConfig {
|
||||||
keys: initial_authorities
|
keys: initial_authorities
|
||||||
@@ -165,12 +168,3 @@ fn testnet_genesis(
|
|||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_rialto_bridge_config() -> Option<BridgeRialtoConfig> {
|
|
||||||
Some(BridgeRialtoConfig {
|
|
||||||
initial_header: Some(millau_runtime::rialto::initial_header()),
|
|
||||||
initial_authority_list: millau_runtime::rialto::initial_authority_set().authorities,
|
|
||||||
initial_set_id: millau_runtime::rialto::initial_authority_set().set_id,
|
|
||||||
first_scheduled_change: None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -186,9 +186,6 @@ fn load_kovan_bridge_config() -> Option<BridgeKovanConfig> {
|
|||||||
|
|
||||||
fn load_millau_bridge_config() -> Option<BridgeMillauConfig> {
|
fn load_millau_bridge_config() -> Option<BridgeMillauConfig> {
|
||||||
Some(BridgeMillauConfig {
|
Some(BridgeMillauConfig {
|
||||||
initial_header: Some(rialto_runtime::millau::initial_header()),
|
init_data: Some(rialto_runtime::millau::init_data()),
|
||||||
initial_authority_list: rialto_runtime::millau::initial_authority_set().authorities,
|
|
||||||
initial_set_id: rialto_runtime::millau::initial_authority_set().set_id,
|
|
||||||
first_scheduled_change: None,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,11 +18,23 @@
|
|||||||
|
|
||||||
use bp_rialto::Header;
|
use bp_rialto::Header;
|
||||||
use hex_literal::hex;
|
use hex_literal::hex;
|
||||||
use pallet_substrate_bridge::AuthoritySet;
|
use pallet_substrate_bridge::{AuthoritySet, InitializationData};
|
||||||
use sp_core::crypto::Public;
|
use sp_core::crypto::Public;
|
||||||
use sp_finality_grandpa::AuthorityId;
|
use sp_finality_grandpa::AuthorityId;
|
||||||
use sp_std::vec;
|
use sp_std::vec;
|
||||||
|
|
||||||
|
/// Information about where the bridge palelt should start syncing from. This includes things like
|
||||||
|
/// the initial header and the initial authorities of the briged chain.
|
||||||
|
pub fn init_data() -> InitializationData<Header> {
|
||||||
|
let authority_set = initial_authority_set();
|
||||||
|
InitializationData {
|
||||||
|
header: initial_header(),
|
||||||
|
authority_list: authority_set.authorities,
|
||||||
|
set_id: authority_set.set_id,
|
||||||
|
scheduled_change: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The first header known to the pallet.
|
/// The first header known to the pallet.
|
||||||
///
|
///
|
||||||
/// Note that this does not need to be the genesis header of the Millau
|
/// Note that this does not need to be the genesis header of the Millau
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ pub struct ValidatorsSet {
|
|||||||
/// Validators set change as it is stored in the runtime storage.
|
/// Validators set change as it is stored in the runtime storage.
|
||||||
#[derive(Encode, Decode, PartialEq, RuntimeDebug)]
|
#[derive(Encode, Decode, PartialEq, RuntimeDebug)]
|
||||||
#[cfg_attr(test, derive(Clone))]
|
#[cfg_attr(test, derive(Clone))]
|
||||||
pub struct ScheduledChange {
|
pub struct AuraScheduledChange {
|
||||||
/// Validators of this set.
|
/// Validators of this set.
|
||||||
pub validators: Vec<Address>,
|
pub validators: Vec<Address>,
|
||||||
/// Hash of the block which has emitted previous validators change signal.
|
/// Hash of the block which has emitted previous validators change signal.
|
||||||
@@ -187,7 +187,7 @@ pub struct ImportContext<Submitter> {
|
|||||||
parent_hash: H256,
|
parent_hash: H256,
|
||||||
parent_header: AuraHeader,
|
parent_header: AuraHeader,
|
||||||
parent_total_difficulty: U256,
|
parent_total_difficulty: U256,
|
||||||
parent_scheduled_change: Option<ScheduledChange>,
|
parent_scheduled_change: Option<AuraScheduledChange>,
|
||||||
validators_set_id: u64,
|
validators_set_id: u64,
|
||||||
validators_set: ValidatorsSet,
|
validators_set: ValidatorsSet,
|
||||||
last_signal_block: Option<HeaderId>,
|
last_signal_block: Option<HeaderId>,
|
||||||
@@ -210,7 +210,7 @@ impl<Submitter> ImportContext<Submitter> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the validator set change if the parent header has signaled a change.
|
/// Returns the validator set change if the parent header has signaled a change.
|
||||||
pub fn parent_scheduled_change(&self) -> Option<&ScheduledChange> {
|
pub fn parent_scheduled_change(&self) -> Option<&AuraScheduledChange> {
|
||||||
self.parent_scheduled_change.as_ref()
|
self.parent_scheduled_change.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,7 +293,7 @@ pub trait Storage {
|
|||||||
) -> Option<ImportContext<Self::Submitter>>;
|
) -> Option<ImportContext<Self::Submitter>>;
|
||||||
/// Get new validators that are scheduled by given header and hash of the previous
|
/// Get new validators that are scheduled by given header and hash of the previous
|
||||||
/// block that has scheduled change.
|
/// block that has scheduled change.
|
||||||
fn scheduled_change(&self, hash: &H256) -> Option<ScheduledChange>;
|
fn scheduled_change(&self, hash: &H256) -> Option<AuraScheduledChange>;
|
||||||
/// Insert imported header.
|
/// Insert imported header.
|
||||||
fn insert_header(&mut self, header: HeaderToImport<Self::Submitter>);
|
fn insert_header(&mut self, header: HeaderToImport<Self::Submitter>);
|
||||||
/// Finalize given block and schedules pruning of all headers
|
/// Finalize given block and schedules pruning of all headers
|
||||||
@@ -479,7 +479,7 @@ decl_storage! {
|
|||||||
/// When it reaches zero, we are free to prune validator set as well.
|
/// When it reaches zero, we are free to prune validator set as well.
|
||||||
ValidatorsSetsRc: map hasher(twox_64_concat) u64 => Option<u64>;
|
ValidatorsSetsRc: map hasher(twox_64_concat) u64 => Option<u64>;
|
||||||
/// Map of validators set changes scheduled by given header.
|
/// Map of validators set changes scheduled by given header.
|
||||||
ScheduledChanges: map hasher(identity) H256 => Option<ScheduledChange>;
|
ScheduledChanges: map hasher(identity) H256 => Option<AuraScheduledChange>;
|
||||||
}
|
}
|
||||||
add_extra_genesis {
|
add_extra_genesis {
|
||||||
config(initial_header): AuraHeader;
|
config(initial_header): AuraHeader;
|
||||||
@@ -766,7 +766,7 @@ impl<T: Trait<I>, I: Instance> Storage for BridgeStorage<T, I> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scheduled_change(&self, hash: &H256) -> Option<ScheduledChange> {
|
fn scheduled_change(&self, hash: &H256) -> Option<AuraScheduledChange> {
|
||||||
ScheduledChanges::<I>::get(hash)
|
ScheduledChanges::<I>::get(hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -777,7 +777,7 @@ impl<T: Trait<I>, I: Instance> Storage for BridgeStorage<T, I> {
|
|||||||
if let Some(scheduled_change) = header.scheduled_change {
|
if let Some(scheduled_change) = header.scheduled_change {
|
||||||
ScheduledChanges::<I>::insert(
|
ScheduledChanges::<I>::insert(
|
||||||
&header.id.hash,
|
&header.id.hash,
|
||||||
ScheduledChange {
|
AuraScheduledChange {
|
||||||
validators: scheduled_change,
|
validators: scheduled_change,
|
||||||
prev_signal_block: header.context.last_signal_block,
|
prev_signal_block: header.context.last_signal_block,
|
||||||
},
|
},
|
||||||
@@ -1119,7 +1119,7 @@ pub(crate) mod tests {
|
|||||||
if i == 7 && j == 1 {
|
if i == 7 && j == 1 {
|
||||||
ScheduledChanges::<DefaultInstance>::insert(
|
ScheduledChanges::<DefaultInstance>::insert(
|
||||||
hash,
|
hash,
|
||||||
ScheduledChange {
|
AuraScheduledChange {
|
||||||
validators: validators_addresses(5),
|
validators: validators_addresses(5),
|
||||||
prev_signal_block: None,
|
prev_signal_block: None,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -277,7 +277,7 @@ pub(crate) mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::mock::{run_test, validators_addresses, validators_change_receipt, TestRuntime};
|
use crate::mock::{run_test, validators_addresses, validators_change_receipt, TestRuntime};
|
||||||
use crate::DefaultInstance;
|
use crate::DefaultInstance;
|
||||||
use crate::{BridgeStorage, Headers, ScheduledChange, ScheduledChanges, StoredHeader};
|
use crate::{AuraScheduledChange, BridgeStorage, Headers, ScheduledChanges, StoredHeader};
|
||||||
use bp_eth_poa::compute_merkle_root;
|
use bp_eth_poa::compute_merkle_root;
|
||||||
use frame_support::StorageMap;
|
use frame_support::StorageMap;
|
||||||
|
|
||||||
@@ -428,7 +428,7 @@ pub(crate) mod tests {
|
|||||||
next_validators_set_id: 0,
|
next_validators_set_id: 0,
|
||||||
last_signal_block: scheduled_at,
|
last_signal_block: scheduled_at,
|
||||||
};
|
};
|
||||||
let scheduled_change = ScheduledChange {
|
let scheduled_change = AuraScheduledChange {
|
||||||
validators: validators_addresses(1),
|
validators: validators_addresses(1),
|
||||||
prev_signal_block: None,
|
prev_signal_block: None,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use crate::validators::{Validators, ValidatorsConfiguration};
|
use crate::validators::{Validators, ValidatorsConfiguration};
|
||||||
use crate::{AuraConfiguration, ChainTime, ImportContext, PoolConfiguration, ScheduledChange, Storage};
|
use crate::{AuraConfiguration, AuraScheduledChange, ChainTime, ImportContext, PoolConfiguration, Storage};
|
||||||
use bp_eth_poa::{
|
use bp_eth_poa::{
|
||||||
public_to_address, step_validator, Address, AuraHeader, HeaderId, Receipt, SealedEmptyStep, H256, H520, U128, U256,
|
public_to_address, step_validator, Address, AuraHeader, HeaderId, Receipt, SealedEmptyStep, H256, H520, U128, U256,
|
||||||
};
|
};
|
||||||
@@ -341,7 +341,7 @@ fn find_next_validators_signal<S: Storage>(storage: &S, context: &ImportContext<
|
|||||||
// if parent schedules validators set change, then it may be our set
|
// if parent schedules validators set change, then it may be our set
|
||||||
// else we'll start with last known change
|
// else we'll start with last known change
|
||||||
let mut current_set_signal_block = context.last_signal_block();
|
let mut current_set_signal_block = context.last_signal_block();
|
||||||
let mut next_scheduled_set: Option<ScheduledChange> = None;
|
let mut next_scheduled_set: Option<AuraScheduledChange> = None;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
// if we have reached block that signals finalized change, then
|
// if we have reached block that signals finalized change, then
|
||||||
@@ -456,7 +456,7 @@ mod tests {
|
|||||||
});
|
});
|
||||||
ScheduledChanges::<DefaultInstance>::insert(
|
ScheduledChanges::<DefaultInstance>::insert(
|
||||||
header.header.parent_hash,
|
header.header.parent_hash,
|
||||||
ScheduledChange {
|
AuraScheduledChange {
|
||||||
validators: signalled_set,
|
validators: signalled_set,
|
||||||
prev_signal_block: None,
|
prev_signal_block: None,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -33,15 +33,15 @@
|
|||||||
|
|
||||||
use crate::storage::ImportedHeader;
|
use crate::storage::ImportedHeader;
|
||||||
use bp_runtime::{BlockNumberOf, Chain, HashOf, HasherOf, HeaderOf};
|
use bp_runtime::{BlockNumberOf, Chain, HashOf, HasherOf, HeaderOf};
|
||||||
use frame_support::{decl_error, decl_module, decl_storage, dispatch::DispatchResult};
|
use frame_support::{decl_error, decl_module, decl_storage, dispatch::DispatchResult, ensure};
|
||||||
use frame_system::ensure_signed;
|
use frame_system::{ensure_root, ensure_signed};
|
||||||
use sp_runtime::traits::Header as HeaderT;
|
use sp_runtime::traits::Header as HeaderT;
|
||||||
use sp_runtime::RuntimeDebug;
|
use sp_runtime::RuntimeDebug;
|
||||||
use sp_std::{marker::PhantomData, prelude::*};
|
use sp_std::{marker::PhantomData, prelude::*};
|
||||||
use sp_trie::StorageProof;
|
use sp_trie::StorageProof;
|
||||||
|
|
||||||
// Re-export since the node uses these when configuring genesis
|
// Re-export since the node uses these when configuring genesis
|
||||||
pub use storage::{AuthoritySet, ScheduledChange};
|
pub use storage::{AuthoritySet, InitializationData, ScheduledChange};
|
||||||
|
|
||||||
pub use justification::decode_justification_target;
|
pub use justification::decode_justification_target;
|
||||||
pub use storage_proof::StorageProofChecker;
|
pub use storage_proof::StorageProofChecker;
|
||||||
@@ -105,53 +105,18 @@ decl_storage! {
|
|||||||
// Grandpa doesn't require there to always be a pending change. In fact, most of the time
|
// Grandpa doesn't require there to always be a pending change. In fact, most of the time
|
||||||
// there will be no pending change available.
|
// there will be no pending change available.
|
||||||
NextScheduledChange: map hasher(identity) BridgedBlockHash<T> => Option<ScheduledChange<BridgedBlockNumber<T>>>;
|
NextScheduledChange: map hasher(identity) BridgedBlockHash<T> => Option<ScheduledChange<BridgedBlockNumber<T>>>;
|
||||||
|
/// Whether or not the bridge has been initialized.
|
||||||
|
///
|
||||||
|
/// This is important to know to ensure that we don't try and initialize the bridge twice
|
||||||
|
/// and create an inconsistent genesis state.
|
||||||
|
IsInitialized: bool;
|
||||||
}
|
}
|
||||||
add_extra_genesis {
|
add_extra_genesis {
|
||||||
config(initial_header): Option<BridgedHeader<T>>;
|
config(init_data): Option<InitializationData<BridgedHeader<T>>>;
|
||||||
config(initial_authority_list): sp_finality_grandpa::AuthorityList;
|
|
||||||
config(initial_set_id): sp_finality_grandpa::SetId;
|
|
||||||
config(first_scheduled_change): Option<ScheduledChange<BridgedBlockNumber<T>>>;
|
|
||||||
build(|config| {
|
build(|config| {
|
||||||
assert!(
|
if let Some(init_data) = config.init_data.clone() {
|
||||||
!config.initial_authority_list.is_empty(),
|
initialize_bridge::<T>(init_data);
|
||||||
"An initial authority list is needed."
|
}
|
||||||
);
|
|
||||||
|
|
||||||
let initial_header = config
|
|
||||||
.initial_header
|
|
||||||
.clone()
|
|
||||||
.expect("An initial header is needed");
|
|
||||||
let initial_hash = initial_header.hash();
|
|
||||||
|
|
||||||
<BestHeight<T>>::put(initial_header.number());
|
|
||||||
<BestHeaders<T>>::put(vec![initial_hash]);
|
|
||||||
<BestFinalized<T>>::put(initial_hash);
|
|
||||||
|
|
||||||
let authority_set =
|
|
||||||
AuthoritySet::new(config.initial_authority_list.clone(), config.initial_set_id);
|
|
||||||
CurrentAuthoritySet::put(authority_set);
|
|
||||||
|
|
||||||
let mut signal_hash = None;
|
|
||||||
if let Some(ref change) = config.first_scheduled_change {
|
|
||||||
assert!(
|
|
||||||
change.height > *initial_header.number(),
|
|
||||||
"Changes must be scheduled past initial header."
|
|
||||||
);
|
|
||||||
|
|
||||||
signal_hash = Some(initial_hash);
|
|
||||||
<NextScheduledChange<T>>::insert(initial_hash, change);
|
|
||||||
};
|
|
||||||
|
|
||||||
<ImportedHeaders<T>>::insert(
|
|
||||||
initial_hash,
|
|
||||||
ImportedHeader {
|
|
||||||
header: initial_header,
|
|
||||||
requires_justification: false,
|
|
||||||
is_finalized: true,
|
|
||||||
signal_hash,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -164,6 +129,8 @@ decl_error! {
|
|||||||
UnfinalizedHeader,
|
UnfinalizedHeader,
|
||||||
/// The header is unknown.
|
/// The header is unknown.
|
||||||
UnknownHeader,
|
UnknownHeader,
|
||||||
|
/// The pallet has already been initialized.
|
||||||
|
AlreadyInitialized,
|
||||||
/// The storage proof doesn't contains storage root. So it is invalid for given header.
|
/// The storage proof doesn't contains storage root. So it is invalid for given header.
|
||||||
StorageRootMismatch,
|
StorageRootMismatch,
|
||||||
/// Error when trying to fetch storage value from the proof.
|
/// Error when trying to fetch storage value from the proof.
|
||||||
@@ -225,6 +192,27 @@ decl_module! {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Bootstrap the bridge pallet with an initial header and authority set from which to sync.
|
||||||
|
///
|
||||||
|
/// The initial configuration provided does not need to be the genesis header of the bridged
|
||||||
|
/// chain, it can be any arbirary header. You can also provide the next scheduled set change
|
||||||
|
/// if it is already know.
|
||||||
|
///
|
||||||
|
/// This function is only allowed to be called from a trusted origin and writes to storage
|
||||||
|
/// with practically no checks in terms of the validity of the data. It is important that
|
||||||
|
/// you ensure that valid data is being passed in.
|
||||||
|
//TODO: Update weights [#78]
|
||||||
|
#[weight = 0]
|
||||||
|
pub fn initialize(
|
||||||
|
origin,
|
||||||
|
init_data: InitializationData<BridgedHeader<T>>,
|
||||||
|
) {
|
||||||
|
let _ = ensure_root(origin)?;
|
||||||
|
let init_allowed = !IsInitialized::get();
|
||||||
|
ensure!(init_allowed, <Error<T>>::AlreadyInitialized);
|
||||||
|
initialize_bridge::<T>(init_data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -300,6 +288,49 @@ impl<T: Trait> Module<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Since this writes to storage with no real checks this should only be used in functions that were
|
||||||
|
// called by a trusted origin.
|
||||||
|
fn initialize_bridge<T: Trait>(init_params: InitializationData<BridgedHeader<T>>) {
|
||||||
|
let InitializationData {
|
||||||
|
header,
|
||||||
|
authority_list,
|
||||||
|
set_id,
|
||||||
|
scheduled_change,
|
||||||
|
} = init_params;
|
||||||
|
|
||||||
|
let initial_hash = header.hash();
|
||||||
|
|
||||||
|
let mut signal_hash = None;
|
||||||
|
if let Some(ref change) = scheduled_change {
|
||||||
|
assert!(
|
||||||
|
change.height > *header.number(),
|
||||||
|
"Changes must be scheduled past initial header."
|
||||||
|
);
|
||||||
|
|
||||||
|
signal_hash = Some(initial_hash);
|
||||||
|
<NextScheduledChange<T>>::insert(initial_hash, change);
|
||||||
|
};
|
||||||
|
|
||||||
|
<BestHeight<T>>::put(header.number());
|
||||||
|
<BestHeaders<T>>::put(vec![initial_hash]);
|
||||||
|
<BestFinalized<T>>::put(initial_hash);
|
||||||
|
|
||||||
|
let authority_set = AuthoritySet::new(authority_list, set_id);
|
||||||
|
CurrentAuthoritySet::put(authority_set);
|
||||||
|
|
||||||
|
<ImportedHeaders<T>>::insert(
|
||||||
|
initial_hash,
|
||||||
|
ImportedHeader {
|
||||||
|
header,
|
||||||
|
requires_justification: false,
|
||||||
|
is_finalized: true,
|
||||||
|
signal_hash,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
IsInitialized::put(true);
|
||||||
|
}
|
||||||
|
|
||||||
/// Expected interface for interacting with bridge pallet storage.
|
/// Expected interface for interacting with bridge pallet storage.
|
||||||
// TODO: This should be split into its own less-Substrate-dependent crate
|
// TODO: This should be split into its own less-Substrate-dependent crate
|
||||||
pub trait BridgeStorage {
|
pub trait BridgeStorage {
|
||||||
@@ -468,8 +499,75 @@ impl<T: Trait> BridgeStorage for PalletStorage<T> {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::mock::{helpers::unfinalized_header, run_test, TestRuntime};
|
use crate::mock::helpers::{authority_list, test_header, unfinalized_header};
|
||||||
|
use crate::mock::{run_test, Origin, TestRuntime};
|
||||||
use frame_support::{assert_noop, assert_ok};
|
use frame_support::{assert_noop, assert_ok};
|
||||||
|
use sp_runtime::DispatchError;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn only_root_origin_can_initialize_pallet() {
|
||||||
|
run_test(|| {
|
||||||
|
let init_data = InitializationData {
|
||||||
|
header: test_header(1),
|
||||||
|
authority_list: authority_list(),
|
||||||
|
set_id: 1,
|
||||||
|
scheduled_change: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_noop!(
|
||||||
|
Module::<TestRuntime>::initialize(Origin::signed(1), init_data.clone()),
|
||||||
|
DispatchError::BadOrigin,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_ok!(Module::<TestRuntime>::initialize(Origin::root(), init_data));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn can_only_initialize_pallet_once() {
|
||||||
|
run_test(|| {
|
||||||
|
let init_data = InitializationData {
|
||||||
|
header: test_header(1),
|
||||||
|
authority_list: authority_list(),
|
||||||
|
set_id: 1,
|
||||||
|
scheduled_change: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_ok!(Module::<TestRuntime>::initialize(Origin::root(), init_data.clone()));
|
||||||
|
assert_noop!(
|
||||||
|
Module::<TestRuntime>::initialize(Origin::root(), init_data,),
|
||||||
|
<Error<TestRuntime>>::AlreadyInitialized,
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn storage_entries_are_correctly_initialized() {
|
||||||
|
run_test(|| {
|
||||||
|
let init_data = InitializationData {
|
||||||
|
header: test_header(1),
|
||||||
|
authority_list: authority_list(),
|
||||||
|
set_id: 1,
|
||||||
|
scheduled_change: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_ok!(Module::<TestRuntime>::initialize(Origin::root(), init_data.clone()));
|
||||||
|
|
||||||
|
let storage = PalletStorage::<TestRuntime>::new();
|
||||||
|
|
||||||
|
assert!(IsInitialized::get());
|
||||||
|
assert!(storage.header_exists(init_data.header.hash()));
|
||||||
|
assert_eq!(
|
||||||
|
storage.best_headers()[0],
|
||||||
|
crate::HeaderId {
|
||||||
|
number: *init_data.header.number(),
|
||||||
|
hash: init_data.header.hash()
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(storage.best_finalized_header().hash(), init_data.header.hash());
|
||||||
|
assert_eq!(storage.current_authority_set().authorities, init_data.authority_list);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_finalized_storage_proof_rejects_proof_on_unknown_header() {
|
fn parse_finalized_storage_proof_rejects_proof_on_unknown_header() {
|
||||||
|
|||||||
@@ -24,6 +24,22 @@ use sp_finality_grandpa::{AuthorityList, SetId};
|
|||||||
use sp_runtime::traits::Header as HeaderT;
|
use sp_runtime::traits::Header as HeaderT;
|
||||||
use sp_runtime::RuntimeDebug;
|
use sp_runtime::RuntimeDebug;
|
||||||
|
|
||||||
|
/// Data required for initializing the bridge pallet.
|
||||||
|
///
|
||||||
|
/// The bridge needs to know where to start its sync from, and this provides that initial context.
|
||||||
|
#[derive(Default, Encode, Decode, RuntimeDebug, PartialEq, Clone)]
|
||||||
|
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||||
|
pub struct InitializationData<H: HeaderT> {
|
||||||
|
/// The header from which we should start syncing.
|
||||||
|
pub header: H,
|
||||||
|
/// The initial authorities of the pallet.
|
||||||
|
pub authority_list: AuthorityList,
|
||||||
|
/// The ID of the initial authority set.
|
||||||
|
pub set_id: SetId,
|
||||||
|
/// The first scheduled authority set change of the pallet.
|
||||||
|
pub scheduled_change: Option<ScheduledChange<H::Number>>,
|
||||||
|
}
|
||||||
|
|
||||||
/// A Grandpa Authority List and ID.
|
/// A Grandpa Authority List and ID.
|
||||||
#[derive(Default, Encode, Decode, RuntimeDebug, PartialEq, Clone)]
|
#[derive(Default, Encode, Decode, RuntimeDebug, PartialEq, Clone)]
|
||||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||||
|
|||||||
Reference in New Issue
Block a user