Skip to content

Commit 096f205

Browse files
author
Georges
authored
Implementing MaxEncodedLen for generate_solution_type (paritytech#11032)
* Move `sp-npos-elections-solution-type` to `frame-election-provider-support` First stab at it, will need to amend some more stuff * Fixing tests * Fixing tests * Fixing cargo.toml for std configuration * Implementing `MaxEncodedLen` on `generate_solution_type` * Full implementation of `max_encoded_len` * Fixing implementation bug adding some comments and documentation * fmt * Committing suggested changes renaming, and re exporting macro. * Removing unneeded imports * Implementing `MaxEncodedLen` on `generate_solution_type` * Full implementation of `max_encoded_len` * Fixing implementation bug adding some comments and documentation * Move `NposSolution` to frame * Implementing `MaxEncodedLen` on `generate_solution_type` * Full implementation of `max_encoded_len` * Fixing implementation bug adding some comments and documentation * Fixing test * Removing unneeded dependencies * `VoterSnapshotPerBlock` -> `MaxElectingVoters` * rename `SizeBound` to `MaxVoters` * Removing TODO and change bound * renaming `size_bound` to `max_voters` * Enabling tests for `solution-type` These got dropped off after the crate was moved from `sp_npos_elections` * Adding tests for `MaxEncodedLen` of solution_type * Better rustdocs. Better indenting and comments. Removing unneeded imports in tests.
1 parent 77c15d2 commit 096f205

File tree

20 files changed

+184
-12
lines changed

20 files changed

+184
-12
lines changed

Cargo.lock

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bin/node/runtime/src/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -597,11 +597,13 @@ frame_election_provider_support::generate_solution_type!(
597597
VoterIndex = u32,
598598
TargetIndex = u16,
599599
Accuracy = sp_runtime::PerU16,
600+
MaxVoters = MaxElectingVoters,
600601
>(16)
601602
);
602603

603604
parameter_types! {
604605
pub MaxNominations: u32 = <NposSolution16 as frame_election_provider_support::NposSolution>::LIMIT as u32;
606+
pub MaxElectingVoters: u32 = 10_000;
605607
}
606608

607609
/// The numbers configured here could always be more than the the maximum limits of staking pallet
@@ -677,7 +679,7 @@ impl pallet_election_provider_multi_phase::Config for Runtime {
677679
>;
678680
type ForceOrigin = EnsureRootOrHalfCouncil;
679681
type MaxElectableTargets = ConstU16<{ u16::MAX }>;
680-
type MaxElectingVoters = ConstU32<10_000>;
682+
type MaxElectingVoters = MaxElectingVoters;
681683
type BenchmarkingConfig = ElectionProviderBenchmarkConfig;
682684
type WeightInfo = pallet_election_provider_multi_phase::weights::SubstrateWeight<Self>;
683685
}

frame/election-provider-multi-phase/src/mock.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,12 @@ pub(crate) type TargetIndex = u16;
7070

7171
frame_election_provider_support::generate_solution_type!(
7272
#[compact]
73-
pub struct TestNposSolution::<VoterIndex = VoterIndex, TargetIndex = TargetIndex, Accuracy = PerU16>(16)
73+
pub struct TestNposSolution::<
74+
VoterIndex = VoterIndex,
75+
TargetIndex = TargetIndex,
76+
Accuracy = PerU16,
77+
MaxVoters = ConstU32::<20>
78+
>(16)
7479
);
7580

7681
/// All events of this pallet.

frame/election-provider-support/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ frame-system = { version = "4.0.0-dev", default-features = false, path = "../sys
2424
frame-election-provider-solution-type = { version = "4.0.0-dev", path = "solution-type" }
2525

2626
[dev-dependencies]
27+
rand = "0.7.3"
2728
sp-npos-elections = { version = "4.0.0-dev", path = "../../primitives/npos-elections" }
2829
sp-core = { version = "6.0.0", path = "../../primitives/core" }
2930
sp-io = { version = "6.0.0", path = "../../primitives/io" }

frame/election-provider-support/solution-type/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,5 @@ scale-info = "2.0.1"
2626
sp-arithmetic = { version = "5.0.0", path = "../../../primitives/arithmetic" }
2727
# used by generate_solution_type:
2828
frame-election-provider-support = { version = "4.0.0-dev", path = ".." }
29+
frame-support = { version = "4.0.0-dev", path = "../../support" }
2930
trybuild = "1.0.53"

frame/election-provider-support/solution-type/fuzzer/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ sp-arithmetic = { version = "5.0.0", path = "../../../../primitives/arithmetic"
2525
sp-runtime = { version = "6.0.0", path = "../../../../primitives/runtime" }
2626
# used by generate_solution_type:
2727
sp-npos-elections = { version = "4.0.0-dev", default-features = false, path = "../../../../primitives/npos-elections" }
28+
frame-support = { version = "4.0.0-dev", path = "../../../support" }
2829

2930
[[bin]]
3031
name = "compact"

frame/election-provider-support/solution-type/fuzzer/src/compact.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ fn main() {
88
VoterIndex = u32,
99
TargetIndex = u32,
1010
Accuracy = Percent,
11+
MaxVoters = frame_support::traits::ConstU32::<100_000>,
1112
>(16));
1213
loop {
1314
fuzz!(|fuzzer_data: &[u8]| {

frame/election-provider-support/solution-type/src/lib.rs

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ pub(crate) fn syn_err(message: &'static str) -> syn::Error {
4949
/// compact encoding.
5050
/// - The accuracy of the ratios. This must be one of the `PerThing` types defined in
5151
/// `sp-arithmetic`.
52+
/// - The maximum number of voters. This must be of type `Get<u32>`. Check <https://github.com/paritytech/substrate/issues/10866>
53+
/// for more details. This is used to bound the struct, by leveraging the fact that `votes1.len()
54+
/// < votes2.len() < ... < votesn.len()` (the details of the struct is explained further below).
55+
/// We know that `sum_i votes_i.len() <= MaxVoters`, and we know that the maximum size of the
56+
/// struct would be achieved if all voters fall in the last bucket. One can also check the tests
57+
/// and more specifically `max_encoded_len_exact` for a concrete example.
5258
///
5359
/// Moreover, the maximum number of edges per voter (distribution per assignment) also need to be
5460
/// specified. Attempting to convert from/to an assignment with more distributions will fail.
@@ -59,10 +65,12 @@ pub(crate) fn syn_err(message: &'static str) -> syn::Error {
5965
/// ```
6066
/// # use frame_election_provider_solution_type::generate_solution_type;
6167
/// # use sp_arithmetic::per_things::Perbill;
68+
/// # use frame_support::traits::ConstU32;
6269
/// generate_solution_type!(pub struct TestSolution::<
6370
/// VoterIndex = u16,
6471
/// TargetIndex = u8,
6572
/// Accuracy = Perbill,
73+
/// MaxVoters = ConstU32::<10>,
6674
/// >(4));
6775
/// ```
6876
///
@@ -103,9 +111,15 @@ pub(crate) fn syn_err(message: &'static str) -> syn::Error {
103111
/// # use frame_election_provider_solution_type::generate_solution_type;
104112
/// # use frame_election_provider_support::NposSolution;
105113
/// # use sp_arithmetic::per_things::Perbill;
114+
/// # use frame_support::traits::ConstU32;
106115
/// generate_solution_type!(
107116
/// #[compact]
108-
/// pub struct TestSolutionCompact::<VoterIndex = u16, TargetIndex = u8, Accuracy = Perbill>(8)
117+
/// pub struct TestSolutionCompact::<
118+
/// VoterIndex = u16,
119+
/// TargetIndex = u8,
120+
/// Accuracy = Perbill,
121+
/// MaxVoters = ConstU32::<10>,
122+
/// >(8)
109123
/// );
110124
/// ```
111125
#[proc_macro]
@@ -129,6 +143,7 @@ struct SolutionDef {
129143
voter_type: syn::Type,
130144
target_type: syn::Type,
131145
weight_type: syn::Type,
146+
max_voters: syn::Type,
132147
count: usize,
133148
compact_encoding: bool,
134149
}
@@ -167,11 +182,11 @@ impl Parse for SolutionDef {
167182
let _ = <syn::Token![::]>::parse(input)?;
168183
let generics: syn::AngleBracketedGenericArguments = input.parse()?;
169184

170-
if generics.args.len() != 3 {
171-
return Err(syn_err("Must provide 3 generic args."))
185+
if generics.args.len() != 4 {
186+
return Err(syn_err("Must provide 4 generic args."))
172187
}
173188

174-
let expected_types = ["VoterIndex", "TargetIndex", "Accuracy"];
189+
let expected_types = ["VoterIndex", "TargetIndex", "Accuracy", "MaxVoters"];
175190

176191
let mut types: Vec<syn::Type> = generics
177192
.args
@@ -197,6 +212,7 @@ impl Parse for SolutionDef {
197212
})
198213
.collect::<Result<_>>()?;
199214

215+
let max_voters = types.pop().expect("Vector of length 4 can be popped; qed");
200216
let weight_type = types.pop().expect("Vector of length 3 can be popped; qed");
201217
let target_type = types.pop().expect("Vector of length 2 can be popped; qed");
202218
let voter_type = types.pop().expect("Vector of length 1 can be popped; qed");
@@ -205,7 +221,16 @@ impl Parse for SolutionDef {
205221
let count_expr: syn::ExprParen = input.parse()?;
206222
let count = parse_parenthesized_number::<usize>(count_expr)?;
207223

208-
Ok(Self { vis, ident, voter_type, target_type, weight_type, count, compact_encoding })
224+
Ok(Self {
225+
vis,
226+
ident,
227+
voter_type,
228+
target_type,
229+
weight_type,
230+
max_voters,
231+
count,
232+
compact_encoding,
233+
})
209234
}
210235
}
211236

frame/election-provider-support/solution-type/src/single_page.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ pub(crate) fn generate(def: crate::SolutionDef) -> Result<TokenStream2> {
2828
voter_type,
2929
target_type,
3030
weight_type,
31+
max_voters,
3132
compact_encoding,
3233
} = def;
3334

@@ -178,6 +179,27 @@ pub(crate) fn generate(def: crate::SolutionDef) -> Result<TokenStream2> {
178179
<#ident as _feps::NposSolution>::TargetIndex,
179180
<#ident as _feps::NposSolution>::Accuracy,
180181
>;
182+
impl _feps::codec::MaxEncodedLen for #ident {
183+
fn max_encoded_len() -> usize {
184+
use frame_support::traits::Get;
185+
use _feps::codec::Encode;
186+
let s: u32 = #max_voters::get();
187+
let max_element_size =
188+
// the first voter..
189+
#voter_type::max_encoded_len()
190+
// #count - 1 tuples..
191+
.saturating_add(
192+
(#count - 1).saturating_mul(
193+
#target_type::max_encoded_len().saturating_add(#weight_type::max_encoded_len())))
194+
// and the last target.
195+
.saturating_add(#target_type::max_encoded_len());
196+
// The assumption is that it contains #count-1 empty elements
197+
// and then last element with full size
198+
#count
199+
.saturating_mul(_feps::codec::Compact(0u32).encoded_size())
200+
.saturating_add((s as usize).saturating_mul(max_element_size))
201+
}
202+
}
181203
impl<'a> _feps::sp_std::convert::TryFrom<&'a [__IndexAssignment]> for #ident {
182204
type Error = _feps::Error;
183205
fn try_from(index_assignments: &'a [__IndexAssignment]) -> Result<Self, Self::Error> {

frame/election-provider-support/solution-type/tests/ui/fail/missing_accuracy.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ generate_solution_type!(pub struct TestSolution::<
44
VoterIndex = u16,
55
TargetIndex = u8,
66
Perbill,
7+
MaxVoters = ConstU32::<10>,
78
>(8));
89

910
fn main() {}

0 commit comments

Comments
 (0)