diff --git a/Cargo.lock b/Cargo.lock index 268f9dad..5ca05c8c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1851,7 +1851,7 @@ dependencies = [ [[package]] name = "pallet-parentchain" -version = "0.9.0" +version = "0.10.0" dependencies = [ "env_logger", "frame-support", diff --git a/parentchain/Cargo.toml b/parentchain/Cargo.toml index e419e02d..e3b91c2c 100644 --- a/parentchain/Cargo.toml +++ b/parentchain/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "pallet-parentchain" description = "The remote attestation registry and verification pallet for integritee blockchains and parachains" -version = "0.9.0" +version = "0.10.0" authors = ["Integritee AG "] homepage = "https://integritee.network/" repository = "https://github.com/integritee-network/pallets/" @@ -16,7 +16,8 @@ serde = { version = "1.0.13", features = ["derive"], optional = true } # substrate dependencies frame-support = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -frame-system = { default-features = false, package = "frame-system", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +frame-system = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +pallet-balances = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } sp-core = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } sp-io = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } @@ -25,7 +26,6 @@ sp-std = { default-features = false, git = "https://github.com/paritytech/substr [dev-dependencies] env_logger = "0.9.0" sp-keyring = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } -pallet-balances = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } [features] default = ["std"] @@ -37,6 +37,7 @@ std = [ # substrate dependencies "frame-support/std", "frame-system/std", + "pallet-balances/std", "sp-core/std", "sp-io/std", "sp-runtime/std", diff --git a/parentchain/src/lib.rs b/parentchain/src/lib.rs index 9bdd993c..abb7d447 100644 --- a/parentchain/src/lib.rs +++ b/parentchain/src/lib.rs @@ -2,56 +2,113 @@ pub use pallet::*; +/// Index/Nonce type for parentchain runtime +type ParentchainIndex = u32; +/// Balance type for parentchain runtime +type ParentchainBalance = u128; +/// AccountData type for parentchain runtime +type ParentchainAccountData = pallet_balances::AccountData; + #[frame_support::pallet] pub mod pallet { - use crate::weights::WeightInfo; + use crate::{weights::WeightInfo, ParentchainAccountData, ParentchainIndex}; use frame_support::{pallet_prelude::*, sp_runtime::traits::Header}; - use frame_system::pallet_prelude::*; + use frame_system::{pallet_prelude::*, AccountInfo}; + const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); #[pallet::pallet] - pub struct Pallet(_); + #[pallet::storage_version(STORAGE_VERSION)] + #[pallet::without_storage_info] + pub struct Pallet(PhantomData<(T, I)>); /// Configuration trait. #[pallet::config] - pub trait Config: frame_system::Config { - type RuntimeEvent: From> + IsType<::RuntimeEvent>; + pub trait Config: frame_system::Config { + type RuntimeEvent: From> + + IsType<::RuntimeEvent>; type WeightInfo: WeightInfo; } #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event { + pub enum Event, I: 'static = ()> { /// a parentchain block has been registered - SetBlock { block_number: T::BlockNumber, parent_hash: T::Hash, block_hash: T::Hash }, + SetBlock { + block_number: T::BlockNumber, + parent_hash: T::Hash, + block_hash: T::Hash, + }, + ShardVaultInitialized { + account: T::AccountId, + }, + AccountInfoForcedFor { + account: T::AccountId, + }, + ParentchainGeneisInitialized { + hash: T::Hash, + }, + } + + #[pallet::error] + pub enum Error { + /// Sahrd vault has been previously initialized and can't be overwritten + ShardVaultAlreadyInitialized, + /// Parentchain genesis hash has already been initialized and can^t be overwritten + GenesisAlreadyInitialized, } + /// The parentchain mirror of full account information for a particular account ID. + #[pallet::storage] + #[pallet::getter(fn account)] + pub type Account, I: 'static = ()> = StorageMap< + _, + Blake2_128Concat, + T::AccountId, + AccountInfo, + ValueQuery, + >; + + /// The current block number being processed. Set by `set_block`. + #[pallet::storage] + #[pallet::getter(fn shard_vault)] + pub(super) type ShardVault, I: 'static = ()> = + StorageValue<_, T::AccountId, OptionQuery>; + + #[pallet::storage] + #[pallet::getter(fn parentchain_genesis_hash)] + pub(super) type ParentchainGenesisHash, I: 'static = ()> = + StorageValue<_, T::Hash, OptionQuery>; + /// The current block number being processed. Set by `set_block`. #[pallet::storage] #[pallet::getter(fn block_number)] - pub(super) type Number = StorageValue<_, T::BlockNumber, ValueQuery>; + pub(super) type Number, I: 'static = ()> = + StorageValue<_, T::BlockNumber, OptionQuery>; /// Hash of the previous block. Set by `set_block`. #[pallet::storage] #[pallet::getter(fn parent_hash)] - pub(super) type ParentHash = StorageValue<_, T::Hash, ValueQuery>; + pub(super) type ParentHash, I: 'static = ()> = + StorageValue<_, T::Hash, OptionQuery>; /// Hash of the last block. Set by `set_block`. #[pallet::storage] #[pallet::getter(fn block_hash)] - pub(super) type BlockHash = StorageValue<_, T::Hash, ValueQuery>; + pub(super) type BlockHash, I: 'static = ()> = + StorageValue<_, T::Hash, OptionQuery>; #[pallet::hooks] - impl Hooks> for Pallet {} + impl, I: 'static> Hooks> for Pallet {} #[pallet::call] - impl Pallet { + impl, I: 'static> Pallet { #[pallet::call_index(0)] - #[pallet::weight(::WeightInfo::set_block())] + #[pallet::weight(T::WeightInfo::set_block())] pub fn set_block(origin: OriginFor, header: T::Header) -> DispatchResult { ensure_root(origin)?; - >::put(header.number()); - >::put(header.parent_hash()); - >::put(header.hash()); + >::put(header.number()); + >::put(header.parent_hash()); + >::put(header.hash()); Self::deposit_event(Event::SetBlock { block_number: *header.number(), parent_hash: *header.parent_hash(), @@ -59,6 +116,45 @@ pub mod pallet { }); Ok(()) } + + #[pallet::call_index(1)] + #[pallet::weight(T::WeightInfo::set_block())] + pub fn init_shard_vault(origin: OriginFor, account: T::AccountId) -> DispatchResult { + ensure_root(origin)?; + ensure!(Self::shard_vault().is_none(), Error::::ShardVaultAlreadyInitialized); + >::put(account.clone()); + Self::deposit_event(Event::ShardVaultInitialized { account }); + Ok(()) + } + + #[pallet::call_index(2)] + #[pallet::weight(T::WeightInfo::set_block())] + pub fn init_parentchain_genesis_hash( + origin: OriginFor, + genesis: T::Hash, + ) -> DispatchResult { + ensure_root(origin)?; + ensure!( + Self::parentchain_genesis_hash().is_none(), + Error::::GenesisAlreadyInitialized + ); + >::put(genesis); + Self::deposit_event(Event::ParentchainGeneisInitialized { hash: genesis }); + Ok(()) + } + + #[pallet::call_index(3)] + #[pallet::weight(T::WeightInfo::set_block())] + pub fn force_account_info( + origin: OriginFor, + account: T::AccountId, + account_info: AccountInfo, + ) -> DispatchResult { + ensure_root(origin)?; + >::insert(&account, account_info); + Self::deposit_event(crate::pallet::Event::AccountInfoForcedFor { account }); + Ok(()) + } } } diff --git a/parentchain/src/mock.rs b/parentchain/src/mock.rs index 3e7af712..a9280e60 100644 --- a/parentchain/src/mock.rs +++ b/parentchain/src/mock.rs @@ -50,13 +50,21 @@ frame_support::construct_runtime!( NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic, { - System: frame_system::{Pallet, Call, Config, Storage, Event}, - Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, - Parentchain: pallet_parentchain::{Pallet, Call, Storage, Event}, + System: frame_system::{Pallet, Call, Config, Event}, + Balances: pallet_balances::{Pallet, Call, Config, Event}, + ParentchainIntegritee: pallet_parentchain::::{Pallet, Call, Event}, + ParentchainTargetA: pallet_parentchain::::{Pallet, Call, Event}, } ); -impl Config for Test { +pub type ParentchainInstanceIntegritee = pallet_parentchain::Instance1; +impl Config for Test { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); +} + +pub type ParentchainInstanceTargetA = pallet_parentchain::Instance2; +impl Config for Test { type RuntimeEvent = RuntimeEvent; type WeightInfo = (); } diff --git a/parentchain/src/tests.rs b/parentchain/src/tests.rs index 79a858f2..1970369a 100644 --- a/parentchain/src/tests.rs +++ b/parentchain/src/tests.rs @@ -14,8 +14,10 @@ limitations under the License. */ -use crate::{mock::*, Event as ParentchainEvent}; -use frame_support::{assert_err, assert_ok}; +use crate::{mock::*, Error, Event as ParentchainEvent}; +use frame_support::{assert_err, assert_noop, assert_ok}; +use frame_system::AccountInfo; +use pallet_balances::AccountData; use sp_core::H256; use sp_keyring::AccountKeyring; use sp_runtime::{ @@ -41,14 +43,67 @@ fn verify_storage_works() { let hash = header.hash(); new_test_ext().execute_with(|| { - assert_ok!(Parentchain::set_block(RuntimeOrigin::root(), header)); - assert_eq!(Parentchain::block_number(), block_number); - assert_eq!(Parentchain::parent_hash(), parent_hash); - assert_eq!(Parentchain::block_hash(), hash); + assert_ok!(ParentchainIntegritee::set_block(RuntimeOrigin::root(), header)); + assert_eq!(ParentchainIntegritee::block_number().unwrap(), block_number); + assert_eq!(ParentchainIntegritee::parent_hash().unwrap(), parent_hash); + assert_eq!(ParentchainIntegritee::block_hash().unwrap(), hash); - System::assert_last_event( - ParentchainEvent::SetBlock { block_number, parent_hash, block_hash: hash }.into(), - ); + System::assert_last_event(RuntimeEvent::ParentchainIntegritee( + ParentchainEvent::SetBlock { block_number, parent_hash, block_hash: hash }, + )); + }) +} + +#[test] +fn multi_pallet_instance_storage_works() { + let block_number = 3; + let parent_hash = H256::from_low_u64_be(420); + + let header: Header = HeaderT::new( + block_number, + Default::default(), + Default::default(), + parent_hash, + Default::default(), + ); + let hash = header.hash(); + + let block_number_a = 5; + let parent_hash_a = H256::from_low_u64_be(421); + + let header_a: Header = HeaderT::new( + block_number_a, + Default::default(), + Default::default(), + parent_hash_a, + Default::default(), + ); + let hash_a = header_a.hash(); + + new_test_ext().execute_with(|| { + assert_ok!(ParentchainIntegritee::set_block(RuntimeOrigin::root(), header)); + assert_eq!(ParentchainIntegritee::block_number().unwrap(), block_number); + assert_eq!(ParentchainIntegritee::parent_hash().unwrap(), parent_hash); + assert_eq!(ParentchainIntegritee::block_hash().unwrap(), hash); + + System::assert_last_event(RuntimeEvent::ParentchainIntegritee( + ParentchainEvent::SetBlock { block_number, parent_hash, block_hash: hash }, + )); + + assert_ok!(ParentchainTargetA::set_block(RuntimeOrigin::root(), header_a)); + assert_eq!(ParentchainTargetA::block_number().unwrap(), block_number_a); + assert_eq!(ParentchainTargetA::parent_hash().unwrap(), parent_hash_a); + assert_eq!(ParentchainTargetA::block_hash().unwrap(), hash_a); + + System::assert_last_event(RuntimeEvent::ParentchainTargetA(ParentchainEvent::SetBlock { + block_number: block_number_a, + parent_hash: parent_hash_a, + block_hash: hash_a, + })); + + // double check previous storage + assert_eq!(ParentchainIntegritee::block_number().unwrap(), block_number); + assert_eq!(ParentchainIntegritee::block_hash().unwrap(), hash); }) } @@ -64,6 +119,73 @@ fn non_root_account_errs() { new_test_ext().execute_with(|| { let root = AccountKeyring::Ferdie.to_account_id(); - assert_err!(Parentchain::set_block(RuntimeOrigin::signed(root), header), BadOrigin); + assert_err!( + ParentchainIntegritee::set_block(RuntimeOrigin::signed(root), header), + BadOrigin + ); + }) +} + +#[test] +fn init_shard_vault_works() { + new_test_ext().execute_with(|| { + let vault = AccountKeyring::Alice.to_account_id(); + assert_ok!(ParentchainIntegritee::init_shard_vault(RuntimeOrigin::root(), vault.clone())); + assert_eq!(ParentchainIntegritee::shard_vault().unwrap(), vault); + + System::assert_last_event(RuntimeEvent::ParentchainIntegritee( + ParentchainEvent::ShardVaultInitialized { account: vault.clone() }, + )); + assert_noop!( + ParentchainIntegritee::init_shard_vault(RuntimeOrigin::root(), vault.clone()), + Error::::ShardVaultAlreadyInitialized + ); + }) +} +#[test] +fn init_parentchain_genesis_hash_works() { + new_test_ext().execute_with(|| { + let genesis = H256::default(); + assert_ok!(ParentchainIntegritee::init_parentchain_genesis_hash( + RuntimeOrigin::root(), + genesis + )); + assert_eq!(ParentchainIntegritee::parentchain_genesis_hash().unwrap(), genesis); + + System::assert_last_event(RuntimeEvent::ParentchainIntegritee( + ParentchainEvent::ParentchainGeneisInitialized { hash: genesis }, + )); + assert_noop!( + ParentchainIntegritee::init_parentchain_genesis_hash(RuntimeOrigin::root(), genesis), + Error::::GenesisAlreadyInitialized + ); + }) +} +#[test] +fn force_account_info_works() { + new_test_ext().execute_with(|| { + let vault = AccountKeyring::Alice.to_account_id(); + let account_info = AccountInfo { + nonce: 42, + consumers: 1, + providers: 1, + sufficients: 1, + data: AccountData { + free: 123456789, + reserved: 23456, + frozen: 345, + flags: Default::default(), + }, + }; + assert_ok!(ParentchainIntegritee::force_account_info( + RuntimeOrigin::root(), + vault.clone(), + account_info.clone() + )); + assert_eq!(ParentchainIntegritee::account(&vault), account_info); + + System::assert_last_event(RuntimeEvent::ParentchainIntegritee( + ParentchainEvent::AccountInfoForcedFor { account: vault.clone() }, + )); }) }