fuel_core/database/
block.rs1use crate::{
2 database::{
3 OffChainIterableKeyValueView,
4 OnChainIterableKeyValueView,
5 },
6 fuel_core_graphql_api::storage::blocks::FuelBlockIdsToHeights,
7};
8use fuel_core_storage::{
9 Error as StorageError,
10 Result as StorageResult,
11 StorageAsRef,
12 iter::{
13 IterDirection,
14 IteratorOverTable,
15 },
16 not_found,
17 tables::{
18 FuelBlocks,
19 Transactions,
20 merkle::{
21 DenseMetadataKey,
22 FuelBlockMerkleData,
23 FuelBlockMerkleMetadata,
24 },
25 },
26};
27use fuel_core_types::{
28 blockchain::{
29 block::{
30 Block,
31 CompressedBlock,
32 },
33 primitives::BlockId,
34 },
35 entities::relayer::message::MerkleProof,
36 fuel_merkle::binary::MerkleTree,
37 fuel_types::BlockHeight,
38};
39use itertools::Itertools;
40use std::borrow::Cow;
41
42impl OffChainIterableKeyValueView {
43 pub fn get_block_height(&self, id: &BlockId) -> StorageResult<Option<BlockHeight>> {
44 self.storage::<FuelBlockIdsToHeights>()
45 .get(id)
46 .map(|v| v.map(|v| v.into_owned()))
47 }
48}
49
50impl OnChainIterableKeyValueView {
51 pub fn latest_compressed_block(&self) -> StorageResult<Option<CompressedBlock>> {
52 let pair = self
53 .iter_all::<FuelBlocks>(Some(IterDirection::Reverse))
54 .next()
55 .transpose()?;
56
57 Ok(pair.map(|(_, compressed_block)| compressed_block))
58 }
59
60 pub fn get_current_block(&self) -> StorageResult<Option<CompressedBlock>> {
62 self.latest_compressed_block()
63 }
64
65 pub fn get_full_block(&self, height: &BlockHeight) -> StorageResult<Option<Block>> {
67 let db_block = self.storage::<FuelBlocks>().get(height)?;
68 if let Some(block) = db_block {
69 let txs = block
73 .transactions()
74 .iter()
75 .map(|tx_id| {
76 self.storage::<Transactions>()
77 .get(tx_id)
78 .and_then(|tx| tx.ok_or(not_found!(Transactions)))
79 .map(Cow::into_owned)
80 })
81 .try_collect()?;
82 Ok(Some(block.into_owned().uncompress(txs)))
83 } else {
84 Ok(None)
85 }
86 }
87}
88
89impl OnChainIterableKeyValueView {
90 pub fn block_history_proof(
91 &self,
92 message_block_height: &BlockHeight,
93 commit_block_height: &BlockHeight,
94 ) -> StorageResult<MerkleProof> {
95 if message_block_height > commit_block_height {
96 Err(anyhow::anyhow!(
97 "The `message_block_height` is higher than `commit_block_height`"
98 ))?;
99 }
100
101 let message_merkle_metadata = self
102 .storage::<FuelBlockMerkleMetadata>()
103 .get(&DenseMetadataKey::Primary(*message_block_height))?
104 .ok_or(not_found!(FuelBlockMerkleMetadata))?;
105
106 let commit_merkle_metadata = self
107 .storage::<FuelBlockMerkleMetadata>()
108 .get(&DenseMetadataKey::Primary(*commit_block_height))?
109 .ok_or(not_found!(FuelBlockMerkleMetadata))?;
110
111 let storage = self;
112 let tree: MerkleTree<FuelBlockMerkleData, _> =
113 MerkleTree::load(storage, commit_merkle_metadata.version())
114 .map_err(|err| StorageError::Other(anyhow::anyhow!(err)))?;
115
116 let proof_index = message_merkle_metadata
117 .version()
118 .checked_sub(1)
119 .ok_or(anyhow::anyhow!("The count of leaves - messages is zero"))?;
120 let (_, proof_set) = tree
121 .prove(proof_index)
122 .map_err(|err| StorageError::Other(anyhow::anyhow!(err)))?;
123
124 Ok(MerkleProof {
125 proof_set,
126 proof_index,
127 })
128 }
129}
130
131#[allow(clippy::arithmetic_side_effects)]
132#[cfg(test)]
133mod tests {
134 use super::*;
135 use crate::database::Database;
136 use fuel_core_storage::{
137 StorageMutate,
138 transactional::AtomicView,
139 };
140 use fuel_core_types::{
141 blockchain::{
142 block::PartialFuelBlock,
143 header::{
144 ConsensusHeader,
145 PartialBlockHeader,
146 },
147 primitives::Empty,
148 },
149 fuel_types::ChainId,
150 };
151 use test_case::test_case;
152
153 const TEST_BLOCKS_COUNT: u32 = 10;
154
155 fn insert_test_ascending_blocks(
156 database: &mut Database,
157 genesis_height: BlockHeight,
158 ) {
159 let start = *genesis_height;
160 let blocks = (start..start + TEST_BLOCKS_COUNT)
162 .map(|height| {
163 let header = PartialBlockHeader {
164 application: Default::default(),
165 consensus: ConsensusHeader::<Empty> {
166 height: BlockHeight::from(height),
167 ..Default::default()
168 },
169 };
170 let block = PartialFuelBlock::new(header, vec![]);
171 block
172 .generate(
173 &[],
174 Default::default(),
175 #[cfg(feature = "fault-proving")]
176 &Default::default(),
177 )
178 .unwrap()
179 })
180 .collect::<Vec<_>>();
181
182 for block in &blocks {
184 StorageMutate::<FuelBlocks>::insert(
185 database,
186 block.header().height(),
187 &block.compress(&ChainId::default()),
188 )
189 .unwrap();
190 }
191 }
192
193 #[test]
194 fn get_merkle_root_for_invalid_block_height_returns_not_found_error() {
195 let mut database = Database::default();
196
197 insert_test_ascending_blocks(&mut database, BlockHeight::from(0));
198
199 let err = database
201 .storage::<FuelBlocks>()
202 .root(&100u32.into())
203 .expect_err("expected error getting invalid Block Merkle root");
204
205 assert!(matches!(err, fuel_core_storage::Error::NotFound(_, _)));
206 }
207
208 #[test_case(0; "genesis block at height 0")]
209 #[test_case(1; "genesis block at height 1")]
210 #[test_case(100; "genesis block at height 100")]
211 fn block_history_proof_works(genesis_height: u32) {
212 let mut database = Database::default();
213
214 insert_test_ascending_blocks(&mut database, BlockHeight::from(genesis_height));
215 let view = database.latest_view().unwrap();
216
217 for l in 0..TEST_BLOCKS_COUNT {
218 for r in l..TEST_BLOCKS_COUNT {
219 let proof = view
220 .block_history_proof(
221 &BlockHeight::from(genesis_height + l),
222 &BlockHeight::from(genesis_height + r),
223 )
224 .expect("Should return the merkle proof");
225 assert_eq!(proof.proof_index, l as u64);
226 }
227 }
228 }
229
230 #[test]
231 fn block_history_proof_error_if_message_higher_than_commit() {
232 let mut database = Database::default();
233
234 insert_test_ascending_blocks(&mut database, BlockHeight::from(0));
235 let view = database.latest_view().unwrap();
236
237 let result = view.block_history_proof(
238 &BlockHeight::from(TEST_BLOCKS_COUNT),
239 &BlockHeight::from(TEST_BLOCKS_COUNT - 1),
240 );
241 assert!(result.is_err());
242 }
243}