// This file is part of Bizinikiwi. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see . //! Schema for stuff in the aux-db. use std::fmt::Debug; use codec::{Decode, Encode}; use finality_grandpa::round::State as RoundState; use log::{info, warn}; use pez_fork_tree::ForkTree; use pezsc_client_api::backend::AuxStore; use pezsp_blockchain::{Error as ClientError, Result as ClientResult}; use pezsp_consensus_grandpa::{AuthorityList, RoundNumber, SetId}; use pezsp_runtime::traits::{Block as BlockT, NumberFor}; use crate::{ authorities::{ AuthoritySet, AuthoritySetChanges, DelayKind, PendingChange, SharedAuthoritySet, }, environment::{ CompletedRound, CompletedRounds, CurrentRounds, HasVoted, SharedVoterSetState, VoterSetState, }, GrandpaJustification, NewAuthoritySet, LOG_TARGET, }; const VERSION_KEY: &[u8] = b"grandpa_schema_version"; const SET_STATE_KEY: &[u8] = b"grandpa_completed_round"; const CONCLUDED_ROUNDS: &[u8] = b"grandpa_concluded_rounds"; const AUTHORITY_SET_KEY: &[u8] = b"grandpa_voters"; const BEST_JUSTIFICATION: &[u8] = b"grandpa_best_justification"; const CURRENT_VERSION: u32 = 3; /// The voter set state. #[derive(Debug, Clone, Encode, Decode)] #[cfg_attr(test, derive(PartialEq))] pub enum V1VoterSetState { /// The voter set state, currently paused. Paused(RoundNumber, RoundState), /// The voter set state, currently live. Live(RoundNumber, RoundState), } type V0VoterSetState = (RoundNumber, RoundState); #[derive(Debug, Clone, Encode, Decode, PartialEq)] struct V0PendingChange { next_authorities: AuthorityList, delay: N, canon_height: N, canon_hash: H, } #[derive(Debug, Clone, Encode, Decode, PartialEq)] struct V0AuthoritySet { current_authorities: AuthorityList, set_id: SetId, pending_changes: Vec>, } impl Into> for V0AuthoritySet where H: Clone + Debug + PartialEq, N: Clone + Debug + Ord, { fn into(self) -> AuthoritySet { let mut pending_standard_changes = ForkTree::new(); for old_change in self.pending_changes { let new_change = PendingChange { next_authorities: old_change.next_authorities, delay: old_change.delay, canon_height: old_change.canon_height, canon_hash: old_change.canon_hash, delay_kind: DelayKind::Finalized, }; if let Err(err) = pending_standard_changes.import::<_, ClientError>( new_change.canon_hash.clone(), new_change.canon_height.clone(), new_change, // previously we only supported at most one pending change per fork &|_, _| Ok(false), ) { warn!(target: LOG_TARGET, "Error migrating pending authority set change: {}", err); warn!(target: LOG_TARGET, "Node is in a potentially inconsistent state."); } } let authority_set = AuthoritySet::new( self.current_authorities, self.set_id, pending_standard_changes, Vec::new(), AuthoritySetChanges::empty(), ); authority_set.expect("current_authorities is non-empty and weights are non-zero; qed.") } } impl Into> for V2AuthoritySet where H: Clone + Debug + PartialEq, N: Clone + Debug + Ord, { fn into(self) -> AuthoritySet { AuthoritySet::new( self.current_authorities, self.set_id, self.pending_standard_changes, self.pending_forced_changes, AuthoritySetChanges::empty(), ) .expect("current_authorities is non-empty and weights are non-zero; qed.") } } #[derive(Debug, Clone, Encode, Decode, PartialEq)] struct V2AuthoritySet { current_authorities: AuthorityList, set_id: u64, pending_standard_changes: ForkTree>, pending_forced_changes: Vec>, } pub(crate) fn load_decode( backend: &B, key: &[u8], ) -> ClientResult> { match backend.get_aux(key)? { None => Ok(None), Some(t) => T::decode(&mut &t[..]) .map_err(|e| ClientError::Backend(format!("GRANDPA DB is corrupted: {}", e))) .map(Some), } } /// Persistent data kept between runs. pub(crate) struct PersistentData { pub(crate) authority_set: SharedAuthoritySet>, pub(crate) set_state: SharedVoterSetState, } fn migrate_from_version0( backend: &B, genesis_round: &G, ) -> ClientResult>, VoterSetState)>> where B: AuxStore, G: Fn() -> RoundState>, { CURRENT_VERSION.using_encoded(|s| backend.insert_aux(&[(VERSION_KEY, s)], &[]))?; if let Some(old_set) = load_decode::<_, V0AuthoritySet>>(backend, AUTHORITY_SET_KEY)? { let new_set: AuthoritySet> = old_set.into(); backend.insert_aux(&[(AUTHORITY_SET_KEY, new_set.encode().as_slice())], &[])?; let (last_round_number, last_round_state) = match load_decode::< _, V0VoterSetState>, >(backend, SET_STATE_KEY)? { Some((number, state)) => (number, state), None => (0, genesis_round()), }; let set_id = new_set.set_id; let base = last_round_state.prevote_ghost.expect( "state is for completed round; completed rounds must have a prevote ghost; qed.", ); let mut current_rounds = CurrentRounds::::new(); current_rounds.insert(last_round_number + 1, HasVoted::No); let set_state = VoterSetState::Live { completed_rounds: CompletedRounds::new( CompletedRound { number: last_round_number, state: last_round_state, votes: Vec::new(), base, }, set_id, &new_set, ), current_rounds, }; backend.insert_aux(&[(SET_STATE_KEY, set_state.encode().as_slice())], &[])?; return Ok(Some((new_set, set_state))); } Ok(None) } fn migrate_from_version1( backend: &B, genesis_round: &G, ) -> ClientResult>, VoterSetState)>> where B: AuxStore, G: Fn() -> RoundState>, { CURRENT_VERSION.using_encoded(|s| backend.insert_aux(&[(VERSION_KEY, s)], &[]))?; if let Some(set) = load_decode::<_, AuthoritySet>>(backend, AUTHORITY_SET_KEY)? { let set_id = set.set_id; let completed_rounds = |number, state, base| { CompletedRounds::new( CompletedRound { number, state, votes: Vec::new(), base }, set_id, &set, ) }; let set_state = match load_decode::<_, V1VoterSetState>>( backend, SET_STATE_KEY, )? { Some(V1VoterSetState::Paused(last_round_number, set_state)) => { let base = set_state.prevote_ghost .expect("state is for completed round; completed rounds must have a prevote ghost; qed."); VoterSetState::Paused { completed_rounds: completed_rounds(last_round_number, set_state, base), } }, Some(V1VoterSetState::Live(last_round_number, set_state)) => { let base = set_state.prevote_ghost .expect("state is for completed round; completed rounds must have a prevote ghost; qed."); let mut current_rounds = CurrentRounds::::new(); current_rounds.insert(last_round_number + 1, HasVoted::No); VoterSetState::Live { completed_rounds: completed_rounds(last_round_number, set_state, base), current_rounds, } }, None => { let set_state = genesis_round(); let base = set_state.prevote_ghost .expect("state is for completed round; completed rounds must have a prevote ghost; qed."); VoterSetState::live(set_id, &set, base) }, }; backend.insert_aux(&[(SET_STATE_KEY, set_state.encode().as_slice())], &[])?; return Ok(Some((set, set_state))); } Ok(None) } fn migrate_from_version2( backend: &B, genesis_round: &G, ) -> ClientResult>, VoterSetState)>> where B: AuxStore, G: Fn() -> RoundState>, { CURRENT_VERSION.using_encoded(|s| backend.insert_aux(&[(VERSION_KEY, s)], &[]))?; if let Some(old_set) = load_decode::<_, V2AuthoritySet>>(backend, AUTHORITY_SET_KEY)? { let new_set: AuthoritySet> = old_set.into(); backend.insert_aux(&[(AUTHORITY_SET_KEY, new_set.encode().as_slice())], &[])?; let set_state = match load_decode::<_, VoterSetState>(backend, SET_STATE_KEY)? { Some(state) => state, None => { let state = genesis_round(); let base = state.prevote_ghost .expect("state is for completed round; completed rounds must have a prevote ghost; qed."); VoterSetState::live(new_set.set_id, &new_set, base) }, }; return Ok(Some((new_set, set_state))); } Ok(None) } /// Load or initialize persistent data from backend. pub(crate) fn load_persistent( backend: &B, genesis_hash: Block::Hash, genesis_number: NumberFor, genesis_authorities: G, ) -> ClientResult> where B: AuxStore, G: FnOnce() -> ClientResult, { let version: Option = load_decode(backend, VERSION_KEY)?; let make_genesis_round = move || RoundState::genesis((genesis_hash, genesis_number)); match version { None => { if let Some((new_set, set_state)) = migrate_from_version0::(backend, &make_genesis_round)? { return Ok(PersistentData { authority_set: new_set.into(), set_state: set_state.into(), }); } }, Some(1) => { if let Some((new_set, set_state)) = migrate_from_version1::(backend, &make_genesis_round)? { return Ok(PersistentData { authority_set: new_set.into(), set_state: set_state.into(), }); } }, Some(2) => { if let Some((new_set, set_state)) = migrate_from_version2::(backend, &make_genesis_round)? { return Ok(PersistentData { authority_set: new_set.into(), set_state: set_state.into(), }); } }, Some(3) => { if let Some(set) = load_decode::<_, AuthoritySet>>( backend, AUTHORITY_SET_KEY, )? { let set_state = match load_decode::<_, VoterSetState>(backend, SET_STATE_KEY)? { Some(state) => state, None => { let state = make_genesis_round(); let base = state.prevote_ghost .expect("state is for completed round; completed rounds must have a prevote ghost; qed."); VoterSetState::live(set.set_id, &set, base) }, }; return Ok(PersistentData { authority_set: set.into(), set_state: set_state.into(), }); } }, Some(other) => { return Err(ClientError::Backend(format!( "Unsupported GRANDPA DB version: {:?}", other ))) }, } // genesis. info!( target: LOG_TARGET, "👴 Loading GRANDPA authority set \ from genesis on what appears to be first startup." ); let genesis_authorities = genesis_authorities()?; let genesis_set = AuthoritySet::genesis(genesis_authorities) .expect("genesis authorities is non-empty; all weights are non-zero; qed."); let state = make_genesis_round(); let base = state .prevote_ghost .expect("state is for completed round; completed rounds must have a prevote ghost; qed."); let genesis_state = VoterSetState::live(0, &genesis_set, base); backend.insert_aux( &[ (AUTHORITY_SET_KEY, genesis_set.encode().as_slice()), (SET_STATE_KEY, genesis_state.encode().as_slice()), ], &[], )?; Ok(PersistentData { authority_set: genesis_set.into(), set_state: genesis_state.into() }) } /// Update the authority set on disk after a change. /// /// If there has just been a handoff, pass a `new_set` parameter that describes the /// handoff. `set` in all cases should reflect the current authority set, with all /// changes and handoffs applied. pub(crate) fn update_authority_set( set: &AuthoritySet>, new_set: Option<&NewAuthoritySet>>, write_aux: F, ) -> R where F: FnOnce(&[(&'static [u8], &[u8])]) -> R, { // write new authority set state to disk. let encoded_set = set.encode(); if let Some(new_set) = new_set { // we also overwrite the "last completed round" entry with a blank slate // because from the perspective of the finality gadget, the chain has // reset. let set_state = VoterSetState::::live( new_set.set_id, set, (new_set.canon_hash, new_set.canon_number), ); let encoded = set_state.encode(); write_aux(&[(AUTHORITY_SET_KEY, &encoded_set[..]), (SET_STATE_KEY, &encoded[..])]) } else { write_aux(&[(AUTHORITY_SET_KEY, &encoded_set[..])]) } } /// Update the justification for the latest finalized block on-disk. /// /// We always keep around the justification for the best finalized block and overwrite it /// as we finalize new blocks, this makes sure that we don't store useless justifications /// but can always prove finality of the latest block. pub(crate) fn update_best_justification( justification: &GrandpaJustification, write_aux: F, ) -> R where F: FnOnce(&[(&'static [u8], &[u8])]) -> R, { let encoded_justification = justification.encode(); write_aux(&[(BEST_JUSTIFICATION, &encoded_justification[..])]) } /// Fetch the justification for the latest block finalized by GRANDPA, if any. pub fn best_justification( backend: &B, ) -> ClientResult>> where B: AuxStore, Block: BlockT, { load_decode::<_, GrandpaJustification>(backend, BEST_JUSTIFICATION) } /// Write voter set state. pub(crate) fn write_voter_set_state( backend: &B, state: &VoterSetState, ) -> ClientResult<()> { backend.insert_aux(&[(SET_STATE_KEY, state.encode().as_slice())], &[]) } /// Write concluded round. pub(crate) fn write_concluded_round( backend: &B, round_data: &CompletedRound, ) -> ClientResult<()> { let mut key = CONCLUDED_ROUNDS.to_vec(); let round_number = round_data.number; round_number.using_encoded(|n| key.extend(n)); backend.insert_aux(&[(&key[..], round_data.encode().as_slice())], &[]) } #[cfg(test)] pub(crate) fn load_authorities( backend: &B, ) -> Option> { load_decode::<_, AuthoritySet>(backend, AUTHORITY_SET_KEY).expect("backend error") } #[cfg(test)] mod test { use super::*; use bizinikiwi_test_runtime_client::{self, runtime::Block}; use pezsp_consensus_grandpa::AuthorityId; use pezsp_core::{crypto::UncheckedFrom, H256}; fn dummy_id() -> AuthorityId { AuthorityId::unchecked_from([1; 32]) } #[test] fn load_decode_from_v0_migrates_data_format() { let client = bizinikiwi_test_runtime_client::new(); let authorities = vec![(dummy_id(), 100)]; let set_id = 3; let round_number: RoundNumber = 42; let round_state = RoundState:: { prevote_ghost: Some((H256::random(), 32)), finalized: None, estimate: None, completable: false, }; { let authority_set = V0AuthoritySet:: { current_authorities: authorities.clone(), pending_changes: Vec::new(), set_id, }; let voter_set_state = (round_number, round_state.clone()); client .insert_aux( &[ (AUTHORITY_SET_KEY, authority_set.encode().as_slice()), (SET_STATE_KEY, voter_set_state.encode().as_slice()), ], &[], ) .unwrap(); } assert_eq!(load_decode::<_, u32>(&client, VERSION_KEY).unwrap(), None); // should perform the migration load_persistent::( &client, H256::random(), 0, || unreachable!(), ) .unwrap(); assert_eq!(load_decode::<_, u32>(&client, VERSION_KEY).unwrap(), Some(3)); let PersistentData { authority_set, set_state, .. } = load_persistent::( &client, H256::random(), 0, || unreachable!(), ) .unwrap(); assert_eq!( *authority_set.inner(), AuthoritySet::new( authorities.clone(), set_id, ForkTree::new(), Vec::new(), AuthoritySetChanges::empty(), ) .unwrap(), ); let mut current_rounds = CurrentRounds::::new(); current_rounds.insert(round_number + 1, HasVoted::No); assert_eq!( &*set_state.read(), &VoterSetState::Live { completed_rounds: CompletedRounds::new( CompletedRound { number: round_number, state: round_state.clone(), base: round_state.prevote_ghost.unwrap(), votes: vec![], }, set_id, &*authority_set.inner(), ), current_rounds, }, ); } #[test] fn load_decode_from_v1_migrates_data_format() { let client = bizinikiwi_test_runtime_client::new(); let authorities = vec![(dummy_id(), 100)]; let set_id = 3; let round_number: RoundNumber = 42; let round_state = RoundState:: { prevote_ghost: Some((H256::random(), 32)), finalized: None, estimate: None, completable: false, }; { let authority_set = AuthoritySet::::new( authorities.clone(), set_id, ForkTree::new(), Vec::new(), AuthoritySetChanges::empty(), ) .unwrap(); let voter_set_state = V1VoterSetState::Live(round_number, round_state.clone()); client .insert_aux( &[ (AUTHORITY_SET_KEY, authority_set.encode().as_slice()), (SET_STATE_KEY, voter_set_state.encode().as_slice()), (VERSION_KEY, 1u32.encode().as_slice()), ], &[], ) .unwrap(); } assert_eq!(load_decode::<_, u32>(&client, VERSION_KEY).unwrap(), Some(1)); // should perform the migration load_persistent::( &client, H256::random(), 0, || unreachable!(), ) .unwrap(); assert_eq!(load_decode::<_, u32>(&client, VERSION_KEY).unwrap(), Some(3)); let PersistentData { authority_set, set_state, .. } = load_persistent::( &client, H256::random(), 0, || unreachable!(), ) .unwrap(); assert_eq!( *authority_set.inner(), AuthoritySet::new( authorities.clone(), set_id, ForkTree::new(), Vec::new(), AuthoritySetChanges::empty(), ) .unwrap(), ); let mut current_rounds = CurrentRounds::::new(); current_rounds.insert(round_number + 1, HasVoted::No); assert_eq!( &*set_state.read(), &VoterSetState::Live { completed_rounds: CompletedRounds::new( CompletedRound { number: round_number, state: round_state.clone(), base: round_state.prevote_ghost.unwrap(), votes: vec![], }, set_id, &*authority_set.inner(), ), current_rounds, }, ); } #[test] fn load_decode_from_v2_migrates_data_format() { let client = bizinikiwi_test_runtime_client::new(); let authorities = vec![(dummy_id(), 100)]; let set_id = 3; { let authority_set = V2AuthoritySet:: { current_authorities: authorities.clone(), set_id, pending_standard_changes: ForkTree::new(), pending_forced_changes: Vec::new(), }; let genesis_state = (H256::random(), 32); let voter_set_state: VoterSetState = VoterSetState::live( set_id, &authority_set.clone().into(), // Note the conversion! genesis_state, ); client .insert_aux( &[ (AUTHORITY_SET_KEY, authority_set.encode().as_slice()), (SET_STATE_KEY, voter_set_state.encode().as_slice()), (VERSION_KEY, 2u32.encode().as_slice()), ], &[], ) .unwrap(); } assert_eq!(load_decode::<_, u32>(&client, VERSION_KEY).unwrap(), Some(2)); // should perform the migration load_persistent::( &client, H256::random(), 0, || unreachable!(), ) .unwrap(); assert_eq!(load_decode::<_, u32>(&client, VERSION_KEY).unwrap(), Some(3)); let PersistentData { authority_set, .. } = load_persistent::< bizinikiwi_test_runtime_client::runtime::Block, _, _, >( &client, H256::random(), 0, || unreachable!() ) .unwrap(); assert_eq!( *authority_set.inner(), AuthoritySet::new( authorities.clone(), set_id, ForkTree::new(), Vec::new(), AuthoritySetChanges::empty(), ) .unwrap(), ); } #[test] fn write_read_concluded_rounds() { let client = bizinikiwi_test_runtime_client::new(); let hash = H256::random(); let round_state = RoundState::genesis((hash, 0)); let completed_round = CompletedRound:: { number: 42, state: round_state.clone(), base: round_state.prevote_ghost.unwrap(), votes: vec![], }; assert!(write_concluded_round(&client, &completed_round).is_ok()); let round_number = completed_round.number; let mut key = CONCLUDED_ROUNDS.to_vec(); round_number.using_encoded(|n| key.extend(n)); assert_eq!( load_decode::<_, CompletedRound::>( &client, &key ) .unwrap(), Some(completed_round), ); } }