Skip to main content

ethexe_common/
db.rs

1// Copyright (C) Gear Technologies Inc.
2// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
3
4//! Common db types and traits.
5
6use crate::{
7    Address, BlockHeader, CodeBlobInfo, Digest, HashOf, ProgramStates, ProtocolTimelines, Schedule,
8    SimpleBlockData, ValidatorsVec,
9    events::BlockEvent,
10    gear::StateTransition,
11    injected::{InjectedTransaction, Promise, SignedInjectedTransaction, SignedTxReceipt},
12    malachite::Transactions,
13};
14use alloc::{
15    collections::{BTreeSet, VecDeque},
16    vec::Vec,
17};
18use gear_core::{
19    code::{CodeMetadata, InstrumentedCode},
20    ids::{ActorId, CodeId},
21};
22use gprimitives::H256;
23use parity_scale_codec::{Decode, Encode};
24use scale_info::TypeInfo;
25
26/// Ethexe metadata associated with an on-chain block.
27#[derive(Clone, Debug, Default, Encode, Decode, TypeInfo, PartialEq, Eq, Hash)]
28pub struct BlockMeta {
29    /// Block has been prepared, meaning:
30    /// all metadata is ready, all predecessors till start block are prepared too.
31    pub prepared: bool,
32    /// Queue of code ids waiting for validation status commitment on-chain.
33    pub codes_queue: Option<VecDeque<CodeId>>,
34    /// Last committed on-chain batch hash (digest).
35    pub last_committed_batch: Option<Digest>,
36    /// Last committed MB hash.
37    pub last_committed_mb: Option<H256>,
38    /// Last committed EB hash.
39    pub last_committed_eb: Option<H256>,
40    /// Latest era with committed validators.
41    pub latest_era_validators_committed: Option<u64>,
42}
43
44#[auto_impl::auto_impl(&, Box)]
45pub trait HashStorageRO {
46    fn read_by_hash(&self, hash: H256) -> Option<Vec<u8>>;
47}
48
49#[auto_impl::auto_impl(&, Box)]
50pub trait BlockMetaStorageRO {
51    /// NOTE: if `BlockMeta` doesn't exist in the database, it will return the default value.
52    fn block_meta(&self, block_hash: H256) -> BlockMeta;
53}
54
55#[auto_impl::auto_impl(&)]
56pub trait BlockMetaStorageRW: BlockMetaStorageRO {
57    /// NOTE: if `BlockMeta` doesn't exist in the database,
58    /// it will be created with default values and then will be mutated.
59    fn mutate_block_meta(&self, block_hash: H256, f: impl FnOnce(&mut BlockMeta));
60}
61
62#[auto_impl::auto_impl(&, Box)]
63pub trait CodesStorageRO {
64    fn original_code_exists(&self, code_id: CodeId) -> bool;
65    fn original_code(&self, code_id: CodeId) -> Option<Vec<u8>>;
66    fn program_code_id(&self, program_id: ActorId) -> Option<CodeId>;
67    fn instrumented_code_exists(&self, runtime_id: u32, code_id: CodeId) -> bool;
68    fn instrumented_code(&self, runtime_id: u32, code_id: CodeId) -> Option<InstrumentedCode>;
69    fn code_metadata(&self, code_id: CodeId) -> Option<CodeMetadata>;
70    fn code_valid(&self, code_id: CodeId) -> Option<bool>;
71    fn valid_codes(&self) -> BTreeSet<CodeId>;
72}
73
74#[auto_impl::auto_impl(&)]
75pub trait CodesStorageRW: CodesStorageRO {
76    fn set_original_code(&self, code: &[u8]) -> CodeId;
77    fn set_program_code_id(&self, program_id: ActorId, code_id: CodeId);
78    fn set_instrumented_code(&self, runtime_id: u32, code_id: CodeId, code: InstrumentedCode);
79    fn set_code_metadata(&self, code_id: CodeId, code_metadata: CodeMetadata);
80    fn set_code_valid(&self, code_id: CodeId, valid: bool);
81}
82
83#[auto_impl::auto_impl(&, Box)]
84pub trait OnChainStorageRO {
85    fn block_header(&self, block_hash: H256) -> Option<BlockHeader>;
86    fn block_events(&self, block_hash: H256) -> Option<Vec<BlockEvent>>;
87    fn code_blob_info(&self, code_id: CodeId) -> Option<CodeBlobInfo>;
88    fn block_synced(&self, block_hash: H256) -> bool;
89    fn validators(&self, era_index: u64) -> Option<ValidatorsVec>;
90
91    fn block_simple_data(&self, block_hash: H256) -> Option<SimpleBlockData> {
92        self.block_header(block_hash).map(|header| SimpleBlockData {
93            hash: block_hash,
94            header,
95        })
96    }
97}
98
99#[auto_impl::auto_impl(&)]
100pub trait OnChainStorageRW: OnChainStorageRO {
101    fn set_block_header(&self, block_hash: H256, header: BlockHeader);
102    fn set_block_events(&self, block_hash: H256, events: &[BlockEvent]);
103    fn set_code_blob_info(&self, code_id: CodeId, code_info: CodeBlobInfo);
104    fn set_validators(&self, era_index: u64, validator_set: ValidatorsVec);
105    fn set_block_synced(&self, block_hash: H256);
106}
107
108#[auto_impl::auto_impl(&)]
109pub trait InjectedStorageRO {
110    /// Returns the transactions by its hash.
111    fn injected_transaction(
112        &self,
113        hash: HashOf<InjectedTransaction>,
114    ) -> Option<SignedInjectedTransaction>;
115
116    /// Returns the promise by its transaction hash.
117    fn promise(&self, hash: HashOf<InjectedTransaction>) -> Option<Promise>;
118
119    /// Returns the receipt by its transaction hash.
120    fn receipt(&self, hash: HashOf<InjectedTransaction>) -> Option<SignedTxReceipt>;
121}
122
123#[auto_impl::auto_impl(&)]
124pub trait InjectedStorageRW: InjectedStorageRO {
125    fn set_injected_transaction(&self, tx: SignedInjectedTransaction);
126
127    fn set_promise(&self, promise: &Promise);
128
129    fn set_receipt(&self, receipt: &SignedTxReceipt);
130}
131
132/// MB static identity. Keyed by the Blake2b envelope hash; existence implies
133/// the matching `Transactions` blob is in CAS at `transactions_hash`.
134#[derive(
135    Debug, Clone, Copy, Default, Encode, Decode, TypeInfo, PartialEq, Eq, Hash, derive_more::Display,
136)]
137#[display("MB(height {height}, parent {parent}, transactions_hash {transactions_hash})")]
138pub struct CompactMb {
139    pub parent: H256,
140    pub height: u64,
141    pub transactions_hash: H256,
142}
143
144/// MB dynamic state. `last_advanced_eb` is propagated forward at save time
145/// (resets on `AdvanceTillEthereumBlock`); `synced` requires this MB and every
146/// ancestor to be persisted.
147#[derive(Debug, Clone, Default, Encode, Decode, TypeInfo, PartialEq, Eq, Hash)]
148pub struct MbMeta {
149    pub computed: bool,
150    pub last_advanced_eb: H256,
151}
152
153#[auto_impl::auto_impl(&, Box)]
154pub trait MbStorageRO {
155    /// Static identity (parent + height + `transactions_hash`).
156    /// Existence implies the matching [`Transactions`] blob is in the
157    /// CAS at `transactions_hash`.
158    fn mb_compact_block(&self, mb_hash: H256) -> Option<CompactMb>;
159    /// Read the [`Transactions`] blob from CAS by its content hash.
160    fn transactions(&self, transactions_hash: H256) -> Option<Transactions>;
161    fn mb_program_states(&self, mb_hash: H256) -> Option<ProgramStates>;
162    fn mb_outcome(&self, mb_hash: H256) -> Option<Vec<StateTransition>>;
163    fn mb_schedule(&self, mb_hash: H256) -> Option<Schedule>;
164    fn mb_meta(&self, mb_hash: H256) -> MbMeta;
165}
166
167#[auto_impl::auto_impl(&)]
168pub trait MbStorageRW: MbStorageRO {
169    fn set_mb_compact_block(&self, mb_hash: H256, compact: CompactMb);
170    /// Write a [`Transactions`] blob into the CAS and return its hash
171    /// (the value stored in [`CompactMb::transactions_hash`]).
172    fn set_transactions(&self, transactions: Transactions) -> H256;
173    fn set_mb_program_states(&self, mb_hash: H256, program_states: ProgramStates);
174    fn set_mb_outcome(&self, mb_hash: H256, outcome: Vec<StateTransition>);
175    fn set_mb_schedule(&self, mb_hash: H256, schedule: Schedule);
176    fn mutate_mb_meta(&self, mb_hash: H256, f: impl FnOnce(&mut MbMeta));
177}
178
179pub struct PreparedBlockData {
180    pub header: BlockHeader,
181    pub events: Vec<BlockEvent>,
182    pub latest_era_with_committed_validators: u64,
183    pub codes_queue: VecDeque<CodeId>,
184    pub last_committed_batch: Digest,
185    pub last_committed_mb: H256,
186    pub last_committed_eb: H256,
187}
188
189#[derive(Debug, Clone, Encode, Decode, TypeInfo, PartialEq, Eq)]
190pub struct DBConfig {
191    pub version: u32,
192    pub chain_id: u64,
193    pub router_address: Address,
194    pub timelines: ProtocolTimelines,
195    pub genesis_block_hash: H256,
196    pub max_validators: u16,
197}
198
199#[derive(Debug, Clone, Encode, Decode, TypeInfo, PartialEq, Eq)]
200pub struct DBGlobals {
201    pub start_block_hash: H256,
202    pub latest_synced_eb: SimpleBlockData,
203    pub latest_prepared_eb_hash: H256,
204    /// Latest MB BFT-finalized by Malachite. Rows
205    /// (`mb_program_states`/`mb_outcome`/`mb_schedule`) may not yet
206    /// be persisted — use [`Self::latest_computed_mb_hash`] for any
207    /// read that depends on those rows existing.
208    pub latest_finalized_mb_hash: H256,
209    /// Latest MB whose per-row state has been written by the compute
210    /// pipeline. Trails `latest_finalized_mb_hash` until compute
211    /// catches up.
212    pub latest_computed_mb_hash: H256,
213}
214
215#[cfg(feature = "std")]
216mod std_interfaces {
217    use super::{DBConfig, DBGlobals};
218    use std::sync::RwLockReadGuard;
219
220    #[auto_impl::auto_impl(&, Box)]
221    pub trait GlobalsStorageRO {
222        fn globals(&self) -> RwLockReadGuard<'_, DBGlobals>;
223    }
224
225    #[auto_impl::auto_impl(&, Box)]
226    pub trait GlobalsStorageRW: GlobalsStorageRO {
227        fn globals_mutate<R>(&self, f: impl FnMut(&mut DBGlobals) -> R) -> R;
228    }
229
230    #[auto_impl::auto_impl(&, Box)]
231    pub trait ConfigStorageRO {
232        fn config(&self) -> RwLockReadGuard<'_, DBConfig>;
233    }
234}
235
236#[cfg(feature = "std")]
237pub use std_interfaces::{ConfigStorageRO, GlobalsStorageRO, GlobalsStorageRW};
238
239#[cfg(feature = "mock")]
240mod mock_interfaces {
241    use super::{DBConfig, DBGlobals};
242
243    #[auto_impl::auto_impl(&, Box)]
244    pub trait SetGlobals {
245        fn set_globals(&self, globals: DBGlobals);
246    }
247
248    #[auto_impl::auto_impl(&, Box)]
249    pub trait SetConfig {
250        fn set_config(&self, config: DBConfig);
251    }
252}
253
254#[cfg(feature = "mock")]
255pub use mock_interfaces::{SetConfig, SetGlobals};
256
257#[cfg(test)]
258mod tests {
259    use super::*;
260    use crate::malachite::Transactions;
261    use indoc::formatdoc;
262    use scale_info::{PortableRegistry, Registry, meta_type};
263    use sha3::{Digest, Sha3_256};
264
265    #[test]
266    fn ensure_types_unchanged() {
267        const EXPECTED_TYPE_INFO_HASH: &str =
268            "d43d8ab319fb6d934231dba55950c9825e28c6ecf603e8076a90e0cab3855671";
269
270        let types = [
271            meta_type::<BlockMeta>(),
272            meta_type::<InstrumentedCode>(),
273            meta_type::<CodeMetadata>(),
274            meta_type::<BlockHeader>(),
275            meta_type::<BlockEvent>(),
276            meta_type::<CodeBlobInfo>(),
277            meta_type::<ValidatorsVec>(),
278            meta_type::<ProtocolTimelines>(),
279            meta_type::<HashOf<InjectedTransaction>>(),
280            meta_type::<SignedInjectedTransaction>(),
281            meta_type::<ProgramStates>(),
282            meta_type::<StateTransition>(),
283            meta_type::<Schedule>(),
284            meta_type::<MbMeta>(),
285            meta_type::<CompactMb>(),
286            meta_type::<Transactions>(),
287            meta_type::<DBConfig>(),
288            meta_type::<DBGlobals>(),
289        ];
290
291        let mut registry = Registry::new();
292        registry.register_types(types);
293
294        let portable_registry = PortableRegistry::from(registry);
295        let encoded_registry = portable_registry.encode();
296        let type_info_hash = hex::encode(Sha3_256::digest(encoded_registry));
297
298        if type_info_hash != EXPECTED_TYPE_INFO_HASH {
299            panic!(
300                "{}",
301                formatdoc!(
302                    "
303                    Some of database types has been changed.
304
305                    It can break existing databases, so be very careful and think at least
306                    twice before committing such changes. Ensure that SCALE representations
307                    of all changed database types are still the same.
308
309                    If you know what exactly has been changed and sure about it,
310                    please update `EXPECTED_TYPE_INFO_HASH` constant in this test
311                    to the new value to fix the assertion.
312
313                    Expected hash: {EXPECTED_TYPE_INFO_HASH}
314                    Found hash:    {type_info_hash}
315                    "
316                )
317            );
318        }
319    }
320}