Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Referendum proposal's metadata #12568

Merged
merged 34 commits into from
Feb 8, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
f8d2e88
referenda metadata
muharem Oct 26, 2022
cba7282
todo comment
muharem Oct 26, 2022
40d4bc3
remove TODO, update rustdocs
muharem Oct 27, 2022
9a86866
referenda clear_metadata origin signed or root
muharem Oct 27, 2022
05d7b4d
referenda metadata unit tests
muharem Oct 27, 2022
746afcf
drop schema type for referenda metadata
muharem Oct 28, 2022
564f483
remove metadata type
muharem Oct 28, 2022
bd9a868
referenda metadata benches
muharem Oct 28, 2022
9025ace
note different preimages
muharem Oct 28, 2022
d1cff03
metadata for democracy pallet
muharem Oct 28, 2022
afa1b24
metadata democracy pallet tests and benches
muharem Oct 30, 2022
7ffe4c8
fix cargo clippy
muharem Oct 30, 2022
418262d
update docs
muharem Oct 31, 2022
5efc3fd
Merge branch 'master' of https://github.com/paritytech/substrate into…
Oct 31, 2022
ef4e0cb
".git/.scripts/bench-bot.sh" pallet dev pallet_democracy
Oct 31, 2022
4211b34
".git/.scripts/bench-bot.sh" pallet dev pallet_referenda
Oct 31, 2022
83ee3f4
Update the doc frame/democracy/src/lib.rs
muharem Nov 14, 2022
32ec398
Update the doc frame/democracy/src/lib.rs
muharem Nov 14, 2022
49c9453
reference instead clone for take
muharem Nov 14, 2022
0abe070
error rename BadMetadata to PreimageNotExist
muharem Nov 14, 2022
0efce50
clear metadata within internal_cancel_referendum fn
muharem Nov 14, 2022
7f89c1a
remove redundant clone
muharem Nov 14, 2022
c22876f
collapse metadata api into one set_metadata method
muharem Nov 17, 2022
fe9636c
fmt
muharem Nov 17, 2022
2c5c46e
Merge remote-tracking branch 'origin/master' into muharem-referenda-m…
muharem Jan 23, 2023
e6a1edb
review fixes
muharem Jan 23, 2023
48ebf6d
not request preimage on set_metadata
muharem Jan 23, 2023
850a931
rename events and update docs
muharem Jan 27, 2023
744ed11
Merge remote-tracking branch 'origin/master' into muharem-referenda-m…
muharem Jan 27, 2023
04dede2
".git/.scripts/commands/bench/bench.sh" pallet dev pallet_democracy
Jan 27, 2023
d338071
".git/.scripts/commands/bench/bench.sh" pallet dev pallet_referenda
Jan 27, 2023
5f26d5b
rename reset_metadata to transfer_metadata
muharem Feb 1, 2023
c886c5b
Merge remote-tracking branch 'origin/master' into muharem-referenda-m…
muharem Feb 7, 2023
fe56de2
Merge remote-tracking branch 'origin/master' into muharem-referenda-m…
Feb 8, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -847,6 +847,14 @@ impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
}
pallet_referenda::impl_tracksinfo_get!(TracksInfo, Balance, BlockNumber);

#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, scale_info::TypeInfo, MaxEncodedLen)]
pub enum MetadataSchema {
IpfsJsonV1,
IpfsJsonV2,
BinJsonV1,
BinJsonV2,
}

