mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-15 16:11:05 +00:00
client/beefy: persist voter state (#12712)
* client/beefy: prepare worker for persisting state * client/beefy: persist voter state * client/beefy: initialize persistent state * client/beefy: try to vote from the very beginning Now that voter is initialized from persistent state, it makes sense that it can attempt voting right away. This also helps the genesis case when we consider block `One` as mandatory. * client/beefy: add tests for voter state db * client/beefy: persist voter state as soon as initialized * client/beefy: make sure min-block-delta is at least 1 * client/beefy: persist state after voting Persist state after handling self vote to avoid double voting in case of voter restarts. * client/beefy: persist state after handling mandatory block vote For mandatory blocks we want to make sure we're not losing votes in case of crashes or restarts, since voter will not make further progress without finalizing them. * frame/beefy: use GENESIS_AUTHORITY_SET_ID on pallet genesis * client/beefy: initialize voter at either genesis or last finalized To guarantee unbroken chain of mandatory blocks justifications, voter will always resume from either last BEEFY-justified block or `pallet-beefy` genesis, whichever is more recent. Initialization walks back the chain from latest GRANDPA finalized block looking for one of the above. Along the way, it also records and enqueues for processing any BEEFY mandatory blocks that have been already GRANDPA finalized but not BEEFY finalized. * client/beefy: decouple voter init from aux db state load * client/beefy: fix voter init tests * remove debug prints * gadget future must be type () * fix init from last justification Signed-off-by: Adrian Catangiu <adrian@parity.io>
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2019-2022 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! Schema for BEEFY state persisted in the aux-db.
|
||||
|
||||
use crate::worker::PersistedState;
|
||||
use codec::{Decode, Encode};
|
||||
use log::{info, trace};
|
||||
use sc_client_api::{backend::AuxStore, Backend};
|
||||
use sp_blockchain::{Error as ClientError, Result as ClientResult};
|
||||
use sp_runtime::traits::Block as BlockT;
|
||||
|
||||
const VERSION_KEY: &[u8] = b"beefy_auxschema_version";
|
||||
const WORKER_STATE: &[u8] = b"beefy_voter_state";
|
||||
|
||||
const CURRENT_VERSION: u32 = 1;
|
||||
|
||||
pub(crate) fn write_current_version<B: AuxStore>(backend: &B) -> ClientResult<()> {
|
||||
info!(target: "beefy", "🥩 write aux schema version {:?}", CURRENT_VERSION);
|
||||
AuxStore::insert_aux(backend, &[(VERSION_KEY, CURRENT_VERSION.encode().as_slice())], &[])
|
||||
}
|
||||
|
||||
/// Write voter state.
|
||||
pub(crate) fn write_voter_state<Block: BlockT, B: AuxStore>(
|
||||
backend: &B,
|
||||
state: &PersistedState<Block>,
|
||||
) -> ClientResult<()> {
|
||||
trace!(target: "beefy", "🥩 persisting {:?}", state);
|
||||
backend.insert_aux(&[(WORKER_STATE, state.encode().as_slice())], &[])
|
||||
}
|
||||
|
||||
fn load_decode<B: AuxStore, T: Decode>(backend: &B, key: &[u8]) -> ClientResult<Option<T>> {
|
||||
match backend.get_aux(key)? {
|
||||
None => Ok(None),
|
||||
Some(t) => T::decode(&mut &t[..])
|
||||
.map_err(|e| ClientError::Backend(format!("BEEFY DB is corrupted: {}", e)))
|
||||
.map(Some),
|
||||
}
|
||||
}
|
||||
|
||||
/// Load or initialize persistent data from backend.
|
||||
pub(crate) fn load_persistent<B, BE>(backend: &BE) -> ClientResult<Option<PersistedState<B>>>
|
||||
where
|
||||
B: BlockT,
|
||||
BE: Backend<B>,
|
||||
{
|
||||
let version: Option<u32> = load_decode(backend, VERSION_KEY)?;
|
||||
|
||||
match version {
|
||||
None => (),
|
||||
Some(1) => return load_decode::<_, PersistedState<B>>(backend, WORKER_STATE),
|
||||
other =>
|
||||
return Err(ClientError::Backend(format!("Unsupported BEEFY DB version: {:?}", other))),
|
||||
}
|
||||
|
||||
// No persistent state found in DB.
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::tests::BeefyTestNet;
|
||||
use sc_network_test::TestNetFactory;
|
||||
|
||||
// also used in tests.rs
|
||||
pub fn verify_persisted_version<B: BlockT, BE: Backend<B>>(backend: &BE) -> bool {
|
||||
let version: u32 = load_decode(backend, VERSION_KEY).unwrap().unwrap();
|
||||
version == CURRENT_VERSION
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_load_persistent_sanity_checks() {
|
||||
let mut net = BeefyTestNet::new(1);
|
||||
let backend = net.peer(0).client().as_backend();
|
||||
|
||||
// version not available in db -> None
|
||||
assert_eq!(load_persistent(&*backend).unwrap(), None);
|
||||
|
||||
// populate version in db
|
||||
write_current_version(&*backend).unwrap();
|
||||
// verify correct version is retrieved
|
||||
assert_eq!(load_decode(&*backend, VERSION_KEY).unwrap(), Some(CURRENT_VERSION));
|
||||
|
||||
// version is available in db but state isn't -> None
|
||||
assert_eq!(load_persistent(&*backend).unwrap(), None);
|
||||
|
||||
// full `PersistedState` load is tested in `tests.rs`.
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user