1use codec::{Decode, Encode};
22use log::info;
23
24use crate::{migration::EpochV0, Epoch, LOG_TARGET};
25use sc_client_api::backend::AuxStore;
26use sc_consensus_epochs::{
27 migration::{EpochChangesV0For, EpochChangesV1For},
28 EpochChangesFor, SharedEpochChanges,
29};
30use sp_blockchain::{Error as ClientError, Result as ClientResult};
31use sp_consensus_babe::{BabeBlockWeight, BabeConfiguration};
32use sp_runtime::traits::Block as BlockT;
33
34const BABE_EPOCH_CHANGES_VERSION: &[u8] = b"babe_epoch_changes_version";
35const BABE_EPOCH_CHANGES_KEY: &[u8] = b"babe_epoch_changes";
36const BABE_EPOCH_CHANGES_CURRENT_VERSION: u32 = 3;
37
38pub fn block_weight_key<H: Encode>(block_hash: H) -> Vec<u8> {
40 (b"block_weight", block_hash).encode()
41}
42
43fn load_decode<B, T>(backend: &B, key: &[u8]) -> ClientResult<Option<T>>
44where
45 B: AuxStore,
46 T: Decode,
47{
48 let corrupt = |e: codec::Error| {
49 ClientError::Backend(format!("BABE DB is corrupted. Decode error: {}", e))
50 };
51 match backend.get_aux(key)? {
52 None => Ok(None),
53 Some(t) => T::decode(&mut &t[..]).map(Some).map_err(corrupt),
54 }
55}
56
57pub fn load_epoch_changes<Block: BlockT, B: AuxStore>(
59 backend: &B,
60 config: &BabeConfiguration,
61) -> ClientResult<SharedEpochChanges<Block, Epoch>> {
62 let version = load_decode::<_, u32>(backend, BABE_EPOCH_CHANGES_VERSION)?;
63
64 let maybe_epoch_changes = match version {
65 None => {
66 load_decode::<_, EpochChangesV0For<Block, EpochV0>>(backend, BABE_EPOCH_CHANGES_KEY)?
67 .map(|v0| v0.migrate().map(|_, _, epoch| epoch.migrate(config)))
68 },
69 Some(1) => {
70 load_decode::<_, EpochChangesV1For<Block, EpochV0>>(backend, BABE_EPOCH_CHANGES_KEY)?
71 .map(|v1| v1.migrate().map(|_, _, epoch| epoch.migrate(config)))
72 },
73 Some(2) => {
74 load_decode::<_, EpochChangesV1For<Block, Epoch>>(backend, BABE_EPOCH_CHANGES_KEY)?
76 .map(|v2| v2.migrate())
77 },
78 Some(BABE_EPOCH_CHANGES_CURRENT_VERSION) => {
79 load_decode::<_, EpochChangesFor<Block, Epoch>>(backend, BABE_EPOCH_CHANGES_KEY)?
80 },
81 Some(other) => {
82 return Err(ClientError::Backend(format!("Unsupported BABE DB version: {:?}", other)))
83 },
84 };
85
86 let epoch_changes =
87 SharedEpochChanges::<Block, Epoch>::new(maybe_epoch_changes.unwrap_or_else(|| {
88 info!(
89 target: LOG_TARGET,
90 "👶 Creating empty BABE epoch changes on what appears to be first startup.",
91 );
92 EpochChangesFor::<Block, Epoch>::default()
93 }));
94
95 epoch_changes.shared_data().rebalance();
100
101 Ok(epoch_changes)
102}
103
104pub(crate) fn write_epoch_changes<Block: BlockT, F, R>(
106 epoch_changes: &EpochChangesFor<Block, Epoch>,
107 write_aux: F,
108) -> R
109where
110 F: FnOnce(&[(&'static [u8], &[u8])]) -> R,
111{
112 BABE_EPOCH_CHANGES_CURRENT_VERSION.using_encoded(|version| {
113 let encoded_epoch_changes = epoch_changes.encode();
114 write_aux(&[
115 (BABE_EPOCH_CHANGES_KEY, encoded_epoch_changes.as_slice()),
116 (BABE_EPOCH_CHANGES_VERSION, version),
117 ])
118 })
119}
120
121pub(crate) fn write_block_weight<H: Encode, F, R>(
123 block_hash: H,
124 block_weight: BabeBlockWeight,
125 write_aux: F,
126) -> R
127where
128 F: FnOnce(&[(Vec<u8>, &[u8])]) -> R,
129{
130 let key = block_weight_key(block_hash);
131 block_weight.using_encoded(|s| write_aux(&[(key, s)]))
132}
133
134pub fn load_block_weight<H: Encode, B: AuxStore>(
136 backend: &B,
137 block_hash: H,
138) -> ClientResult<Option<BabeBlockWeight>> {
139 load_decode(backend, block_weight_key(block_hash).as_slice())
140}
141
142#[cfg(test)]
143mod test {
144 use super::*;
145 use crate::migration::EpochV0;
146 use fork_tree::ForkTree;
147 use sc_consensus_epochs::{EpochHeader, PersistedEpoch, PersistedEpochHeader};
148 use sc_network_test::Block as TestBlock;
149 use sp_consensus::Error as ConsensusError;
150 use sp_consensus_babe::AllowedSlots;
151 use sp_core::H256;
152 use sp_runtime::traits::NumberFor;
153 use substrate_test_runtime_client;
154
155 #[test]
156 fn load_decode_from_v0_epoch_changes() {
157 let epoch = EpochV0 {
158 start_slot: 0.into(),
159 authorities: vec![],
160 randomness: [0; 32],
161 epoch_index: 1,
162 duration: 100,
163 };
164 let client = substrate_test_runtime_client::new();
165 let mut v0_tree = ForkTree::<H256, NumberFor<TestBlock>, _>::new();
166 v0_tree
167 .import::<_, ConsensusError>(
168 Default::default(),
169 Default::default(),
170 PersistedEpoch::Regular(epoch),
171 &|_, _| Ok(false), )
173 .unwrap();
174
175 client
176 .insert_aux(
177 &[(
178 BABE_EPOCH_CHANGES_KEY,
179 &EpochChangesV0For::<TestBlock, EpochV0>::from_raw(v0_tree).encode()[..],
180 )],
181 &[],
182 )
183 .unwrap();
184
185 assert_eq!(load_decode::<_, u32>(&client, BABE_EPOCH_CHANGES_VERSION).unwrap(), None);
186
187 let epoch_changes = load_epoch_changes::<TestBlock, _>(
188 &client,
189 &BabeConfiguration {
190 slot_duration: 10,
191 epoch_length: 4,
192 c: (3, 10),
193 authorities: Vec::new(),
194 randomness: Default::default(),
195 allowed_slots: AllowedSlots::PrimaryAndSecondaryPlainSlots,
196 },
197 )
198 .unwrap();
199
200 assert!(
201 epoch_changes
202 .shared_data()
203 .tree()
204 .iter()
205 .map(|(_, _, epoch)| epoch.clone())
206 .collect::<Vec<_>>() ==
207 vec![PersistedEpochHeader::Regular(EpochHeader {
208 start_slot: 0.into(),
209 end_slot: 100.into(),
210 })],
211 ); write_epoch_changes::<TestBlock, _, _>(&epoch_changes.shared_data(), |values| {
214 client.insert_aux(values, &[]).unwrap();
215 });
216
217 assert_eq!(load_decode::<_, u32>(&client, BABE_EPOCH_CHANGES_VERSION).unwrap(), Some(3));
218 }
219}