Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add voting hooks to conviction-voting pallet #3

Closed
wants to merge 5 commits into from

Conversation

enthusiastmartin
Copy link

@enthusiastmartin enthusiastmartin commented Sep 16, 2024

Description

We, at Hydration, need to perform additional actions when user votes and/or remove vote. Our use case is tied to staking, where we need to reward an account for voting.

Therefore, we need to know when a new vote is registeered. We propose this PR to add voting hooks into the conviction-voting pallet.

This change does not change the original behaviour ( except one special case described below).

In the pallet config, there is a new config parameter:

    type VotingHooks: VotingHooks<Self::AccountId, PollIndexOf<Self, I>, BalanceOf<Self, I>>;

Anything that integrates conviction-voting pallet, can decide to implement the voting hooks and perform desirable action on events such as on_vote and on_remove_vote.

The VotingHooks traits looks like:

pub trait VotingHooks<AccountId, Index, Balance> {
	// Called when vote is executed.
	fn on_vote(who: &AccountId, ref_index: Index, vote: AccountVote<Balance>) -> DispatchResult;

	// Called when removed vote is executed.
	// is_finished indicates the state of the referendum = None if referendum is cancelled, Some(true) if referendum is ongoing and Some(false) when finished.
	fn on_remove_vote(who: &AccountId, ref_index: Index, ongoing: Option<bool>);

	// Called when removed vote is executed and voter lost the direction to possibly lock some balance.
	// Can return an amount that should be locked for the conviction time.
	fn balance_locked_on_unsuccessful_vote(who: &AccountId, ref_index: Index) -> Option<Balance>;

	#[cfg(feature = "runtime-benchmarks")]
	fn on_vote_worst_case(who: &AccountId);

	#[cfg(feature = "runtime-benchmarks")]
	fn on_remove_vote_worst_case(who: &AccountId);
}

The on_voteand on_remove_vote are pretty self-explanatory and are called when a vote is added/removed.

There is a special hook called balance_locked_on_unsuccessful_vote. This is a special hook that is called when a vote that is being removed was in opposition to the winning vote.
The result of this function can return a balance that will locked for the conviction time. That means, if a value is returned, it is locked for conviction time. This is the only change that modifies original behavior of the pallet where there was no locking in such case.

It is a special case, where a chain can decide that it needs to lock the balance even in the case of not-winning vote. We, at Hydration, uses voting hooks with staking and require such functionality. Obviously, returning None by defaut, completely disables this feature and its behavior remains as it is.

Integration

To integrate VotingHooks and to perform certain actions as described above, it is required to implement corresponding trait.

Otherwise, simple

    type VotingHooks = ();

can be used and conviction-voting pallet works as nothing happens.

Review Notes

The hooks are called in try_vote and try_remove_vote with parameters.

on_vote hook needs an account that voted on referendum identified by ref_index and the vote itself.

    fn on_vote(who: &AccountId, ref_index: Index, vote: AccountVote<Balance>) -> DispatchResult;

on_remove_vote needs an account that is removing the vote from referendum identified by ref_index. Also bool flag is added to provide information if the referendum is still ongoing.

    fn on_remove_vote(who: &AccountId, ref_index: Index, ongoing: Option<bool>);

ongoing flag is an option because value None is used when referendum has been cancelled.

Tests

Unit tests are added to verify that each hooks is called with correct parameters.

Benchmarks

VotingHook trait contains:

	#[cfg(feature = "runtime-benchmarks")]
	fn on_vote_worst_case(who: &AccountId);

	#[cfg(feature = "runtime-benchmarks")]
	fn on_remove_vote_worst_case(who: &AccountId);

These are called in benchmarks to set up worst cases for the hooks.

Question/Consideration: WE considered adding BenchmarkHelper instead of having the runtime-benchmarks functions in the trait. But I believe this is easier to understand when and why it needs to implement worst cases scenarios ( only if voting hook is used).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant