diff --git a/Cargo.lock b/Cargo.lock index 70a50a6f3e23b..4c642918ae2c7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5041,6 +5041,7 @@ dependencies = [ "pallet-randomness-collective-flip", "pallet-recovery", "pallet-referenda", + "pallet-remark", "pallet-scheduler", "pallet-session", "pallet-session-benchmarking", @@ -6314,6 +6315,22 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-remark" +version = "4.0.0-dev" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-scheduler" version = "4.0.0-dev" diff --git a/Cargo.toml b/Cargo.toml index 13657dd1234a5..8cea30a37eda6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -111,6 +111,7 @@ members = [ "frame/randomness-collective-flip", "frame/recovery", "frame/referenda", + "frame/remark", "frame/scheduler", "frame/scored-pool", "frame/session", diff --git a/bin/node/runtime/Cargo.toml b/bin/node/runtime/Cargo.toml index 686508b47dba1..15d0d974db9c5 100644 --- a/bin/node/runtime/Cargo.toml +++ b/bin/node/runtime/Cargo.toml @@ -82,6 +82,7 @@ pallet-proxy = { version = "4.0.0-dev", default-features = false, path = "../../ pallet-randomness-collective-flip = { version = "4.0.0-dev", default-features = false, path = "../../../frame/randomness-collective-flip" } pallet-recovery = { version = "4.0.0-dev", default-features = false, path = "../../../frame/recovery" } pallet-referenda = { version = "4.0.0-dev", default-features = false, path = "../../../frame/referenda" } +pallet-remark = { version = "4.0.0-dev", default-features = false, path = "../../../frame/remark" } pallet-session = { version = "4.0.0-dev", features = [ "historical" ], path = "../../../frame/session", default-features = false } pallet-session-benchmarking = { version = "4.0.0-dev", path = "../../../frame/session/benchmarking", default-features = false, optional = true } pallet-staking = { version = "4.0.0-dev", default-features = false, path = "../../../frame/staking" } @@ -172,6 +173,7 @@ std = [ "sp-version/std", "pallet-society/std", "pallet-referenda/std", + "pallet-remark/std", "pallet-recovery/std", "pallet-uniques/std", "pallet-vesting/std", @@ -211,6 +213,7 @@ runtime-benchmarks = [ "pallet-proxy/runtime-benchmarks", "pallet-scheduler/runtime-benchmarks", "pallet-referenda/runtime-benchmarks", + "pallet-remark/runtime-benchmarks", "pallet-session-benchmarking", "pallet-society/runtime-benchmarks", "pallet-staking/runtime-benchmarks", diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 797e0c079028e..d732efe7709dc 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -773,6 +773,11 @@ impl pallet_referenda::Config for Runtime { type Tracks = TracksInfo; } +impl pallet_remark::Config for Runtime { + type WeightInfo = pallet_remark::weights::SubstrateWeight; + type Event = Event; +} + parameter_types! { pub const LaunchPeriod: BlockNumber = 28 * 24 * 60 * MINUTES; pub const VotingPeriod: BlockNumber = 28 * 24 * 60 * MINUTES; @@ -1447,6 +1452,7 @@ construct_runtime!( StateTrieMigration: pallet_state_trie_migration, ChildBounties: pallet_child_bounties, Referenda: pallet_referenda, + Remark: pallet_remark, ConvictionVoting: pallet_conviction_voting, Whitelist: pallet_whitelist, } @@ -1536,6 +1542,7 @@ mod benches { [pallet_preimage, Preimage] [pallet_proxy, Proxy] [pallet_referenda, Referenda] + [pallet_remark, Remark] [pallet_scheduler, Scheduler] [pallet_session, SessionBench::] [pallet_staking, Staking] diff --git a/frame/remark/Cargo.toml b/frame/remark/Cargo.toml new file mode 100644 index 0000000000000..82c76d679cc10 --- /dev/null +++ b/frame/remark/Cargo.toml @@ -0,0 +1,42 @@ +[package] +name = "pallet-remark" +version = "4.0.0-dev" +authors = ["Parity Technologies "] +edition = "2021" +license = "Apache-2.0" +homepage = "https://substrate.io" +repository = "https://github.com/paritytech/substrate/" +description = "Remark storage pallet" +readme = "README.md" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +serde = { version = "1.0.136", optional = true } +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +scale-info = { version = "2.0.1", default-features = false, features = ["derive"] } +frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } +frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } +sp-runtime = { version = "6.0.0", default-features = false, path = "../../primitives/runtime" } +sp-std = { version = "4.0.0", default-features = false, path = "../../primitives/std" } +sp-io = { version = "6.0.0", default-features = false, path = "../../primitives/io" } +sp-core = { version = "6.0.0", default-features = false, path = "../../primitives/core" } +frame-benchmarking = { version = "4.0.0-dev", default-features = false, path = "../benchmarking", optional = true } + +[dev-dependencies] +sp-core = { version = "6.0.0", path = "../../primitives/core", default-features = false } + +[features] +default = ["std"] +runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"] +std = [ + "serde", + "codec/std", + "scale-info/std", + "sp-runtime/std", + "frame-support/std", + "frame-system/std", + "sp-io/std", + "sp-std/std", +] diff --git a/frame/remark/README.md b/frame/remark/README.md new file mode 100644 index 0000000000000..f2341d6a0eaec --- /dev/null +++ b/frame/remark/README.md @@ -0,0 +1,6 @@ +# Remark Storage Pallet + +Allows storing arbitrary data off chain. + + +License: Apache-2.0 diff --git a/frame/remark/src/benchmarking.rs b/frame/remark/src/benchmarking.rs new file mode 100644 index 0000000000000..d30a8aa5df07d --- /dev/null +++ b/frame/remark/src/benchmarking.rs @@ -0,0 +1,47 @@ +// This file is part of Substrate. + +// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Benchmarks for remarks pallet + +#![cfg(feature = "runtime-benchmarks")] + +use super::*; +use frame_benchmarking::{benchmarks, whitelisted_caller}; +use frame_system::{EventRecord, Pallet as System, RawOrigin}; +use sp_std::*; + +#[cfg(test)] +use crate::Pallet as Remark; + +fn assert_last_event(generic_event: ::Event) { + let events = System::::events(); + let system_event: ::Event = generic_event.into(); + let EventRecord { event, .. } = &events[events.len() - 1]; + assert_eq!(event, &system_event); +} + +benchmarks! { + store { + let l in 1 .. 1024*1024; + let caller: T::AccountId = whitelisted_caller(); + }: _(RawOrigin::Signed(caller.clone()), vec![0u8; l as usize]) + verify { + assert_last_event::(Event::Stored { sender: caller, content_hash: sp_io::hashing::blake2_256(&vec![0u8; l as usize]).into() }.into()); + } + + impl_benchmark_test_suite!(Remark, crate::mock::new_test_ext(), crate::mock::Test); +} diff --git a/frame/remark/src/lib.rs b/frame/remark/src/lib.rs new file mode 100644 index 0000000000000..6803b2a60085f --- /dev/null +++ b/frame/remark/src/lib.rs @@ -0,0 +1,85 @@ +// This file is part of Substrate. + +// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Remark storage pallet. Indexes remarks and stores them off chain. + +// Ensure we're `no_std` when compiling for Wasm. +#![cfg_attr(not(feature = "std"), no_std)] + +mod benchmarking; +pub mod weights; + +#[cfg(test)] +mod mock; +#[cfg(test)] +mod tests; + +use sp_std::prelude::*; + +// Re-export pallet items so that they can be accessed from the crate namespace. +pub use pallet::*; +pub use weights::WeightInfo; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + /// The overarching event type. + type Event: From> + IsType<::Event>; + /// Weight information for extrinsics in this pallet. + type WeightInfo: WeightInfo; + } + + #[pallet::error] + pub enum Error { + /// Attempting to store empty data. + Empty, + /// Attempted to call `store` outside of block execution. + BadContext, + } + + #[pallet::pallet] + #[pallet::without_storage_info] + pub struct Pallet(_); + + #[pallet::call] + impl Pallet { + /// Index and store data off chain. + #[pallet::weight(T::WeightInfo::store(remark.len() as u32))] + pub fn store(origin: OriginFor, remark: Vec) -> DispatchResultWithPostInfo { + ensure!(!remark.is_empty(), Error::::Empty); + let sender = ensure_signed(origin)?; + let content_hash = sp_io::hashing::blake2_256(&remark); + let extrinsic_index = >::extrinsic_index() + .ok_or_else(|| Error::::BadContext)?; + sp_io::transaction_index::index(extrinsic_index, remark.len() as u32, content_hash); + Self::deposit_event(Event::Stored { sender, content_hash: content_hash.into() }); + Ok(().into()) + } + } + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// Stored data off chain. + Stored { sender: T::AccountId, content_hash: sp_core::H256 }, + } +} diff --git a/frame/remark/src/mock.rs b/frame/remark/src/mock.rs new file mode 100644 index 0000000000000..67a0399e9c386 --- /dev/null +++ b/frame/remark/src/mock.rs @@ -0,0 +1,79 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Test environment for remarks pallet. + +use crate as pallet_remark; +use frame_support::traits::{ConstU16, ConstU32, ConstU64}; +use sp_core::H256; +use sp_runtime::{ + testing::Header, + traits::{BlakeTwo256, IdentityLookup}, + BuildStorage, +}; + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +pub type Block = frame_system::mocking::MockBlock; + +// Configure a mock runtime to test the pallet. +frame_support::construct_runtime!( + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Remark: pallet_remark::{ Pallet, Call, Event }, + } +); + +impl frame_system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = (); + type BlockLength = (); + type Origin = Origin; + type Call = Call; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type Event = Event; + type BlockHashCount = ConstU64<250>; + type DbWeight = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = ConstU16<42>; + type OnSetCode = (); + type MaxConsumers = ConstU32<16>; +} + +impl pallet_remark::Config for Test { + type Event = Event; + type WeightInfo = (); +} + +pub fn new_test_ext() -> sp_io::TestExternalities { + let t = GenesisConfig { system: Default::default() }.build_storage().unwrap(); + t.into() +} diff --git a/frame/remark/src/tests.rs b/frame/remark/src/tests.rs new file mode 100644 index 0000000000000..60a376c5afca5 --- /dev/null +++ b/frame/remark/src/tests.rs @@ -0,0 +1,55 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Tests for remarks pallet. + +use super::{Error, Event, Pallet as Remark}; +use crate::mock::*; +use frame_support::{assert_noop, assert_ok}; +use frame_system::RawOrigin; + +#[test] +fn generates_event() { + new_test_ext().execute_with(|| { + let caller = 1; + let data = vec![0u8; 100]; + System::set_block_number(System::block_number() + 1); //otherwise event won't be registered. + assert_ok!(Remark::::store(RawOrigin::Signed(caller.clone()).into(), data.clone(),)); + let events = System::events(); + let system_event: ::Event = Event::Stored { + content_hash: sp_io::hashing::blake2_256(&data).into(), + sender: caller, + } + .into(); + let frame_system::EventRecord { event, .. } = &events[events.len() - 1]; + assert_eq!(event, &system_event); + }); +} + +#[test] +fn does_not_store_empty() { + new_test_ext().execute_with(|| { + let caller = 1; + let data = vec![]; + System::set_block_number(System::block_number() + 1); //otherwise event won't be registered. + assert_noop!( + Remark::::store(RawOrigin::Signed(caller.clone()).into(), data.clone(),), + Error::::Empty + ); + assert!(System::events().is_empty()); + }); +} diff --git a/frame/remark/src/weights.rs b/frame/remark/src/weights.rs new file mode 100644 index 0000000000000..50b0fc3ebfc19 --- /dev/null +++ b/frame/remark/src/weights.rs @@ -0,0 +1,71 @@ +// This file is part of Substrate. + +// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Autogenerated weights for pallet_remark +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2022-03-11, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/substrate +// benchmark +// --chain=dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_remark +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --output=./frame/remark/src/weights.rs +// --template=./.maintain/frame-weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for pallet_remark. +pub trait WeightInfo { + fn store(l: u32, ) -> Weight; +} + +/// Weights for pallet_remark using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + // Storage: unknown [0x3a65787472696e7369635f696e646578] (r:1 w:0) + fn store(l: u32, ) -> Weight { + (18_328_000 as Weight) + // Standard Error: 0 + .saturating_add((1_000 as Weight).saturating_mul(l as Weight)) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + // Storage: unknown [0x3a65787472696e7369635f696e646578] (r:1 w:0) + fn store(l: u32, ) -> Weight { + (18_328_000 as Weight) + // Standard Error: 0 + .saturating_add((1_000 as Weight).saturating_mul(l as Weight)) + .saturating_add(RocksDbWeight::get().reads(1 as Weight)) + } +} diff --git a/frame/transaction-storage/src/lib.rs b/frame/transaction-storage/src/lib.rs index d95a60b495121..e9aa786766dac 100644 --- a/frame/transaction-storage/src/lib.rs +++ b/frame/transaction-storage/src/lib.rs @@ -170,7 +170,7 @@ pub mod pallet { #[pallet::call] impl Pallet { - /// Index and store data on chain. Minimum data size is 1 bytes, maximum is + /// Index and store data off chain. Minimum data size is 1 bytes, maximum is /// `MaxTransactionSize`. Data will be removed after `STORAGE_PERIOD` blocks, unless `renew` /// is called. # /// - n*log(n) of data size, as all data is pushed to an in-memory trie.