1
// Copyright 2022 Parity Technologies (UK) Ltd.
2
// This file is part of Polkadot.
3

            
4
// Polkadot is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8

            
9
// Polkadot is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13

            
14
// You should have received a copy of the GNU General Public License
15
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
16

            
17
//! Track configurations for governance.
18

            
19
use super::*;
20
use crate::currency::{KILOMOVR, MOVR, SUPPLY_FACTOR};
21
use sp_std::str::FromStr;
22

            
23
const fn percent(x: i32) -> sp_runtime::FixedI64 {
24
	sp_runtime::FixedI64::from_rational(x as u128, 100)
25
}
26
const fn permill(x: i32) -> sp_runtime::FixedI64 {
27
	sp_runtime::FixedI64::from_rational(x as u128, 1000)
28
}
29

            
30
use pallet_referenda::Curve;
31
const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo<Balance, BlockNumber>); 6] = [
32
	(
33
		0,
34
		pallet_referenda::TrackInfo {
35
			// Name of this track.
36
			name: "root",
37
			// A limit for the number of referenda on this track that can be being decided at once.
38
			// For Root origin this should generally be just one.
39
			max_deciding: 5,
40
			// Amount that must be placed on deposit before a decision can be made.
41
			decision_deposit: 100 * KILOMOVR * SUPPLY_FACTOR,
42
			// Amount of time this must be submitted for before a decision can be made.
43
			prepare_period: 1 * DAYS,
44
			// Amount of time that a decision may take to be approved prior to cancellation.
45
			decision_period: 14 * DAYS,
46
			// Amount of time that the approval criteria must hold before it can be approved.
47
			confirm_period: 1 * DAYS,
48
			// Minimum amount of time that an approved proposal must be in the dispatch queue.
49
			min_enactment_period: 1 * DAYS,
50
			// Minimum aye votes as percentage of overall conviction-weighted votes needed for
51
			// approval as a function of time into decision period.
52
			min_approval: Curve::make_reciprocal(4, 14, percent(80), percent(50), percent(100)),
53
			// Minimum pre-conviction aye-votes ("support") as percentage of overall population that
54
			// is needed for approval as a function of time into decision period.
55
			min_support: Curve::make_linear(14, 14, permill(5), percent(25)),
56
		},
57
	),
58
	(
59
		1,
60
		pallet_referenda::TrackInfo {
61
			name: "whitelisted_caller",
62
			max_deciding: 100,
63
			decision_deposit: 10 * KILOMOVR * SUPPLY_FACTOR,
64
			prepare_period: 10 * MINUTES,
65
			decision_period: 14 * DAYS,
66
			confirm_period: 10 * MINUTES,
67
			min_enactment_period: 30 * MINUTES,
68
			min_approval: Curve::make_reciprocal(1, 14, percent(96), percent(50), percent(100)),
69
			min_support: Curve::make_reciprocal(1, 14 * 24, percent(1), percent(0), percent(2)),
70
		},
71
	),
72
	(
73
		2,
74
		pallet_referenda::TrackInfo {
75
			name: "general_admin",
76
			max_deciding: 10,
77
			decision_deposit: 500 * MOVR * SUPPLY_FACTOR,
78
			prepare_period: 1 * HOURS,
79
			decision_period: 14 * DAYS,
80
			confirm_period: 1 * DAYS,
81
			min_enactment_period: 1 * DAYS,
82
			min_approval: Curve::make_reciprocal(4, 14, percent(80), percent(50), percent(100)),
83
			min_support: Curve::make_reciprocal(7, 14, percent(10), percent(0), percent(50)),
84
		},
85
	),
86
	(
87
		3,
88
		pallet_referenda::TrackInfo {
89
			name: "referendum_canceller",
90
			max_deciding: 20,
91
			decision_deposit: 10 * KILOMOVR * SUPPLY_FACTOR,
92
			prepare_period: 1 * HOURS,
93
			decision_period: 14 * DAYS,
94
			confirm_period: 3 * HOURS,
95
			min_enactment_period: 10 * MINUTES,
96
			min_approval: Curve::make_reciprocal(1, 14, percent(96), percent(50), percent(100)),
97
			min_support: Curve::make_reciprocal(1, 14, percent(1), percent(0), percent(10)),
98
		},
99
	),
100
	(
101
		4,
102
		pallet_referenda::TrackInfo {
103
			name: "referendum_killer",
104
			max_deciding: 100,
105
			decision_deposit: 20 * KILOMOVR * SUPPLY_FACTOR,
106
			prepare_period: 1 * HOURS,
107
			decision_period: 14 * DAYS,
108
			confirm_period: 3 * HOURS,
109
			min_enactment_period: 10 * MINUTES,
110
			min_approval: Curve::make_reciprocal(1, 14, percent(96), percent(50), percent(100)),
111
			min_support: Curve::make_reciprocal(1, 14, percent(1), percent(0), percent(10)),
112
		},
113
	),
114
	(
115
		5,
116
		pallet_referenda::TrackInfo {
117
			name: "fast_general_admin",
118
			max_deciding: 10,
119
			decision_deposit: 500 * MOVR * SUPPLY_FACTOR,
120
			prepare_period: 1 * HOURS,
121
			decision_period: 14 * DAYS,
122
			confirm_period: 3 * HOURS,
123
			min_enactment_period: 10 * MINUTES,
124
			min_approval: Curve::make_reciprocal(4, 14, percent(80), percent(50), percent(100)),
125
			min_support: Curve::make_reciprocal(5, 14, percent(1), percent(0), percent(50)),
126
		},
127
	),
128
];
129

            
130
pub struct TracksInfo;
131
impl pallet_referenda::TracksInfo<Balance, BlockNumber> for TracksInfo {
132
	type Id = u16;
133
	type RuntimeOrigin = <RuntimeOrigin as frame_support::traits::OriginTrait>::PalletsOrigin;
134
8
	fn tracks() -> &'static [(Self::Id, pallet_referenda::TrackInfo<Balance, BlockNumber>)] {
135
8
		&TRACKS_DATA[..]
136
8
	}
