Skip to content

Commit a4522b3

Browse files
Merge pull request #1433 from galacticcouncil/fix/remove-process-votes-from-on-before-vote
fix(staking): remove O(N) vote processing from vote extrinsic
2 parents 1fcbca4 + 4fed6e3 commit a4522b3

9 files changed

Lines changed: 175 additions & 166 deletions

File tree

Cargo.lock

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

integration-tests/src/staking.rs

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2508,7 +2508,7 @@ fn increase_stake_should_work_when_referendum_ongoing_and_votes_processed() {
25082508
}
25092509

25102510
#[test]
2511-
fn voting_on_next_referenda_should_process_votes() {
2511+
fn removing_vote_should_process_votes() {
25122512
TestNet::reset();
25132513
Hydra::execute_with(|| {
25142514
init_omnipool();
@@ -2569,27 +2569,41 @@ fn voting_on_next_referenda_should_process_votes() {
25692569

25702570
end_referendum();
25712571

2572+
let alice_position_id = pallet_staking::Pallet::<hydradx_runtime::Runtime>::get_user_position_id(&ALICE.into())
2573+
.unwrap()
2574+
.unwrap();
2575+
2576+
// Before remove_vote, the finished vote has not been settled: still recorded in Votes,
2577+
// no points awarded, and not present in VotesRewarded.
2578+
let position_before =
2579+
pallet_staking::Pallet::<hydradx_runtime::Runtime>::get_position(alice_position_id).unwrap();
2580+
assert!(
2581+
pallet_staking::Pallet::<hydradx_runtime::Runtime>::get_position_votes(alice_position_id)
2582+
.votes
2583+
.iter()
2584+
.any(|(idx, _)| *idx == r)
2585+
);
25722586
assert!(
25732587
pallet_staking::Pallet::<hydradx_runtime::Runtime>::processed_votes::<AccountId, u32>(ALICE.into(), r)
25742588
.is_none()
25752589
);
25762590

2577-
let r = begin_referendum();
2578-
assert_ok!(ConvictionVoting::vote(
2591+
assert_ok!(ConvictionVoting::remove_vote(
25792592
hydradx_runtime::RuntimeOrigin::signed(ALICE.into()),
2580-
r,
2581-
AccountVote::Standard {
2582-
vote: Vote {
2583-
aye: true,
2584-
conviction: Conviction::Locked6x,
2585-
},
2586-
balance: 1_000_000 * UNITS,
2587-
}
2593+
Some(ROOT_TRACK),
2594+
r
25882595
));
2596+
2597+
// After remove_vote, settlement has happened: vote removed from Votes and points awarded.
25892598
assert!(
2590-
pallet_staking::Pallet::<hydradx_runtime::Runtime>::processed_votes::<AccountId, u32>(ALICE.into(), 0)
2591-
.is_some()
2599+
!pallet_staking::Pallet::<hydradx_runtime::Runtime>::get_position_votes(alice_position_id)
2600+
.votes
2601+
.iter()
2602+
.any(|(idx, _)| *idx == r)
25922603
);
2604+
let position_after =
2605+
pallet_staking::Pallet::<hydradx_runtime::Runtime>::get_position(alice_position_id).unwrap();
2606+
assert!(position_after.get_action_points() > position_before.get_action_points());
25932607
});
25942608
}
25952609

pallets/staking/src/integrations/conviction_voting.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,6 @@ where
3535
}
3636
};
3737

38-
Pallet::<T>::process_votes(who, position_id, position)?;
39-
4038
let amount = vote.balance();
4139
let conviction = if let AccountVote::Standard { vote, .. } = vote {
4240
match vote.conviction {

pallets/staking/src/tests/tests.rs

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ fn process_votes_should_do_nothing_when_referendum_doesnt_exists() {
269269
}
270270

271271
#[test]
272-
fn process_votes_should_work_when_on_vote_is_called() {
272+
fn on_before_vote_should_not_process_finished_votes() {
273273
ExtBuilder::default()
274274
.with_endowed_accounts(vec![
275275
(ALICE, HDX, 150_000 * ONE),
@@ -351,14 +351,9 @@ fn process_votes_should_work_when_on_vote_is_called() {
351351
));
352352

353353
//Assert
354-
assert_eq!(
355-
Position {
356-
action_points: 64_u128,
357-
..position_before
358-
},
359-
Staking::positions(position_id).unwrap()
360-
);
361-
assert_eq!(Votes::<Test>::get(position_id).votes.len(), 3);
354+
// on_before_vote no longer processes finished votes - settlement happens at remove_vote time.
355+
assert_eq!(position_before, Staking::positions(position_id).unwrap());
356+
assert_eq!(Votes::<Test>::get(position_id).votes.len(), 7);
362357
});
363358
}
364359

runtime/hydradx/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ sp-version = { workspace = true }
162162
sp-trie = { workspace = true }
163163
sp-io = { workspace = true }
164164
primitive-types = { workspace = true }
165+
static_assertions = { workspace = true }
165166

166167
# Frontier
167168
fp-rpc = { workspace = true }

runtime/hydradx/src/assets.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1629,10 +1629,6 @@ impl GetByKey<FixedU128, Point> for StakingMinSlash {
16291629
}
16301630
}
16311631

1632-
parameter_types! {
1633-
pub const MaxVotes: u32 = 25;
1634-
}
1635-
16361632
impl pallet_staking::Config for Runtime {
16371633
type AuthorityOrigin = EitherOf<EnsureRoot<Self::AccountId>, GeneralAdmin>;
16381634
type AssetId = AssetId;
@@ -1653,7 +1649,7 @@ impl pallet_staking::Config for Runtime {
16531649
type NFTCollectionId = ConstU128<2222>;
16541650
type Collections = FreezableNFT<Runtime, Self::RuntimeOrigin>;
16551651
type NFTHandler = Uniques;
1656-
type MaxVotes = MaxVotes;
1652+
type MaxVotes = governance::MaxVotes;
16571653
type ReferendumInfo = pallet_staking::integrations::conviction_voting::DirectReferendumStatus<Runtime>;
16581654
type MaxPointsPerAction = PointsPerAction;
16591655
type Vesting = VestingInfo<Runtime>;
@@ -1664,6 +1660,12 @@ impl pallet_staking::Config for Runtime {
16641660
type MaxLocks = MaxLocks;
16651661
}
16661662

1663+
//Make sure staking and conviction voting are using same `MaxVotes` value
1664+
static_assertions::const_assert_eq!(
1665+
<Runtime as pallet_staking::Config>::MaxVotes::get(),
1666+
<Runtime as pallet_conviction_voting::Config>::MaxVotes::get()
1667+
);
1668+
16671669
// LBP
16681670
pub struct AssetPairAccountId<T: frame_system::Config>(PhantomData<T>);
16691671
impl<T: frame_system::Config> AssetPairAccountIdFor<AssetId, T::AccountId> for AssetPairAccountId<T>

runtime/hydradx/src/governance/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,14 +180,16 @@ impl pallet_treasury::Config for Runtime {
180180

181181
parameter_types! {
182182
pub const VoteLockingPeriod: BlockNumber = 7 * DAYS;
183+
#[derive(Debug)]
184+
pub const MaxVotes: u32 = 25;
183185
}
184186

185187
impl pallet_conviction_voting::Config for Runtime {
186188
type WeightInfo = weights::pallet_conviction_voting::HydraWeight<Runtime>;
187189
type RuntimeEvent = RuntimeEvent;
188190
type Currency = Balances;
189191
type VoteLockingPeriod = VoteLockingPeriod;
190-
type MaxVotes = ConstU32<25>;
192+
type MaxVotes = MaxVotes;
191193
type MaxTurnout = frame_support::traits::tokens::currency::ActiveIssuanceOf<Balances, Self::AccountId>;
192194
type Polls = Referenda;
193195
type VotingHooks = pallet_staking::integrations::conviction_voting::StakingConvictionVoting<Runtime>;

runtime/hydradx/src/weights/pallet_conviction_voting.rs

Lines changed: 33 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
//! Autogenerated weights for `pallet_conviction_voting`
2020
//!
2121
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 49.1.0
22-
//! DATE: 2026-04-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
22+
//! DATE: 2026-05-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
2323
//! WORST CASE MAP SIZE: `1000000`
2424
//! HOSTNAME: `bench-bot`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz`
2525
//! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: `1024`
@@ -64,12 +64,12 @@ impl<T: frame_system::Config> pallet_conviction_voting::WeightInfo for HydraWeig
6464
/// Proof: `Uniques::Account` (`max_values`: None, `max_size`: Some(112), added: 2587, mode: `MaxEncodedLen`)
6565
/// Storage: `Staking::Positions` (r:1 w:1)
6666
/// Proof: `Staking::Positions` (`max_values`: None, `max_size`: Some(132), added: 2607, mode: `MaxEncodedLen`)
67+
/// Storage: `Balances::Locks` (r:1 w:1)
68+
/// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`)
6769
/// Storage: `Staking::Votes` (r:1 w:1)
6870
/// Proof: `Staking::Votes` (`max_values`: None, `max_size`: Some(558), added: 3033, mode: `MaxEncodedLen`)
69-
/// Storage: `Referenda::ReferendumInfoFor` (r:25 w:1)
71+
/// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1)
7072
/// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`)
71-
/// Storage: `Balances::Locks` (r:1 w:1)
72-
/// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`)
7373
/// Storage: `ConvictionVoting::VotingFor` (r:1 w:1)
7474
/// Proof: `ConvictionVoting::VotingFor` (`max_values`: None, `max_size`: Some(1429), added: 3904, mode: `MaxEncodedLen`)
7575
/// Storage: `ConvictionVoting::ClassLocksFor` (r:1 w:1)
@@ -82,23 +82,23 @@ impl<T: frame_system::Config> pallet_conviction_voting::WeightInfo for HydraWeig
8282
/// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`)
8383
fn vote_new() -> Weight {
8484
// Proof Size summary in bytes:
85-
// Measured: `12032`
86-
// Estimated: `86265`
87-
// Minimum execution time: 256_121_000 picoseconds.
88-
Weight::from_parts(258_036_000, 86265)
89-
.saturating_add(T::DbWeight::get().reads(35_u64))
85+
// Measured: `4692`
86+
// Estimated: `42428`
87+
// Minimum execution time: 124_368_000 picoseconds.
88+
Weight::from_parts(125_529_000, 42428)
89+
.saturating_add(T::DbWeight::get().reads(11_u64))
9090
.saturating_add(T::DbWeight::get().writes(7_u64))
9191
}
9292
/// Storage: `Uniques::Account` (r:2 w:0)
9393
/// Proof: `Uniques::Account` (`max_values`: None, `max_size`: Some(112), added: 2587, mode: `MaxEncodedLen`)
9494
/// Storage: `Staking::Positions` (r:1 w:1)
9595
/// Proof: `Staking::Positions` (`max_values`: None, `max_size`: Some(132), added: 2607, mode: `MaxEncodedLen`)
96+
/// Storage: `Balances::Locks` (r:1 w:1)
97+
/// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`)
9698
/// Storage: `Staking::Votes` (r:1 w:1)
9799
/// Proof: `Staking::Votes` (`max_values`: None, `max_size`: Some(558), added: 3033, mode: `MaxEncodedLen`)
98-
/// Storage: `Referenda::ReferendumInfoFor` (r:25 w:1)
100+
/// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1)
99101
/// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`)
100-
/// Storage: `Balances::Locks` (r:1 w:1)
101-
/// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`)
102102
/// Storage: `ConvictionVoting::VotingFor` (r:1 w:1)
103103
/// Proof: `ConvictionVoting::VotingFor` (`max_values`: None, `max_size`: Some(1429), added: 3904, mode: `MaxEncodedLen`)
104104
/// Storage: `ConvictionVoting::ClassLocksFor` (r:1 w:1)
@@ -113,11 +113,11 @@ impl<T: frame_system::Config> pallet_conviction_voting::WeightInfo for HydraWeig
113113
/// Proof: `Scheduler::Retries` (`max_values`: None, `max_size`: Some(30), added: 2505, mode: `MaxEncodedLen`)
114114
fn vote_existing() -> Weight {
115115
// Proof Size summary in bytes:
116-
// Measured: `12424`
117-
// Estimated: `86265`
118-
// Minimum execution time: 282_759_000 picoseconds.
119-
Weight::from_parts(290_746_000, 86265)
120-
.saturating_add(T::DbWeight::get().reads(36_u64))
116+
// Measured: `5084`
117+
// Estimated: `83866`
118+
// Minimum execution time: 149_529_000 picoseconds.
119+
Weight::from_parts(152_350_000, 83866)
120+
.saturating_add(T::DbWeight::get().reads(12_u64))
121121
.saturating_add(T::DbWeight::get().writes(9_u64))
122122
}
123123
/// Storage: `ConvictionVoting::VotingFor` (r:1 w:1)
@@ -140,8 +140,8 @@ impl<T: frame_system::Config> pallet_conviction_voting::WeightInfo for HydraWeig
140140
// Proof Size summary in bytes:
141141
// Measured: `3672`
142142
// Estimated: `83866`
143-
// Minimum execution time: 114_162_000 picoseconds.
144-
Weight::from_parts(115_861_000, 83866)
143+
// Minimum execution time: 115_365_000 picoseconds.
144+
Weight::from_parts(117_060_000, 83866)
145145
.saturating_add(T::DbWeight::get().reads(9_u64))
146146
.saturating_add(T::DbWeight::get().writes(7_u64))
147147
}
@@ -161,8 +161,8 @@ impl<T: frame_system::Config> pallet_conviction_voting::WeightInfo for HydraWeig
161161
// Proof Size summary in bytes:
162162
// Measured: `3147`
163163
// Estimated: `6164`
164-
// Minimum execution time: 83_726_000 picoseconds.
165-
Weight::from_parts(84_478_000, 6164)
164+
// Minimum execution time: 84_125_000 picoseconds.
165+
Weight::from_parts(84_753_000, 6164)
166166
.saturating_add(T::DbWeight::get().reads(7_u64))
167167
.saturating_add(T::DbWeight::get().writes(3_u64))
168168
}
@@ -185,12 +185,12 @@ impl<T: frame_system::Config> pallet_conviction_voting::WeightInfo for HydraWeig
185185
/// The range of component `r` is `[0, 3]`.
186186
fn delegate(r: u32, ) -> Weight {
187187
// Proof Size summary in bytes:
188-
// Measured: `253 + r * (1028 ±0)`
188+
// Measured: `298 + r * (1028 ±0)`
189189
// Estimated: `83866 + r * (3411 ±0)`
190-
// Minimum execution time: 59_745_000 picoseconds.
191-
Weight::from_parts(64_994_711, 83866)
192-
// Standard Error: 293_828
193-
.saturating_add(Weight::from_parts(36_220_681, 0).saturating_mul(r.into()))
190+
// Minimum execution time: 61_872_000 picoseconds.
191+
Weight::from_parts(67_798_487, 83866)
192+
// Standard Error: 288_095
193+
.saturating_add(Weight::from_parts(36_733_587, 0).saturating_mul(r.into()))
194194
.saturating_add(T::DbWeight::get().reads(6_u64))
195195
.saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(r.into())))
196196
.saturating_add(T::DbWeight::get().writes(4_u64))
@@ -210,10 +210,10 @@ impl<T: frame_system::Config> pallet_conviction_voting::WeightInfo for HydraWeig
210210
// Proof Size summary in bytes:
211211
// Measured: `477 + r * (890 ±0)`
212212
// Estimated: `83866 + r * (3411 ±0)`
213-
// Minimum execution time: 30_226_000 picoseconds.
214-
Weight::from_parts(34_231_950, 83866)
215-
// Standard Error: 217_216
216-
.saturating_add(Weight::from_parts(33_445_261, 0).saturating_mul(r.into()))
213+
// Minimum execution time: 31_153_000 picoseconds.
214+
Weight::from_parts(34_865_705, 83866)
215+
// Standard Error: 215_623
216+
.saturating_add(Weight::from_parts(33_825_763, 0).saturating_mul(r.into()))
217217
.saturating_add(T::DbWeight::get().reads(2_u64))
218218
.saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(r.into())))
219219
.saturating_add(T::DbWeight::get().writes(2_u64))
@@ -232,10 +232,10 @@ impl<T: frame_system::Config> pallet_conviction_voting::WeightInfo for HydraWeig
232232
/// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`)
233233
fn unlock() -> Weight {
234234
// Proof Size summary in bytes:
235-
// Measured: `1456`
235+
// Measured: `1501`
236236
// Estimated: `4894`
237-
// Minimum execution time: 63_264_000 picoseconds.
238-
Weight::from_parts(64_155_000, 4894)
237+
// Minimum execution time: 66_518_000 picoseconds.
238+
Weight::from_parts(66_986_000, 4894)
239239
.saturating_add(T::DbWeight::get().reads(5_u64))
240240
.saturating_add(T::DbWeight::get().writes(3_u64))
241241
}

0 commit comments

Comments
 (0)