From 89e24d1c6ab3691201f376abd291df2564504bcc Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Mon, 23 Sep 2024 20:46:16 +0300 Subject: [PATCH] Remove unused pallets --- Cargo.lock | 51 -- crates/pallet-feeds/Cargo.toml | 38 -- crates/pallet-feeds/README.md | 18 - crates/pallet-feeds/src/feed_processor.rs | 154 ----- crates/pallet-feeds/src/lib.rs | 415 ------------ crates/pallet-feeds/src/mock.rs | 108 --- crates/pallet-feeds/src/tests.rs | 346 ---------- .../Cargo.toml | 54 -- .../README.md | 13 - .../src/chain.rs | 118 ---- .../src/grandpa.rs | 259 ------- .../src/lib.rs | 354 ---------- .../src/tests/justification.rs | 220 ------ .../src/tests/keyring.rs | 70 -- .../src/tests/mock.rs | 35 - .../src/tests/mod.rs | 640 ------------------ crates/pallet-object-store/Cargo.toml | 40 -- crates/pallet-object-store/README.md | 5 - crates/pallet-object-store/src/lib.rs | 114 ---- crates/pallet-object-store/src/mock.rs | 32 - crates/pallet-object-store/src/tests.rs | 24 - 21 files changed, 3108 deletions(-) delete mode 100644 crates/pallet-feeds/Cargo.toml delete mode 100644 crates/pallet-feeds/README.md delete mode 100644 crates/pallet-feeds/src/feed_processor.rs delete mode 100644 crates/pallet-feeds/src/lib.rs delete mode 100644 crates/pallet-feeds/src/mock.rs delete mode 100644 crates/pallet-feeds/src/tests.rs delete mode 100644 crates/pallet-grandpa-finality-verifier/Cargo.toml delete mode 100644 crates/pallet-grandpa-finality-verifier/README.md delete mode 100644 crates/pallet-grandpa-finality-verifier/src/chain.rs delete mode 100644 crates/pallet-grandpa-finality-verifier/src/grandpa.rs delete mode 100644 crates/pallet-grandpa-finality-verifier/src/lib.rs delete mode 100644 crates/pallet-grandpa-finality-verifier/src/tests/justification.rs delete mode 100644 crates/pallet-grandpa-finality-verifier/src/tests/keyring.rs delete mode 100644 crates/pallet-grandpa-finality-verifier/src/tests/mock.rs delete mode 100644 crates/pallet-grandpa-finality-verifier/src/tests/mod.rs delete mode 100644 crates/pallet-object-store/Cargo.toml delete mode 100644 crates/pallet-object-store/README.md delete mode 100644 crates/pallet-object-store/src/lib.rs delete mode 100644 crates/pallet-object-store/src/mock.rs delete mode 100644 crates/pallet-object-store/src/tests.rs diff --git a/Cargo.lock b/Cargo.lock index cc11754497..c619051341 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7800,41 +7800,6 @@ dependencies = [ "sp-io", ] -[[package]] -name = "pallet-feeds" -version = "0.1.0" -dependencies = [ - "frame-support", - "frame-system", - "parity-scale-codec", - "scale-info", - "sp-core", - "sp-io", - "sp-runtime", - "subspace-core-primitives", -] - -[[package]] -name = "pallet-grandpa-finality-verifier" -version = "0.1.0" -dependencies = [ - "ed25519-dalek", - "finality-grandpa", - "frame-support", - "frame-system", - "log", - "num-traits", - "parity-scale-codec", - "scale-info", - "serde", - "sp-application-crypto", - "sp-consensus-grandpa", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", -] - [[package]] name = "pallet-history-seeding" version = "0.1.0" @@ -7891,22 +7856,6 @@ dependencies = [ "sp-runtime", ] -[[package]] -name = "pallet-object-store" -version = "0.1.0" -dependencies = [ - "frame-support", - "frame-system", - "hex", - "log", - "parity-scale-codec", - "scale-info", - "sp-core", - "sp-io", - "sp-runtime", - "subspace-core-primitives", -] - [[package]] name = "pallet-offences-subspace" version = "0.1.0" diff --git a/crates/pallet-feeds/Cargo.toml b/crates/pallet-feeds/Cargo.toml deleted file mode 100644 index cee72d276b..0000000000 --- a/crates/pallet-feeds/Cargo.toml +++ /dev/null @@ -1,38 +0,0 @@ -[package] -name = "pallet-feeds" -version = "0.1.0" -authors = ["Serge Kovbasiuk "] -edition = "2021" -license = "Apache-2.0" -homepage = "https://subspace.network" -repository = "https://github.com/autonomys/subspace" -description = "Subspace node pallet for interacting with storage" -readme = "README.md" - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.6.12", default-features = false, features = ["derive"] } -frame-support = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "5626154d0781ac9a6ffd5a6207ed237f425ae631" } -frame-system = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "5626154d0781ac9a6ffd5a6207ed237f425ae631" } -scale-info = { version = "2.11.2", default-features = false, features = ["derive"] } -sp-core = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "5626154d0781ac9a6ffd5a6207ed237f425ae631" } -sp-runtime = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "5626154d0781ac9a6ffd5a6207ed237f425ae631" } -subspace-core-primitives = { version = "0.1.0", default-features = false, path = "../subspace-core-primitives" } - -[dev-dependencies] -sp-io = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "5626154d0781ac9a6ffd5a6207ed237f425ae631" } - -[features] -default = ["std"] -std = [ - "codec/std", - "frame-support/std", - "frame-system/std", - "scale-info/std", - "sp-core/std", - "sp-runtime/std", - "subspace-core-primitives/std", -] -try-runtime = ["frame-support/try-runtime"] diff --git a/crates/pallet-feeds/README.md b/crates/pallet-feeds/README.md deleted file mode 100644 index 28288eeed2..0000000000 --- a/crates/pallet-feeds/README.md +++ /dev/null @@ -1,18 +0,0 @@ -# Pallet Feeds - -License: Apache-2.0 - -Pallet feeds provides the interactions with Subspace storage. The main design goal for Feeds is not only to push objects -to the Storage but also to provide a way for the feed owners to inject some verification logic through `FeedProcessor` -impls. - -## Calls - -The pallet provides following calls. -1. Create(permissionless): Creates a new Feed for the caller -2. Update: Updates the Feeds with some initial data. All the underlying FeedProcessors -will be reinitialized. -3. Transfer: Transfers a feed from one owner to another -4. Close: Closes the feed and doesn't accept any new objects -5. Put: Puts a new object in the Feed. The object is passed to FeedProcessor for verification if any. - diff --git a/crates/pallet-feeds/src/feed_processor.rs b/crates/pallet-feeds/src/feed_processor.rs deleted file mode 100644 index 7ee2a0ff77..0000000000 --- a/crates/pallet-feeds/src/feed_processor.rs +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright (C) 2021 Subspace Labs, Inc. -// 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. - -//! Defines FeedProcessor and its types - -#[cfg(not(feature = "std"))] -extern crate alloc; - -use crate::CallObject; -#[cfg(not(feature = "std"))] -use alloc::vec; -#[cfg(not(feature = "std"))] -use alloc::vec::Vec; -use codec::{Compact, CompactLen, Decode, Encode}; -use sp_runtime::{DispatchError, DispatchResult}; -use subspace_core_primitives::Blake3Hash; - -/// Holds the offset to some portion of data within/or the object -#[derive(Debug)] -pub enum FeedObjectMapping { - /// Maps the object or some data within the object at the specific offset. - /// The key is derived from the content. - Content { offset: u32 }, - - /// Maps the object or some data within the object at the specific offset. - /// The key provided is namespaced to feed to avoid collisions - Custom { key: Vec, offset: u32 }, -} - -impl FeedObjectMapping { - pub(crate) fn try_into_call_object Blake3Hash>( - self, - feed_id: FeedID, - object: &[u8], - hasher: Hasher, - ) -> Option { - match self { - // If this is a custom key, then name space the key. - FeedObjectMapping::Custom { key, offset } => { - let mut data = feed_id.encode(); - data.extend_from_slice(&key); - Some(CallObject { - key: hasher(&data), - offset, - }) - } - // For content, we try to extract the content to derive the key - FeedObjectMapping::Content { offset } => { - // If offset is 0, then we want to map the entire object. - // Since the object is already decoded, no need to decode it further - let key = if offset == 0 { - hasher(object) - } else { - // This is referring to some content within the object that is encoded. - // Move the offset back by the encoded bytes of object to get the right offset since the object is already decoded. - let offset = offset - .saturating_sub(Compact::::compact_len(&(object.len() as u32)) as u32); - hasher(&Vec::decode(&mut &object[offset as usize..]).ok()?) - }; - - Some(CallObject { key, offset }) - } - } - } -} - -/// Metadata of a feed object as raw bytes. -pub type FeedMetadata = Vec; - -/// # Feed Processor -/// Feed Processor defines some useful abstractions that are used during the life cycle of the Feed and its objects. -/// We can provide some custom logic specific to the Feed by implementing a Custom Feed Processor. -/// -/// ## Feed Metadata -/// -/// Before an object is added to Subspace storage, `put` on Feed processor to give the impl an opportunity to run the object -/// through their custom logic and returns some metadata about the object. Metadata is then stored in the runtime overwriting -/// any metadata of the previous object. The default implementation of Feed processor gives empty metadata about the object. -/// -/// ## Feed object mapping -/// Feed indexes the objects in the DSN using offsets within the Block the object is present in. `object_mappings` is the -/// only that must be implemented by the Feed processor. Since DSN is a key value store, there are two different ways keys -/// are derived for given data at the offset within the block -/// - Key derived from content. Feeds use BLAKE2b-256 to derive the key for the data at the offset. -/// - Key provided by the feed processor. Feed processor implementations can instead provide a key for object at the offset. -/// -/// ## Examples -/// ### Content based addressing with default hasher -/// ```rust -/// use pallet_feeds::feed_processor::{FeedProcessor, FeedObjectMapping}; -/// struct IPFSLike; -/// impl FeedProcessor for IPFSLike { /// -/// fn object_mappings(&self, _feed_id: FeedId, _object: &[u8]) -> Vec { -/// vec![FeedObjectMapping::Content { offset: 0 }] -/// } -/// } -/// ``` -/// This implements a Content addressable Feed using default Hasher. The entire object is treated as data and hence the offset is zero. -/// -/// ### Content based addressing using custom Hasher -/// ```rust -/// use sp_runtime::traits::{BlakeTwo256, Hash}; -/// use pallet_feeds::feed_processor::{FeedProcessor, FeedObjectMapping}; -/// struct IPFSLike; -/// impl FeedProcessor for IPFSLike { /// -/// fn object_mappings(&self, _feed_id: FeedId, object: &[u8]) -> Vec { -/// vec![FeedObjectMapping::Custom { key: BlakeTwo256::hash(object).as_bytes().to_vec(), offset: 0 }] -/// } -/// } -/// ``` -/// This implements a Content addressable Feed using BlakeTwo256 hasher. The entire object is treated as data and hence the offset is zero. -pub trait FeedProcessor { - /// Initiates a specific Feed with data transparent to FeedProcessor - /// Can be called when re-initializing the feed. - fn init(&self, _feed_id: FeedId, _data: &[u8]) -> DispatchResult { - Ok(()) - } - - /// Puts a feed and returns the Metadata if any. - /// This is called once per extrinsic that puts a feed into a given feed stream. - fn put(&self, _feed_id: FeedId, _object: &[u8]) -> Result, DispatchError> { - Ok(None) - } - - /// Returns any object mappings inside the given object - fn object_mappings(&self, _feed_id: FeedId, object: &[u8]) -> Vec; - - /// Signals a delete to any underlying feed data. - fn delete(&self, _feed_id: FeedId) -> DispatchResult { - Ok(()) - } -} - -/// Content addressable feed processor impl -/// Offsets the whole object as content thereby signalling to derive `key = hash(object)` -/// Put do not provide any metadata. -impl FeedProcessor for () { - /// Maps the entire object as content. - fn object_mappings(&self, _feed_id: FeedId, _object: &[u8]) -> Vec { - vec![FeedObjectMapping::Content { offset: 0 }] - } -} diff --git a/crates/pallet-feeds/src/lib.rs b/crates/pallet-feeds/src/lib.rs deleted file mode 100644 index 68bf6254af..0000000000 --- a/crates/pallet-feeds/src/lib.rs +++ /dev/null @@ -1,415 +0,0 @@ -// Copyright (C) 2021 Subspace Labs, Inc. -// 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. - -//! Pallet feeds, used for storing arbitrary user-provided data combined into feeds. - -#![cfg_attr(not(feature = "std"), no_std)] -#![forbid(unsafe_code)] -#![warn(rust_2018_idioms, missing_debug_implementations)] - -#[cfg(not(feature = "std"))] -extern crate alloc; - -#[cfg(not(feature = "std"))] -use alloc::vec; -#[cfg(not(feature = "std"))] -use alloc::vec::Vec; -use core::mem; -pub use pallet::*; -use subspace_core_primitives::{crypto, Blake3Hash}; - -pub mod feed_processor; -#[cfg(all(feature = "std", test))] -mod mock; -#[cfg(all(feature = "std", test))] -mod tests; - -#[frame_support::pallet] -mod pallet { - use crate::feed_processor::{FeedMetadata, FeedProcessor as FeedProcessorT}; - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::*; - use sp_runtime::traits::{CheckedAdd, Hash, One, StaticLookup}; - use sp_runtime::ArithmeticError; - - #[pallet::config] - pub trait Config: frame_system::Config { - /// `pallet-feeds` events - type RuntimeEvent: From> + IsType<::RuntimeEvent>; - - // Feed ID uniquely identifies a Feed - type FeedId: Parameter + Member + Default + Copy + PartialOrd + CheckedAdd + One; - - // Type that references to a particular impl of feed processor - type FeedProcessorKind: Parameter + Member + Default + Copy; - - #[pallet::constant] - type MaxFeeds: Get; - - fn feed_processor( - feed_processor_kind: Self::FeedProcessorKind, - ) -> Box>; - } - - /// Pallet feeds, used for storing arbitrary user-provided data combined into feeds. - #[pallet::pallet] - #[pallet::without_storage_info] - pub struct Pallet(_); - - /// User-provided object to store - pub(super) type Object = Vec; - /// User provided initial data for validation - pub(super) type InitData = Vec; - - /// Total amount of data and number of objects stored in a feed - #[derive(Debug, Decode, Encode, TypeInfo, Default, PartialEq, Eq)] - pub struct TotalObjectsAndSize { - /// Total size of objects in bytes - pub size: u64, - /// Total number of objects - pub count: u64, - } - - #[derive(Debug, Decode, Encode, TypeInfo, Default)] - pub struct FeedConfig { - pub active: bool, - pub feed_processor_id: FeedProcessorId, - pub owner: AccountId, - } - - #[pallet::storage] - #[pallet::getter(fn metadata)] - pub(super) type Metadata = - StorageMap<_, Identity, T::FeedId, FeedMetadata, OptionQuery>; - - #[pallet::storage] - #[pallet::getter(fn feed_configs)] - pub(super) type FeedConfigs = StorageMap< - _, - Identity, - T::FeedId, - FeedConfig, - OptionQuery, - >; - - #[pallet::storage] - #[pallet::getter(fn feeds)] - pub(super) type Feeds = - StorageMap<_, Identity, T::AccountId, BoundedVec, OptionQuery>; - - #[pallet::storage] - #[pallet::getter(fn totals)] - pub(super) type Totals = - StorageMap<_, Identity, T::FeedId, TotalObjectsAndSize, ValueQuery>; - - #[pallet::storage] - #[pallet::getter(fn next_feed_id)] - pub(super) type NextFeedId = StorageValue<_, T::FeedId, ValueQuery>; - - #[pallet::storage] - pub(super) type SuccessfulPuts = StorageValue<_, Vec, ValueQuery>; - - /// `pallet-feeds` events - #[pallet::event] - #[pallet::generate_deposit(pub (super) fn deposit_event)] - pub enum Event { - /// New object was added. - ObjectSubmitted { - feed_id: T::FeedId, - who: T::AccountId, - metadata: FeedMetadata, - object_size: u64, - }, - /// New feed was created. - FeedCreated { - feed_id: T::FeedId, - who: T::AccountId, - }, - - /// An existing feed was updated. - FeedUpdated { - feed_id: T::FeedId, - who: T::AccountId, - }, - - /// Feed was closed. - FeedClosed { - feed_id: T::FeedId, - who: T::AccountId, - }, - - /// Feed was deleted. - FeedDeleted { - feed_id: T::FeedId, - who: T::AccountId, - }, - - /// feed ownership transferred - OwnershipTransferred { - feed_id: T::FeedId, - old_owner: T::AccountId, - new_owner: T::AccountId, - }, - } - - /// `pallet-feeds` errors - #[pallet::error] - pub enum Error { - /// `FeedId` doesn't exist - UnknownFeedId, - - /// Feed was closed - FeedClosed, - - /// Not a feed owner - NotFeedOwner, - - /// Maximum feeds created by the caller - MaxFeedsReached, - } - - macro_rules! ensure_owner { - ( $origin:expr, $feed_id:expr ) => {{ - let sender = ensure_signed($origin)?; - let feed_config = FeedConfigs::::get($feed_id).ok_or(Error::::UnknownFeedId)?; - ensure!(feed_config.owner == sender, Error::::NotFeedOwner); - (sender, feed_config) - }}; - } - - #[pallet::hooks] - impl Hooks> for Pallet { - fn on_initialize(_now: BlockNumberFor) -> Weight { - SuccessfulPuts::::kill(); - T::DbWeight::get().writes(1) - } - } - - #[pallet::call] - impl Pallet { - // TODO: add proper weights - /// Create a new feed - #[pallet::call_index(0)] - #[pallet::weight((10_000, Pays::No))] - pub fn create( - origin: OriginFor, - feed_processor_id: T::FeedProcessorKind, - init_data: Option, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - let feed_id = NextFeedId::::get(); - let next_feed_id = feed_id - .checked_add(&One::one()) - .ok_or(ArithmeticError::Overflow)?; - let feed_processor = T::feed_processor(feed_processor_id); - if let Some(init_data) = init_data { - feed_processor.init(feed_id, init_data.as_slice())?; - } - - // check if max feeds are reached - let mut owned_feeds = Feeds::::get(who.clone()).unwrap_or_default(); - owned_feeds - .try_push(feed_id) - .map_err(|_| Error::::MaxFeedsReached)?; - - NextFeedId::::set(next_feed_id); - FeedConfigs::::insert( - feed_id, - FeedConfig { - active: true, - feed_processor_id, - owner: who.clone(), - }, - ); - Feeds::::insert(who.clone(), owned_feeds); - Totals::::insert(feed_id, TotalObjectsAndSize::default()); - - Self::deposit_event(Event::FeedCreated { feed_id, who }); - - Ok(()) - } - - /// Updates the feed with init data provided. - #[pallet::call_index(1)] - #[pallet::weight((10_000, Pays::No))] - pub fn update( - origin: OriginFor, - feed_id: T::FeedId, - feed_processor_id: T::FeedProcessorKind, - init_data: Option, - ) -> DispatchResult { - let (owner, feed_config) = ensure_owner!(origin, feed_id); - let feed_processor = T::feed_processor(feed_processor_id); - if let Some(init_data) = init_data { - feed_processor.init(feed_id, init_data.as_slice())?; - } - - FeedConfigs::::insert( - feed_id, - FeedConfig { - active: feed_config.active, - feed_processor_id, - owner: owner.clone(), - }, - ); - - Self::deposit_event(Event::FeedUpdated { - feed_id, - who: owner, - }); - - Ok(()) - } - - // TODO: add proper weights - // TODO: For now we don't have fees, but we will have them in the future - /// Put a new object into a feed - #[pallet::call_index(2)] - #[pallet::weight((10_000, Pays::No))] - pub fn put(origin: OriginFor, feed_id: T::FeedId, object: Object) -> DispatchResult { - let (owner, feed_config) = ensure_owner!(origin, feed_id); - // ensure feed is active - ensure!(feed_config.active, Error::::FeedClosed); - - let object_size = object.len() as u64; - let feed_processor = T::feed_processor(feed_config.feed_processor_id); - - let metadata = feed_processor - .put(feed_id, object.as_slice())? - .unwrap_or_default(); - Metadata::::insert(feed_id, metadata.clone()); - - Totals::::mutate(feed_id, |feed_totals| { - feed_totals.size += object_size; - feed_totals.count += 1; - }); - - Self::deposit_event(Event::ObjectSubmitted { - feed_id, - who: owner, - metadata, - object_size, - }); - - // store the call - // there could be multiple calls with same hash and that is fine - // since we assume the same order - let uniq = T::Hashing::hash(Call::::put { feed_id, object }.encode().as_slice()); - SuccessfulPuts::::append(uniq); - Ok(()) - } - - /// Closes the feed and stops accepting new feed. - #[pallet::call_index(3)] - #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), Pays::No))] - pub fn close(origin: OriginFor, feed_id: T::FeedId) -> DispatchResult { - let (owner, mut feed_config) = ensure_owner!(origin, feed_id); - feed_config.active = false; - FeedConfigs::::insert(feed_id, feed_config); - Self::deposit_event(Event::FeedClosed { - feed_id, - who: owner, - }); - Ok(()) - } - - /// Transfers feed from current owner to new owner - #[pallet::call_index(4)] - #[pallet::weight((T::DbWeight::get().reads_writes(3, 3), Pays::No))] - pub fn transfer( - origin: OriginFor, - feed_id: T::FeedId, - new_owner: ::Source, - ) -> DispatchResult { - let (owner, mut feed_config) = ensure_owner!(origin, feed_id); - let new_owner = T::Lookup::lookup(new_owner)?; - - // remove current owner details - let mut current_owner_feeds = Feeds::::get(owner.clone()).unwrap_or_default(); - current_owner_feeds.retain(|x| *x != feed_id); - - // update new owner details - feed_config.owner = new_owner.clone(); - let mut new_owner_feeds = Feeds::::get(new_owner.clone()).unwrap_or_default(); - new_owner_feeds - .try_push(feed_id) - .map_err(|_| Error::::MaxFeedsReached)?; - - // if the owner doesn't own any feed, then reclaim empty storage - if current_owner_feeds.is_empty() { - Feeds::::remove(owner.clone()); - } else { - Feeds::::insert(owner.clone(), current_owner_feeds); - } - - Feeds::::insert(new_owner.clone(), new_owner_feeds); - FeedConfigs::::insert(feed_id, feed_config); - Self::deposit_event(Event::OwnershipTransferred { - feed_id, - old_owner: owner, - new_owner, - }); - Ok(()) - } - } -} - -/// Mapping to the object offset within an extrinsic associated with given key -#[derive(Debug)] -pub struct CallObject { - /// Key to the object located at the offset. - pub key: Blake3Hash, - /// Offset of object in the encoded call. - pub offset: u32, -} - -impl Pallet { - pub fn successful_puts() -> Vec { - SuccessfulPuts::::get() - } -} - -impl Call { - /// Extract the call objects if an extrinsic corresponds to `put` call - pub fn extract_call_objects(&self) -> Vec { - match self { - Self::put { feed_id, object } => { - let feed_processor_id = match FeedConfigs::::get(feed_id) { - Some(config) => config.feed_processor_id, - // return if this was a invalid extrinsic - None => return vec![], - }; - let feed_processor = T::feed_processor(feed_processor_id); - let objects_mappings = feed_processor.object_mappings(*feed_id, object); - // +1 for the Call::put enum variant - // Since first arg is feed_id, we bump the offset by its encoded size - let base_offset = 1 + mem::size_of::() as u32; - objects_mappings - .into_iter() - .filter_map(|object_mapping| { - let mut co = object_mapping.try_into_call_object( - feed_id, - object.as_slice(), - crypto::blake3_hash, - )?; - co.offset += base_offset; - Some(co) - }) - .collect() - } - _ => Default::default(), - } - } -} diff --git a/crates/pallet-feeds/src/mock.rs b/crates/pallet-feeds/src/mock.rs deleted file mode 100644 index 28949989f1..0000000000 --- a/crates/pallet-feeds/src/mock.rs +++ /dev/null @@ -1,108 +0,0 @@ -// Silence a rust-analyzer warning in `construct_runtime!`. This warning isn't present in rustc output. -// TODO: remove when upstream issue is fixed: -#![allow(non_camel_case_types)] - -use crate::feed_processor::{FeedObjectMapping, FeedProcessor, FeedProcessor as FeedProcessorT}; -use crate::{self as pallet_feeds}; -use codec::{Compact, CompactLen, Decode, Encode}; -use frame_support::{derive_impl, parameter_types}; -use scale_info::TypeInfo; -use sp_runtime::BuildStorage; - -type Block = frame_system::mocking::MockBlock; -type FeedId = u64; - -frame_support::construct_runtime!( - pub struct Test { - System: frame_system, - Feeds: pallet_feeds, - } -); - -#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] -impl frame_system::Config for Test { - type Block = Block; -} - -parameter_types! { - pub const ExistentialDeposit: u64 = 1; - pub const MaxFeeds: u32 = 1; -} - -#[derive(Default, Debug, Copy, Clone, Encode, Decode, TypeInfo, Eq, PartialEq)] -pub enum MockFeedProcessorKind { - #[default] - Content, - ContentWithin, - Custom([u8; 32]), -} - -impl pallet_feeds::Config for Test { - type RuntimeEvent = RuntimeEvent; - type FeedId = FeedId; - type FeedProcessorKind = MockFeedProcessorKind; - type MaxFeeds = MaxFeeds; - - fn feed_processor( - feed_processor_kind: Self::FeedProcessorKind, - ) -> Box> { - match feed_processor_kind { - MockFeedProcessorKind::Content => Box::new(()), - MockFeedProcessorKind::ContentWithin => Box::new(ContentEnumFeedProcessor), - MockFeedProcessorKind::Custom(key) => { - Box::new(CustomContentFeedProcessor(key.to_vec())) - } - } - } -} - -pub fn new_test_ext() -> sp_io::TestExternalities { - let t = frame_system::GenesisConfig::::default() - .build_storage() - .unwrap(); - - let mut t: sp_io::TestExternalities = t.into(); - - t.execute_with(|| System::set_block_number(1)); - - t -} - -/// Same as default except key is not derived from object -struct CustomContentFeedProcessor(Vec); - -impl FeedProcessor for CustomContentFeedProcessor { - fn object_mappings(&self, _feed_id: FeedId, _object: &[u8]) -> Vec { - vec![FeedObjectMapping::Custom { - key: self.0.clone(), - offset: 0, - }] - } -} - -// this is the content enum encoded as object for the put call -// we want to index content_a or content_b by an index either content addressable or name spaced key -#[derive(Debug, Clone, Encode, Decode)] -pub(crate) enum ContentEnum { - ContentA(Vec), - ContentB(Vec), -} - -struct ContentEnumFeedProcessor; - -impl FeedProcessor for ContentEnumFeedProcessor { - fn object_mappings(&self, _feed_id: FeedId, object: &[u8]) -> Vec { - let content = - ContentEnum::decode(&mut object.to_vec().as_slice()).expect("must decode to content"); - - match content { - ContentEnum::ContentA(_) | ContentEnum::ContentB(_) => { - vec![FeedObjectMapping::Content { - // also need to consider the encoded length of the object - // encoded content_a or content_b starts at offset 1 due to enum variant - offset: 1 + Compact::::compact_len(&(object.len() as u32)) as u32, - }] - } - } - } -} diff --git a/crates/pallet-feeds/src/tests.rs b/crates/pallet-feeds/src/tests.rs deleted file mode 100644 index be4f6e44a5..0000000000 --- a/crates/pallet-feeds/src/tests.rs +++ /dev/null @@ -1,346 +0,0 @@ -use crate::mock::{ - new_test_ext, ContentEnum, Feeds, MockFeedProcessorKind, RuntimeEvent, RuntimeOrigin, System, - Test, -}; -use crate::{Call as FeedsCall, Error, Object, SuccessfulPuts, TotalObjectsAndSize}; -use codec::{Decode, Encode}; -use frame_support::{assert_noop, assert_ok}; -use sp_core::Hasher; -use sp_runtime::traits::BlakeTwo256; -use subspace_core_primitives::crypto; - -const FEED_ID: u64 = 0; -const OWNER: u64 = 100; -const NOT_OWNER: u64 = 101; - -#[test] -fn create_feed() { - new_test_ext().execute_with(|| { - assert_ok!(Feeds::create( - RuntimeOrigin::signed(OWNER), - Default::default(), - None - )); - - assert_eq!(Feeds::totals(0), TotalObjectsAndSize::default()); - - System::assert_last_event(RuntimeEvent::Feeds(crate::Event::::FeedCreated { - feed_id: FEED_ID, - who: OWNER, - })); - assert_eq!(Feeds::next_feed_id(), 1); - assert_eq!(Feeds::feeds(OWNER).unwrap().to_vec(), vec![FEED_ID]); - }); -} - -#[test] -fn can_do_put() { - new_test_ext().execute_with(|| { - let object: Object = vec![1, 2, 3, 4, 5]; - let object_size = object.len() as u64; - // create feed before putting any data - assert_ok!(Feeds::create( - RuntimeOrigin::signed(OWNER), - Default::default(), - None - )); - - assert_ok!(Feeds::put( - RuntimeOrigin::signed(OWNER), - FEED_ID, - object.clone() - )); - - // check Metadata hashmap for updated metadata - assert_eq!(Feeds::metadata(FEED_ID), Some(vec![])); - - // check Totals hashmap - assert_eq!( - Feeds::totals(FEED_ID), - TotalObjectsAndSize { - count: 1, - size: object_size, - } - ); - - assert_eq!( - SuccessfulPuts::::get()[0], - BlakeTwo256::hash( - FeedsCall::::put { - feed_id: FEED_ID, - object: object.clone() - } - .encode() - .as_slice() - ) - ); - - System::assert_last_event(RuntimeEvent::Feeds(crate::Event::::ObjectSubmitted { - feed_id: FEED_ID, - who: OWNER, - metadata: vec![], - object_size, - })); - - // only owner can put - assert_noop!( - Feeds::put(RuntimeOrigin::signed(NOT_OWNER), FEED_ID, object), - Error::::NotFeedOwner - ); - }); -} - -#[test] -fn cannot_do_put_without_creating_feed() { - new_test_ext().execute_with(|| { - let object: Object = vec![1, 2, 3, 4, 5]; - assert_noop!( - Feeds::put(RuntimeOrigin::signed(OWNER), FEED_ID, object), - Error::::UnknownFeedId - ); - - assert_eq!(System::events().len(), 0); - }); -} - -#[test] -fn can_close_open_feed() { - new_test_ext().execute_with(|| { - let object: Object = vec![1, 2, 3, 4, 5]; - // create feed before putting any data - assert_ok!(Feeds::create( - RuntimeOrigin::signed(OWNER), - Default::default(), - None - )); - - assert_ok!(Feeds::put( - RuntimeOrigin::signed(OWNER), - FEED_ID, - object.clone() - )); - - // only owner can close - assert_noop!( - Feeds::close(RuntimeOrigin::signed(NOT_OWNER), FEED_ID), - Error::::NotFeedOwner - ); - - assert_ok!(Feeds::close(RuntimeOrigin::signed(OWNER), FEED_ID)); - - System::assert_last_event(RuntimeEvent::Feeds(crate::Event::::FeedClosed { - feed_id: FEED_ID, - who: OWNER, - })); - - // cannot put a closed feed - assert_noop!( - Feeds::put(RuntimeOrigin::signed(OWNER), FEED_ID, object), - Error::::FeedClosed - ); - }); -} - -#[test] -fn cannot_close_invalid_feed() { - new_test_ext().execute_with(|| { - let feed_id = 10; // invalid - assert_noop!( - Feeds::close(RuntimeOrigin::signed(OWNER), feed_id), - Error::::UnknownFeedId - ); - }); -} - -#[test] -fn can_update_existing_feed() { - new_test_ext().execute_with(|| { - assert_ok!(Feeds::create( - RuntimeOrigin::signed(OWNER), - Default::default(), - None - )); - // only owner can update - assert_noop!( - Feeds::update( - RuntimeOrigin::signed(NOT_OWNER), - FEED_ID, - Default::default(), - None - ), - Error::::NotFeedOwner - ); - - assert_ok!(Feeds::update( - RuntimeOrigin::signed(OWNER), - FEED_ID, - Default::default(), - None - )); - System::assert_last_event(RuntimeEvent::Feeds(crate::Event::::FeedUpdated { - feed_id: FEED_ID, - who: OWNER, - })); - }); -} - -#[test] -fn cannot_update_unknown_feed() { - new_test_ext().execute_with(|| { - assert_noop!( - Feeds::update( - RuntimeOrigin::signed(OWNER), - FEED_ID, - Default::default(), - None - ), - Error::::UnknownFeedId - ); - }); -} - -#[test] -fn transfer_feed_ownership() { - new_test_ext().execute_with(|| { - assert_ok!(Feeds::create( - RuntimeOrigin::signed(OWNER), - Default::default(), - None - )); - assert_eq!(Feeds::feeds(OWNER).unwrap().to_vec(), vec![FEED_ID]); - - let new_owner = 102u64; - // only owner can transfer - assert_noop!( - Feeds::transfer(RuntimeOrigin::signed(NOT_OWNER), FEED_ID, new_owner), - Error::::NotFeedOwner - ); - assert_ok!(Feeds::transfer( - RuntimeOrigin::signed(OWNER), - FEED_ID, - new_owner - )); - assert_eq!(Feeds::feeds(OWNER), None); - assert_eq!(Feeds::feeds(new_owner).unwrap().to_vec(), vec![FEED_ID]); - }); -} - -#[test] -fn cannot_create_after_max_feeds() { - new_test_ext().execute_with(|| { - assert_ok!(Feeds::create( - RuntimeOrigin::signed(OWNER), - Default::default(), - None - )); - assert_eq!(Feeds::feeds(OWNER).unwrap().to_vec(), vec![FEED_ID]); - - // mock limits one feed per user - assert_noop!( - Feeds::create(RuntimeOrigin::signed(OWNER), Default::default(), None), - Error::::MaxFeedsReached - ); - }); -} - -fn create_content_feed(object: Object, kind: MockFeedProcessorKind, contents: Vec>) { - new_test_ext().execute_with(|| { - assert_ok!(Feeds::create(RuntimeOrigin::signed(OWNER), kind, None)); - - let call = FeedsCall::::put { - feed_id: FEED_ID, - object: object.clone(), - }; - let mappings = call.extract_call_objects(); - assert_eq!(mappings.len(), contents.len()); - let encoded_call = call.encode(); - contents.into_iter().enumerate().for_each(|(i, content)| { - assert_eq!( - Vec::::decode(&mut &encoded_call[mappings[i].offset as usize..]).unwrap(), - content - ); - - assert_eq!(mappings[i].key, crypto::blake3_hash(&content)); - }) - }); -} - -fn create_custom_content_feed( - object: Object, - feed_processor_kind: MockFeedProcessorKind, - keys: Vec>, - contents: Vec>, -) { - new_test_ext().execute_with(|| { - assert_ok!(Feeds::create( - RuntimeOrigin::signed(OWNER), - feed_processor_kind, - None - )); - - let call = FeedsCall::::put { - feed_id: FEED_ID, - object: object.clone(), - }; - let mappings = call.extract_call_objects(); - assert_eq!(mappings.len(), keys.len()); - - // keys should match - keys.into_iter().enumerate().for_each(|(i, key)| { - // key should match the feed name spaced key - assert_eq!( - mappings[i].key, - crypto::blake3_hash_list(&[&FEED_ID.encode(), key.as_slice()]) - ); - }); - - // contents should match - let encoded_call = call.encode(); - contents.into_iter().enumerate().for_each(|(i, content)| { - assert_eq!( - Vec::::decode(&mut &encoded_call[mappings[i].offset as usize..]).unwrap(), - content - ); - }) - }); -} - -#[test] -fn create_full_object_feed() { - let object: Object = (1..255).collect(); - create_content_feed(object.clone(), MockFeedProcessorKind::Content, vec![object]) -} - -#[test] -fn create_full_object_feed_with_key_override() { - let object: Object = (1..255).collect(); - let key = (0..32) - .collect::>() - .try_into() - .expect("must be 32 bytes"); - create_custom_content_feed( - object.clone(), - MockFeedProcessorKind::Custom(key), - vec![key.to_vec()], - vec![object], - ); -} - -#[test] -fn create_content_within_object_feed() { - let content_a = (1..128).collect::>(); - let object = ContentEnum::ContentA(content_a.clone()).encode(); - create_content_feed( - object, - MockFeedProcessorKind::ContentWithin, - vec![content_a], - ); - - let content_b = (129..255).collect::>(); - let object = ContentEnum::ContentB(content_b.clone()).encode(); - create_content_feed( - object, - MockFeedProcessorKind::ContentWithin, - vec![content_b], - ) -} diff --git a/crates/pallet-grandpa-finality-verifier/Cargo.toml b/crates/pallet-grandpa-finality-verifier/Cargo.toml deleted file mode 100644 index 038ea5f20c..0000000000 --- a/crates/pallet-grandpa-finality-verifier/Cargo.toml +++ /dev/null @@ -1,54 +0,0 @@ -[package] -name = "pallet-grandpa-finality-verifier" -version = "0.1.0" -authors = ["Vedhavyas Singareddi "] -edition = "2021" -license = "Apache-2.0" -homepage = "https://subspace.network" -repository = "https://github.com/autonomys/subspace" -description = "Pallet to verify GRANDPA finality proofs for Substrate based chains" -readme = "README.md" - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.6.12", default-features = false } -finality-grandpa = { version = "0.16.1", default-features = false } -log = { version = "0.4.22", default-features = false } -num-traits = { version = "0.2.18", default-features = false } -scale-info = { version = "2.11.2", default-features = false, features = ["derive"] } -serde = { version = "1.0.206", optional = true } - -# Substrate Dependencies - -frame-support = { git = "https://github.com/subspace/polkadot-sdk", rev = "5626154d0781ac9a6ffd5a6207ed237f425ae631", default-features = false } -frame-system = { git = "https://github.com/subspace/polkadot-sdk", rev = "5626154d0781ac9a6ffd5a6207ed237f425ae631", default-features = false } -sp-consensus-grandpa = { git = "https://github.com/subspace/polkadot-sdk", rev = "5626154d0781ac9a6ffd5a6207ed237f425ae631", default-features = false } -sp-core = { git = "https://github.com/subspace/polkadot-sdk", rev = "5626154d0781ac9a6ffd5a6207ed237f425ae631", default-features = false } -sp-runtime = { git = "https://github.com/subspace/polkadot-sdk", rev = "5626154d0781ac9a6ffd5a6207ed237f425ae631", default-features = false } -sp-std = { git = "https://github.com/subspace/polkadot-sdk", rev = "5626154d0781ac9a6ffd5a6207ed237f425ae631", default-features = false } - -[dev-dependencies] -ed25519-dalek = { version = "2.1.1", default-features = false } -sp-io = { git = "https://github.com/subspace/polkadot-sdk", rev = "5626154d0781ac9a6ffd5a6207ed237f425ae631" } -sp-application-crypto = { git = "https://github.com/subspace/polkadot-sdk", rev = "5626154d0781ac9a6ffd5a6207ed237f425ae631" } - -[features] -default = ["std"] -std = [ - "codec/std", - "finality-grandpa/std", - "frame-support/std", - "frame-system/std", - "log/std", - "num-traits/std", - "scale-info/std", - "serde", - "sp-consensus-grandpa/std", - "sp-core/std", - "sp-runtime/std", - "sp-std/std", -] -try-runtime = [ - "frame-support/try-runtime", - "frame-system/try-runtime", - "sp-runtime/try-runtime", -] diff --git a/crates/pallet-grandpa-finality-verifier/README.md b/crates/pallet-grandpa-finality-verifier/README.md deleted file mode 100644 index 1f258e20e2..0000000000 --- a/crates/pallet-grandpa-finality-verifier/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# pallet-grandpa-finality-verifier -License: Apache-2.0 - -GRANDPA finality verifier is used to verify the justifications provided within the substrate based blocks indexing them on our DSN. - -The pallet is responsible for: -- providing a basic abstraction over any substrate based chains through `Chain` trait. -- decoding the block and its components. -- verifying the blocks and its justifications using the current authority set the block was produced in. -- importing any authority set changes from the header after the verification. - -This pallet is not responsible for: -- verifying or recognizing the forks. So this is left for the admin to reinitialize the chain state after the fork. diff --git a/crates/pallet-grandpa-finality-verifier/src/chain.rs b/crates/pallet-grandpa-finality-verifier/src/chain.rs deleted file mode 100644 index 1e74225588..0000000000 --- a/crates/pallet-grandpa-finality-verifier/src/chain.rs +++ /dev/null @@ -1,118 +0,0 @@ -#[cfg(not(feature = "std"))] -extern crate alloc; - -use crate::grandpa::GrandpaJustification; -use crate::{Config, EncodedBlockHash, EncodedBlockNumber, Error}; -#[cfg(not(feature = "std"))] -use alloc::vec::Vec; -use codec::Decode; -use frame_support::Parameter; -use num_traits::AsPrimitive; -use sp_runtime::generic; -use sp_runtime::traits::{ - AtLeast32BitUnsigned, Hash as HashT, Header as HeaderT, MaybeDisplay, - MaybeSerializeDeserialize, Member, Saturating, SimpleBitOps, -}; -use sp_std::hash::Hash; -use sp_std::str::FromStr; - -pub(crate) type OpaqueExtrinsic = Vec; -pub type SignedBlock
= generic::SignedBlock>; - -/// Minimal Substrate-based chain representation that may be used from no_std environment. -pub trait Chain { - /// A type that fulfills the abstract idea of what a Substrate block number is. - // Constraints come from the associated Number type of `sp_runtime::traits::Header` - // See here for more info: - // https://crates.parity.io/sp_runtime/traits/trait.Header.html#associatedtype.Number - // - // Note that the `AsPrimitive` trait is required by the GRANDPA justification - // verifier, and is not usually part of a Substrate Header's Number type. - type BlockNumber: Parameter - + Member - + MaybeSerializeDeserialize - + Hash - + Copy - + Default - + MaybeDisplay - + AtLeast32BitUnsigned - + FromStr - + AsPrimitive - + Default - + Saturating; - - /// A type that fulfills the abstract idea of what a Substrate hash is. - // Constraints come from the associated Hash type of `sp_runtime::traits::Header` - // See here for more info: - // https://crates.parity.io/sp_runtime/traits/trait.Header.html#associatedtype.Hash - type Hash: Parameter - + Member - + MaybeSerializeDeserialize - + Hash - + Ord - + Copy - + MaybeDisplay - + Default - + SimpleBitOps - + AsRef<[u8]> - + AsMut<[u8]>; - - /// A type that fulfills the abstract idea of what a Substrate header is. - // See here for more info: - // https://crates.parity.io/sp_runtime/traits/trait.Header.html - type Header: Parameter - + HeaderT - + MaybeSerializeDeserialize; - - /// A type that fulfills the abstract idea of what a Substrate hasher (a type - /// that produces hashes) is. - // Constraints come from the associated Hashing type of `sp_runtime::traits::Header` - // See here for more info: - // https://crates.parity.io/sp_runtime/traits/trait.Header.html#associatedtype.Hashing - type Hasher: HashT; - - fn decode_block(block: &[u8]) -> Result, Error> { - SignedBlock::::decode(&mut &*block).map_err(|error| { - log::error!("Cannot decode block, error: {:?}", error); - Error::::FailedDecodingBlock - }) - } - - fn decode_header(header: &[u8]) -> Result> { - Self::Header::decode(&mut &*header).map_err(|error| { - log::error!("Cannot decode header, error: {:?}", error); - Error::::FailedDecodingHeader - }) - } - - fn decode_grandpa_justifications( - justifications: &[u8], - ) -> Result, Error> { - GrandpaJustification::::decode(&mut &*justifications).map_err(|error| { - log::error!("Cannot decode justifications, error: {:?}", error); - Error::::FailedDecodingJustifications - }) - } - - fn decode_block_number_and_hash( - pair: (EncodedBlockNumber, EncodedBlockHash), - ) -> Result<(Self::BlockNumber, Self::Hash), Error> { - let number = Self::decode_block_number::(pair.0.as_slice())?; - let hash = Self::decode_block_hash::(pair.1.as_slice())?; - Ok((number, hash)) - } - - fn decode_block_number(number: &[u8]) -> Result> { - Self::BlockNumber::decode(&mut &*number).map_err(|error| { - log::error!("Cannot decode block number, error: {:?}", error); - Error::::FailedDecodingBlockNumber - }) - } - - fn decode_block_hash(hash: &[u8]) -> Result> { - Self::Hash::decode(&mut &*hash).map_err(|error| { - log::error!("Cannot decode block hash, error: {:?}", error); - Error::::FailedDecodingBlockHash - }) - } -} diff --git a/crates/pallet-grandpa-finality-verifier/src/grandpa.rs b/crates/pallet-grandpa-finality-verifier/src/grandpa.rs deleted file mode 100644 index cf62d4c2fd..0000000000 --- a/crates/pallet-grandpa-finality-verifier/src/grandpa.rs +++ /dev/null @@ -1,259 +0,0 @@ -// Copyright (C) 2022 Subspace Labs, Inc. -// 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. - -#[cfg(not(feature = "std"))] -extern crate alloc; - -// GRANDPA verification is mostly taken from Parity's bridges https://github.com/paritytech/parity-bridges-common/tree/master/primitives/header-chain -#[cfg(not(feature = "std"))] -use alloc::vec::Vec; -use codec::{Decode, Encode}; -use finality_grandpa::voter_set::VoterSet; -use scale_info::TypeInfo; -#[cfg(feature = "std")] -use serde::{Deserialize, Serialize}; -use sp_consensus_grandpa::{ - AuthorityId, AuthorityList, AuthoritySignature, ConsensusLog, SetId, GRANDPA_ENGINE_ID, -}; -use sp_runtime::traits::Header as HeaderT; -use sp_std::collections::btree_map::BTreeMap; -use sp_std::collections::btree_set::BTreeSet; -use sp_std::prelude::*; - -/// A GRANDPA Justification is a proof that a given header was finalized -/// at a certain height and with a certain set of authorities. -/// -/// This particular proof is used to prove that headers on a bridged chain -/// (so not our chain) have been finalized correctly. -#[derive(Encode, Decode, Clone, PartialEq, Eq, TypeInfo)] -pub struct GrandpaJustification { - /// The round (voting period) this justification is valid for. - pub round: u64, - /// The set of votes for the chain which is to be finalized. - pub commit: - finality_grandpa::Commit, - /// A proof that the chain of blocks in the commit are related to each other. - pub votes_ancestries: Vec
, -} - -/// A GRANDPA Authority List and ID. -#[derive(Debug, Default, Encode, Decode, Eq, PartialEq, Clone, TypeInfo)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] -pub struct AuthoritySet { - /// List of GRANDPA authorities for the current round. - pub authorities: AuthorityList, - /// Monotonic identifier of the current GRANDPA authority set. - pub set_id: SetId, -} - -/// Votes ancestries with useful methods. -#[derive(Debug)] -struct AncestryChain { - /// Header hash => parent header hash mapping. - parents: BTreeMap, - /// Hashes of headers that were not visited by `is_ancestor` method. - unvisited: BTreeSet, -} - -impl AncestryChain
{ - /// Create new ancestry chain. - fn new(ancestry: &[Header]) -> AncestryChain
{ - let mut parents = BTreeMap::new(); - let mut unvisited = BTreeSet::new(); - for ancestor in ancestry { - let hash = ancestor.hash(); - let parent_hash = *ancestor.parent_hash(); - parents.insert(hash, parent_hash); - unvisited.insert(hash); - } - AncestryChain { parents, unvisited } - } - - /// Returns `Ok(_)` if `precommit_target` is a descendant of the `commit_target` block and - /// `Err(_)` otherwise. - fn ensure_descendant( - mut self, - commit_target: &Header::Hash, - precommit_target: &Header::Hash, - ) -> Result { - let mut current_hash = *precommit_target; - while current_hash != *commit_target { - let is_visited_before = !self.unvisited.remove(¤t_hash); - current_hash = match self.parents.get(¤t_hash) { - Some(parent_hash) => { - if is_visited_before { - // `Some(parent_hash)` means that the `current_hash` is in the `parents` - // container `is_visited_before` means that it has been visited before in - // some of previous calls => since we assume that previous call has finished - // with `true`, this also will be finished with `true` - return Ok(self); - } - - *parent_hash - } - None => return Err(Error::PrecommitIsNotCommitDescendant), - }; - } - - Ok(self) - } -} - -/// Justification verification error. -#[derive(Debug, Eq, PartialEq)] -pub enum Error { - /// Justification is finalizing unexpected header. - InvalidJustificationTarget, - /// The authority has provided an invalid signature. - InvalidAuthoritySignature, - /// The justification contains precommit for header that is not a descendant of the commit - /// header. - PrecommitIsNotCommitDescendant, - /// The cumulative weight of all votes in the justification is not enough to justify commit - /// header finalization. - TooLowCumulativeWeight, - /// The justification contains extra (unused) headers in its `votes_ancestries` field. - ExtraHeadersInVotesAncestries, - /// Arithematic overflow - ArithematicOverflow, -} - -/// Verify that justification, that is generated by given authority set, finalizes given header. -pub(crate) fn verify_justification( - finalized_target: (Header::Hash, Header::Number), - authorities_set_id: SetId, - authorities_set: &VoterSet, - justification: &GrandpaJustification
, -) -> Result<(), Error> -where - Header::Number: finality_grandpa::BlockNumberOps, -{ - // always ensure the justification belongs to either current target or its descendent - let (_finalized_hash, finalized_number) = finalized_target; - if justification.commit.target_number < finalized_number { - return Err(Error::InvalidJustificationTarget); - } - - let mut chain = AncestryChain::new(&justification.votes_ancestries); - let mut signature_buffer = Vec::new(); - let mut votes = BTreeSet::new(); - let mut cumulative_weight = 0u64; - for signed in &justification.commit.precommits { - // authority must be in the set - let authority_info = match authorities_set.get(&signed.id) { - Some(authority_info) => authority_info, - None => { - // just ignore precommit from unknown authority as - // `finality_grandpa::import_precommit` does - continue; - } - }; - - // check if authority has already voted in the same round. - // - // there's a lot of code in `validate_commit` and `import_precommit` functions inside - // `finality-grandpa` crate (mostly related to reporting equivocations). But the only thing - // that we care about is that only first vote from the authority is accepted - if !votes.insert(signed.id.clone()) { - continue; - } - - // everything below this line can't just `continue`, because state is already altered - - // precommits aren't allowed for block lower than the target - if signed.precommit.target_number < justification.commit.target_number { - return Err(Error::PrecommitIsNotCommitDescendant); - } - // all precommits must be descendants of target block - chain = chain.ensure_descendant( - &justification.commit.target_hash, - &signed.precommit.target_hash, - )?; - // since we know now that the precommit target is the descendant of the justification - // target, we may increase 'weight' of the justification target - // - // there's a lot of code in the `VoteGraph::insert` method inside `finality-grandpa` crate, - // but in the end it is only used to find GHOST, which we don't care about. The only thing - // that we care about is that the justification target has enough weight - cumulative_weight = cumulative_weight - .checked_add(authority_info.weight().0.into()) - .ok_or(Error::ArithematicOverflow)?; - // verify authority signature - if !sp_consensus_grandpa::check_message_signature_with_buffer( - &finality_grandpa::Message::Precommit(signed.precommit.clone()), - &signed.id, - &signed.signature, - justification.round, - authorities_set_id, - &mut signature_buffer, - ) { - return Err(Error::InvalidAuthoritySignature); - } - } - - // check that there are no extra headers in the justification - if !chain.unvisited.is_empty() { - return Err(Error::ExtraHeadersInVotesAncestries); - } - - // check that the cumulative weight of validators voted for the justification target (or one - // of its descendents) is larger than required threshold. - let threshold = authorities_set.threshold().0.into(); - if cumulative_weight >= threshold { - Ok(()) - } else { - Err(Error::TooLowCumulativeWeight) - } -} - -pub(crate) fn find_scheduled_change( - header: &H, -) -> Option> { - use sp_runtime::generic::OpaqueDigestItemId; - - let id = OpaqueDigestItemId::Consensus(&GRANDPA_ENGINE_ID); - - let filter_log = |log: ConsensusLog| match log { - ConsensusLog::ScheduledChange(change) => Some(change), - _ => None, - }; - - // find the first consensus digest with the right ID which converts to - // the right kind of consensus log. - header - .digest() - .convert_first(|l| l.try_to(id).and_then(filter_log)) -} - -/// Checks the given header for a consensus digest signaling a **forced** scheduled change and -/// extracts it. -pub(crate) fn find_forced_change( - header: &H, -) -> Option<(H::Number, sp_consensus_grandpa::ScheduledChange)> { - use sp_runtime::generic::OpaqueDigestItemId; - - let id = OpaqueDigestItemId::Consensus(&GRANDPA_ENGINE_ID); - - let filter_log = |log: ConsensusLog| match log { - ConsensusLog::ForcedChange(delay, change) => Some((delay, change)), - _ => None, - }; - - // find the first consensus digest with the right ID which converts to - // the right kind of consensus log. - header - .digest() - .convert_first(|l| l.try_to(id).and_then(filter_log)) -} diff --git a/crates/pallet-grandpa-finality-verifier/src/lib.rs b/crates/pallet-grandpa-finality-verifier/src/lib.rs deleted file mode 100644 index 177d3f230f..0000000000 --- a/crates/pallet-grandpa-finality-verifier/src/lib.rs +++ /dev/null @@ -1,354 +0,0 @@ -// Copyright (C) 2022 Subspace Labs, Inc. -// 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. - -//! Substrate GRANDPA finality verifier -//! -//! This pallet is an on-chain GRANDPA finality verifier for Substrate based chains. -//! -//! The pallet is responsible for tracking GRANDPA validator set hand-offs. We only accept headers -//! with justifications signed by the current validator set we know of. The header is inspected for -//! a `ScheduledChanges` digest item, which is then used to update to next validator set. -//! -//! Since this pallet only tracks finalized headers it does not deal with forks. Forks can only -//! occur if the GRANDPA validator set on the bridged chain is either colluding or there is a severe -//! bug causing resulting in an equivocation. Such events are outside the scope of this pallet. -//! Shall the fork occur on the bridged chain governance intervention will be required to -//! re-initialize the bridge and track the right fork. - -#![cfg_attr(not(feature = "std"), no_std)] - -pub mod chain; -mod grandpa; -#[cfg(test)] -mod tests; - -#[cfg(not(feature = "std"))] -extern crate alloc; - -#[cfg(not(feature = "std"))] -use alloc::vec::Vec; -use codec::{Decode, Encode}; -use scale_info::TypeInfo; -#[cfg(feature = "std")] -use serde::{Deserialize, Serialize}; -use sp_consensus_grandpa::SetId; -use sp_std::fmt::Debug; - -// Re-export in crate namespace for `construct_runtime!` -pub use pallet::*; - -/// Data required to initialize a Chain -#[derive(Default, Debug, Encode, Decode, Clone, Eq, PartialEq, TypeInfo)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] -pub struct InitializationData { - /// Scale encoded best finalized header we know. - pub best_known_finalized_header: Vec, - /// The ID of the current authority set - pub set_id: SetId, -} - -// Scale encoded block number, hash, and header of the target chain -type EncodedBlockNumber = Vec; -type EncodedBlockHash = Vec; -type EncodedHeader = Vec; - -#[frame_support::pallet] -pub mod pallet { - use crate::chain::Chain; - use crate::grandpa::{ - find_forced_change, find_scheduled_change, verify_justification, AuthoritySet, - }; - use crate::{EncodedBlockHash, EncodedBlockNumber, EncodedHeader, InitializationData}; - use finality_grandpa::voter_set::VoterSet; - use frame_support::pallet_prelude::*; - use sp_consensus_grandpa::GRANDPA_ENGINE_ID; - use sp_runtime::traits::{CheckedAdd, CheckedSub, Hash, Header, One, Zero}; - use sp_runtime::ArithmeticError; - use sp_std::fmt::Debug; - - #[pallet::config] - pub trait Config: frame_system::Config { - // Chain ID uniquely identifies a substrate based chain - type ChainId: Parameter + Member + Debug + Default + Copy; - } - - #[pallet::pallet] - #[pallet::without_storage_info] - pub struct Pallet(PhantomData); - - /// The point after which the block validation begins - #[pallet::storage] - pub(super) type ValidationCheckPoint = - StorageMap<_, Identity, T::ChainId, (EncodedBlockNumber, EncodedHeader), ValueQuery>; - - /// Oldest known parent - #[pallet::storage] - pub(super) type OldestKnownParent = - StorageMap<_, Identity, T::ChainId, (EncodedBlockNumber, EncodedBlockHash), ValueQuery>; - - /// Known tip of the chain - #[pallet::storage] - pub(super) type ChainTip = - StorageMap<_, Identity, T::ChainId, (EncodedBlockNumber, EncodedBlockHash), ValueQuery>; - - /// The current GRANDPA Authority set for a given Chain - #[pallet::storage] - pub(super) type CurrentAuthoritySet = - StorageMap<_, Identity, T::ChainId, AuthoritySet, ValueQuery>; - - #[pallet::error] - pub enum Error { - /// The block and its contents are not valid - InvalidBlock, - /// The authority set from the underlying header chain is invalid. - InvalidAuthoritySet, - /// Justification is missing.. - MissingJustification, - /// The given justification is invalid for the given header. - InvalidJustification, - /// Failed to decode initialization data - FailedDecodingInitData, - /// Failed to Decode header - FailedDecodingHeader, - /// Failed to Decode block number - FailedDecodingBlockNumber, - /// Failed to Decode block hash - FailedDecodingBlockHash, - /// Failed to Decode block - FailedDecodingBlock, - /// Failed to decode justifications - FailedDecodingJustifications, - /// The header is already finalized - InvalidHeader, - /// The scheduled authority set change found in the header is unsupported by the pallet. - /// - /// This is the case for non-standard (e.g forced) authority set changes. - UnsupportedScheduledChange, - } - - /// Initializes the chain by extracting the Authority set and best known parent of the chain. - /// After the initialization the import of blocks can happen in forward and reverse direction based on the parent stored - /// If Genesis is the validation point, then parent is set to Genesis. - /// Else parent is set to the parent of the best finalized header - pub(crate) fn initialize_chain( - chain_id: T::ChainId, - init_params: InitializationData, - ) -> DispatchResult { - let InitializationData { - best_known_finalized_header: encoded_header, - set_id, - } = init_params; - let header = C::decode_header::(encoded_header.as_slice())?; - let change = - find_scheduled_change(&header).ok_or(Error::::UnsupportedScheduledChange)?; - - // Set the validation point - let encoded_number = header.number().encode(); - ValidationCheckPoint::::insert(chain_id, (encoded_number.clone(), encoded_header)); - - // Set authority set - let authority_set = AuthoritySet { - authorities: change.next_authorities, - set_id, - }; - CurrentAuthoritySet::::insert(chain_id, authority_set); - - // set the oldest known parent - let (parent_number, parent_hash) = header - .number() - .checked_sub(&One::one()) - .map(|number| (number.encode(), header.parent_hash().encode())) - .unwrap_or((encoded_number, header.hash().encode())); - - OldestKnownParent::::insert(chain_id, (parent_number.clone(), parent_hash.clone())); - // we also set the chain tip to parent so that we sequentially import blocks from parent + 1 - ChainTip::::insert(chain_id, (parent_number, parent_hash)); - Ok(()) - } - - pub fn validate_finalized_block( - chain_id: T::ChainId, - object: &[u8], - ) -> Result<(C::Hash, C::BlockNumber), DispatchError> { - // basic block validation - let block = C::decode_block::(object)?; - let number = *block.block.header.number(); - let hash = block.block.header.hash(); - - let extrinsics_root = C::Hasher::ordered_trie_root( - block.block.extrinsics.iter().map(Encode::encode).collect(), - sp_runtime::StateVersion::V0, - ); - ensure!( - extrinsics_root == *block.block.header.extrinsics_root(), - Error::::InvalidBlock - ); - - let (oldest_known_parent_height, oldest_known_parent_hash) = - C::decode_block_number_and_hash::(OldestKnownParent::::get(chain_id))?; - - // if the target is the known oldest parent, we import the block and progress backward - if oldest_known_parent_height == number { - ensure!(oldest_known_parent_hash == hash, Error::::InvalidBlock); - - OldestKnownParent::::insert( - chain_id, - ( - number.checked_sub(&One::one()).unwrap_or(number).encode(), - block.block.header.parent_hash().encode(), - ), - ); - - return Ok((hash, number)); - } - - // get last imported block height and hash - let (parent_number, parent_hash) = - C::decode_block_number_and_hash::(ChainTip::::get(chain_id))?; - - // block height must be always increasing - ensure!( - number - == parent_number - .checked_add(&One::one()) - .ok_or(ArithmeticError::Overflow)?, - Error::::InvalidBlock - ); - ensure!( - *block.block.header.parent_hash() == parent_hash, - Error::::InvalidBlock - ); - - // double check the validation header before importing the block - let (encoded_number, encoded_validation_header) = ValidationCheckPoint::::get(chain_id); - let validation_number = C::decode_block_number::(encoded_number.as_slice())?; - if number == validation_number { - ensure!( - encoded_validation_header == block.block.header.encode(), - Error::::InvalidHeader - ); - } - - // if the target header is a descendent of validation block, validate the justification - if number > validation_number { - let justification = block - .justifications - .ok_or(Error::::MissingJustification)? - .into_justification(GRANDPA_ENGINE_ID) - .ok_or(Error::::MissingJustification)?; - let justification = C::decode_grandpa_justifications::(justification.as_slice())?; - - // fetch current authority set - let authority_set = >::get(chain_id); - let voter_set = - VoterSet::new(authority_set.authorities).ok_or(Error::::InvalidAuthoritySet)?; - let set_id = authority_set.set_id; - - // verify justification - verify_justification::((hash, number), set_id, &voter_set, &justification) - .map_err(|e| { - log::error!( - target: "runtime::grandpa-finality-verifier", - "Received invalid justification for {:?}: {:?}", - hash, - e, - ); - Error::::InvalidJustification - })?; - - // Update any next authority set if any - try_enact_authority_change::(chain_id, &block.block.header, set_id)?; - } - - // update the latest descendant - ChainTip::::insert(chain_id, (number.encode(), hash.encode())); - Ok((hash, number)) - } - - /// Check the given header for a GRANDPA scheduled authority set change. If a change - /// is found it will be enacted immediately. - /// - /// This function does not support forced changes, or scheduled changes with delays - /// since these types of changes are indicative of abnormal behavior from GRANDPA. - pub(crate) fn try_enact_authority_change( - chain_id: T::ChainId, - header: &C::Header, - current_set_id: sp_consensus_grandpa::SetId, - ) -> DispatchResult { - // We don't support forced changes - at that point governance intervention is required. - ensure!( - find_forced_change(header).is_none(), - Error::::UnsupportedScheduledChange - ); - - if let Some(change) = find_scheduled_change(header) { - // GRANDPA only includes a `delay` for forced changes, so this isn't valid. - ensure!( - change.delay == Zero::zero(), - Error::::UnsupportedScheduledChange - ); - - let next_authorities = AuthoritySet { - authorities: change.next_authorities, - set_id: current_set_id + 1, - }; - - // Since our header schedules a change and we know the delay is 0, it must also enact - // the change. - CurrentAuthoritySet::::insert(chain_id, &next_authorities); - - log::info!( - target: "runtime::grandpa-finality-verifier", - "Transitioned from authority set {} to {}! New authorities are: {:?}", - current_set_id, - current_set_id + 1, - next_authorities, - ); - }; - - Ok(()) - } - - /// Bootstrap the chain to start importing valid finalized blocks - /// - /// The initial configuration provided does not need to be the genesis header of the bridged - /// chain, it can be any arbitrary 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. - pub fn initialize( - chain_id: T::ChainId, - init_data: &[u8], - ) -> DispatchResult { - let data = InitializationData::decode(&mut &*init_data).map_err(|error| { - log::error!("Cannot decode init data, error: {:?}", error); - Error::::FailedDecodingInitData - })?; - - initialize_chain::(chain_id, data)?; - Ok(()) - } - - /// purges the on chain state of a given chain - pub fn purge(chain_id: T::ChainId) -> DispatchResult { - ValidationCheckPoint::::remove(chain_id); - CurrentAuthoritySet::::remove(chain_id); - ChainTip::::remove(chain_id); - OldestKnownParent::::remove(chain_id); - Ok(()) - } -} diff --git a/crates/pallet-grandpa-finality-verifier/src/tests/justification.rs b/crates/pallet-grandpa-finality-verifier/src/tests/justification.rs deleted file mode 100644 index ccc065d7a7..0000000000 --- a/crates/pallet-grandpa-finality-verifier/src/tests/justification.rs +++ /dev/null @@ -1,220 +0,0 @@ -//! Utilities for testing runtime code. - -use crate::grandpa::GrandpaJustification; -use crate::tests::keyring::{test_keyring, Account}; -use crate::tests::valid_extrinsics_root; -use codec::Encode; -use sp_consensus_grandpa::{AuthorityId, AuthoritySignature, AuthorityWeight, SetId}; -use sp_runtime::traits::{Header as HeaderT, One, Zero}; -use sp_std::prelude::*; - -pub(crate) const TEST_GRANDPA_ROUND: u64 = 1; -pub(crate) const TEST_GRANDPA_SET_ID: SetId = 1; - -/// Configuration parameters when generating test GRANDPA justifications. -#[derive(Clone)] -pub(crate) struct JustificationGeneratorParams { - /// The header which we want to finalize. - pub header: H, - /// The GRANDPA round number for the current authority set. - pub round: u64, - /// The current authority set ID. - pub set_id: SetId, - /// The current GRANDPA authority set. - /// - /// The size of the set will determine the number of pre-commits in our justification. - pub authorities: Vec<(Account, AuthorityWeight)>, - /// The total number of precommit ancestors in the `votes_ancestries` field our justification. - /// - /// These may be distributed among many forks. - pub ancestors: u32, - /// The number of forks. - /// - /// Useful for creating a "worst-case" scenario in which each authority is on its own fork. - pub forks: u32, -} - -impl Default for JustificationGeneratorParams { - #[inline] - fn default() -> Self { - Self { - header: test_header(One::one()), - round: TEST_GRANDPA_ROUND, - set_id: TEST_GRANDPA_SET_ID, - authorities: test_keyring(), - ancestors: 2, - forks: 1, - } - } -} - -/// Make a valid GRANDPA justification with sensible defaults -pub(crate) fn make_default_justification(header: &H) -> GrandpaJustification { - let params = JustificationGeneratorParams:: { - header: header.clone(), - ..Default::default() - }; - - make_justification_for_header(params) -} - -/// Generate justifications in a way where we are able to tune the number of pre-commits -/// and vote ancestries which are included in the justification. -/// -/// This is useful for benchmarkings where we want to generate valid justifications with -/// a specific number of pre-commits (tuned with the number of "authorities") and/or a specific -/// number of vote ancestries (tuned with the "votes" parameter). -/// -/// Note: This needs at least three authorities or else the verifier will complain about -/// being given an invalid commit. -pub(crate) fn make_justification_for_header( - params: JustificationGeneratorParams, -) -> GrandpaJustification { - let JustificationGeneratorParams { - header, - round, - set_id, - authorities, - mut ancestors, - forks, - } = params; - let (target_hash, target_number) = (header.hash(), *header.number()); - let mut votes_ancestries = vec![]; - let mut precommits = vec![]; - - assert!(forks != 0, "Need at least one fork to have a chain.."); - assert!( - forks as usize <= authorities.len(), - "If we have more forks than authorities we can't create valid pre-commits for all the forks." - ); - - // Roughly, how many vote ancestries do we want per fork - let target_depth = (ancestors + forks - 1) / forks; - - let mut unsigned_precommits = vec![]; - for i in 0..forks { - let depth = if ancestors >= target_depth { - ancestors -= target_depth; - target_depth - } else { - ancestors - }; - - // Note: Adding 1 to account for the target header - let chain = generate_chain(i, depth + 1, &header); - - // We don't include our finality target header in the vote ancestries - for child in &chain[1..] { - votes_ancestries.push(child.clone()); - } - - // The header we need to use when pre-commiting is the one at the highest height - // on our chain. - let precommit_candidate = chain.last().map(|h| (h.hash(), *h.number())).unwrap(); - unsigned_precommits.push(precommit_candidate); - } - - for (i, (id, _weight)) in authorities.iter().enumerate() { - // Assign authorities to sign pre-commits in a round-robin fashion - let target = unsigned_precommits[i % forks as usize]; - let precommit = signed_precommit::(id, target, round, set_id); - - precommits.push(precommit); - } - - GrandpaJustification { - round, - commit: finality_grandpa::Commit { - target_hash, - target_number, - precommits, - }, - votes_ancestries, - } -} - -fn generate_chain(fork_id: u32, depth: u32, ancestor: &H) -> Vec { - let mut headers = vec![ancestor.clone()]; - - for i in 1..depth { - let parent = &headers[(i - 1) as usize]; - let (hash, num) = (parent.hash(), *parent.number()); - - let mut header = test_header::(num + One::one()); - header.set_parent_hash(hash); - - // Modifying the digest so headers at the same height but in different forks have different - // hashes - header - .digest_mut() - .logs - .push(sp_runtime::DigestItem::Other(fork_id.encode())); - - headers.push(header); - } - - headers -} - -/// Create signed precommit with given target. -pub(crate) fn signed_precommit( - signer: &Account, - target: (H::Hash, H::Number), - round: u64, - set_id: SetId, -) -> finality_grandpa::SignedPrecommit { - let precommit = finality_grandpa::Precommit { - target_hash: target.0, - target_number: target.1, - }; - - let encoded = sp_consensus_grandpa::localized_payload( - round, - set_id, - &finality_grandpa::Message::Precommit(precommit.clone()), - ); - - let signature = signer.sign(&encoded); - let raw_signature: Vec = signature.to_bytes().into(); - - // Need to wrap our signature and id types that they match what our `SignedPrecommit` is - // expecting - let signature = AuthoritySignature::try_from(raw_signature).expect( - "We know our Keypair is good, - so our signature must also be good.", - ); - let id = (*signer).into(); - - finality_grandpa::SignedPrecommit { - precommit, - signature, - id, - } -} - -/// Get a header for testing. -/// -/// The correct parent hash will be used if given a non-zero header. -pub(crate) fn test_header(number: H::Number) -> H { - let default = |num| { - H::new( - num, - valid_extrinsics_root::(), - Default::default(), - Default::default(), - Default::default(), - ) - }; - - let mut header = default(number); - if number != Zero::zero() { - let parent_hash = test_header::(number - One::one()).hash(); - header.set_parent_hash(parent_hash); - } - header -} - -/// Convenience function for generating a Header ID at a given block number. -pub(crate) fn header_id(index: u8) -> (H::Hash, H::Number) { - (test_header::(index.into()).hash(), index.into()) -} diff --git a/crates/pallet-grandpa-finality-verifier/src/tests/keyring.rs b/crates/pallet-grandpa-finality-verifier/src/tests/keyring.rs deleted file mode 100644 index 857d008fb3..0000000000 --- a/crates/pallet-grandpa-finality-verifier/src/tests/keyring.rs +++ /dev/null @@ -1,70 +0,0 @@ -//! Utilities for working with test accounts. - -use codec::Encode; -use ed25519_dalek::{ - SecretKey, Signature, Signer, SigningKey, PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, -}; -use finality_grandpa::voter_set::VoterSet; -use sp_consensus_grandpa::{AuthorityId, AuthorityList, AuthorityWeight}; -use sp_std::prelude::*; - -/// Set of test accounts with friendly names. -pub(crate) const ALICE: Account = Account(0); -pub(crate) const BOB: Account = Account(1); -pub(crate) const CHARLIE: Account = Account(2); -pub(crate) const DAVE: Account = Account(3); -pub(crate) const EVE: Account = Account(4); - -/// A test account which can be used to sign messages. -#[derive(Debug, Clone, Copy)] -pub(crate) struct Account(pub u16); - -impl Account { - pub(crate) fn public(&self) -> [u8; PUBLIC_KEY_LENGTH] { - self.signing_key().to_keypair_bytes()[SECRET_KEY_LENGTH..][..PUBLIC_KEY_LENGTH] - .try_into() - .unwrap() - } - - pub(crate) fn signing_key(&self) -> SigningKey { - let data = self.0.encode(); - let mut secret_key: SecretKey = [0_u8; 32]; - secret_key[0..data.len()].copy_from_slice(&data); - - SigningKey::from_bytes(&secret_key) - } - - pub(crate) fn sign(&self, msg: &[u8]) -> Signature { - self.signing_key().sign(msg) - } -} - -impl From for AuthorityId { - #[inline] - fn from(p: Account) -> Self { - sp_application_crypto::UncheckedFrom::unchecked_from(p.public()) - } -} - -/// Get a valid set of voters for a Grandpa round. -pub(crate) fn voter_set() -> VoterSet { - VoterSet::new(authority_list()).unwrap() -} - -/// Convenience function to get a list of Grandpa authorities. -pub(crate) fn authority_list() -> AuthorityList { - test_keyring() - .iter() - .map(|(id, w)| (AuthorityId::from(*id), *w)) - .collect() -} - -/// Get the corresponding identities from the keyring for the "standard" authority set. -pub(crate) fn test_keyring() -> Vec<(Account, AuthorityWeight)> { - vec![(ALICE, 1), (BOB, 1), (CHARLIE, 1)] -} - -/// Get a list of "unique" accounts. -pub(crate) fn accounts(len: u16) -> Vec { - (0..len).map(Account).collect() -} diff --git a/crates/pallet-grandpa-finality-verifier/src/tests/mock.rs b/crates/pallet-grandpa-finality-verifier/src/tests/mock.rs deleted file mode 100644 index 56a1ce23c2..0000000000 --- a/crates/pallet-grandpa-finality-verifier/src/tests/mock.rs +++ /dev/null @@ -1,35 +0,0 @@ -use frame_support::weights::Weight; -use frame_support::{construct_runtime, derive_impl, parameter_types}; -use sp_runtime::Perbill; - -pub(crate) type ChainId = u64; -type Block = frame_system::mocking::MockBlock; - -use crate as grandpa; - -construct_runtime! { - pub struct TestRuntime { - System: frame_system, - Grandpa: grandpa, - } -} - -parameter_types! { - pub const BlockHashCount: u64 = 250; - pub const MaximumBlockWeight: Weight = Weight::from_parts(1024, 0); - pub const MaximumBlockLength: u32 = 2 * 1024; - pub const AvailableBlockRatio: Perbill = Perbill::one(); -} - -#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] -impl frame_system::Config for TestRuntime { - type Block = Block; -} - -impl grandpa::Config for TestRuntime { - type ChainId = ChainId; -} - -pub fn run_test(test: impl FnOnce() -> T) -> T { - sp_io::TestExternalities::new(Default::default()).execute_with(test) -} diff --git a/crates/pallet-grandpa-finality-verifier/src/tests/mod.rs b/crates/pallet-grandpa-finality-verifier/src/tests/mod.rs deleted file mode 100644 index 8f10df64d0..0000000000 --- a/crates/pallet-grandpa-finality-verifier/src/tests/mod.rs +++ /dev/null @@ -1,640 +0,0 @@ -mod justification; -mod keyring; -mod mock; - -use crate::chain::{Chain, OpaqueExtrinsic}; -use crate::grandpa::{verify_justification, AuthoritySet, Error, GrandpaJustification}; -use crate::{ - initialize, validate_finalized_block, ChainTip, CurrentAuthoritySet, Error as ErrorP, - InitializationData, OldestKnownParent, ValidationCheckPoint, -}; -use codec::Encode; -use frame_support::dispatch::DispatchResult; -use frame_support::{assert_err, assert_ok}; -use justification::*; -use keyring::*; -use mock::{run_test, ChainId, TestRuntime}; -use sp_consensus_grandpa::{ConsensusLog, ScheduledChange, GRANDPA_ENGINE_ID}; -use sp_core::Hasher as HasherT; -use sp_runtime::generic::SignedBlock; -use sp_runtime::traits::{BlakeTwo256, Hash, Header}; -use sp_runtime::{generic, Digest, DigestItem, DispatchError}; - -type TestHeader = generic::Header; - -struct TestFeedChain; - -impl Chain for TestFeedChain { - type BlockNumber = u32; - type Hash = ::Out; - type Header = generic::Header; - type Hasher = BlakeTwo256; -} - -#[test] -fn valid_justification_accepted() { - let authorities = vec![(ALICE, 1), (BOB, 1), (CHARLIE, 1), (DAVE, 1)]; - let params = JustificationGeneratorParams { - header: test_header(1), - round: TEST_GRANDPA_ROUND, - set_id: TEST_GRANDPA_SET_ID, - authorities: authorities.clone(), - ancestors: 7, - forks: 3, - }; - - let justification = make_justification_for_header::(params.clone()); - assert_eq!( - verify_justification::( - header_id::(1), - TEST_GRANDPA_SET_ID, - &voter_set(), - &justification, - ), - Ok(()), - ); - - assert_eq!(justification.commit.precommits.len(), authorities.len()); - assert_eq!( - justification.votes_ancestries.len(), - params.ancestors as usize - ); -} - -#[test] -fn valid_justification_accepted_with_single_fork() { - let params = JustificationGeneratorParams { - header: test_header(1), - round: TEST_GRANDPA_ROUND, - set_id: TEST_GRANDPA_SET_ID, - authorities: vec![(ALICE, 1), (BOB, 1), (CHARLIE, 1), (DAVE, 1), (EVE, 1)], - ancestors: 5, - forks: 1, - }; - - assert_eq!( - verify_justification::( - header_id::(1), - TEST_GRANDPA_SET_ID, - &voter_set(), - &make_justification_for_header::(params) - ), - Ok(()), - ); -} - -#[test] -fn valid_justification_accepted_with_arbitrary_number_of_authorities() { - use finality_grandpa::voter_set::VoterSet; - use sp_consensus_grandpa::AuthorityId; - - let n = 15; - let authorities = accounts(n).iter().map(|k| (*k, 1)).collect::>(); - - let params = JustificationGeneratorParams { - header: test_header(1), - round: TEST_GRANDPA_ROUND, - set_id: TEST_GRANDPA_SET_ID, - authorities: authorities.clone(), - ancestors: n.into(), - forks: n.into(), - }; - - let authorities = authorities - .iter() - .map(|(id, w)| (AuthorityId::from(*id), *w)) - .collect::>(); - let voter_set = VoterSet::new(authorities).unwrap(); - - assert_eq!( - verify_justification::( - header_id::(1), - TEST_GRANDPA_SET_ID, - &voter_set, - &make_justification_for_header::(params) - ), - Ok(()), - ); -} - -#[test] -fn justification_with_invalid_target_rejected() { - assert_eq!( - verify_justification::( - header_id::(2), - TEST_GRANDPA_SET_ID, - &voter_set(), - &make_default_justification::(&test_header(1)), - ), - Err(Error::InvalidJustificationTarget), - ); -} - -#[test] -fn justification_with_invalid_commit_rejected() { - let mut justification = make_default_justification::(&test_header(1)); - justification.commit.precommits.clear(); - - assert_eq!( - verify_justification::( - header_id::(1), - TEST_GRANDPA_SET_ID, - &voter_set(), - &justification, - ), - Err(Error::ExtraHeadersInVotesAncestries), - ); -} - -#[test] -fn justification_with_invalid_authority_signature_rejected() { - let mut justification = make_default_justification::(&test_header(1)); - justification.commit.precommits[0].signature = - sp_core::crypto::UncheckedFrom::unchecked_from([1u8; 64]); - - assert_eq!( - verify_justification::( - header_id::(1), - TEST_GRANDPA_SET_ID, - &voter_set(), - &justification, - ), - Err(Error::InvalidAuthoritySignature), - ); -} - -#[test] -fn justification_with_invalid_precommit_ancestry() { - let mut justification = make_default_justification::(&test_header(1)); - justification.votes_ancestries.push(test_header(10)); - - assert_eq!( - verify_justification::( - header_id::(1), - TEST_GRANDPA_SET_ID, - &voter_set(), - &justification, - ), - Err(Error::ExtraHeadersInVotesAncestries), - ); -} - -#[test] -fn justification_is_invalid_if_we_dont_meet_threshold() { - // Need at least three authorities to sign off or else the voter set threshold can't be reached - let authorities = vec![(ALICE, 1), (BOB, 1)]; - - let params = JustificationGeneratorParams { - header: test_header(1), - round: TEST_GRANDPA_ROUND, - set_id: TEST_GRANDPA_SET_ID, - authorities: authorities.clone(), - ancestors: 2 * authorities.len() as u32, - forks: 2, - }; - - assert_eq!( - verify_justification::( - header_id::(1), - TEST_GRANDPA_SET_ID, - &voter_set(), - &make_justification_for_header::(params) - ), - Err(Error::TooLowCumulativeWeight), - ); -} - -fn valid_digests() -> Vec { - vec![DigestItem::Consensus( - GRANDPA_ENGINE_ID, - ConsensusLog::ScheduledChange::(ScheduledChange { - next_authorities: authority_list(), - delay: 0, - }) - .encode(), - )] -} - -fn init_with_origin(chain_id: ChainId, number: u32) -> Result { - let mut best_finalized = test_header::(number); - valid_digests() - .into_iter() - .for_each(|digest| best_finalized.digest_mut().push(digest)); - let init_data = InitializationData { - best_known_finalized_header: best_finalized.encode(), - set_id: 1, - }; - - initialize::(chain_id, init_data.encode().as_slice())?; - // import block - assert_ok!(submit_finality_proof( - chain_id, - best_finalized.clone(), - Some(make_default_justification(&best_finalized)) - )); - Ok(best_finalized) -} - -fn valid_extrinsics() -> Vec { - vec![(0..255).collect()] -} - -fn invalid_extrinsics() -> Vec { - vec![(128..255).collect()] -} - -fn valid_extrinsics_root() -> H::Hash { - H::Hashing::ordered_trie_root( - valid_extrinsics().iter().map(Encode::encode).collect(), - sp_runtime::StateVersion::V0, - ) -} - -fn submit_valid_finality_proof(chain_id: ChainId, header: u8) -> Result { - let header = test_header::(header.into()); - let justification = make_default_justification(&header); - submit_finality_proof(chain_id, header.clone(), Some(justification))?; - Ok(header) -} - -fn submit_finality_proof( - chain_id: ChainId, - header: TestHeader, - maybe_justification: Option>, -) -> DispatchResult { - let justification = - maybe_justification.map(|justification| (GRANDPA_ENGINE_ID, justification.encode()).into()); - let block = SignedBlock { - block: generic::Block:: { - header, - extrinsics: valid_extrinsics(), - }, - justifications: justification, - }; - - validate_finalized_block::(chain_id, block.encode().as_slice())?; - Ok(()) -} - -#[test] -fn test_init_storage_entries_are_correctly_initialized_with_genesis() { - run_test(|| { - let chain_id: ChainId = 1; - let validation_header = init_with_origin(chain_id, 0).unwrap(); - assert_eq!( - CurrentAuthoritySet::::get(chain_id).authorities, - authority_list() - ); - assert_eq!( - >::get(chain_id), - (0u32.encode(), validation_header.encode()) - ); - assert_eq!( - >::get(chain_id), - (0u32.encode(), validation_header.hash().encode()) - ); - - // since the block 0 is imported already - // the oldest know parent will be 00000.... with so that block 0 is not imported again - assert_eq!( - >::get(chain_id), - (0u32.encode(), [0u8; 32].encode()) - ); - }) -} - -#[test] -fn test_init_storage_entries_are_correctly_initialized() { - run_test(|| { - let chain_id: ChainId = 1; - let validation_header = init_with_origin(chain_id, 10).unwrap(); - assert_eq!( - CurrentAuthoritySet::::get(chain_id).authorities, - authority_list() - ); - assert_eq!( - >::get(chain_id), - (10u32.encode(), validation_header.encode()) - ); - assert_eq!( - >::get(chain_id), - (9u32.encode(), test_header::(9).hash().encode()) - ); - assert_eq!( - >::get(chain_id), - (10u32.encode(), validation_header.hash().encode()) - ); - }) -} - -#[test] -fn successfully_imports_header_in_forward_direction() { - run_test(|| { - let chain_id: ChainId = 1; - let validation_header = init_with_origin(chain_id, 0).expect("must not fail to init chain"); - - // cannot import block 0 twice - assert_err!( - submit_finality_proof( - chain_id, - validation_header.clone(), - Some(make_default_justification(&validation_header)) - ), - ErrorP::::InvalidBlock - ); - - let mut parent_header = validation_header; - for tip in 1..10 { - let mut header = test_header::(tip); - header.set_parent_hash(parent_header.hash()); - assert_ok!(submit_finality_proof( - chain_id, - header.clone(), - Some(make_default_justification(&header)) - )); - assert_eq!( - >::get(chain_id), - (tip.encode(), header.hash().encode()) - ); - parent_header = header; - } - }) -} - -#[test] -fn successfully_imports_parent_headers_in_reverse_and_forward() { - run_test(|| { - let chain_id: ChainId = 1; - let validation_header = init_with_origin(chain_id, 5).expect("must not fail to init chain"); - - for parent in (1..=4).rev() { - assert_ok!(submit_valid_finality_proof(chain_id, parent)); - assert_eq!( - >::get(chain_id), - ( - ((parent - 1) as u32).encode(), - test_header::((parent - 1) as u32) - .hash() - .encode() - ) - ); - assert_eq!( - >::get(chain_id), - (5u32.encode(), validation_header.hash().encode()) - ); - } - - // import block 0 - assert_ok!(submit_valid_finality_proof(chain_id, 0)); - - // cannot import block 0 twice - assert_err!( - submit_valid_finality_proof(chain_id, 0), - ErrorP::::InvalidBlock - ); - - let mut parent_header = validation_header; - for tip in 6..10 { - let mut header = test_header::(tip); - header.set_parent_hash(parent_header.hash()); - assert_ok!(submit_finality_proof( - chain_id, - header.clone(), - Some(make_default_justification(&header)) - )); - assert_eq!( - >::get(chain_id), - (tip.encode(), header.hash().encode()) - ); - parent_header = header; - } - }) -} - -#[test] -fn rejects_justification_that_skips_authority_set_transition() { - run_test(|| { - let chain_id: ChainId = 1; - let validation_header = init_with_origin(chain_id, 0).unwrap(); - - let mut header = test_header::(1); - header.set_parent_hash(validation_header.hash()); - - let params = JustificationGeneratorParams:: { - set_id: 2, - ..Default::default() - }; - let justification = make_justification_for_header(params); - - assert_err!( - submit_finality_proof(chain_id, header, Some(justification)), - >::InvalidJustification - ); - }) -} - -#[test] -fn does_not_import_header_with_invalid_finality_proof() { - run_test(|| { - let chain_id: ChainId = 1; - let validation_header = init_with_origin(chain_id, 0).unwrap(); - - let mut header = test_header::(1); - header.set_parent_hash(validation_header.hash()); - let mut justification = make_default_justification(&header); - justification.round = 42; - - assert_err!( - submit_finality_proof(chain_id, header, Some(justification)), - >::InvalidJustification - ); - }) -} - -#[test] -fn does_not_import_header_with_invalid_extrinsics() { - run_test(|| { - let chain_id: ChainId = 1; - let validation_header = init_with_origin(chain_id, 0).unwrap(); - - let mut header = test_header::(1); - header.set_parent_hash(validation_header.hash()); - let block = SignedBlock { - block: generic::Block:: { - header: header.clone(), - extrinsics: invalid_extrinsics(), - }, - justifications: Some( - ( - GRANDPA_ENGINE_ID, - make_default_justification(&header).encode(), - ) - .into(), - ), - }; - - assert_err!( - validate_finalized_block::( - chain_id, - block.encode().as_slice(), - ), - >::InvalidBlock - ); - }) -} - -#[test] -fn disallows_invalid_authority_set() { - run_test(|| { - let chain_id: ChainId = 1; - let invalid_authority_list = vec![(ALICE.into(), u64::MAX), (BOB.into(), u64::MAX)]; - let mut genesis = test_header::(0); - let mut digest: Digest = Default::default(); - digest.push(DigestItem::Consensus( - GRANDPA_ENGINE_ID, - ConsensusLog::ScheduledChange::(ScheduledChange { - next_authorities: invalid_authority_list, - delay: 0, - }) - .encode(), - )); - genesis.digest = digest; - let init_data = InitializationData { - best_known_finalized_header: genesis.encode(), - set_id: 1, - }; - - assert_ok!(initialize::( - chain_id, - init_data.encode().as_slice() - )); - - let mut header = test_header::(1); - header.set_parent_hash(genesis.hash()); - let justification = make_default_justification(&header); - - assert_err!( - submit_finality_proof(chain_id, header, Some(justification)), - >::InvalidAuthoritySet - ); - }) -} - -fn change_log(delay: u32) -> Digest { - let consensus_log = ConsensusLog::::ScheduledChange(ScheduledChange { - next_authorities: vec![(ALICE.into(), 1), (BOB.into(), 1)], - delay, - }); - - Digest { - logs: vec![DigestItem::Consensus( - GRANDPA_ENGINE_ID, - consensus_log.encode(), - )], - } -} - -#[test] -fn importing_header_enacts_new_authority_set() { - run_test(|| { - let chain_id: ChainId = 1; - let validation_header = init_with_origin(chain_id, 0).unwrap(); - - let next_set_id = 2; - let next_authorities = vec![(ALICE.into(), 1), (BOB.into(), 1)]; - - // Need to update the header digest to indicate that our header signals an authority set - // change. The change will be enacted when we import our header. - let mut header = test_header::(1); - header.set_parent_hash(validation_header.hash()); - header.digest = change_log(0); - - // Create a valid justification for the header - let justification = make_default_justification(&header); - - // Let's import our test header - assert_ok!(submit_finality_proof( - chain_id, - header.clone(), - Some(justification) - )); - - // Make sure that our header is the best finalized - assert_eq!( - >::get(chain_id), - (1u32.encode(), header.hash().encode()) - ); - - // Make sure that the authority set actually changed upon importing our header - assert_eq!( - >::get(chain_id), - AuthoritySet { - authorities: next_authorities, - set_id: next_set_id - }, - ); - }) -} - -#[test] -fn importing_header_rejects_header_with_scheduled_change_delay() { - run_test(|| { - let chain_id: ChainId = 1; - let validation_header = init_with_origin(chain_id, 0).unwrap(); - - // Need to update the header digest to indicate that our header signals an authority set - // change. However, the change doesn't happen until the next block. - let mut header = test_header::(1); - header.set_parent_hash(validation_header.hash()); - header.digest = change_log(1); - - // Create a valid justification for the header - let justification = make_default_justification(&header); - - // Should not be allowed to import this header - assert_err!( - submit_finality_proof(chain_id, header, Some(justification)), - >::UnsupportedScheduledChange - ); - }) -} - -fn forced_change_log(delay: u32) -> Digest { - let consensus_log = ConsensusLog::::ForcedChange( - delay, - ScheduledChange { - next_authorities: vec![(ALICE.into(), 1), (BOB.into(), 1)], - delay, - }, - ); - - Digest { - logs: vec![DigestItem::Consensus( - GRANDPA_ENGINE_ID, - consensus_log.encode(), - )], - } -} - -#[test] -fn importing_header_rejects_header_with_forced_changes() { - run_test(|| { - let chain_id: ChainId = 1; - let validation_header = init_with_origin(chain_id, 0).unwrap(); - - // Need to update the header digest to indicate that it signals a forced authority set - // change. - let mut header = test_header::(1); - header.set_parent_hash(validation_header.hash()); - header.digest = forced_change_log(0); - - // Create a valid justification for the header - let justification = make_default_justification(&header); - - // Should not be allowed to import this header - assert_err!( - submit_finality_proof(chain_id, header, Some(justification)), - >::UnsupportedScheduledChange - ); - }) -} diff --git a/crates/pallet-object-store/Cargo.toml b/crates/pallet-object-store/Cargo.toml deleted file mode 100644 index 6d9476f8eb..0000000000 --- a/crates/pallet-object-store/Cargo.toml +++ /dev/null @@ -1,40 +0,0 @@ -[package] -name = "pallet-object-store" -version = "0.1.0" -authors = ["Nazar Mokrynskyi "] -edition = "2021" -license = "Apache-2.0" -homepage = "https://subspace.network" -repository = "https://github.com/autonomys/subspace" -description = "Subspace node pallet for simple objects storage" -readme = "README.md" - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.6.12", default-features = false, features = ["derive"] } -frame-support = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "5626154d0781ac9a6ffd5a6207ed237f425ae631" } -frame-system = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "5626154d0781ac9a6ffd5a6207ed237f425ae631" } -hex = { version = "0.4.3", default-features = false, features = ["alloc"] } -log = { version = "0.4.22", default-features = false } -scale-info = { version = "2.11.2", default-features = false, features = ["derive"] } -subspace-core-primitives = { version = "0.1.0", default-features = false, path = "../subspace-core-primitives" } - -[dev-dependencies] -sp-core = { git = "https://github.com/subspace/polkadot-sdk", rev = "5626154d0781ac9a6ffd5a6207ed237f425ae631" } -sp-io = { git = "https://github.com/subspace/polkadot-sdk", rev = "5626154d0781ac9a6ffd5a6207ed237f425ae631" } -sp-runtime = { git = "https://github.com/subspace/polkadot-sdk", rev = "5626154d0781ac9a6ffd5a6207ed237f425ae631" } - -[features] -default = ["std"] -std = [ - "codec/std", - "frame-support/std", - "frame-system/std", - "hex/std", - "log/std", - "scale-info/std", - "subspace-core-primitives/std", -] -try-runtime = ["frame-support/try-runtime"] diff --git a/crates/pallet-object-store/README.md b/crates/pallet-object-store/README.md deleted file mode 100644 index 0304d824de..0000000000 --- a/crates/pallet-object-store/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Pallet Object Store - -Subspace node pallet for simple objects storage - -License: Apache-2.0 diff --git a/crates/pallet-object-store/src/lib.rs b/crates/pallet-object-store/src/lib.rs deleted file mode 100644 index d6eec38eab..0000000000 --- a/crates/pallet-object-store/src/lib.rs +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright (C) 2021 Subspace Labs, Inc. -// 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. - -//! Pallet object store, used for simple object storage on the network. - -#![cfg_attr(not(feature = "std"), no_std)] -#![forbid(unsafe_code)] -#![warn(rust_2018_idioms, missing_debug_implementations)] - -#[cfg(all(feature = "std", test))] -mod mock; -#[cfg(all(feature = "std", test))] -mod tests; - -pub use pallet::*; -use subspace_core_primitives::{crypto, Blake3Hash}; - -#[frame_support::pallet] -mod pallet { - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::*; - use log::debug; - use subspace_core_primitives::{crypto, Blake3Hash}; - - #[pallet::config] - pub trait Config: frame_system::Config { - /// `pallet-object-store` events - type RuntimeEvent: From> + IsType<::RuntimeEvent>; - } - - /// Pallet object-store, used for storing arbitrary user-provided data combined into object-store. - #[pallet::pallet] - pub struct Pallet(_); - - /// `pallet-object-store` events - #[pallet::event] - #[pallet::generate_deposit(pub (super) fn deposit_event)] - pub enum Event { - /// New object was added. - ObjectSubmitted { - who: T::AccountId, - object_id: Blake3Hash, - object_size: u32, - }, - } - - #[pallet::call] - impl Pallet { - // TODO: add proper weights - // TODO: For now we don't have fees, but we will have them in the future - /// Put a new object into a feed - #[pallet::call_index(0)] - #[pallet::weight((10_000, Pays::No))] - pub fn put(origin: OriginFor, object: Vec) -> DispatchResult { - let who = ensure_signed(origin)?; - - let object_size = object.len() as u32; - - let object_id = crypto::blake3_hash(&object); - - debug!( - target: "runtime:object-store", - "New object {}, size {} bytes", - hex::encode(object_id), - object_size - ); - - Self::deposit_event(Event::ObjectSubmitted { - who, - object_id, - object_size, - }); - - Ok(()) - } - } -} - -/// Mapping to the object offset and size within an extrinsic -#[derive(Debug)] -pub struct CallObject { - /// Object hash - pub hash: Blake3Hash, - /// Offset of object in the encoded call. - pub offset: u32, -} - -impl Call { - /// Extract object location if an extrinsic corresponds to `put` call - pub fn extract_call_object(&self) -> Option { - match self { - Self::put { object } => { - // `1` corresponds to `Call::put {}` enum variant encoding. - Some(CallObject { - hash: crypto::blake3_hash(object), - offset: 1, - }) - } - _ => None, - } - } -} diff --git a/crates/pallet-object-store/src/mock.rs b/crates/pallet-object-store/src/mock.rs deleted file mode 100644 index a55ab71851..0000000000 --- a/crates/pallet-object-store/src/mock.rs +++ /dev/null @@ -1,32 +0,0 @@ -use frame_support::derive_impl; -use sp_runtime::BuildStorage; - -type Block = frame_system::mocking::MockBlock; - -frame_support::construct_runtime!( - pub struct Test { - System: frame_system, - ObjectStore: crate, - } -); - -#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] -impl frame_system::Config for Test { - type Block = Block; -} - -impl crate::Config for Test { - type RuntimeEvent = RuntimeEvent; -} - -pub fn new_test_ext() -> sp_io::TestExternalities { - let t = frame_system::GenesisConfig::::default() - .build_storage() - .unwrap(); - - let mut t: sp_io::TestExternalities = t.into(); - - t.execute_with(|| System::set_block_number(1)); - - t -} diff --git a/crates/pallet-object-store/src/tests.rs b/crates/pallet-object-store/src/tests.rs deleted file mode 100644 index 9a1a7c7e65..0000000000 --- a/crates/pallet-object-store/src/tests.rs +++ /dev/null @@ -1,24 +0,0 @@ -use crate::mock::{new_test_ext, ObjectStore, RuntimeEvent, RuntimeOrigin, System, Test}; -use frame_support::assert_ok; -use subspace_core_primitives::crypto; - -const ACCOUNT_ID: u64 = 100; - -#[test] -fn can_do_put() { - new_test_ext().execute_with(|| { - let object = vec![1, 2, 3, 4, 5]; - let object_id = crypto::blake3_hash(&object); - let object_size = object.len() as u32; - - assert_ok!(ObjectStore::put(RuntimeOrigin::signed(ACCOUNT_ID), object)); - - System::assert_last_event(RuntimeEvent::ObjectStore( - crate::Event::::ObjectSubmitted { - who: ACCOUNT_ID, - object_id, - object_size, - }, - )); - }); -}