137
	fn track_for(id: &Self::RuntimeOrigin) -> Result<Self::Id, ()> {
138
		if let Ok(system_origin) = frame_system::RawOrigin::try_from(id.clone()) {
139
			match system_origin {
140
				frame_system::RawOrigin::Root => {
141
					if let Some((track_id, _)) = Self::tracks()
142
						.into_iter()
143
						.find(|(_, track)| track.name == "root")
144
					{
145
						Ok(*track_id)
146
					} else {
147
						Err(())
148
					}
149
				}
150
				_ => Err(()),
151
			}
152
		} else if let Ok(custom_origin) = custom_origins::Origin::try_from(id.clone()) {
153
			if let Some((track_id, _)) = Self::tracks().into_iter().find(|(_, track)| {
154
				if let Ok(track_custom_origin) = custom_origins::Origin::from_str(track.name) {
155
					track_custom_origin == custom_origin
156
				} else {
157
					false
158
				}
159
			}) {
160
				Ok(*track_id)
161
			} else {
162
				Err(())
163
			}
164
		} else {
165
			Err(())
166
		}
167
	}
168
}
169

            
170
#[test]
171
/// To ensure voters are always locked into their vote
172
1
fn vote_locking_always_longer_than_enactment_period() {
173
7
	for (_, track) in TRACKS_DATA {
174
6
		assert!(
175
6
			<Runtime as pallet_conviction_voting::Config>::VoteLockingPeriod::get()
176
6
				>= track.min_enactment_period,
177
			"Track {} has enactment period {} < vote locking period {}",
178
			track.name,
179
			track.min_enactment_period,
180
			<Runtime as pallet_conviction_voting::Config>::VoteLockingPeriod::get(),
181
		);
182
	}
183
1
}
184

            
185
#[test]
186
1
fn all_tracks_have_origins() {
187
7
	for (_, track) in TRACKS_DATA {
188
		// check name.into() is successful either converts into "root" or custom origin
189
6
		let track_is_root = track.name == "root";
190
6
		let track_has_custom_origin = custom_origins::Origin::from_str(track.name).is_ok();
191
6
		assert!(track_is_root || track_has_custom_origin);
192
	}
193
1
}