-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Council motions for approving treasury proposals #694
Conversation
#[derive(Encode, Decode, Default, Copy, Clone, PartialEq, Eq)] | ||
pub struct Permill(u32); | ||
|
||
// TODO: impl Mul<Permill> for N where N: As<usize> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still todo; not introduced with this PR.
demo/cli/src/lib.rs
Outdated
@@ -211,6 +211,12 @@ pub fn run<I, T>(args: I) -> error::Result<()> where | |||
timestamp: Some(TimestampConfig { | |||
period: 5, // 5 second block time. | |||
}), | |||
treasury: Some(TreasuryConfig { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Double space
|
||
<Proposals<T>>::mutate(|proposals| proposals.push(proposal_hash)); | ||
<ProposalOf<T>>::insert(proposal_hash, *proposal); | ||
<Voting<T>>::insert(proposal_hash, (threshold, vec![who.clone()], vec![])); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens if threshold
is less than 2? Does it makes sense at all?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1 is technically fine (just means "one council member").
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hm, but the condition for the approval is:
let approved = yes_votes >= threshold;
then if threshold=1
then such a proposal should be accepted on creation, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
indeed.
return Err("duplicate vote ignored") | ||
} | ||
if let Some(pos) = position_no { | ||
voting.2.remove(pos); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we use swap_remove
here and for the disapprove case?
} | ||
|
||
let yes_votes = voting.1.len() as u32; | ||
let no_votes = voting.2.len() as u32; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
voting.1
and voting.2
seems a little obscure to me here and above, consider introducing a separate structure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
an extra structure is fiddly to get right when you need to have it generic over an associated type of the configuration trait thanks to serde and compiler bugs.
return Err("duplicate vote ignored") | ||
} | ||
if let Some(pos) = position_yes { | ||
voting.1.remove(pos); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why would one want to change his mind?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
they might be female.
ducks :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
heh, but i still wonder if it's strictly necessary.
I'm a bit worried that it might be susceptible to be gamable somehow...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can't see how...
Self::deposit_event(RawEvent::Voted(who, proposal, approve, yes_votes, no_votes)); | ||
|
||
let threshold = voting.0; | ||
let potential_votes = <Council<T>>::active_council().len() as u32; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we care about changing the structure of the council?
As far as I can see, it's possible, but we don't check for that and the code seems to depend on this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you expand on that?
no_votes
cannot ever be greater than potential_votes
, right?
demo/cli/src/lib.rs
Outdated
treasury: Some(TreasuryConfig { | ||
proposal_bond: Permill::from_percent(5), | ||
proposal_bond_minimum: 1_000_000, | ||
spend_period: 12 * 60 * 24, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hm, this looks like a timestamp or something, but it's actually should be a BlockNumber
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it should not be a block number per se but rather a number of blocks for which the type is BlockNumber
. in this case, we assume 12 blocks per minute (1 every 5 seconds), multiply by 60 and 24 to get approximately one day.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ha, that's clear now!
|
||
ensure!(Self::is_councillor(&who), "voter not on council"); | ||
|
||
let mut voting = Self::voting(&proposal); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider there are two extrinsics that vote for the same proposal. The first one will lead to execution of proposal. What would happen with the second one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
panic, and indeed, that should just be a bail.
|
||
let proposal_hash = T::Hashing::hash_of(&proposal); | ||
|
||
ensure!(!<ProposalOf<T>>::exists(proposal_hash), "duplicate proposals not allowed"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note that there is a (slight) chance of reusing votes.
Let's say that we have proposal P, 4 active councillors (c0..c3) and the following extrinsics were published:
propose(c0, 2, P)
vote(c1, H(P))
vote(c2, H(P))
vote(c3, H(P))
Now it seems that the 3rd extrinsic will panic, but let's assume that it's fixed by no-oping.
Some rogue councillor c0
can issue propose(c0, 2, P)
extrinsic that has a slight chance to be applied before votes from c2
and c3
and thus use these votes for approving the second proposal. This would be more effective, if c0
also act as front-running validator.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i guess i can introduce a proposal index to ensure they're unique.
@pepyakin ^^^ |
/// The actual value represented by the impl'ing type. | ||
const VALUE: u32; | ||
} | ||
/// Type representingn the value 0 for the `Value` trait. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpick: representingn
spelling mistake that repeats for all defintions.
type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>; | ||
} | ||
|
||
/// Origin for the system module. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we update this comment?
@pepyakin ^^^ |
@bkchr ^^^ |
* Add some tests Signed-off-by: koushiro <[email protected]> * Add test Signed-off-by: koushiro <[email protected]> * Cargo fmt Signed-off-by: koushiro <[email protected]> * Replace assert with assert_ok and assert_noop Signed-off-by: koushiro <[email protected]> * Cargo fmt Signed-off-by: koushiro <[email protected]>
* error: `RpcError` with custom client error Signed-off-by: Alexandru Vasile <[email protected]> * error: Add `SubscriptionDropped` and panic on param errors Signed-off-by: Alexandru Vasile <[email protected]> * Update subxt/src/rpc/rpc_client_t.rs Co-authored-by: James Wilson <[email protected]> * Apply rustfmt Signed-off-by: Alexandru Vasile <[email protected]> Signed-off-by: Alexandru Vasile <[email protected]> Co-authored-by: James Wilson <[email protected]>
Closes #680
Permill
and add constructorsEnsureOrigin
trait for allowing parameterised origin-guardsEnsureOrigin
Members
origin and an according implementation ofEnsureOrigin
council_motions
module that can dispatch from council origincouncil_motions
into demo runtime.