impl pallet_referenda::Config for Runtime {
type WeightInfo = pallet_referenda::weights::SubstrateWeight<Self>;
type RuntimeCall = RuntimeCall;
Expand All @@ -865,6 +873,7 @@ impl pallet_referenda::Config for Runtime {
type AlarmInterval = AlarmInterval;
type Tracks = TracksInfo;
type Preimages = Preimage;
type MetadataSchema = MetadataSchema;
}

impl pallet_referenda::Config<pallet_referenda::Instance2> for Runtime {
Expand All @@ -885,6 +894,7 @@ impl pallet_referenda::Config<pallet_referenda::Instance2> for Runtime {
type AlarmInterval = AlarmInterval;
type Tracks = TracksInfo;
type Preimages = Preimage;
type MetadataSchema = MetadataSchema;
}

impl pallet_ranked_collective::Config for Runtime {
Expand Down
81 changes: 78 additions & 3 deletions frame/referenda/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,9 @@ pub use self::{
pallet::*,
types::{
BalanceOf, BoundedCallOf, CallOf, Curve, DecidingStatus, DecidingStatusOf, Deposit,
InsertSorted, NegativeImbalanceOf, PalletsOriginOf, ReferendumIndex, ReferendumInfo,
ReferendumInfoOf, ReferendumStatus, ReferendumStatusOf, ScheduleAddressOf, TallyOf,
TrackIdOf, TrackInfo, TrackInfoOf, TracksInfo, VotesOf,
InsertSorted, MetadataOf, NegativeImbalanceOf, PalletsOriginOf, ReferendumIndex,
ReferendumInfo, ReferendumInfoOf, ReferendumStatus, ReferendumStatusOf, ScheduleAddressOf,
TallyOf, TrackIdOf, TrackInfo, TrackInfoOf, TracksInfo, VotesOf,
},
weights::WeightInfo,
};
Expand Down Expand Up @@ -217,6 +217,13 @@ pub mod pallet {

/// The preimage provider.
type Preimages: QueryPreimage + StorePreimage;

/// The schema type of the referendum [`MetadataOf`].
/// e.g. enum of `IpfsJsonV1` (the hash of an off-chain IPFS json file),
/// `BinJsonV2` (on-chain json dump).
/// Consider a garbage collection for [`MetadataFor`] of finished referendums
/// to `unrequest` large preimages.
type MetadataSchema: Clone + Codec + Eq + Debug + TypeInfo + MaxEncodedLen;
}

/// The next free referendum index, aka the number of referenda started so far.
Expand Down Expand Up @@ -246,6 +253,11 @@ pub mod pallet {
pub type DecidingCount<T: Config<I>, I: 'static = ()> =
StorageMap<_, Twox64Concat, TrackIdOf<T, I>, u32, ValueQuery>;

/// The metadata of the referendum.
#[pallet::storage]
pub type MetadataFor<T: Config<I>, I: 'static = ()> =
StorageMap<_, Blake2_128Concat, ReferendumIndex, MetadataOf<T, I>>;

#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config<I>, I: 'static = ()> {
Expand Down Expand Up @@ -342,6 +354,16 @@ pub mod pallet {
/// The final tally of votes in this referendum.
tally: T::Tally,
},
/// Metadata for a referendum has been set.
MetadataSet {
/// Index of the referendum.
index: ReferendumIndex,
},
/// Metadata for a referendum has been cleared.
MetadataCleared {
/// Index of the referendum.
index: ReferendumIndex,
},
}

#[pallet::error]
Expand All @@ -368,6 +390,8 @@ pub mod pallet {
NoPermission,
/// The deposit cannot be refunded since none was made.
NoDeposit,
/// The metadata preimage for a given hash does not exist.
BadMetadata,
}

#[pallet::call]
Expand Down Expand Up @@ -519,6 +543,7 @@ pub mod pallet {
Self::deposit_event(Event::<T, I>::Killed { index, tally: status.tally });
Self::slash_deposit(Some(status.submission_deposit.clone()));
Self::slash_deposit(status.decision_deposit.clone());
Self::do_clear_metadata(index);
let info = ReferendumInfo::Killed(frame_system::Pallet::<T>::block_number());
ReferendumInfoFor::<T, I>::insert(index, info);
Ok(())
Expand Down Expand Up @@ -579,6 +604,46 @@ pub mod pallet {
};
Ok(Some(branch.weight::<T, I>()).into())
}

/// Set the metadata to an ongoing referendum.
///
/// - `origin`: Must be `Signed`, and the creator of the referendum.
/// - `index`: The index of the referendum to add metadata for.
/// - `metadata`: The metadata of the referendum.
// TODO replace the weight function
#[pallet::weight(1)]
pub fn set_metadata(
origin: OriginFor<T>,
index: ReferendumIndex,
metadata: MetadataOf<T, I>,
) -> DispatchResult {
let who = ensure_signed(origin)?;
let status = Self::ensure_ongoing(index)?;
ensure!(status.submission_deposit.who == who, Error::<T, I>::NoPermission);
ensure!(T::Preimages::len(&metadata.hash).is_some(), Error::<T, I>::BadMetadata);
T::Preimages::request(&metadata.hash);
MetadataFor::<T, I>::insert(index, metadata);
Self::deposit_event(Event::<T, I>::MetadataSet { index });
Ok(())
}

/// Clear the referendum metadata.
///
/// - `origin`: Must be `Signed` or `Root`. If the referendum is ongoing, it must also be
/// the creator of the referendum.
/// - `index`: The index of the ongoing referendum to clear metadata for.
// TODO replace the weight function
#[pallet::weight(1)]
pub fn clear_metadata(origin: OriginFor<T>, index: ReferendumIndex) -> DispatchResult {
let maybe_who = ensure_signed_or_root(origin)?;
if let Some(who) = maybe_who {
muharem marked this conversation as resolved.
Show resolved Hide resolved
if let Some(status) = Self::ensure_ongoing(index).ok() {
ensure!(status.submission_deposit.who == who, Error::<T, I>::NoPermission);
}
}
Self::do_clear_metadata(index);
Ok(())
}
}
}

Expand Down Expand Up @@ -1137,4 +1202,14 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
support_needed.passing(x, tally.support(id)) &&
approval_needed.passing(x, tally.approval(id))
}

/// Clear metadata, if `Some` and unrequest associated preimage.
fn do_clear_metadata(index: ReferendumIndex) {
if let Some(metadata) = MetadataFor::<T, I>::take(index) {
if T::Preimages::is_requested(&metadata.hash) {
T::Preimages::unrequest(&metadata.hash);
}
Self::deposit_event(Event::<T, I>::MetadataCleared { index });
}
}
}
11 changes: 10 additions & 1 deletion frame/referenda/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ use sp_core::H256;
use sp_runtime::{
testing::Header,
traits::{BlakeTwo256, IdentityLookup},
DispatchResult, Perbill,
DispatchResult, Perbill, RuntimeDebug,
};

type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
Expand Down Expand Up @@ -61,6 +61,14 @@ impl Contains<RuntimeCall> for BaseFilter {
}
}

#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
pub enum MetadataSchema {
IpfsJsonV1,
IpfsJsonV2,
BinJsonV1,
BinJsonV2,
}

parameter_types! {
pub MaxWeight: Weight = Weight::from_ref_time(2_000_000_000_000);
pub BlockWeights: frame_system::limits::BlockWeights =
Expand Down Expand Up @@ -228,6 +236,7 @@ impl Config for Test {
type AlarmInterval = AlarmInterval;
type Tracks = TestTracksInfo;
type Preimages = Preimage;
type MetadataSchema = MetadataSchema;
}

pub fn new_test_ext() -> sp_io::TestExternalities {
Expand Down
79 changes: 79 additions & 0 deletions frame/referenda/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -519,3 +519,82 @@ fn curve_handles_all_inputs() {
let threshold = test_curve.threshold(Perbill::one());
assert_eq!(threshold, Perbill::zero());
}

#[test]
fn set_metadata_works() {
new_test_ext().execute_with(|| {
use sp_std::borrow::Cow;

let invalid_metadata =
MetadataOf::<Test, ()> { schema: MetadataSchema::IpfsJsonV1, hash: [1u8; 32].into() };
let metadata = MetadataOf::<Test, ()> {
schema: MetadataSchema::IpfsJsonV1,
hash: Preimage::note(Cow::from(vec![1])).unwrap(),
};

// fails to set metadata for a finished referendum.
assert_ok!(Referenda::submit(
RuntimeOrigin::signed(1),
Box::new(RawOrigin::Root.into()),
set_balance_proposal_bounded(1),
DispatchTime::At(1),
));
let index = ReferendumCount::<Test>::get() - 1;
assert_ok!(Referenda::kill(RuntimeOrigin::root(), index));
assert_noop!(
Referenda::set_metadata(RuntimeOrigin::signed(1), index, invalid_metadata.clone(),),
Error::<Test>::NotOngoing,
);
// no permission to set metadata
assert_ok!(Referenda::submit(
RuntimeOrigin::signed(1),
Box::new(RawOrigin::Root.into()),
set_balance_proposal_bounded(1),
DispatchTime::At(1),
));
let index = ReferendumCount::<Test>::get() - 1;
assert_noop!(
Referenda::set_metadata(RuntimeOrigin::signed(2), index, invalid_metadata.clone(),),
Error::<Test>::NoPermission,
);
// preimage does not exist
let index = ReferendumCount::<Test>::get() - 1;
assert_noop!(
Referenda::set_metadata(RuntimeOrigin::signed(1), index, invalid_metadata,),
Error::<Test>::BadMetadata,
);
// metadata set
let index = ReferendumCount::<Test>::get() - 1;
assert_ok!(Referenda::set_metadata(RuntimeOrigin::signed(1), index, metadata.clone(),));
System::assert_last_event(RuntimeEvent::Referenda(crate::Event::MetadataSet { index }));
assert!(Preimage::is_requested(&metadata.hash));
});
}

#[test]
fn clear_metadata_works() {
new_test_ext().execute_with(|| {
use sp_std::borrow::Cow;

let metadata = MetadataOf::<Test, ()> {
schema: MetadataSchema::IpfsJsonV1,
hash: Preimage::note(Cow::from(vec![1, 2])).unwrap(),
};

assert_ok!(Referenda::submit(
RuntimeOrigin::signed(1),
Box::new(RawOrigin::Root.into()),
set_balance_proposal_bounded(1),
DispatchTime::At(1),
));
let index = ReferendumCount::<Test>::get() - 1;
assert_ok!(Referenda::set_metadata(RuntimeOrigin::signed(1), index, metadata.clone(),));
assert!(Preimage::is_requested(&metadata.hash));
assert_noop!(
Referenda::clear_metadata(RuntimeOrigin::signed(2), index,),
Error::<Test>::NoPermission,
);
assert_ok!(Referenda::clear_metadata(RuntimeOrigin::signed(1), index,),);
System::assert_last_event(RuntimeEvent::Referenda(crate::Event::MetadataCleared { index }));
});
}
16 changes: 15 additions & 1 deletion frame/referenda/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
use super::*;
use codec::{Decode, Encode, EncodeLike, MaxEncodedLen};
use frame_support::{
traits::{schedule::v3::Anon, Bounded},
traits::{schedule::v3::Anon, Bounded, Hash as PreimageHash},
Parameter,
};
use scale_info::TypeInfo;
Expand Down Expand Up @@ -72,6 +72,8 @@ pub type ScheduleAddressOf<T, I> = <<T as Config<I>>::Scheduler as Anon<
PalletsOriginOf<T>,
>>::Address;

pub type MetadataOf<T, I> = Metadata<<T as Config<I>>::MetadataSchema>;

/// A referendum index.
pub type ReferendumIndex = u32;

Expand Down Expand Up @@ -512,6 +514,18 @@ impl Debug for Curve {
}
}

/// General information about the item referring to the metadata.
#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
pub struct Metadata<Schema: Eq + PartialEq + Debug + Encode + Decode + TypeInfo + Clone> {
/// The schema/s descriptor of the data the preimage `hash` referring to.
/// e.g. enum of `IpfsJsonV1` (the hash of an off-chain IPFS json file),
/// `BinJsonV2` (on-chain json dump).
pub(super) schema: Schema,
muharem marked this conversation as resolved.
Show resolved Hide resolved

/// The hash of the preimage holding the data of the `schema`.
pub(super) hash: PreimageHash,
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down