referrerpolicy=no-referrer-when-downgrade

sc_consensus_beefy/
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 BEEFY state persisted in the aux-db.
20
21use crate::{error::Error, worker::PersistedState, LOG_TARGET};
22use codec::{Decode, Encode};
23use log::{debug, trace, warn};
24use sc_client_api::{backend::AuxStore, Backend};
25use sp_blockchain::{Error as ClientError, Result as ClientResult};
26use sp_consensus_beefy::AuthorityIdBound;
27use sp_runtime::traits::Block as BlockT;
28
29const VERSION_KEY: &[u8] = b"beefy_auxschema_version";
30const WORKER_STATE_KEY: &[u8] = b"beefy_voter_state";
31
32const CURRENT_VERSION: u32 = 4;
33
34pub(crate) fn write_current_version<BE: AuxStore>(backend: &BE) -> Result<(), Error> {
35	debug!(target: LOG_TARGET, "🥩 write aux schema version {:?}", CURRENT_VERSION);
36	AuxStore::insert_aux(backend, &[(VERSION_KEY, CURRENT_VERSION.encode().as_slice())], &[])
37		.map_err(|e| Error::Backend(e.to_string()))
38}
39
40/// Write voter state.
41pub(crate) fn write_voter_state<B: BlockT, BE: AuxStore, AuthorityId: AuthorityIdBound>(
42	backend: &BE,
43	state: &PersistedState<B, AuthorityId>,
44) -> ClientResult<()> {
45	trace!(target: LOG_TARGET, "🥩 persisting {:?}", state);
46	AuxStore::insert_aux(backend, &[(WORKER_STATE_KEY, state.encode().as_slice())], &[])
47}
48
49fn load_decode<BE: AuxStore, T: Decode>(backend: &BE, key: &[u8]) -> ClientResult<Option<T>> {
50	match backend.get_aux(key)? {
51		None => Ok(None),
52		Some(t) => T::decode(&mut &t[..])
53			.map_err(|e| ClientError::Backend(format!("BEEFY DB is corrupted: {}", e)))
54			.map(Some),
55	}
56}
57
58/// Load or initialize persistent data from backend.
59pub(crate) fn load_persistent<B, BE, AuthorityId: AuthorityIdBound>(
60	backend: &BE,
61) -> ClientResult<Option<PersistedState<B, AuthorityId>>>
62where
63	B: BlockT,
64	BE: Backend<B>,
65{
66	let version: Option<u32> = load_decode(backend, VERSION_KEY)?;
67
68	match version {
69		None => (),
70
71		Some(v) if 1 <= v && v <= 3 =>
72		// versions 1, 2 & 3 are obsolete and should be ignored
73			warn!(target: LOG_TARGET,  "🥩 backend contains a BEEFY state of an obsolete version {v}. ignoring..."),
74		Some(4) =>
75			return load_decode::<_, PersistedState<B, AuthorityId>>(backend, WORKER_STATE_KEY),
76		other =>
77			return Err(ClientError::Backend(format!("Unsupported BEEFY DB version: {:?}", other))),
78	}
79
80	// No persistent state found in DB.
81	Ok(None)
82}
83
84#[cfg(test)]
85pub(crate) mod tests {
86	use super::*;
87	use crate::tests::BeefyTestNet;
88	use sc_network_test::TestNetFactory;
89	use sp_consensus_beefy::ecdsa_crypto;
90
91	// also used in tests.rs
92	pub fn verify_persisted_version<B: BlockT, BE: Backend<B>>(backend: &BE) -> bool {
93		let version: u32 = load_decode(backend, VERSION_KEY).unwrap().unwrap();
94		version == CURRENT_VERSION
95	}
96
97	#[tokio::test]
98	async fn should_load_persistent_sanity_checks() {
99		let mut net = BeefyTestNet::new(1);
100		let backend = net.peer(0).client().as_backend();
101
102		// version not available in db -> None
103		assert_eq!(load_persistent::<_, _, ecdsa_crypto::AuthorityId>(&*backend).unwrap(), None);
104
105		// populate version in db
106		write_current_version(&*backend).unwrap();
107		// verify correct version is retrieved
108		assert_eq!(load_decode(&*backend, VERSION_KEY).unwrap(), Some(CURRENT_VERSION));
109
110		// version is available in db but state isn't -> None
111		assert_eq!(load_persistent::<_, _, ecdsa_crypto::AuthorityId>(&*backend).unwrap(), None);
112
113		// full `PersistedState` load is tested in `tests.rs`.
114	}
115}