referrerpolicy=no-referrer-when-downgrade

sc_consensus_grandpa/
aux_schema.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19//! Schema for stuff in the aux-db.
20
21use std::fmt::Debug;
22
23use codec::{Decode, Encode};
24use finality_grandpa::round::State as RoundState;
25use log::{info, warn};
26
27use fork_tree::ForkTree;
28use sc_client_api::backend::AuxStore;
29use sp_blockchain::{Error as ClientError, Result as ClientResult};
30use sp_consensus_grandpa::{AuthorityList, RoundNumber, SetId};
31use sp_runtime::traits::{Block as BlockT, NumberFor};
32
33use crate::{
34	authorities::{
35		AuthoritySet, AuthoritySetChanges, DelayKind, PendingChange, SharedAuthoritySet,
36	},
37	environment::{
38		CompletedRound, CompletedRounds, CurrentRounds, HasVoted, SharedVoterSetState,
39		VoterSetState,
40	},
41	GrandpaJustification, NewAuthoritySet, LOG_TARGET,
42};
43
44const VERSION_KEY: &[u8] = b"grandpa_schema_version";
45const SET_STATE_KEY: &[u8] = b"grandpa_completed_round";
46const CONCLUDED_ROUNDS: &[u8] = b"grandpa_concluded_rounds";
47const AUTHORITY_SET_KEY: &[u8] = b"grandpa_voters";
48const BEST_JUSTIFICATION: &[u8] = b"grandpa_best_justification";
49
50const CURRENT_VERSION: u32 = 3;
51
52/// The voter set state.
53#[derive(Debug, Clone, Encode, Decode)]
54#[cfg_attr(test, derive(PartialEq))]
55pub enum V1VoterSetState<H, N> {
56	/// The voter set state, currently paused.
57	Paused(RoundNumber, RoundState<H, N>),
58	/// The voter set state, currently live.
59	Live(RoundNumber, RoundState<H, N>),
60}
61
62type V0VoterSetState<H, N> = (RoundNumber, RoundState<H, N>);
63
64#[derive(Debug, Clone, Encode, Decode, PartialEq)]
65struct V0PendingChange<H, N> {
66	next_authorities: AuthorityList,
67	delay: N,
68	canon_height: N,
69	canon_hash: H,
70}
71
72#[derive(Debug, Clone, Encode, Decode, PartialEq)]
73struct V0AuthoritySet<H, N> {
74	current_authorities: AuthorityList,
75	set_id: SetId,
76	pending_changes: Vec<V0PendingChange<H, N>>,
77}
78
79impl<H, N> Into<AuthoritySet<H, N>> for V0AuthoritySet<H, N>
80where
81	H: Clone + Debug + PartialEq,
82	N: Clone + Debug + Ord,
83{
84	fn into(self) -> AuthoritySet<H, N> {
85		let mut pending_standard_changes = ForkTree::new();
86
87		for old_change in self.pending_changes {
88			let new_change = PendingChange {
89				next_authorities: old_change.next_authorities,
90				delay: old_change.delay,
91				canon_height: old_change.canon_height,
92				canon_hash: old_change.canon_hash,
93				delay_kind: DelayKind::Finalized,
94			};
95
96			if let Err(err) = pending_standard_changes.import::<_, ClientError>(
97				new_change.canon_hash.clone(),
98				new_change.canon_height.clone(),
99				new_change,
100				// previously we only supported at most one pending change per fork
101				&|_, _| Ok(false),
102			) {
103				warn!(target: LOG_TARGET, "Error migrating pending authority set change: {}", err);
104				warn!(target: LOG_TARGET, "Node is in a potentially inconsistent state.");
105			}
106		}
107
108		let authority_set = AuthoritySet::new(
109			self.current_authorities,
110			self.set_id,
111			pending_standard_changes,
112			Vec::new(),
113			AuthoritySetChanges::empty(),
114		);
115
116		authority_set.expect("current_authorities is non-empty and weights are non-zero; qed.")
117	}
118}
119
120impl<H, N> Into<AuthoritySet<H, N>> for V2AuthoritySet<H, N>
121where
122	H: Clone + Debug + PartialEq,
123	N: Clone + Debug + Ord,
124{
125	fn into(self) -> AuthoritySet<H, N> {
126		AuthoritySet::new(
127			self.current_authorities,
128			self.set_id,
129			self.pending_standard_changes,
130			self.pending_forced_changes,
131			AuthoritySetChanges::empty(),
132		)
133		.expect("current_authorities is non-empty and weights are non-zero; qed.")
134	}
135}
136
137#[derive(Debug, Clone, Encode, Decode, PartialEq)]
138struct V2AuthoritySet<H, N> {
139	current_authorities: AuthorityList,
140	set_id: u64,
141	pending_standard_changes: ForkTree<H, N, PendingChange<H, N>>,
142	pending_forced_changes: Vec<PendingChange<H, N>>,
143}
144
145pub(crate) fn load_decode<B: AuxStore, T: Decode>(
146	backend: &B,
147	key: &[u8],
148) -> ClientResult<Option<T>> {
149	match backend.get_aux(key)? {
150		None => Ok(None),
151		Some(t) => T::decode(&mut &t[..])
152			.map_err(|e| ClientError::Backend(format!("GRANDPA DB is corrupted: {}", e)))
153			.map(Some),
154	}
155}
156
157/// Persistent data kept between runs.
158pub(crate) struct PersistentData<Block: BlockT> {
159	pub(crate) authority_set: SharedAuthoritySet<Block::Hash, NumberFor<Block>>,
160	pub(crate) set_state: SharedVoterSetState<Block>,
161}
162
163fn migrate_from_version0<Block: BlockT, B, G>(
164	backend: &B,
165	genesis_round: &G,
166) -> ClientResult<Option<(AuthoritySet<Block::Hash, NumberFor<Block>>, VoterSetState<Block>)>>
167where
168	B: AuxStore,
169	G: Fn() -> RoundState<Block::Hash, NumberFor<Block>>,
170{
171	CURRENT_VERSION.using_encoded(|s| backend.insert_aux(&[(VERSION_KEY, s)], &[]))?;
172
173	if let Some(old_set) =
174		load_decode::<_, V0AuthoritySet<Block::Hash, NumberFor<Block>>>(backend, AUTHORITY_SET_KEY)?
175	{
176		let new_set: AuthoritySet<Block::Hash, NumberFor<Block>> = old_set.into();
177		backend.insert_aux(&[(AUTHORITY_SET_KEY, new_set.encode().as_slice())], &[])?;
178
179		let (last_round_number, last_round_state) = match load_decode::<
180			_,
181			V0VoterSetState<Block::Hash, NumberFor<Block>>,
182		>(backend, SET_STATE_KEY)?
183		{
184			Some((number, state)) => (number, state),
185			None => (0, genesis_round()),
186		};
187
188		let set_id = new_set.set_id;
189
190		let base = last_round_state.prevote_ghost.expect(
191			"state is for completed round; completed rounds must have a prevote ghost; qed.",
192		);
193
194		let mut current_rounds = CurrentRounds::<Block>::new();
195		current_rounds.insert(last_round_number + 1, HasVoted::No);
196
197		let set_state = VoterSetState::Live {
198			completed_rounds: CompletedRounds::new(
199				CompletedRound {
200					number: last_round_number,
201					state: last_round_state,
202					votes: Vec::new(),
203					base,
204				},
205				set_id,
206				&new_set,
207			),
208			current_rounds,
209		};
210
211		backend.insert_aux(&[(SET_STATE_KEY, set_state.encode().as_slice())], &[])?;
212
213		return Ok(Some((new_set, set_state)));
214	}
215
216	Ok(None)
217}
218
219fn migrate_from_version1<Block: BlockT, B, G>(
220	backend: &B,
221	genesis_round: &G,
222) -> ClientResult<Option<(AuthoritySet<Block::Hash, NumberFor<Block>>, VoterSetState<Block>)>>
223where
224	B: AuxStore,
225	G: Fn() -> RoundState<Block::Hash, NumberFor<Block>>,
226{
227	CURRENT_VERSION.using_encoded(|s| backend.insert_aux(&[(VERSION_KEY, s)], &[]))?;
228
229	if let Some(set) =
230		load_decode::<_, AuthoritySet<Block::Hash, NumberFor<Block>>>(backend, AUTHORITY_SET_KEY)?
231	{
232		let set_id = set.set_id;
233
234		let completed_rounds = |number, state, base| {
235			CompletedRounds::new(
236				CompletedRound { number, state, votes: Vec::new(), base },
237				set_id,
238				&set,
239			)
240		};
241
242		let set_state = match load_decode::<_, V1VoterSetState<Block::Hash, NumberFor<Block>>>(
243			backend,
244			SET_STATE_KEY,
245		)? {
246			Some(V1VoterSetState::Paused(last_round_number, set_state)) => {
247				let base = set_state.prevote_ghost
248					.expect("state is for completed round; completed rounds must have a prevote ghost; qed.");
249
250				VoterSetState::Paused {
251					completed_rounds: completed_rounds(last_round_number, set_state, base),
252				}
253			},
254			Some(V1VoterSetState::Live(last_round_number, set_state)) => {
255				let base = set_state.prevote_ghost
256					.expect("state is for completed round; completed rounds must have a prevote ghost; qed.");
257
258				let mut current_rounds = CurrentRounds::<Block>::new();
259				current_rounds.insert(last_round_number + 1, HasVoted::No);
260
261				VoterSetState::Live {
262					completed_rounds: completed_rounds(last_round_number, set_state, base),
263					current_rounds,
264				}
265			},
266			None => {
267				let set_state = genesis_round();
268				let base = set_state.prevote_ghost
269					.expect("state is for completed round; completed rounds must have a prevote ghost; qed.");
270
271				VoterSetState::live(set_id, &set, base)
272			},
273		};
274
275		backend.insert_aux(&[(SET_STATE_KEY, set_state.encode().as_slice())], &[])?;
276
277		return Ok(Some((set, set_state)));
278	}
279
280	Ok(None)
281}
282
283fn migrate_from_version2<Block: BlockT, B, G>(
284	backend: &B,
285	genesis_round: &G,
286) -> ClientResult<Option<(AuthoritySet<Block::Hash, NumberFor<Block>>, VoterSetState<Block>)>>
287where
288	B: AuxStore,
289	G: Fn() -> RoundState<Block::Hash, NumberFor<Block>>,
290{
291	CURRENT_VERSION.using_encoded(|s| backend.insert_aux(&[(VERSION_KEY, s)], &[]))?;
292
293	if let Some(old_set) =
294		load_decode::<_, V2AuthoritySet<Block::Hash, NumberFor<Block>>>(backend, AUTHORITY_SET_KEY)?
295	{
296		let new_set: AuthoritySet<Block::Hash, NumberFor<Block>> = old_set.into();
297		backend.insert_aux(&[(AUTHORITY_SET_KEY, new_set.encode().as_slice())], &[])?;
298
299		let set_state = match load_decode::<_, VoterSetState<Block>>(backend, SET_STATE_KEY)? {
300			Some(state) => state,
301			None => {
302				let state = genesis_round();
303				let base = state.prevote_ghost
304					.expect("state is for completed round; completed rounds must have a prevote ghost; qed.");
305
306				VoterSetState::live(new_set.set_id, &new_set, base)
307			},
308		};
309
310		return Ok(Some((new_set, set_state)));
311	}
312
313	Ok(None)
314}
315
316/// Load or initialize persistent data from backend.
317pub(crate) fn load_persistent<Block: BlockT, B, G>(
318	backend: &B,
319	genesis_hash: Block::Hash,
320	genesis_number: NumberFor<Block>,
321	genesis_authorities: G,
322) -> ClientResult<PersistentData<Block>>
323where
324	B: AuxStore,
325	G: FnOnce() -> ClientResult<AuthorityList>,
326{
327	let version: Option<u32> = load_decode(backend, VERSION_KEY)?;
328
329	let make_genesis_round = move || RoundState::genesis((genesis_hash, genesis_number));
330
331	match version {
332		None => {
333			if let Some((new_set, set_state)) =
334				migrate_from_version0::<Block, _, _>(backend, &make_genesis_round)?
335			{
336				return Ok(PersistentData {
337					authority_set: new_set.into(),
338					set_state: set_state.into(),
339				});
340			}
341		},
342		Some(1) => {
343			if let Some((new_set, set_state)) =
344				migrate_from_version1::<Block, _, _>(backend, &make_genesis_round)?
345			{
346				return Ok(PersistentData {
347					authority_set: new_set.into(),
348					set_state: set_state.into(),
349				});
350			}
351		},
352		Some(2) => {
353			if let Some((new_set, set_state)) =
354				migrate_from_version2::<Block, _, _>(backend, &make_genesis_round)?
355			{
356				return Ok(PersistentData {
357					authority_set: new_set.into(),
358					set_state: set_state.into(),
359				});
360			}
361		},
362		Some(3) => {
363			if let Some(set) = load_decode::<_, AuthoritySet<Block::Hash, NumberFor<Block>>>(
364				backend,
365				AUTHORITY_SET_KEY,
366			)? {
367				let set_state =
368					match load_decode::<_, VoterSetState<Block>>(backend, SET_STATE_KEY)? {
369						Some(state) => state,
370						None => {
371							let state = make_genesis_round();
372							let base = state.prevote_ghost
373							.expect("state is for completed round; completed rounds must have a prevote ghost; qed.");
374
375							VoterSetState::live(set.set_id, &set, base)
376						},
377					};
378
379				return Ok(PersistentData {
380					authority_set: set.into(),
381					set_state: set_state.into(),
382				});
383			}
384		},
385		Some(other) => {
386			return Err(ClientError::Backend(format!(
387				"Unsupported GRANDPA DB version: {:?}",
388				other
389			)))
390		},
391	}
392
393	// genesis.
394	info!(
395		target: LOG_TARGET,
396		"👴 Loading GRANDPA authority set \
397		from genesis on what appears to be first startup."
398	);
399
400	let genesis_authorities = genesis_authorities()?;
401	let genesis_set = AuthoritySet::genesis(genesis_authorities)
402		.expect("genesis authorities is non-empty; all weights are non-zero; qed.");
403	let state = make_genesis_round();
404	let base = state
405		.prevote_ghost
406		.expect("state is for completed round; completed rounds must have a prevote ghost; qed.");
407
408	let genesis_state = VoterSetState::live(0, &genesis_set, base);
409
410	backend.insert_aux(
411		&[
412			(AUTHORITY_SET_KEY, genesis_set.encode().as_slice()),
413			(SET_STATE_KEY, genesis_state.encode().as_slice()),
414		],
415		&[],
416	)?;
417
418	Ok(PersistentData { authority_set: genesis_set.into(), set_state: genesis_state.into() })
419}
420
421/// Update the authority set on disk after a change.
422///
423/// If there has just been a handoff, pass a `new_set` parameter that describes the
424/// handoff. `set` in all cases should reflect the current authority set, with all
425/// changes and handoffs applied.
426pub(crate) fn update_authority_set<Block: BlockT, F, R>(
427	set: &AuthoritySet<Block::Hash, NumberFor<Block>>,
428	new_set: Option<&NewAuthoritySet<Block::Hash, NumberFor<Block>>>,
429	write_aux: F,
430) -> R
431where
432	F: FnOnce(&[(&'static [u8], &[u8])]) -> R,
433{
434	// write new authority set state to disk.
435	let encoded_set = set.encode();
436
437	if let Some(new_set) = new_set {
438		// we also overwrite the "last completed round" entry with a blank slate
439		// because from the perspective of the finality gadget, the chain has
440		// reset.
441		let set_state = VoterSetState::<Block>::live(
442			new_set.set_id,
443			set,
444			(new_set.canon_hash, new_set.canon_number),
445		);
446		let encoded = set_state.encode();
447
448		write_aux(&[(AUTHORITY_SET_KEY, &encoded_set[..]), (SET_STATE_KEY, &encoded[..])])
449	} else {
450		write_aux(&[(AUTHORITY_SET_KEY, &encoded_set[..])])
451	}
452}
453
454/// Update the justification for the latest finalized block on-disk.
455///
456/// We always keep around the justification for the best finalized block and overwrite it
457/// as we finalize new blocks, this makes sure that we don't store useless justifications
458/// but can always prove finality of the latest block.
459pub(crate) fn update_best_justification<Block: BlockT, F, R>(
460	justification: &GrandpaJustification<Block>,
461	write_aux: F,
462) -> R
463where
464	F: FnOnce(&[(&'static [u8], &[u8])]) -> R,
465{
466	let encoded_justification = justification.encode();
467	write_aux(&[(BEST_JUSTIFICATION, &encoded_justification[..])])
468}
469
470/// Fetch the justification for the latest block finalized by GRANDPA, if any.
471pub fn best_justification<B, Block>(
472	backend: &B,
473) -> ClientResult<Option<GrandpaJustification<Block>>>
474where
475	B: AuxStore,
476	Block: BlockT,
477{
478	load_decode::<_, GrandpaJustification<Block>>(backend, BEST_JUSTIFICATION)
479}
480
481/// Write voter set state.
482pub(crate) fn write_voter_set_state<Block: BlockT, B: AuxStore>(
483	backend: &B,
484	state: &VoterSetState<Block>,
485) -> ClientResult<()> {
486	backend.insert_aux(&[(SET_STATE_KEY, state.encode().as_slice())], &[])
487}
488
489/// Write concluded round.
490pub(crate) fn write_concluded_round<Block: BlockT, B: AuxStore>(
491	backend: &B,
492	round_data: &CompletedRound<Block>,
493) -> ClientResult<()> {
494	let mut key = CONCLUDED_ROUNDS.to_vec();
495	let round_number = round_data.number;
496	round_number.using_encoded(|n| key.extend(n));
497
498	backend.insert_aux(&[(&key[..], round_data.encode().as_slice())], &[])
499}
500
501#[cfg(test)]
502pub(crate) fn load_authorities<B: AuxStore, H: Decode, N: Decode + Clone + Ord>(
503	backend: &B,
504) -> Option<AuthoritySet<H, N>> {
505	load_decode::<_, AuthoritySet<H, N>>(backend, AUTHORITY_SET_KEY).expect("backend error")
506}
507
508#[cfg(test)]
509mod test {
510	use super::*;
511	use sp_consensus_grandpa::AuthorityId;
512	use sp_core::{crypto::UncheckedFrom, H256};
513	use substrate_test_runtime_client::{self, runtime::Block};
514
515	fn dummy_id() -> AuthorityId {
516		AuthorityId::unchecked_from([1; 32])
517	}
518
519	#[test]
520	fn load_decode_from_v0_migrates_data_format() {
521		let client = substrate_test_runtime_client::new();
522
523		let authorities = vec![(dummy_id(), 100)];
524		let set_id = 3;
525		let round_number: RoundNumber = 42;
526		let round_state = RoundState::<H256, u64> {
527			prevote_ghost: Some((H256::random(), 32)),
528			finalized: None,
529			estimate: None,
530			completable: false,
531		};
532
533		{
534			let authority_set = V0AuthoritySet::<H256, u64> {
535				current_authorities: authorities.clone(),
536				pending_changes: Vec::new(),
537				set_id,
538			};
539
540			let voter_set_state = (round_number, round_state.clone());
541
542			client
543				.insert_aux(
544					&[
545						(AUTHORITY_SET_KEY, authority_set.encode().as_slice()),
546						(SET_STATE_KEY, voter_set_state.encode().as_slice()),
547					],
548					&[],
549				)
550				.unwrap();
551		}
552
553		assert_eq!(load_decode::<_, u32>(&client, VERSION_KEY).unwrap(), None);
554
555		// should perform the migration
556		load_persistent::<substrate_test_runtime_client::runtime::Block, _, _>(
557			&client,
558			H256::random(),
559			0,
560			|| unreachable!(),
561		)
562		.unwrap();
563
564		assert_eq!(load_decode::<_, u32>(&client, VERSION_KEY).unwrap(), Some(3));
565
566		let PersistentData { authority_set, set_state, .. } =
567			load_persistent::<substrate_test_runtime_client::runtime::Block, _, _>(
568				&client,
569				H256::random(),
570				0,
571				|| unreachable!(),
572			)
573			.unwrap();
574
575		assert_eq!(
576			*authority_set.inner(),
577			AuthoritySet::new(
578				authorities.clone(),
579				set_id,
580				ForkTree::new(),
581				Vec::new(),
582				AuthoritySetChanges::empty(),
583			)
584			.unwrap(),
585		);
586
587		let mut current_rounds = CurrentRounds::<Block>::new();
588		current_rounds.insert(round_number + 1, HasVoted::No);
589
590		assert_eq!(
591			&*set_state.read(),
592			&VoterSetState::Live {
593				completed_rounds: CompletedRounds::new(
594					CompletedRound {
595						number: round_number,
596						state: round_state.clone(),
597						base: round_state.prevote_ghost.unwrap(),
598						votes: vec![],
599					},
600					set_id,
601					&*authority_set.inner(),
602				),
603				current_rounds,
604			},
605		);
606	}
607
608	#[test]
609	fn load_decode_from_v1_migrates_data_format() {
610		let client = substrate_test_runtime_client::new();
611
612		let authorities = vec![(dummy_id(), 100)];
613		let set_id = 3;
614		let round_number: RoundNumber = 42;
615		let round_state = RoundState::<H256, u64> {
616			prevote_ghost: Some((H256::random(), 32)),
617			finalized: None,
618			estimate: None,
619			completable: false,
620		};
621
622		{
623			let authority_set = AuthoritySet::<H256, u64>::new(
624				authorities.clone(),
625				set_id,
626				ForkTree::new(),
627				Vec::new(),
628				AuthoritySetChanges::empty(),
629			)
630			.unwrap();
631
632			let voter_set_state = V1VoterSetState::Live(round_number, round_state.clone());
633
634			client
635				.insert_aux(
636					&[
637						(AUTHORITY_SET_KEY, authority_set.encode().as_slice()),
638						(SET_STATE_KEY, voter_set_state.encode().as_slice()),
639						(VERSION_KEY, 1u32.encode().as_slice()),
640					],
641					&[],
642				)
643				.unwrap();
644		}
645
646		assert_eq!(load_decode::<_, u32>(&client, VERSION_KEY).unwrap(), Some(1));
647
648		// should perform the migration
649		load_persistent::<substrate_test_runtime_client::runtime::Block, _, _>(
650			&client,
651			H256::random(),
652			0,
653			|| unreachable!(),
654		)
655		.unwrap();
656
657		assert_eq!(load_decode::<_, u32>(&client, VERSION_KEY).unwrap(), Some(3));
658
659		let PersistentData { authority_set, set_state, .. } =
660			load_persistent::<substrate_test_runtime_client::runtime::Block, _, _>(
661				&client,
662				H256::random(),
663				0,
664				|| unreachable!(),
665			)
666			.unwrap();
667
668		assert_eq!(
669			*authority_set.inner(),
670			AuthoritySet::new(
671				authorities.clone(),
672				set_id,
673				ForkTree::new(),
674				Vec::new(),
675				AuthoritySetChanges::empty(),
676			)
677			.unwrap(),
678		);
679
680		let mut current_rounds = CurrentRounds::<Block>::new();
681		current_rounds.insert(round_number + 1, HasVoted::No);
682
683		assert_eq!(
684			&*set_state.read(),
685			&VoterSetState::Live {
686				completed_rounds: CompletedRounds::new(
687					CompletedRound {
688						number: round_number,
689						state: round_state.clone(),
690						base: round_state.prevote_ghost.unwrap(),
691						votes: vec![],
692					},
693					set_id,
694					&*authority_set.inner(),
695				),
696				current_rounds,
697			},
698		);
699	}
700
701	#[test]
702	fn load_decode_from_v2_migrates_data_format() {
703		let client = substrate_test_runtime_client::new();
704
705		let authorities = vec![(dummy_id(), 100)];
706		let set_id = 3;
707
708		{
709			let authority_set = V2AuthoritySet::<H256, u64> {
710				current_authorities: authorities.clone(),
711				set_id,
712				pending_standard_changes: ForkTree::new(),
713				pending_forced_changes: Vec::new(),
714			};
715
716			let genesis_state = (H256::random(), 32);
717			let voter_set_state: VoterSetState<substrate_test_runtime_client::runtime::Block> =
718				VoterSetState::live(
719					set_id,
720					&authority_set.clone().into(), // Note the conversion!
721					genesis_state,
722				);
723
724			client
725				.insert_aux(
726					&[
727						(AUTHORITY_SET_KEY, authority_set.encode().as_slice()),
728						(SET_STATE_KEY, voter_set_state.encode().as_slice()),
729						(VERSION_KEY, 2u32.encode().as_slice()),
730					],
731					&[],
732				)
733				.unwrap();
734		}
735
736		assert_eq!(load_decode::<_, u32>(&client, VERSION_KEY).unwrap(), Some(2));
737
738		// should perform the migration
739		load_persistent::<substrate_test_runtime_client::runtime::Block, _, _>(
740			&client,
741			H256::random(),
742			0,
743			|| unreachable!(),
744		)
745		.unwrap();
746
747		assert_eq!(load_decode::<_, u32>(&client, VERSION_KEY).unwrap(), Some(3));
748
749		let PersistentData { authority_set, .. } = load_persistent::<
750			substrate_test_runtime_client::runtime::Block,
751			_,
752			_,
753		>(
754			&client, H256::random(), 0, || unreachable!()
755		)
756		.unwrap();
757
758		assert_eq!(
759			*authority_set.inner(),
760			AuthoritySet::new(
761				authorities.clone(),
762				set_id,
763				ForkTree::new(),
764				Vec::new(),
765				AuthoritySetChanges::empty(),
766			)
767			.unwrap(),
768		);
769	}
770
771	#[test]
772	fn write_read_concluded_rounds() {
773		let client = substrate_test_runtime_client::new();
774		let hash = H256::random();
775		let round_state = RoundState::genesis((hash, 0));
776
777		let completed_round = CompletedRound::<substrate_test_runtime_client::runtime::Block> {
778			number: 42,
779			state: round_state.clone(),
780			base: round_state.prevote_ghost.unwrap(),
781			votes: vec![],
782		};
783
784		assert!(write_concluded_round(&client, &completed_round).is_ok());
785
786		let round_number = completed_round.number;
787		let mut key = CONCLUDED_ROUNDS.to_vec();
788		round_number.using_encoded(|n| key.extend(n));
789
790		assert_eq!(
791			load_decode::<_, CompletedRound::<substrate_test_runtime_client::runtime::Block>>(
792				&client, &key
793			)
794			.unwrap(),
795			Some(completed_round),
796		);
797	}
798}