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 { authority_set: set.into(), set_state: set_state.into() })
380			}
381		},
382		Some(other) =>
383			return Err(ClientError::Backend(format!("Unsupported GRANDPA DB version: {:?}", other))),
384	}
385
386	// genesis.
387	info!(
388		target: LOG_TARGET,
389		"👴 Loading GRANDPA authority set \
390		from genesis on what appears to be first startup."
391	);
392
393	let genesis_authorities = genesis_authorities()?;
394	let genesis_set = AuthoritySet::genesis(genesis_authorities)
395		.expect("genesis authorities is non-empty; all weights are non-zero; qed.");
396	let state = make_genesis_round();
397	let base = state
398		.prevote_ghost
399		.expect("state is for completed round; completed rounds must have a prevote ghost; qed.");
400
401	let genesis_state = VoterSetState::live(0, &genesis_set, base);
402
403	backend.insert_aux(
404		&[
405			(AUTHORITY_SET_KEY, genesis_set.encode().as_slice()),
406			(SET_STATE_KEY, genesis_state.encode().as_slice()),
407		],
408		&[],
409	)?;
410
411	Ok(PersistentData { authority_set: genesis_set.into(), set_state: genesis_state.into() })
412}
413
414/// Update the authority set on disk after a change.
415///
416/// If there has just been a handoff, pass a `new_set` parameter that describes the
417/// handoff. `set` in all cases should reflect the current authority set, with all
418/// changes and handoffs applied.
419pub(crate) fn update_authority_set<Block: BlockT, F, R>(
420	set: &AuthoritySet<Block::Hash, NumberFor<Block>>,
421	new_set: Option<&NewAuthoritySet<Block::Hash, NumberFor<Block>>>,
422	write_aux: F,
423) -> R
424where
425	F: FnOnce(&[(&'static [u8], &[u8])]) -> R,
426{
427	// write new authority set state to disk.
428	let encoded_set = set.encode();
429
430	if let Some(new_set) = new_set {
431		// we also overwrite the "last completed round" entry with a blank slate
432		// because from the perspective of the finality gadget, the chain has
433		// reset.
434		let set_state = VoterSetState::<Block>::live(
435			new_set.set_id,
436			set,
437			(new_set.canon_hash, new_set.canon_number),
438		);
439		let encoded = set_state.encode();
440
441		write_aux(&[(AUTHORITY_SET_KEY, &encoded_set[..]), (SET_STATE_KEY, &encoded[..])])
442	} else {
443		write_aux(&[(AUTHORITY_SET_KEY, &encoded_set[..])])
444	}
445}
446
447/// Update the justification for the latest finalized block on-disk.
448///
449/// We always keep around the justification for the best finalized block and overwrite it
450/// as we finalize new blocks, this makes sure that we don't store useless justifications
451/// but can always prove finality of the latest block.
452pub(crate) fn update_best_justification<Block: BlockT, F, R>(
453	justification: &GrandpaJustification<Block>,
454	write_aux: F,
455) -> R
456where
457	F: FnOnce(&[(&'static [u8], &[u8])]) -> R,
458{
459	let encoded_justification = justification.encode();
460	write_aux(&[(BEST_JUSTIFICATION, &encoded_justification[..])])
461}
462
463/// Fetch the justification for the latest block finalized by GRANDPA, if any.
464pub fn best_justification<B, Block>(
465	backend: &B,
466) -> ClientResult<Option<GrandpaJustification<Block>>>
467where
468	B: AuxStore,
469	Block: BlockT,
470{
471	load_decode::<_, GrandpaJustification<Block>>(backend, BEST_JUSTIFICATION)
472}
473
474/// Write voter set state.
475pub(crate) fn write_voter_set_state<Block: BlockT, B: AuxStore>(
476	backend: &B,
477	state: &VoterSetState<Block>,
478) -> ClientResult<()> {
479	backend.insert_aux(&[(SET_STATE_KEY, state.encode().as_slice())], &[])
480}
481
482/// Write concluded round.
483pub(crate) fn write_concluded_round<Block: BlockT, B: AuxStore>(
484	backend: &B,
485	round_data: &CompletedRound<Block>,
486) -> ClientResult<()> {
487	let mut key = CONCLUDED_ROUNDS.to_vec();
488	let round_number = round_data.number;
489	round_number.using_encoded(|n| key.extend(n));
490
491	backend.insert_aux(&[(&key[..], round_data.encode().as_slice())], &[])
492}
493
494#[cfg(test)]
495pub(crate) fn load_authorities<B: AuxStore, H: Decode, N: Decode + Clone + Ord>(
496	backend: &B,
497) -> Option<AuthoritySet<H, N>> {
498	load_decode::<_, AuthoritySet<H, N>>(backend, AUTHORITY_SET_KEY).expect("backend error")
499}
500
501#[cfg(test)]
502mod test {
503	use super::*;
504	use sp_consensus_grandpa::AuthorityId;
505	use sp_core::{crypto::UncheckedFrom, H256};
506	use substrate_test_runtime_client::{self, runtime::Block};
507
508	fn dummy_id() -> AuthorityId {
509		AuthorityId::unchecked_from([1; 32])
510	}
511
512	#[test]
513	fn load_decode_from_v0_migrates_data_format() {
514		let client = substrate_test_runtime_client::new();
515
516		let authorities = vec![(dummy_id(), 100)];
517		let set_id = 3;
518		let round_number: RoundNumber = 42;
519		let round_state = RoundState::<H256, u64> {
520			prevote_ghost: Some((H256::random(), 32)),
521			finalized: None,
522			estimate: None,
523			completable: false,
524		};
525
526		{
527			let authority_set = V0AuthoritySet::<H256, u64> {
528				current_authorities: authorities.clone(),
529				pending_changes: Vec::new(),
530				set_id,
531			};
532
533			let voter_set_state = (round_number, round_state.clone());
534
535			client
536				.insert_aux(
537					&[
538						(AUTHORITY_SET_KEY, authority_set.encode().as_slice()),
539						(SET_STATE_KEY, voter_set_state.encode().as_slice()),
540					],
541					&[],
542				)
543				.unwrap();
544		}
545
546		assert_eq!(load_decode::<_, u32>(&client, VERSION_KEY).unwrap(), None);
547
548		// should perform the migration
549		load_persistent::<substrate_test_runtime_client::runtime::Block, _, _>(
550			&client,
551			H256::random(),
552			0,
553			|| unreachable!(),
554		)
555		.unwrap();
556
557		assert_eq!(load_decode::<_, u32>(&client, VERSION_KEY).unwrap(), Some(3));
558
559		let PersistentData { authority_set, set_state, .. } =
560			load_persistent::<substrate_test_runtime_client::runtime::Block, _, _>(
561				&client,
562				H256::random(),
563				0,
564				|| unreachable!(),
565			)
566			.unwrap();
567
568		assert_eq!(
569			*authority_set.inner(),
570			AuthoritySet::new(
571				authorities.clone(),
572				set_id,
573				ForkTree::new(),
574				Vec::new(),
575				AuthoritySetChanges::empty(),
576			)
577			.unwrap(),
578		);
579
580		let mut current_rounds = CurrentRounds::<Block>::new();
581		current_rounds.insert(round_number + 1, HasVoted::No);
582
583		assert_eq!(
584			&*set_state.read(),
585			&VoterSetState::Live {
586				completed_rounds: CompletedRounds::new(
587					CompletedRound {
588						number: round_number,
589						state: round_state.clone(),
590						base: round_state.prevote_ghost.unwrap(),
591						votes: vec![],
592					},
593					set_id,
594					&*authority_set.inner(),
595				),
596				current_rounds,
597			},
598		);
599	}
600
601	#[test]
602	fn load_decode_from_v1_migrates_data_format() {
603		let client = substrate_test_runtime_client::new();
604
605		let authorities = vec![(dummy_id(), 100)];
606		let set_id = 3;
607		let round_number: RoundNumber = 42;
608		let round_state = RoundState::<H256, u64> {
609			prevote_ghost: Some((H256::random(), 32)),
610			finalized: None,
611			estimate: None,
612			completable: false,
613		};
614
615		{
616			let authority_set = AuthoritySet::<H256, u64>::new(
617				authorities.clone(),
618				set_id,
619				ForkTree::new(),
620				Vec::new(),
621				AuthoritySetChanges::empty(),
622			)
623			.unwrap();
624
625			let voter_set_state = V1VoterSetState::Live(round_number, round_state.clone());
626
627			client
628				.insert_aux(
629					&[
630						(AUTHORITY_SET_KEY, authority_set.encode().as_slice()),
631						(SET_STATE_KEY, voter_set_state.encode().as_slice()),
632						(VERSION_KEY, 1u32.encode().as_slice()),
633					],
634					&[],
635				)
636				.unwrap();
637		}
638
639		assert_eq!(load_decode::<_, u32>(&client, VERSION_KEY).unwrap(), Some(1));
640
641		// should perform the migration
642		load_persistent::<substrate_test_runtime_client::runtime::Block, _, _>(
643			&client,
644			H256::random(),
645			0,
646			|| unreachable!(),
647		)
648		.unwrap();
649
650		assert_eq!(load_decode::<_, u32>(&client, VERSION_KEY).unwrap(), Some(3));
651
652		let PersistentData { authority_set, set_state, .. } =
653			load_persistent::<substrate_test_runtime_client::runtime::Block, _, _>(
654				&client,
655				H256::random(),
656				0,
657				|| unreachable!(),
658			)
659			.unwrap();
660
661		assert_eq!(
662			*authority_set.inner(),
663			AuthoritySet::new(
664				authorities.clone(),
665				set_id,
666				ForkTree::new(),
667				Vec::new(),
668				AuthoritySetChanges::empty(),
669			)
670			.unwrap(),
671		);
672
673		let mut current_rounds = CurrentRounds::<Block>::new();
674		current_rounds.insert(round_number + 1, HasVoted::No);
675
676		assert_eq!(
677			&*set_state.read(),
678			&VoterSetState::Live {
679				completed_rounds: CompletedRounds::new(
680					CompletedRound {
681						number: round_number,
682						state: round_state.clone(),
683						base: round_state.prevote_ghost.unwrap(),
684						votes: vec![],
685					},
686					set_id,
687					&*authority_set.inner(),
688				),
689				current_rounds,
690			},
691		);
692	}
693
694	#[test]
695	fn load_decode_from_v2_migrates_data_format() {
696		let client = substrate_test_runtime_client::new();
697
698		let authorities = vec![(dummy_id(), 100)];
699		let set_id = 3;
700
701		{
702			let authority_set = V2AuthoritySet::<H256, u64> {
703				current_authorities: authorities.clone(),
704				set_id,
705				pending_standard_changes: ForkTree::new(),
706				pending_forced_changes: Vec::new(),
707			};
708
709			let genesis_state = (H256::random(), 32);
710			let voter_set_state: VoterSetState<substrate_test_runtime_client::runtime::Block> =
711				VoterSetState::live(
712					set_id,
713					&authority_set.clone().into(), // Note the conversion!
714					genesis_state,
715				);
716
717			client
718				.insert_aux(
719					&[
720						(AUTHORITY_SET_KEY, authority_set.encode().as_slice()),
721						(SET_STATE_KEY, voter_set_state.encode().as_slice()),
722						(VERSION_KEY, 2u32.encode().as_slice()),
723					],
724					&[],
725				)
726				.unwrap();
727		}
728
729		assert_eq!(load_decode::<_, u32>(&client, VERSION_KEY).unwrap(), Some(2));
730
731		// should perform the migration
732		load_persistent::<substrate_test_runtime_client::runtime::Block, _, _>(
733			&client,
734			H256::random(),
735			0,
736			|| unreachable!(),
737		)
738		.unwrap();
739
740		assert_eq!(load_decode::<_, u32>(&client, VERSION_KEY).unwrap(), Some(3));
741
742		let PersistentData { authority_set, .. } = load_persistent::<
743			substrate_test_runtime_client::runtime::Block,
744			_,
745			_,
746		>(
747			&client, H256::random(), 0, || unreachable!()
748		)
749		.unwrap();
750
751		assert_eq!(
752			*authority_set.inner(),
753			AuthoritySet::new(
754				authorities.clone(),
755				set_id,
756				ForkTree::new(),
757				Vec::new(),
758				AuthoritySetChanges::empty(),
759			)
760			.unwrap(),
761		);
762	}
763
764	#[test]
765	fn write_read_concluded_rounds() {
766		let client = substrate_test_runtime_client::new();
767		let hash = H256::random();
768		let round_state = RoundState::genesis((hash, 0));
769
770		let completed_round = CompletedRound::<substrate_test_runtime_client::runtime::Block> {
771			number: 42,
772			state: round_state.clone(),
773			base: round_state.prevote_ghost.unwrap(),
774			votes: vec![],
775		};
776
777		assert!(write_concluded_round(&client, &completed_round).is_ok());
778
779		let round_number = completed_round.number;
780		let mut key = CONCLUDED_ROUNDS.to_vec();
781		round_number.using_encoded(|n| key.extend(n));
782
783		assert_eq!(
784			load_decode::<_, CompletedRound::<substrate_test_runtime_client::runtime::Block>>(
785				&client, &key
786			)
787			.unwrap(),
788			Some(completed_round),
789		);
790	}
791}