Skip to content
GitLab
Explore
Sign in
Radu Popa
substrate
Compare revisions
9692a80520e3290708207534b5c8d5e6133d8fcc to 1fbcc94bcec7b0fcc038906593f40369bfc5704d
Hide whitespace changes
Inline
Side-by-side
frame/babe/src/tests.rs
View file @
1fbcc94b
...
...
@@ -585,3 +585,26 @@ fn report_equivocation_validate_unsigned_prevents_duplicates() {
);
});
}
#[test]
fn
report_equivocation_has_valid_weight
()
{
// the weight depends on the size of the validator set,
// but there's a lower bound of 100 validators.
assert!
(
(
1
..=
100
)
.map
(
weight_for
::
report_equivocation
::
<
Test
>
)
.collect
::
<
Vec
<
_
>>
()
.windows
(
2
)
.all
(|
w
|
w
[
0
]
==
w
[
1
])
);
// after 100 validators the weight should keep increasing
// with every extra validator.
assert!
(
(
100
..=
1000
)
.map
(
weight_for
::
report_equivocation
::
<
Test
>
)
.collect
::
<
Vec
<
_
>>
()
.windows
(
2
)
.all
(|
w
|
w
[
0
]
<
w
[
1
])
);
}
frame/collective/src/benchmarking.rs
View file @
1fbcc94b
...
...
@@ -45,8 +45,8 @@ benchmarks_instance! {
_
{
}
set_members
{
let
m
in
1
..
MAX_MEMBERS
;
let
n
in
1
..
MAX_MEMBERS
;
let
m
in
1
..
T
::
MaxMembers
::
get
()
;
let
n
in
1
..
T
::
MaxMembers
::
get
()
;
let
p
in
1
..
T
::
MaxProposals
::
get
();
// Set old members.
...
...
@@ -63,7 +63,7 @@ benchmarks_instance! {
SystemOrigin
::
Root
.into
(),
old_members
.clone
(),
Some
(
last_old_member
.clone
()),
MAX_MEMBERS
,
T
::
MaxMembers
::
get
()
,
)
?
;
// Set a high threshold for proposals passing so that they stay around.
...
...
@@ -104,15 +104,15 @@ benchmarks_instance! {
new_members
.push
(
last_member
.clone
());
}
}:
_
(
SystemOrigin
::
Root
,
new_members
.clone
(),
Some
(
last_member
),
MAX_MEMBERS
)
}:
_
(
SystemOrigin
::
Root
,
new_members
.clone
(),
Some
(
last_member
),
T
::
MaxMembers
::
get
()
)
verify
{
new_members
.sort
();
assert_eq!
(
Collective
::
<
T
,
_
>
::
members
(),
new_members
);
}
execute
{
let
m
in
1
..
MAX_MEMBERS
;
let
b
in
1
..
MAX_BYTES
;
let
m
in
1
..
T
::
MaxMembers
::
get
();
let
bytes_in_storage
=
b
+
size_of
::
<
u32
>
()
as
u32
;
...
...
@@ -126,7 +126,7 @@ benchmarks_instance! {
let
caller
:
T
::
AccountId
=
whitelisted_caller
();
members
.push
(
caller
.clone
());
Collective
::
<
T
,
_
>
::
set_members
(
SystemOrigin
::
Root
.into
(),
members
,
None
,
MAX_MEMBERS
)
?
;
Collective
::
<
T
,
_
>
::
set_members
(
SystemOrigin
::
Root
.into
(),
members
,
None
,
T
::
MaxMembers
::
get
()
)
?
;
let
proposal
:
T
::
Proposal
=
SystemCall
::
<
T
>
::
remark
(
vec!
[
1
;
b
as
usize
])
.into
();
...
...
@@ -141,8 +141,8 @@ benchmarks_instance! {
// This tests when execution would happen immediately after proposal
propose_execute
{
let
m
in
1
..
MAX_MEMBERS
;
let
b
in
1
..
MAX_BYTES
;
let
m
in
1
..
T
::
MaxMembers
::
get
();
let
bytes_in_storage
=
b
+
size_of
::
<
u32
>
()
as
u32
;
...
...
@@ -156,7 +156,7 @@ benchmarks_instance! {
let
caller
:
T
::
AccountId
=
whitelisted_caller
();
members
.push
(
caller
.clone
());
Collective
::
<
T
,
_
>
::
set_members
(
SystemOrigin
::
Root
.into
(),
members
,
None
,
MAX_MEMBERS
)
?
;
Collective
::
<
T
,
_
>
::
set_members
(
SystemOrigin
::
Root
.into
(),
members
,
None
,
T
::
MaxMembers
::
get
()
)
?
;
let
proposal
:
T
::
Proposal
=
SystemCall
::
<
T
>
::
remark
(
vec!
[
1
;
b
as
usize
])
.into
();
let
threshold
=
1
;
...
...
@@ -172,9 +172,9 @@ benchmarks_instance! {
// This tests when proposal is created and queued as "proposed"
propose_proposed
{
let
m
in
2
..
MAX_MEMBERS
;
let
p
in
1
..
T
::
MaxProposals
::
get
();
let
b
in
1
..
MAX_BYTES
;
let
m
in
2
..
T
::
MaxMembers
::
get
();
let
p
in
1
..
T
::
MaxProposals
::
get
();
let
bytes_in_storage
=
b
+
size_of
::
<
u32
>
()
as
u32
;
...
...
@@ -186,7 +186,7 @@ benchmarks_instance! {
}
let
caller
:
T
::
AccountId
=
whitelisted_caller
();
members
.push
(
caller
.clone
());
Collective
::
<
T
,
_
>
::
set_members
(
SystemOrigin
::
Root
.into
(),
members
,
None
,
MAX_MEMBERS
)
?
;
Collective
::
<
T
,
_
>
::
set_members
(
SystemOrigin
::
Root
.into
(),
members
,
None
,
T
::
MaxMembers
::
get
()
)
?
;
let
threshold
=
m
;
// Add previous proposals.
...
...
@@ -215,7 +215,7 @@ benchmarks_instance! {
vote
{
// We choose 5 as a minimum so we always trigger a vote in the voting loop (`for j in ...`)
let
m
in
5
..
MAX_MEMBERS
;
let
m
in
5
..
T
::
MaxMembers
::
get
()
;
let
p
=
T
::
MaxProposals
::
get
();
let
b
=
MAX_BYTES
;
...
...
@@ -231,7 +231,7 @@ benchmarks_instance! {
}
let
voter
:
T
::
AccountId
=
account
(
"voter"
,
0
,
SEED
);
members
.push
(
voter
.clone
());
Collective
::
<
T
,
_
>
::
set_members
(
SystemOrigin
::
Root
.into
(),
members
.clone
(),
None
,
MAX_MEMBERS
)
?
;
Collective
::
<
T
,
_
>
::
set_members
(
SystemOrigin
::
Root
.into
(),
members
.clone
(),
None
,
T
::
MaxMembers
::
get
()
)
?
;
// Threshold is 1 less than the number of members so that one person can vote nay
let
threshold
=
m
-
1
;
...
...
@@ -277,6 +277,9 @@ benchmarks_instance! {
// Voter switches vote to nay, but does not kill the vote, just updates + inserts
let
approve
=
false
;
// Whitelist voter account from further DB operations.
let
voter_key
=
frame_system
::
Account
::
<
T
>
::
hashed_key_for
(
&
voter
);
frame_benchmarking
::
benchmarking
::
add_to_whitelist
(
voter_key
.into
());
}:
_
(
SystemOrigin
::
Signed
(
voter
),
last_hash
.clone
(),
index
,
approve
)
verify
{
// All proposals exist and the last proposal has just been updated.
...
...
@@ -288,11 +291,11 @@ benchmarks_instance! {
close_early_disapproved
{
// We choose 4 as a minimum so we always trigger a vote in the voting loop (`for j in ...`)
let
m
in
4
..
MAX_MEMBERS
;
let
m
in
4
..
T
::
MaxMembers
::
get
()
;
let
p
in
1
..
T
::
MaxProposals
::
get
();
let
b
in
1
..
MAX_BYTES
;
let
bytes_in_storage
=
b
+
size_of
::
<
u32
>
()
as
u32
;
let
bytes
=
100
;
let
bytes_in_storage
=
bytes
+
size_of
::
<
u32
>
()
as
u32
;
// Construct `members`.
let
mut
members
=
vec!
[];
...
...
@@ -304,7 +307,7 @@ benchmarks_instance! {
}
let
voter
:
T
::
AccountId
=
account
(
"voter"
,
0
,
SEED
);
members
.push
(
voter
.clone
());
Collective
::
<
T
,
_
>
::
set_members
(
SystemOrigin
::
Root
.into
(),
members
.clone
(),
None
,
MAX_MEMBERS
)
?
;
Collective
::
<
T
,
_
>
::
set_members
(
SystemOrigin
::
Root
.into
(),
members
.clone
(),
None
,
T
::
MaxMembers
::
get
()
)
?
;
// Threshold is total members so that one nay will disapprove the vote
let
threshold
=
m
;
...
...
@@ -313,7 +316,7 @@ benchmarks_instance! {
let
mut
last_hash
=
T
::
Hash
::
default
();
for
i
in
0
..
p
{
// Proposals should be different so that different proposal hashes are generated
let
proposal
:
T
::
Proposal
=
SystemCall
::
<
T
>
::
remark
(
vec!
[
i
as
u8
;
b
as
usize
])
.into
();
let
proposal
:
T
::
Proposal
=
SystemCall
::
<
T
>
::
remark
(
vec!
[
i
as
u8
;
b
ytes
as
usize
])
.into
();
Collective
::
<
T
,
_
>
::
propose
(
SystemOrigin
::
Signed
(
proposer
.clone
())
.into
(),
threshold
,
...
...
@@ -356,6 +359,9 @@ benchmarks_instance! {
approve
,
)
?
;
// Whitelist voter account from further DB operations.
let
voter_key
=
frame_system
::
Account
::
<
T
>
::
hashed_key_for
(
&
voter
);
frame_benchmarking
::
benchmarking
::
add_to_whitelist
(
voter_key
.into
());
}:
close
(
SystemOrigin
::
Signed
(
voter
),
last_hash
.clone
(),
index
,
Weight
::
max_value
(),
bytes_in_storage
)
verify
{
// The last proposal is removed.
...
...
@@ -364,10 +370,10 @@ benchmarks_instance! {
}
close_early_approved
{
let
b
in
1
..
MAX_BYTES
;
// We choose 4 as a minimum so we always trigger a vote in the voting loop (`for j in ...`)
let
m
in
4
..
MAX_MEMBERS
;
let
m
in
4
..
T
::
MaxMembers
::
get
()
;
let
p
in
1
..
T
::
MaxProposals
::
get
();
let
b
in
1
..
MAX_BYTES
;
let
bytes_in_storage
=
b
+
size_of
::
<
u32
>
()
as
u32
;
...
...
@@ -379,7 +385,7 @@ benchmarks_instance! {
}
let
caller
:
T
::
AccountId
=
whitelisted_caller
();
members
.push
(
caller
.clone
());
Collective
::
<
T
,
_
>
::
set_members
(
SystemOrigin
::
Root
.into
(),
members
.clone
(),
None
,
MAX_MEMBERS
)
?
;
Collective
::
<
T
,
_
>
::
set_members
(
SystemOrigin
::
Root
.into
(),
members
.clone
(),
None
,
T
::
MaxMembers
::
get
()
)
?
;
// Threshold is 2 so any two ayes will approve the vote
let
threshold
=
2
;
...
...
@@ -446,11 +452,11 @@ benchmarks_instance! {
close_disapproved
{
// We choose 4 as a minimum so we always trigger a vote in the voting loop (`for j in ...`)
let
m
in
4
..
MAX_MEMBERS
;
let
m
in
4
..
T
::
MaxMembers
::
get
()
;
let
p
in
1
..
T
::
MaxProposals
::
get
();
let
b
in
1
..
MAX_BYTES
;
let
bytes_in_storage
=
b
+
size_of
::
<
u32
>
()
as
u32
;
let
bytes
=
100
;
let
bytes_in_storage
=
bytes
+
size_of
::
<
u32
>
()
as
u32
;
// Construct `members`.
let
mut
members
=
vec!
[];
...
...
@@ -464,7 +470,7 @@ benchmarks_instance! {
SystemOrigin
::
Root
.into
(),
members
.clone
(),
Some
(
caller
.clone
()),
MAX_MEMBERS
,
T
::
MaxMembers
::
get
()
,
)
?
;
// Threshold is one less than total members so that two nays will disapprove the vote
...
...
@@ -474,7 +480,7 @@ benchmarks_instance! {
let
mut
last_hash
=
T
::
Hash
::
default
();
for
i
in
0
..
p
{
// Proposals should be different so that different proposal hashes are generated
let
proposal
:
T
::
Proposal
=
SystemCall
::
<
T
>
::
remark
(
vec!
[
i
as
u8
;
b
as
usize
])
.into
();
let
proposal
:
T
::
Proposal
=
SystemCall
::
<
T
>
::
remark
(
vec!
[
i
as
u8
;
b
ytes
as
usize
])
.into
();
Collective
::
<
T
,
_
>
::
propose
(
SystemOrigin
::
Signed
(
caller
.clone
())
.into
(),
threshold
,
...
...
@@ -517,10 +523,10 @@ benchmarks_instance! {
}
close_approved
{
let
b
in
1
..
MAX_BYTES
;
// We choose 4 as a minimum so we always trigger a vote in the voting loop (`for j in ...`)
let
m
in
4
..
MAX_MEMBERS
;
let
m
in
4
..
T
::
MaxMembers
::
get
()
;
let
p
in
1
..
T
::
MaxProposals
::
get
();
let
b
in
1
..
MAX_BYTES
;
let
bytes_in_storage
=
b
+
size_of
::
<
u32
>
()
as
u32
;
...
...
@@ -536,7 +542,7 @@ benchmarks_instance! {
SystemOrigin
::
Root
.into
(),
members
.clone
(),
Some
(
caller
.clone
()),
MAX_MEMBERS
,
T
::
MaxMembers
::
get
()
,
)
?
;
// Threshold is two, so any two ayes will pass the vote
...
...
@@ -579,6 +585,54 @@ benchmarks_instance! {
assert_eq!
(
Collective
::
<
T
,
_
>
::
proposals
()
.len
(),
(
p
-
1
)
as
usize
);
assert_last_event
::
<
T
,
I
>
(
RawEvent
::
Executed
(
last_hash
,
Err
(
DispatchError
::
BadOrigin
))
.into
());
}
disapprove_proposal
{
let
p
in
1
..
T
::
MaxProposals
::
get
();
let
m
=
3
;
let
b
=
MAX_BYTES
;
let
bytes_in_storage
=
b
+
size_of
::
<
u32
>
()
as
u32
;
// Construct `members`.
let
mut
members
=
vec!
[];
for
i
in
0
..
m
-
1
{
let
member
=
account
(
"member"
,
i
,
SEED
);
members
.push
(
member
);
}
let
caller
:
T
::
AccountId
=
account
(
"caller"
,
0
,
SEED
);
members
.push
(
caller
.clone
());
Collective
::
<
T
,
_
>
::
set_members
(
SystemOrigin
::
Root
.into
(),
members
.clone
(),
Some
(
caller
.clone
()),
T
::
MaxMembers
::
get
(),
)
?
;
// Threshold is one less than total members so that two nays will disapprove the vote
let
threshold
=
m
-
1
;
// Add proposals
let
mut
last_hash
=
T
::
Hash
::
default
();
for
i
in
0
..
p
{
// Proposals should be different so that different proposal hashes are generated
let
proposal
:
T
::
Proposal
=
SystemCall
::
<
T
>
::
remark
(
vec!
[
i
as
u8
;
b
as
usize
])
.into
();
Collective
::
<
T
,
_
>
::
propose
(
SystemOrigin
::
Signed
(
caller
.clone
())
.into
(),
threshold
,
Box
::
new
(
proposal
.clone
()),
bytes_in_storage
,
)
?
;
last_hash
=
T
::
Hashing
::
hash_of
(
&
proposal
);
}
System
::
<
T
>
::
set_block_number
(
T
::
BlockNumber
::
max_value
());
assert_eq!
(
Collective
::
<
T
,
_
>
::
proposals
()
.len
(),
p
as
usize
);
}:
_
(
SystemOrigin
::
Root
,
last_hash
)
verify
{
assert_eq!
(
Collective
::
<
T
,
_
>
::
proposals
()
.len
(),
(
p
-
1
)
as
usize
);
assert_last_event
::
<
T
,
I
>
(
RawEvent
::
Disapproved
(
last_hash
)
.into
());
}
}
#[cfg(test)]
...
...
@@ -649,4 +703,11 @@ mod tests {
assert_ok!
(
test_benchmark_close_approved
::
<
Test
>
());
});
}
#[test]
fn
disapprove_proposal
()
{
new_test_ext
()
.execute_with
(||
{
assert_ok!
(
test_benchmark_disapprove_proposal
::
<
Test
>
());
});
}
}
frame/collective/src/default_weight.rs
0 → 100644
View file @
1fbcc94b
// Copyright (C) 2020 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.
//! Default weights for the Collective Pallet
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0-rc6
#![allow(unused_parens)]
#![allow(unused_imports)]
use
frame_support
::
weights
::{
Weight
,
constants
::
RocksDbWeight
as
DbWeight
};
impl
crate
::
WeightInfo
for
()
{
fn
set_members
(
m
:
u32
,
n
:
u32
,
p
:
u32
,
)
->
Weight
{
(
0
as
Weight
)
.saturating_add
((
21040000
as
Weight
)
.saturating_mul
(
m
as
Weight
))
.saturating_add
((
173000
as
Weight
)
.saturating_mul
(
n
as
Weight
))
.saturating_add
((
31595000
as
Weight
)
.saturating_mul
(
p
as
Weight
))
.saturating_add
(
DbWeight
::
get
()
.reads
(
2
as
Weight
))
.saturating_add
(
DbWeight
::
get
()
.reads
((
1
as
Weight
)
.saturating_mul
(
p
as
Weight
)))
.saturating_add
(
DbWeight
::
get
()
.writes
(
2
as
Weight
))
.saturating_add
(
DbWeight
::
get
()
.writes
((
1
as
Weight
)
.saturating_mul
(
p
as
Weight
)))
}
fn
execute
(
b
:
u32
,
m
:
u32
,
)
->
Weight
{
(
43359000
as
Weight
)
.saturating_add
((
4000
as
Weight
)
.saturating_mul
(
b
as
Weight
))
.saturating_add
((
123000
as
Weight
)
.saturating_mul
(
m
as
Weight
))
.saturating_add
(
DbWeight
::
get
()
.reads
(
1
as
Weight
))
}
fn
propose_execute
(
b
:
u32
,
m
:
u32
,
)
->
Weight
{
(
54134000
as
Weight
)
.saturating_add
((
4000
as
Weight
)
.saturating_mul
(
b
as
Weight
))
.saturating_add
((
239000
as
Weight
)
.saturating_mul
(
m
as
Weight
))
.saturating_add
(
DbWeight
::
get
()
.reads
(
2
as
Weight
))
}
fn
propose_proposed
(
b
:
u32
,
m
:
u32
,
p
:
u32
,
)
->
Weight
{
(
90650000
as
Weight
)
.saturating_add
((
5000
as
Weight
)
.saturating_mul
(
b
as
Weight
))
.saturating_add
((
152000
as
Weight
)
.saturating_mul
(
m
as
Weight
))
.saturating_add
((
970000
as
Weight
)
.saturating_mul
(
p
as
Weight
))
.saturating_add
(
DbWeight
::
get
()
.reads
(
4
as
Weight
))
.saturating_add
(
DbWeight
::
get
()
.writes
(
4
as
Weight
))
}
fn
vote
(
m
:
u32
,
)
->
Weight
{
(
74460000
as
Weight
)
.saturating_add
((
290000
as
Weight
)
.saturating_mul
(
m
as
Weight
))
.saturating_add
(
DbWeight
::
get
()
.reads
(
2
as
Weight
))
.saturating_add
(
DbWeight
::
get
()
.writes
(
1
as
Weight
))
}
fn
close_early_disapproved
(
m
:
u32
,
p
:
u32
,
)
->
Weight
{
(
86360000
as
Weight
)
.saturating_add
((
232000
as
Weight
)
.saturating_mul
(
m
as
Weight
))
.saturating_add
((
954000
as
Weight
)
.saturating_mul
(
p
as
Weight
))
.saturating_add
(
DbWeight
::
get
()
.reads
(
3
as
Weight
))
.saturating_add
(
DbWeight
::
get
()
.writes
(
3
as
Weight
))
}
fn
close_early_approved
(
b
:
u32
,
m
:
u32
,
p
:
u32
,
)
->
Weight
{
(
123653000
as
Weight
)
.saturating_add
((
1000
as
Weight
)
.saturating_mul
(
b
as
Weight
))
.saturating_add
((
287000
as
Weight
)
.saturating_mul
(
m
as
Weight
))
.saturating_add
((
920000
as
Weight
)
.saturating_mul
(
p
as
Weight
))
.saturating_add
(
DbWeight
::
get
()
.reads
(
4
as
Weight
))
.saturating_add
(
DbWeight
::
get
()
.writes
(
3
as
Weight
))
}
fn
close_disapproved
(
m
:
u32
,
p
:
u32
,
)
->
Weight
{
(
95395000
as
Weight
)
.saturating_add
((
236000
as
Weight
)
.saturating_mul
(
m
as
Weight
))
.saturating_add
((
965000
as
Weight
)
.saturating_mul
(
p
as
Weight
))
.saturating_add
(
DbWeight
::
get
()
.reads
(
4
as
Weight
))
.saturating_add
(
DbWeight
::
get
()
.writes
(
3
as
Weight
))
}
fn
close_approved
(
b
:
u32
,
m
:
u32
,
p
:
u32
,
)
->
Weight
{
(
135284000
as
Weight
)
.saturating_add
((
4000
as
Weight
)
.saturating_mul
(
b
as
Weight
))
.saturating_add
((
218000
as
Weight
)
.saturating_mul
(
m
as
Weight
))
.saturating_add
((
951000
as
Weight
)
.saturating_mul
(
p
as
Weight
))
.saturating_add
(
DbWeight
::
get
()
.reads
(
5
as
Weight
))
.saturating_add
(
DbWeight
::
get
()
.writes
(
3
as
Weight
))
}
fn
disapprove_proposal
(
p
:
u32
,
)
->
Weight
{
(
50500000
as
Weight
)
.saturating_add
((
966000
as
Weight
)
.saturating_mul
(
p
as
Weight
))
.saturating_add
(
DbWeight
::
get
()
.reads
(
1
as
Weight
))
.saturating_add
(
DbWeight
::
get
()
.writes
(
3
as
Weight
))
}
}
frame/collective/src/lib.rs
View file @
1fbcc94b
...
...
@@ -20,7 +20,7 @@
//!
//! The membership can be provided in one of two ways: either directly, using the Root-dispatchable
//! function `set_members`, or indirectly, through implementing the `ChangeMembers`.
//! The pallet assumes that the amount of members stays at or below `M
AX_MEMBERS
` for its weight
//! The pallet assumes that the amount of members stays at or below `M
axMembers
` for its weight
//! calculations, but enforces this neither in `set_members` nor in `change_members_sorted`.
//!
//! A "prime" member may be set allowing their vote to act as the default vote in case of any
...
...
@@ -60,6 +60,8 @@ use frame_system::{self as system, ensure_signed, ensure_root};
#[cfg(feature
=
"runtime-benchmarks"
)]
mod
benchmarking
;
mod
default_weight
;
/// Simple index type for proposal counting.
pub
type
ProposalIndex
=
u32
;
...
...
@@ -69,35 +71,17 @@ pub type ProposalIndex = u32;
/// vote exactly once, therefore also the number of votes for any given motion.
pub
type
MemberCount
=
u32
;
/// The maximum number of members supported by the pallet. Used for weight estimation.
///
/// NOTE:
/// + Benchmarks will need to be re-run and weights adjusted if this changes.
/// + This pallet assumes that dependents keep to the limit without enforcing it.
pub
const
MAX_MEMBERS
:
MemberCount
=
100
;
pub
trait
WeightInfo
{
fn
set_members
(
m
:
u32
,
n
:
u32
,
p
:
u32
,
)
->
Weight
;
fn
execute
(
m
:
u32
,
b
:
u32
,
)
->
Weight
;
fn
propose_execute
(
m
:
u32
,
b
:
u32
,
)
->
Weight
;
fn
propose_proposed
(
m
:
u32
,
p
:
u32
,
b
:
u32
,
)
->
Weight
;
fn
execute
(
b
:
u32
,
m
:
u32
,
)
->
Weight
;
fn
propose_execute
(
b
:
u32
,
m
:
u32
,
)
->
Weight
;
fn
propose_proposed
(
b
:
u32
,
m
:
u32
,
p
:
u32
,
)
->
Weight
;
fn
vote
(
m
:
u32
,
)
->
Weight
;
fn
close_early_disapproved
(
m
:
u32
,
p
:
u32
,
b
:
u32
,
)
->
Weight
;
fn
close_early_approved
(
m
:
u32
,
p
:
u32
,
b
:
u32
,
)
->
Weight
;
fn
close_disapproved
(
m
:
u32
,
p
:
u32
,
b
:
u32
,
)
->
Weight
;
fn
close_approved
(
m
:
u32
,
p
:
u32
,
b
:
u32
,
)
->
Weight
;
}
impl
WeightInfo
for
()
{
fn
set_members
(
_m
:
u32
,
_n
:
u32
,
_p
:
u32
,
)
->
Weight
{
1_000_000_000
}
fn
execute
(
_m
:
u32
,
_b
:
u32
,
)
->
Weight
{
1_000_000_000
}
fn
propose_execute
(
_m
:
u32
,
_b
:
u32
,
)
->
Weight
{
1_000_000_000
}
fn
propose_proposed
(
_m
:
u32
,
_p
:
u32
,
_b
:
u32
,
)
->
Weight
{
1_000_000_000
}
fn
vote
(
_m
:
u32
,
)
->
Weight
{
1_000_000_000
}
fn
close_early_disapproved
(
_m
:
u32
,
_p
:
u32
,
_b
:
u32
,
)
->
Weight
{
1_000_000_000
}
fn
close_early_approved
(
_m
:
u32
,
_p
:
u32
,
_b
:
u32
,
)
->
Weight
{
1_000_000_000
}
fn
close_disapproved
(
_m
:
u32
,
_p
:
u32
,
_b
:
u32
,
)
->
Weight
{
1_000_000_000
}
fn
close_approved
(
_m
:
u32
,
_p
:
u32
,
_b
:
u32
,
)
->
Weight
{
1_000_000_000
}
fn
close_early_disapproved
(
m
:
u32
,
p
:
u32
,
)
->
Weight
;
fn
close_early_approved
(
b
:
u32
,
m
:
u32
,
p
:
u32
,
)
->
Weight
;
fn
close_disapproved
(
m
:
u32
,
p
:
u32
,
)
->
Weight
;
fn
close_approved
(
b
:
u32
,
m
:
u32
,
p
:
u32
,
)
->
Weight
;
fn
disapprove_proposal
(
p
:
u32
,
)
->
Weight
;
}
pub
trait
Trait
<
I
:
Instance
=
DefaultInstance
>
:
frame_system
::
Trait
{
...
...
@@ -117,7 +101,14 @@ pub trait Trait<I: Instance=DefaultInstance>: frame_system::Trait {
type
MotionDuration
:
Get
<
Self
::
BlockNumber
>
;
/// Maximum number of proposals allowed to be active in parallel.
type
MaxProposals
:
Get
<
u32
>
;
type
MaxProposals
:
Get
<
ProposalIndex
>
;
/// The maximum number of members supported by the pallet. Used for weight estimation.
///
/// NOTE:
/// + Benchmarks will need to be re-run and weights adjusted if this changes.
/// + This pallet assumes that dependents keep to the limit without enforcing it.
type
MaxMembers
:
Get
<
MemberCount
>
;
/// Weight information for extrinsics in this pallet.
type
WeightInfo
:
WeightInfo
;
...
...
@@ -233,131 +224,6 @@ decl_error! {
}
}
/// Functions for calcuating the weight of dispatchables.
mod
weight_for
{
use
frame_support
::{
traits
::
Get
,
weights
::
Weight
};
use
super
::{
Trait
,
Instance
};
/// Calculate the weight for `set_members`.
///
/// Based on benchmark:
/// 0 + M * 20.47 + N * 0.109 + P * 26.29 µs (min squares analysis)
///
/// Note: The complexity of `set_members` is quadratic (`O(MP + N)`), so the linear approximation
/// of the benchmark is not always permissible. It is here, though, because the linear approximation
/// covered the range of possible values and we estimate weight via the worst case (max paramter
/// values) before execution so we can be sure that we are only overestimating.
pub
(
crate
)
fn
set_members
<
T
:
Trait
<
I
>
,
I
:
Instance
>
(
old_count
:
Weight
,
new_count
:
Weight
,
proposals
:
Weight
,
)
->
Weight
{
let
db
=
T
::
DbWeight
::
get
();
db
.reads_writes
(
1
,
1
)
// mutate `Members`
.saturating_add
(
db
.writes
(
1
))
// set `Prime`
.saturating_add
(
db
.reads
(
1
))
// read `Proposals`
.saturating_add
(
db
.reads_writes
(
proposals
,
proposals
))
// update votes (`Voting`)
.saturating_add
(
old_count
.saturating_mul
(
21_000_000
))
// M
.saturating_add
(
new_count
.saturating_mul
(
110_000
))
// N
.saturating_add
(
proposals
.saturating_mul
(
27_000_000
))
// P
}
/// Calculate the weight for `execute`.
///
/// Based on benchmark:
/// 22.62 + M * 0.115 + B * 0.003 µs (min squares analysis)
pub
(
crate
)
fn
execute
<
T
:
Trait
<
I
>
,
I
:
Instance
>
(
members
:
Weight
,
proposal
:
Weight
,
length
:
Weight
,
)
->
Weight
{
T
::
DbWeight
::
get
()
.reads
(
1
)
// read members for `is_member`
.saturating_add
(
23_000_000
)
// constant
.saturating_add
(
length
.saturating_mul
(
4_000
))
// B
.saturating_add
(
members
.saturating_mul
(
120_000
))
// M
.saturating_add
(
proposal
)
// P
}
/// Calculate the weight for `propose` if the proposal is executed straight away (`threshold < 2`).
///
/// Based on benchmark:
/// 28.12 + M * 0.218 + B * 0.003 µs (min squares analysis)
pub
(
crate
)
fn
propose_execute
<
T
:
Trait
<
I
>
,
I
:
Instance
>
(
members
:
Weight
,
proposal
:
Weight
,
length
:
Weight
,
)
->
Weight
{
T
::
DbWeight
::
get
()
.reads
(
2
)
// `is_member` + `contains_key`
.saturating_add
(
29_000_000
)
// constant
.saturating_add
(
length
.saturating_mul
(
3_000
))
// B
.saturating_add
(
members
.saturating_mul
(
220_000
))
// M
.saturating_add
(
proposal
)
// P1
}
/// Calculate the weight for `propose` if the proposal is put up for a vote (`threshold >= 2`).
///
/// Based on benchmark:
/// 49.75 + M * 0.105 + P2 0.502 + B * 0.006 µs (min squares analysis)
pub
(
crate
)
fn
propose_proposed
<
T
:
Trait
<
I
>
,
I
:
Instance
>
(
members
:
Weight
,
proposals
:
Weight
,
length
:
Weight
,
)
->
Weight
{
T
::
DbWeight
::
get
()
.reads
(
2
)
// `is_member` + `contains_key`
.saturating_add
(
T
::
DbWeight
::
get
()
.reads_writes
(
2
,
4
))
// `proposal` insertion
.saturating_add
(
50_000_000
)
// constant
.saturating_add
(
length
.saturating_mul
(
6_000
))
// B
.saturating_add
(
members
.saturating_mul
(
110_000
))
// M
.saturating_add
(
proposals
.saturating_mul
(
510_000
))
// P2
}
/// Calculate the weight for `vote`.
///
/// Based on benchmark:
/// 24.03 + M * 0.349 + P * 0.119 + B * 0.003 µs (min squares analysis)
pub
(
crate
)
fn
vote
<
T
:
Trait
<
I
>
,
I
:
Instance
>
(
members
:
Weight
,
)
->
Weight
{
T
::
DbWeight
::
get
()
.reads
(
1
)
// read `Members`
.saturating_add
(
T
::
DbWeight
::
get
()
.reads_writes
(
1
,
1
))
// mutate `Voting`
.saturating_add
(
30_000_000
)
// constant
.saturating_add
(
members
.saturating_mul
(
500_000
))
// M
}
/// Calculate the weight for `close`.
///
/// Based on benchmarks:
/// - early disapproved: 37.21 + M * 0.239 + P2 * 0.466 + B * 0.002 µs (min squares analysis)
/// - early approved: 50.82 + M * 0.211 + P2 * 0.478 + B * 0.008 µs (min squares analysis)
/// - disapproved: 51.08 + M * 0.224 + P2 * 0.475 + B * 0.003 µs (min squares analysis)
/// - approved: 65.95 + M * 0.226 + P2 * 0.487 + B * 0.005 µs (min squares analysis)
pub
(
crate
)
fn
close
<
T
:
Trait
<
I
>
,
I
:
Instance
>
(
members
:
Weight
,
proposal_weight
:
Weight
,
proposals
:
Weight
,
length
:
Weight
,
)
->
Weight
{
let
db
=
T
::
DbWeight
::
get
();
close_without_finalize
::
<
T
,
I
>
(
members
,
length
)
.saturating_add
(
db
.reads
(
1
))
// `Prime`
.saturating_add
(
db
.writes
(
1
))
// `Proposals`
.saturating_add
(
db
.writes
(
1
))
// `Voting`
.saturating_add
(
proposal_weight
)
// P1
.saturating_add
(
proposals
.saturating_mul
(
490_000
))
// P2
}
/// Calculate the weight for `close` without the call to `approve/disapprove_proposal`.
pub
(
crate
)
fn
close_without_finalize
<
T
:
Trait
<
I
>
,
I
:
Instance
>
(
members
:
Weight
,
length
:
Weight
,
)
->
Weight
{
T
::
DbWeight
::
get
()
.reads
(
3
)
// `Members`, `Voting`, `ProposalOf`
.saturating_add
(
66_000_000
)
// constant
.saturating_add
(
length
.saturating_mul
(
8_000
))
// B
.saturating_add
(
members
.saturating_mul
(
250_000
))
// M
}
}
/// Return the weight of a dispatch call result as an `Option`.
///
/// Will return the weight regardless of what the state of the result is.
...
...
@@ -385,7 +251,7 @@ decl_module! {
///
/// Requires root origin.
///
/// NOTE: Does not enforce the expected `M
AX_MEMBERS
` limit on the amount of members, but
/// NOTE: Does not enforce the expected `M
axMembers
` limit on the amount of members, but
/// the weight estimations rely on it to estimate dispatchable weight.
///
/// # <weight>
...
...
@@ -401,10 +267,10 @@ decl_module! {
/// - 1 storage write (codec `O(1)`) for deleting the old `prime` and setting the new one
/// # </weight>
#[weight
=
(
w
eight
_
fo
r
::set_members
::
<
T,
I
>
(
(
*
old_count
)
.
into()
,
// M
new_members
.
len()
as
Weight
,
// N
T::MaxProposals::get()
.
into(),
// P
T::W
eight
In
fo::set_members(
*
old_count,
// M
new_members
.
len()
as
u32
,
// N
T::MaxProposals::get()
// P
),
DispatchClass::Operational
)]
...
...
@@ -414,10 +280,10 @@ decl_module! {
old_count
:
MemberCount
,
)
->
DispatchResultWithPostInfo
{
ensure_root
(
origin
)
?
;
if
new_members
.len
()
>
MAX_MEMBERS
as
usize
{
if
new_members
.len
()
>
T
::
MaxMembers
::
get
()
as
usize
{
debug
::
error!
(
"New members count exceeds maximum amount of members expected. (expected: {}, actual: {})"
,
MAX_MEMBERS
,
T
::
MaxMembers
::
get
()
,
new_members
.len
()
);
}
...
...
@@ -435,10 +301,10 @@ decl_module! {
<
Self
as
ChangeMembers
<
T
::
AccountId
>>
::
set_members_sorted
(
&
new_members
,
&
old
);
Prime
::
<
T
,
I
>
::
set
(
prime
);
Ok
(
Some
(
w
eight
_
fo
r
::
set_members
::
<
T
,
I
>
(
old
.len
()
as
Weight
,
// M
new_members
.len
()
as
Weight
,
// N
T
::
MaxProposals
::
get
()
.into
()
,
// P
Ok
(
Some
(
T
::
W
eight
In
fo
::
set_members
(
old
.len
()
as
u32
,
// M
new_members
.len
()
as
u32
,
// N
T
::
MaxProposals
::
get
(),
// P
))
.into
())
}
...
...
@@ -453,11 +319,10 @@ decl_module! {
/// - 1 event
/// # </weight>
#[weight
=
(
weight_for::execute::
<
T,
I
>
(
MAX_MEMBERS
.
into(),
proposal
.
get_dispatch_info()
.
weight,
*
length_bound
as
Weight,
),
T::WeightInfo::execute(
*
length_bound,
// B
T::MaxMembers::get(),
// M
)
.
saturating_add(proposal
.
get_dispatch_info()
.
weight),
// P
DispatchClass::Operational
)]
fn
execute
(
origin
,
...
...
@@ -476,11 +341,12 @@ decl_module! {
RawEvent
::
MemberExecuted
(
proposal_hash
,
result
.map
(|
_
|
())
.map_err
(|
e
|
e
.error
))
);
Ok
(
get_result_weight
(
result
)
.map
(|
w
|
weight_for
::
execute
::
<
T
,
I
>
(
members
.len
()
as
Weight
,
w
,
proposal_len
as
Weight
))
.into
())
Ok
(
get_result_weight
(
result
)
.map
(|
w
|
{
T
::
WeightInfo
::
execute
(
proposal_len
as
u32
,
// B
members
.len
()
as
u32
,
// M
)
.saturating_add
(
w
)
// P
})
.into
())
}
/// Add a new proposal to either be voted on or executed directly.
...
...
@@ -512,16 +378,15 @@ decl_module! {
/// # </weight>
#[weight
=
(
if
*
threshold
<
2
{
weight_for::propose_execute::
<
T,
I
>
(
MAX_MEMBERS
.
into(),
// M
proposal
.
get_dispatch_info()
.
weight,
// P1
*
length_bound
as
Weight,
// B
)
T::WeightInfo::propose_execute(
*
length_bound,
// B
T::MaxMembers::get(),
// M
)
.
saturating_add(proposal
.
get_dispatch_info()
.
weight)
// P1
}
else
{
w
eight
_
fo
r
::propose_proposed
::
<
T,
I
>
(
MAX_MEMBERS
.
into()
,
//
M
T::Max
Proposal
s::get()
.
into()
,
//
P2
*
length_bound
as
Weight
,
//
B
T::W
eight
In
fo::propose_proposed(
*
length_bound
,
//
B
T::Max
Member
s::get(),
//
M
T::MaxProposals::get()
,
//
P2
)
}
,
DispatchClass::Operational
...
...
@@ -547,11 +412,12 @@ decl_module! {
RawEvent
::
Executed
(
proposal_hash
,
result
.map
(|
_
|
())
.map_err
(|
e
|
e
.error
))
);
Ok
(
get_result_weight
(
result
)
.map
(|
w
|
weight_for
::
propose_execute
::
<
T
,
I
>
(
members
.len
()
as
Weight
,
// M
w
,
// P1
proposal_len
as
Weight
,
// B
))
.into
())
Ok
(
get_result_weight
(
result
)
.map
(|
w
|
{
T
::
WeightInfo
::
propose_execute
(
proposal_len
as
u32
,
// B
members
.len
()
as
u32
,
// M
)
.saturating_add
(
w
)
// P1
})
.into
())
}
else
{
let
active_proposals
=
<
Proposals
<
T
,
I
>>
::
try_mutate
(|
proposals
|
->
Result
<
usize
,
DispatchError
>
{
...
...
@@ -571,10 +437,10 @@ decl_module! {
Self
::
deposit_event
(
RawEvent
::
Proposed
(
who
,
index
,
proposal_hash
,
threshold
));
Ok
(
Some
(
w
eight
_
fo
r
::
propose_proposed
::
<
T
,
I
>
(
members
.
len
()
as
Weight
,
//
M
active_proposals
as
Weight
,
//
P2
proposal
_len
as
Weight
,
//
B
Ok
(
Some
(
T
::
W
eight
In
fo
::
propose_proposed
(
proposal_
len
as
u32
,
//
B
members
.len
()
as
u32
,
//
M
active_
proposal
s
as
u32
,
//
P2
))
.into
())
}
}
...
...
@@ -592,7 +458,7 @@ decl_module! {
/// - 1 event
/// # </weight>
#[weight
=
(
w
eight
_
fo
r
::vote
::
<
T,
I
>
(MAX_MEMBERS
.
into
()),
T::W
eight
In
fo::vote
(T::MaxMembers::get
()),
DispatchClass::Operational
)]
fn
vote
(
origin
,
...
...
@@ -636,7 +502,7 @@ decl_module! {
Voting
::
<
T
,
I
>
::
insert
(
&
proposal
,
voting
);
Ok
(
Some
(
w
eight
_
fo
r
::
vote
::
<
T
,
I
>
(
members
.len
()
as
Weight
))
.into
())
Ok
(
Some
(
T
::
W
eight
In
fo
::
vote
(
members
.len
()
as
u32
))
.into
())
}
/// Close a vote that is either approved, disapproved or whose voting period has ended.
...
...
@@ -667,12 +533,17 @@ decl_module! {
/// - up to 3 events
/// # </weight>
#[weight
=
(
weight_for::close::
<
T,
I
>
(
MAX_MEMBERS
.
into(),
// `M`
*
proposal_weight_bound,
// `P1`
T::MaxProposals::get()
.
into(),
// `P2`
*
length_bound
as
Weight,
// B
),
{
let
b
=
*
length_bound
;
let
m
=
T::MaxMembers::get()
;
let
p1
=
*
proposal_weight_bound
;
let
p2
=
T::MaxProposals::get()
;
T::WeightInfo::close_early_approved(b,
m,
p2)
.
max(T::WeightInfo::close_early_disapproved(m,
p2))
.
max(T::WeightInfo::close_approved(b,
m,
p2))
.
max(T::WeightInfo::close_disapproved(m,
p2))
.
saturating_add(p1)
}
,
DispatchClass::Operational
)]
fn
close
(
origin
,
...
...
@@ -699,17 +570,17 @@ decl_module! {
proposal_weight_bound
)
?
;
Self
::
deposit_event
(
RawEvent
::
Closed
(
proposal_hash
,
yes_votes
,
no_votes
));
let
approve_weight
=
Self
::
do_approve_proposal
(
seats
,
voting
,
proposal_hash
,
proposal
);
let
(
proposal_weight
,
proposal_count
)
=
Self
::
do_approve_proposal
(
seats
,
voting
,
proposal_hash
,
proposal
);
return
Ok
(
Some
(
w
eight
_
fo
r
::
close_
without_finalize
::
<
T
,
I
>
(
seats
.into
(),
len
as
Weigh
t
)
.saturating_add
(
ap
pro
ve
_weight
)
T
::
W
eight
In
fo
::
close_
early_approved
(
len
as
u32
,
seats
,
proposal_coun
t
)
.saturating_add
(
pro
posal
_weight
)
)
.into
());
}
else
if
disapproved
{
Self
::
deposit_event
(
RawEvent
::
Closed
(
proposal_hash
,
yes_votes
,
no_votes
));
let
disapprove_weigh
t
=
Self
::
do_disapprove_proposal
(
proposal_hash
);
let
proposal_coun
t
=
Self
::
do_disapprove_proposal
(
proposal_hash
);
return
Ok
(
Some
(
weight_for
::
close_without_finalize
::
<
T
,
I
>
(
seats
.into
(),
0
)
.saturating_add
(
disapprove_weight
)
T
::
WeightInfo
::
close_early_disapproved
(
seats
,
proposal_count
)
)
.into
());
}
...
...
@@ -733,19 +604,17 @@ decl_module! {
proposal_weight_bound
)
?
;
Self
::
deposit_event
(
RawEvent
::
Closed
(
proposal_hash
,
yes_votes
,
no_votes
));
let
approve_weight
=
Self
::
do_approve_proposal
(
seats
,
voting
,
proposal_hash
,
proposal
);
let
(
proposal_weight
,
proposal_count
)
=
Self
::
do_approve_proposal
(
seats
,
voting
,
proposal_hash
,
proposal
);
return
Ok
(
Some
(
weight_for
::
close_without_finalize
::
<
T
,
I
>
(
seats
.into
(),
len
as
Weight
)
.saturating_add
(
T
::
DbWeight
::
get
()
.reads
(
1
))
// read `Prime`
.saturating_add
(
approve_weight
)
T
::
WeightInfo
::
close_approved
(
len
as
u32
,
seats
,
proposal_count
)
.saturating_add
(
proposal_weight
)
)
.into
());
}
else
{
Self
::
deposit_event
(
RawEvent
::
Closed
(
proposal_hash
,
yes_votes
,
no_votes
));
let
disapprove_weigh
t
=
Self
::
do_disapprove_proposal
(
proposal_hash
);
let
proposal_coun
t
=
Self
::
do_disapprove_proposal
(
proposal_hash
);
return
Ok
(
Some
(
weight_for
::
close_without_finalize
::
<
T
,
I
>
(
seats
.into
(),
0
)
.saturating_add
(
T
::
DbWeight
::
get
()
.reads
(
1
))
// read `Prime`
.saturating_add
(
disapprove_weight
)
T
::
WeightInfo
::
close_disapproved
(
seats
,
proposal_count
)
)
.into
());
}
}
...
...
@@ -759,18 +628,15 @@ decl_module! {
///
/// # <weight>
/// Complexity: O(P) where P is the number of max proposals
/// Base Weight: .49 * P
/// DB Weight:
/// * Reads: Proposals
/// * Writes: Voting, Proposals, ProposalOf
/// # </weight>
#[weight
=
T::DbWeight::get()
.
reads_writes(
1
,
3
)
// `Voting`, `Proposals`, `ProposalOf`
.
saturating_add(
490_000
*
Weight::from(T::MaxProposals::get()))
// P2
]
#[weight
=
T::WeightInfo::disapprove_proposal(T::MaxProposals::get())]
fn
disapprove_proposal
(
origin
,
proposal_hash
:
T
::
Hash
)
->
DispatchResultWithPostInfo
{
ensure_root
(
origin
)
?
;
let
actual_weigh
t
=
Self
::
do_disapprove_proposal
(
proposal_hash
);
Ok
(
Some
(
actual_weight
)
.into
())
let
proposal_coun
t
=
Self
::
do_disapprove_proposal
(
proposal_hash
);
Ok
(
Some
(
T
::
WeightInfo
::
disapprove_proposal
(
proposal_count
)
)
.into
())
}
}
}
...
...
@@ -822,8 +688,7 @@ impl<T: Trait<I>, I: Instance> Module<T, I> {
voting
:
Votes
<
T
::
AccountId
,
T
::
BlockNumber
>
,
proposal_hash
:
T
::
Hash
,
proposal
:
<
T
as
Trait
<
I
>>
::
Proposal
,
)
->
Weight
{
let
mut
weight
:
Weight
=
0
;
)
->
(
Weight
,
u32
)
{
Self
::
deposit_event
(
RawEvent
::
Approved
(
proposal_hash
));
let
dispatch_weight
=
proposal
.get_dispatch_info
()
.weight
;
...
...
@@ -832,23 +697,21 @@ impl<T: Trait<I>, I: Instance> Module<T, I> {
Self
::
deposit_event
(
RawEvent
::
Executed
(
proposal_hash
,
result
.map
(|
_
|
())
.map_err
(|
e
|
e
.error
))
);
weight
=
weight
.saturating_add
(
// default to the dispatch info weight for safety
get_result_weight
(
result
)
.unwrap_or
(
dispatch_weight
)
// P1
);
// default to the dispatch info weight for safety
let
proposal_weight
=
get_result_weight
(
result
)
.unwrap_or
(
dispatch_weight
);
// P1
let
remove_
proposal_
weigh
t
=
Self
::
remove_proposal
(
proposal_hash
);
weight
.saturating_add
(
remove_proposal_weigh
t
)
let
proposal_
coun
t
=
Self
::
remove_proposal
(
proposal_hash
);
(
proposal_weight
,
proposal_coun
t
)
}
fn
do_disapprove_proposal
(
proposal_hash
:
T
::
Hash
)
->
Weight
{
fn
do_disapprove_proposal
(
proposal_hash
:
T
::
Hash
)
->
u32
{
// disapproved
Self
::
deposit_event
(
RawEvent
::
Disapproved
(
proposal_hash
));
Self
::
remove_proposal
(
proposal_hash
)
}
// Removes a proposal from the pallet, cleaning up votes and the vector of proposals.
fn
remove_proposal
(
proposal_hash
:
T
::
Hash
)
->
Weight
{
fn
remove_proposal
(
proposal_hash
:
T
::
Hash
)
->
u32
{
// remove proposal and vote
ProposalOf
::
<
T
,
I
>
::
remove
(
&
proposal_hash
);
Voting
::
<
T
,
I
>
::
remove
(
&
proposal_hash
);
...
...
@@ -856,15 +719,14 @@ impl<T: Trait<I>, I: Instance> Module<T, I> {
proposals
.retain
(|
h
|
h
!=
&
proposal_hash
);
proposals
.len
()
+
1
// calculate weight based on original length
});
T
::
DbWeight
::
get
()
.reads_writes
(
1
,
3
)
// `Voting`, `Proposals`, `ProposalOf`
.saturating_add
(
490_000
*
num_proposals
as
Weight
)
// P2
num_proposals
as
u32
}
}
impl
<
T
:
Trait
<
I
>
,
I
:
Instance
>
ChangeMembers
<
T
::
AccountId
>
for
Module
<
T
,
I
>
{
/// Update the members of the collective. Votes are updated and the prime is reset.
///
/// NOTE: Does not enforce the expected `M
AX_MEMBERS
` limit on the amount of members, but
/// NOTE: Does not enforce the expected `M
axMembers
` limit on the amount of members, but
/// the weight estimations rely on it to estimate dispatchable weight.
///
/// # <weight>
...
...
@@ -884,10 +746,10 @@ impl<T: Trait<I>, I: Instance> ChangeMembers<T::AccountId> for Module<T, I> {
outgoing
:
&
[
T
::
AccountId
],
new
:
&
[
T
::
AccountId
],
)
{
if
new
.len
()
>
MAX_MEMBERS
as
usize
{
if
new
.len
()
>
T
::
MaxMembers
::
get
()
as
usize
{
debug
::
error!
(
"New members count exceeds maximum amount of members expected. (expected: {}, actual: {})"
,
MAX_MEMBERS
,
T
::
MaxMembers
::
get
()
,
new
.len
()
);
}
...
...
@@ -1047,6 +909,7 @@ mod tests {
pub
const
AvailableBlockRatio
:
Perbill
=
Perbill
::
one
();
pub
const
MotionDuration
:
u64
=
3
;
pub
const
MaxProposals
:
u32
=
100
;
pub
const
MaxMembers
:
u32
=
100
;
}
impl
frame_system
::
Trait
for
Test
{
type
BaseCallFilter
=
();
...
...
@@ -1081,6 +944,7 @@ mod tests {
type
Event
=
Event
;
type
MotionDuration
=
MotionDuration
;
type
MaxProposals
=
MaxProposals
;
type
MaxMembers
=
MaxMembers
;
type
WeightInfo
=
();
}
impl
Trait
for
Test
{
...
...
@@ -1089,6 +953,7 @@ mod tests {
type
Event
=
Event
;
type
MotionDuration
=
MotionDuration
;
type
MaxProposals
=
MaxProposals
;
type
MaxMembers
=
MaxMembers
;
type
WeightInfo
=
();
}
...
...
@@ -1164,7 +1029,7 @@ mod tests {
#[test]
fn
proposal_weight_limit_works_on_approve
()
{
new_test_ext
()
.execute_with
(||
{
let
proposal
=
Call
::
Collective
(
crate
::
Call
::
set_members
(
vec!
[
1
,
2
,
3
],
None
,
M
AX_MEMBERS
));
let
proposal
=
Call
::
Collective
(
crate
::
Call
::
set_members
(
vec!
[
1
,
2
,
3
],
None
,
M
axMembers
::
get
()
));
let
proposal_len
:
u32
=
proposal
.using_encoded
(|
p
|
p
.len
()
as
u32
);
let
proposal_weight
=
proposal
.get_dispatch_info
()
.weight
;
let
hash
=
BlakeTwo256
::
hash_of
(
&
proposal
);
...
...
@@ -1184,7 +1049,7 @@ mod tests {
#[test]
fn
proposal_weight_limit_ignored_on_disapprove
()
{
new_test_ext
()
.execute_with
(||
{
let
proposal
=
Call
::
Collective
(
crate
::
Call
::
set_members
(
vec!
[
1
,
2
,
3
],
None
,
M
AX_MEMBERS
));
let
proposal
=
Call
::
Collective
(
crate
::
Call
::
set_members
(
vec!
[
1
,
2
,
3
],
None
,
M
axMembers
::
get
()
));
let
proposal_len
:
u32
=
proposal
.using_encoded
(|
p
|
p
.len
()
as
u32
);
let
proposal_weight
=
proposal
.get_dispatch_info
()
.weight
;
let
hash
=
BlakeTwo256
::
hash_of
(
&
proposal
);
...
...
@@ -1205,7 +1070,7 @@ mod tests {
let
proposal_len
:
u32
=
proposal
.using_encoded
(|
p
|
p
.len
()
as
u32
);
let
proposal_weight
=
proposal
.get_dispatch_info
()
.weight
;
let
hash
=
BlakeTwo256
::
hash_of
(
&
proposal
);
assert_ok!
(
Collective
::
set_members
(
Origin
::
root
(),
vec!
[
1
,
2
,
3
],
Some
(
3
),
M
AX_MEMBERS
));
assert_ok!
(
Collective
::
set_members
(
Origin
::
root
(),
vec!
[
1
,
2
,
3
],
Some
(
3
),
M
axMembers
::
get
()
));
assert_ok!
(
Collective
::
propose
(
Origin
::
signed
(
1
),
3
,
Box
::
new
(
proposal
.clone
()),
proposal_len
));
assert_ok!
(
Collective
::
vote
(
Origin
::
signed
(
2
),
hash
.clone
(),
0
,
true
));
...
...
@@ -1230,7 +1095,7 @@ mod tests {
let
proposal_len
:
u32
=
proposal
.using_encoded
(|
p
|
p
.len
()
as
u32
);
let
proposal_weight
=
proposal
.get_dispatch_info
()
.weight
;
let
hash
=
BlakeTwo256
::
hash_of
(
&
proposal
);
assert_ok!
(
Collective
::
set_members
(
Origin
::
root
(),
vec!
[
1
,
2
,
3
],
Some
(
1
),
M
AX_MEMBERS
));
assert_ok!
(
Collective
::
set_members
(
Origin
::
root
(),
vec!
[
1
,
2
,
3
],
Some
(
1
),
M
axMembers
::
get
()
));
assert_ok!
(
Collective
::
propose
(
Origin
::
signed
(
1
),
3
,
Box
::
new
(
proposal
.clone
()),
proposal_len
));
assert_ok!
(
Collective
::
vote
(
Origin
::
signed
(
2
),
hash
.clone
(),
0
,
true
));
...
...
@@ -1298,7 +1163,7 @@ mod tests {
Collective
::
voting
(
&
hash
),
Some
(
Votes
{
index
:
0
,
threshold
:
3
,
ayes
:
vec!
[
1
,
2
],
nays
:
vec!
[],
end
})
);
assert_ok!
(
Collective
::
set_members
(
Origin
::
root
(),
vec!
[
2
,
3
,
4
],
None
,
M
AX_MEMBERS
));
assert_ok!
(
Collective
::
set_members
(
Origin
::
root
(),
vec!
[
2
,
3
,
4
],
None
,
M
axMembers
::
get
()
));
assert_eq!
(
Collective
::
voting
(
&
hash
),
Some
(
Votes
{
index
:
0
,
threshold
:
3
,
ayes
:
vec!
[
2
],
nays
:
vec!
[],
end
})
...
...
@@ -1313,7 +1178,7 @@ mod tests {
Collective
::
voting
(
&
hash
),
Some
(
Votes
{
index
:
1
,
threshold
:
2
,
ayes
:
vec!
[
2
],
nays
:
vec!
[
3
],
end
})
);
assert_ok!
(
Collective
::
set_members
(
Origin
::
root
(),
vec!
[
2
,
4
],
None
,
M
AX_MEMBERS
));
assert_ok!
(
Collective
::
set_members
(
Origin
::
root
(),
vec!
[
2
,
4
],
None
,
M
axMembers
::
get
()
));
assert_eq!
(
Collective
::
voting
(
&
hash
),
Some
(
Votes
{
index
:
1
,
threshold
:
2
,
ayes
:
vec!
[
2
],
nays
:
vec!
[],
end
})
...
...
@@ -1371,7 +1236,7 @@ mod tests {
#[test]
fn
correct_validate_and_get_proposal
()
{
new_test_ext
()
.execute_with
(||
{
let
proposal
=
Call
::
Collective
(
crate
::
Call
::
set_members
(
vec!
[
1
,
2
,
3
],
None
,
M
AX_MEMBERS
));
let
proposal
=
Call
::
Collective
(
crate
::
Call
::
set_members
(
vec!
[
1
,
2
,
3
],
None
,
M
axMembers
::
get
()
));
let
length
=
proposal
.encode
()
.len
()
as
u32
;
assert_ok!
(
Collective
::
propose
(
Origin
::
signed
(
1
),
3
,
Box
::
new
(
proposal
.clone
()),
length
));
...
...
frame/elections-phragmen/src/lib.rs
View file @
1fbcc94b
...
...
@@ -87,7 +87,7 @@ use codec::{Encode, Decode};
use
sp_std
::
prelude
::
*
;
use
sp_runtime
::{
DispatchError
,
RuntimeDebug
,
Perbill
,
traits
::{
Zero
,
StaticLookup
,
Convert
},
traits
::{
Zero
,
StaticLookup
,
Convert
,
Saturating
},
};
use
frame_support
::{
decl_storage
,
decl_event
,
ensure
,
decl_module
,
decl_error
,
...
...
@@ -904,14 +904,20 @@ impl<T: Trait> Module<T> {
to_votes
(
Self
::
locked_stake_of
(
who
))
};
let
voters_and_votes
=
Voting
::
<
T
>
::
iter
()
.map
(|(
voter
,
(
stake
,
targets
))|
{
(
voter
,
to_votes
(
stake
),
targets
)
})
// used for prime election.
let
voters_and_stakes
=
Voting
::
<
T
>
::
iter
()
.map
(|(
voter
,
(
stake
,
targets
))|
{
(
voter
,
stake
,
targets
)
})
.collect
::
<
Vec
<
_
>>
();
// used for phragmen.
let
voters_and_votes
=
voters_and_stakes
.iter
()
.cloned
()
.map
(|(
voter
,
stake
,
targets
)|
{
(
voter
,
to_votes
(
stake
),
targets
)}
)
.collect
::
<
Vec
<
_
>>
();
let
maybe_phragmen_result
=
sp_npos_elections
::
seq_phragmen
::
<
T
::
AccountId
,
Perbill
>
(
num_to_elect
,
0
,
candidates
,
voters_and_votes
.clone
()
,
voters_and_votes
,
);
if
let
Some
(
ElectionResult
{
winners
,
assignments
})
=
maybe_phragmen_result
{
...
...
@@ -965,17 +971,26 @@ impl<T: Trait> Module<T> {
// save the members, sorted based on account id.
new_members
.sort_by
(|
i
,
j
|
i
.0
.cmp
(
&
j
.0
));
let
mut
prime_votes
:
Vec
<
_
>
=
new_members
.iter
()
.map
(|
c
|
(
&
c
.0
,
VoteWeight
::
zero
()))
.collect
();
for
(
_
,
stake
,
targets
)
in
voters_and_votes
.into_iter
()
{
for
(
votes
,
who
)
in
targets
.iter
()
// Now we select a prime member using a [Borda count](https://en.wikipedia.org/wiki/Borda_count).
// We weigh everyone's vote for that new member by a multiplier based on the order
// of the votes. i.e. the first person a voter votes for gets a 16x multiplier,
// the next person gets a 15x multiplier, an so on... (assuming `MAXIMUM_VOTE` = 16)
let
mut
prime_votes
:
Vec
<
_
>
=
new_members
.iter
()
.map
(|
c
|
(
&
c
.0
,
BalanceOf
::
<
T
>
::
zero
()))
.collect
();
for
(
_
,
stake
,
targets
)
in
voters_and_stakes
.into_iter
()
{
for
(
vote_multiplier
,
who
)
in
targets
.iter
()
.enumerate
()
.map
(|(
vote
s
,
who
)|
((
MAXIMUM_VOTE
-
vote
s
)
as
u32
,
who
))
.map
(|(
vote
_position
,
who
)|
((
MAXIMUM_VOTE
-
vote
_position
)
as
u32
,
who
))
{
if
let
Ok
(
i
)
=
prime_votes
.binary_search_by_key
(
&
who
,
|
k
|
k
.0
)
{
prime_votes
[
i
]
.1
+=
stake
*
votes
as
VoteWeight
;
prime_votes
[
i
]
.1
=
prime_votes
[
i
]
.1
.saturating_add
(
stake
.saturating_mul
(
vote_multiplier
.into
())
);
}
}
}
// We then select the new member with the highest weighted stake. In the case of
// a tie, the last person in the list with the tied score is selected. This is
// the person with the "highest" account id based on the sort above.
let
prime
=
prime_votes
.into_iter
()
.max_by_key
(|
x
|
x
.1
)
.map
(|
x
|
x
.0
.clone
());
// new_members_ids is sorted by account id.
...
...
frame/evm/Cargo.toml
View file @
1fbcc94b
...
...
@@ -22,7 +22,7 @@ sp-core = { version = "2.0.0-rc6", default-features = false, path = "../../primi
sp-runtime
=
{
version
=
"2.0.0-rc6"
,
default-features
=
false
,
path
=
"../../primitives/runtime"
}
sp-std
=
{
version
=
"2.0.0-rc6"
,
default-features
=
false
,
path
=
"../../primitives/std"
}
sp-io
=
{
version
=
"2.0.0-rc6"
,
default-features
=
false
,
path
=
"../../primitives/io"
}
primitive-types
=
{
version
=
"0.7.0"
,
default-features
=
false
,
features
=
[
"rlp"
]
}
primitive-types
=
{
version
=
"0.7.0"
,
default-features
=
false
,
features
=
[
"rlp"
,
"byteorder"
]
}
rlp
=
{
version
=
"0.4"
,
default-features
=
false
}
evm
=
{
version
=
"0.17"
,
default-features
=
false
}
sha3
=
{
version
=
"0.8"
,
default-features
=
false
}
...
...
frame/grandpa/src/lib.rs
View file @
1fbcc94b
...
...
@@ -376,7 +376,7 @@ mod weight_for {
pub
fn
report_equivocation
<
T
:
super
::
Trait
>
(
validator_count
:
u32
)
->
Weight
{
// we take the validator set count from the membership proof to
// calculate the weight but we set a floor of 100 validators.
let
validator_count
=
validator_count
.m
in
(
100
)
as
u64
;
let
validator_count
=
validator_count
.m
ax
(
100
)
as
u64
;
// worst case we are considering is that the given offender
// is backed by 200 nominators
...
...
frame/grandpa/src/tests.rs
View file @
1fbcc94b
...
...
@@ -842,3 +842,26 @@ fn always_schedules_a_change_on_new_session_when_stalled() {
assert_eq!
(
Grandpa
::
current_set_id
(),
2
);
});
}
#[test]
fn
report_equivocation_has_valid_weight
()
{
// the weight depends on the size of the validator set,
// but there's a lower bound of 100 validators.
assert!
(
(
1
..=
100
)
.map
(
weight_for
::
report_equivocation
::
<
Test
>
)
.collect
::
<
Vec
<
_
>>
()
.windows
(
2
)
.all
(|
w
|
w
[
0
]
==
w
[
1
])
);
// after 100 validators the weight should keep increasing
// with every extra validator.
assert!
(
(
100
..=
1000
)
.map
(
weight_for
::
report_equivocation
::
<
Test
>
)
.collect
::
<
Vec
<
_
>>
()
.windows
(
2
)
.all
(|
w
|
w
[
0
]
<
w
[
1
])
);
}
frame/nicks/src/lib.rs
View file @
1fbcc94b
...
...
@@ -149,12 +149,12 @@ decl_module! {
ensure!
(
name
.len
()
<=
T
::
MaxLength
::
get
(),
Error
::
<
T
>
::
TooLong
);
let
deposit
=
if
let
Some
((
_
,
deposit
))
=
<
NameOf
<
T
>>
::
get
(
&
sender
)
{
Self
::
deposit_event
(
RawEvent
::
Name
Set
(
sender
.clone
()));
Self
::
deposit_event
(
RawEvent
::
Name
Changed
(
sender
.clone
()));
deposit
}
else
{
let
deposit
=
T
::
ReservationFee
::
get
();
T
::
Currency
::
reserve
(
&
sender
,
deposit
.clone
())
?
;
Self
::
deposit_event
(
RawEvent
::
Name
Changed
(
sender
.clone
()));
Self
::
deposit_event
(
RawEvent
::
Name
Set
(
sender
.clone
()));
deposit
};
...
...
frame/offences/benchmarking/src/lib.rs
View file @
1fbcc94b
...
...
@@ -125,7 +125,7 @@ fn create_offender<T: Trait>(n: u32, nominators: u32) -> Result<Offender<T>, &'s
RawOrigin
::
Signed
(
nominator_stash
.clone
())
.into
(),
nominator_controller_lookup
.clone
(),
amount
.clone
(),
reward_destination
,
reward_destination
.clone
()
,
)
?
;
let
selected_validators
:
Vec
<
LookupSourceOf
<
T
>>
=
vec!
[
controller_lookup
.clone
()];
...
...
frame/staking/src/lib.rs
View file @
1fbcc94b
...
...
@@ -425,16 +425,18 @@ pub enum StakerStatus<AccountId> {
/// A destination account for payment.
#[derive(PartialEq,
Eq,
Copy,
Clone,
Encode,
Decode,
RuntimeDebug)]
pub
enum
RewardDestination
{
pub
enum
RewardDestination
<
AccountId
>
{
/// Pay into the stash account, increasing the amount at stake accordingly.
Staked
,
/// Pay into the stash account, not increasing the amount at stake.
Stash
,
/// Pay into the controller account.
Controller
,
/// Pay into a specified account.
Account
(
AccountId
),
}
impl
Default
for
RewardDestination
{
impl
<
AccountId
>
Default
for
RewardDestination
<
AccountId
>
{
fn
default
()
->
Self
{
RewardDestination
::
Staked
}
...
...
@@ -1049,7 +1051,7 @@ decl_storage! {
=>
Option
<
StakingLedger
<
T
::
AccountId
,
BalanceOf
<
T
>>>
;
/// Where the reward payment should be made. Keyed by stash.
pub
Payee
get
(
fn
payee
):
map
hasher
(
twox_64_concat
)
T
::
AccountId
=>
RewardDestination
;
pub
Payee
get
(
fn
payee
):
map
hasher
(
twox_64_concat
)
T
::
AccountId
=>
RewardDestination
<
T
::
AccountId
>
;
/// The map from (wannabe) validator stash key to the preferences of that validator.
pub
Validators
get
(
fn
validators
):
...
...
@@ -1496,7 +1498,7 @@ decl_module! {
pub
fn
bond
(
origin
,
controller
:
<
T
::
Lookup
as
StaticLookup
>
::
Source
,
#[compact]
value
:
BalanceOf
<
T
>
,
payee
:
RewardDestination
,
payee
:
RewardDestination
<
T
::
AccountId
>
,
)
{
let
stash
=
ensure_signed
(
origin
)
?
;
...
...
@@ -1830,7 +1832,7 @@ decl_module! {
/// - Write: Payee
/// # </weight>
#[weight
=
11
*
WEIGHT_PER_MICROS
+
T::DbWeight::get()
.
reads_writes(
1
,
1
)]
fn
set_payee
(
origin
,
payee
:
RewardDestination
)
{
fn
set_payee
(
origin
,
payee
:
RewardDestination
<
T
::
AccountId
>
)
{
let
controller
=
ensure_signed
(
origin
)
?
;
let
ledger
=
Self
::
ledger
(
&
controller
)
.ok_or
(
Error
::
<
T
>
::
NotController
)
?
;
let
stash
=
&
ledger
.stash
;
...
...
@@ -2489,6 +2491,9 @@ impl<T: Trait> Module<T> {
Self
::
update_ledger
(
&
controller
,
&
l
);
r
}),
RewardDestination
::
Account
(
dest_account
)
=>
{
Some
(
T
::
Currency
::
deposit_creating
(
&
dest_account
,
amount
))
}
}
}
...
...
frame/staking/src/tests.rs
View file @
1fbcc94b
...
...
@@ -4359,7 +4359,7 @@ fn test_payout_stakers() {
// We also test that `payout_extra_nominators` works.
ExtBuilder
::
default
()
.has_stakers
(
false
)
.build_and_execute
(||
{
let
balance
=
1000
;
// Create
three
validator
s
:
// Create
a
validator:
bond_validator
(
11
,
10
,
balance
);
// Default(64)
// Create nominators, targeting stash of validators
...
...
@@ -4597,15 +4597,12 @@ fn on_initialize_weight_is_correct() {
});
}
#[test]
fn
payout_creates_controller
()
{
// Here we will test validator can set `max_nominators_payout` and it works.
// We also test that `payout_extra_nominators` works.
ExtBuilder
::
default
()
.has_stakers
(
false
)
.build_and_execute
(||
{
let
balance
=
1000
;
// Create
three
validator
s
:
bond_validator
(
11
,
10
,
balance
);
// Default(64)
// Create
a
validator:
bond_validator
(
11
,
10
,
balance
);
// Create a stash/controller pair
bond_nominator
(
1234
,
1337
,
100
,
vec!
[
11
]);
...
...
@@ -4626,3 +4623,32 @@ fn payout_creates_controller() {
assert!
(
Balances
::
free_balance
(
1337
)
>
0
);
})
}
#[test]
fn
payout_to_any_account_works
()
{
ExtBuilder
::
default
()
.has_stakers
(
false
)
.build_and_execute
(||
{
let
balance
=
1000
;
// Create a validator:
bond_validator
(
11
,
10
,
balance
);
// Default(64)
// Create a stash/controller pair
bond_nominator
(
1234
,
1337
,
100
,
vec!
[
11
]);
// Update payout location
assert_ok!
(
Staking
::
set_payee
(
Origin
::
signed
(
1337
),
RewardDestination
::
Account
(
42
)));
// Reward Destination account doesn't exist
assert_eq!
(
Balances
::
free_balance
(
42
),
0
);
mock
::
start_era
(
1
);
Staking
::
reward_by_ids
(
vec!
[(
11
,
1
)]);
// Compute total payout now for whole duration as other parameter won't change
let
total_payout_0
=
current_total_payout_for_duration
(
3
*
1000
);
assert!
(
total_payout_0
>
100
);
// Test is meaningful if reward something
mock
::
start_era
(
2
);
assert_ok!
(
Staking
::
payout_stakers
(
Origin
::
signed
(
1337
),
11
,
1
));
// Payment is successful
assert!
(
Balances
::
free_balance
(
42
)
>
0
);
})
}
primitives/core/Cargo.toml
View file @
1fbcc94b
...
...
@@ -26,7 +26,7 @@ hash-db = { version = "0.15.2", default-features = false }
hash256-std-hasher
=
{
version
=
"0.15.2"
,
default-features
=
false
}
base58
=
{
version
=
"0.1.0"
,
optional
=
true
}
rand
=
{
version
=
"0.7.3"
,
optional
=
true
,
features
=
[
"small_rng"
]
}
substrate-bip39
=
{
version
=
"0.4.
1
"
,
optional
=
true
}
substrate-bip39
=
{
version
=
"0.4.
2
"
,
optional
=
true
}
tiny-bip39
=
{
version
=
"0.7"
,
optional
=
true
}
regex
=
{
version
=
"1.3.1"
,
optional
=
true
}
num-traits
=
{
version
=
"0.2.8"
,
default-features
=
false
}
...
...
primitives/utils/Cargo.toml
View file @
1fbcc94b
...
...
@@ -12,7 +12,7 @@ description = "I/O for Substrate runtimes"
futures
=
"0.3.4"
futures-core
=
"0.3.4"
lazy_static
=
"1.4.0"
prometheus
=
{
version
=
"0.
9
.0"
,
default-features
=
false
}
prometheus
=
{
version
=
"0.
10
.0"
,
default-features
=
false
}
futures-timer
=
"3.0.2"
[features]
...
...
utils/prometheus/Cargo.toml
View file @
1fbcc94b
...
...
@@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
log
=
"0.4.8"
prometheus
=
{
version
=
"0.
9
"
,
default-features
=
false
}
prometheus
=
{
version
=
"0.
10.0
"
,
default-features
=
false
}
futures-util
=
{
version
=
"0.3.1"
,
default-features
=
false
,
features
=
[
"io"
]
}
derive_more
=
"0.99"
...
...
Prev
1
2
3
Next