/__w/smoldot/smoldot/repo/lib/src/database/full_sqlite.rs
Line | Count | Source (jump to first uncovered line) |
1 | | // Smoldot |
2 | | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. |
3 | | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 |
4 | | |
5 | | // This program is free software: you can redistribute it and/or modify |
6 | | // it under the terms of the GNU General Public License as published by |
7 | | // the Free Software Foundation, either version 3 of the License, or |
8 | | // (at your option) any later version. |
9 | | |
10 | | // This program is distributed in the hope that it will be useful, |
11 | | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | | // GNU General Public License for more details. |
14 | | |
15 | | // You should have received a copy of the GNU General Public License |
16 | | // along with this program. If not, see <http://www.gnu.org/licenses/>. |
17 | | |
18 | | //! Filesystem-backed database containing all the information about a chain. |
19 | | //! |
20 | | //! This module handles the persistent storage of the chain on disk. |
21 | | //! |
22 | | //! # Usage |
23 | | //! |
24 | | //! Use the [`open()`] function to create a new database or open an existing one. [`open()`] |
25 | | //! returns a [`DatabaseOpen`] enum. This enum will contain either a [`SqliteFullDatabase`] object, |
26 | | //! representing an access to the database, or a [`DatabaseEmpty`] if the database didn't exist or |
27 | | //! is empty. If that is the case, use [`DatabaseEmpty::initialize`] in order to populate it and |
28 | | //! obtain a [`SqliteFullDatabase`]. |
29 | | //! |
30 | | //! Use [`SqliteFullDatabase::insert`] to insert a new block in the database. The block is assumed |
31 | | //! to have been successfully verified prior to insertion. An error is returned if this block is |
32 | | //! already in the database or isn't a descendant or ancestor of the latest finalized block. |
33 | | //! |
34 | | //! Use [`SqliteFullDatabase::set_finalized`] to mark a block already in the database as finalized. |
35 | | //! Any block that isn't an ancestor or descendant will be removed. Reverting finalization is |
36 | | //! not supported. |
37 | | //! |
38 | | //! In order to minimize disk usage, it is not possible to efficiently retrieve the storage items |
39 | | //! of blocks that are ancestors of the finalized block. When a block is finalized, the storage of |
40 | | //! its ancestors is lost, and the only way to reconstruct it is to execute all blocks starting |
41 | | //! from the genesis to the desired one. |
42 | | //! |
43 | | //! # About errors handling |
44 | | //! |
45 | | //! Most of the functions and methods in this module return a `Result` containing notably an |
46 | | //! [`CorruptedError`]. This kind of errors can happen if the operating system returns an error |
47 | | //! when accessing the file system, or if the database has been corrupted, for example by the user |
48 | | //! manually modifying it. |
49 | | //! |
50 | | //! There isn't much that can be done to properly handle an [`CorruptedError`]. The only |
51 | | //! reasonable solutions are either to stop the program, or to delete the entire database and |
52 | | //! recreate it. |
53 | | //! |
54 | | //! # Schema |
55 | | //! |
56 | | //! The SQL schema of the database, with explanatory comments, can be found in `open.rs`. |
57 | | //! |
58 | | //! # About blocking behavior |
59 | | //! |
60 | | //! This implementation uses the SQLite library, which isn't Rust-asynchronous-compatible. Many |
61 | | //! functions will, with the help of the operating system, put the current thread to sleep while |
62 | | //! waiting for an I/O operation to finish. In the context of asynchronous Rust, this is |
63 | | //! undesirable. |
64 | | //! |
65 | | //! For this reason, you are encouraged to isolate the database in its own threads and never |
66 | | //! access it directly from an asynchronous context. |
67 | | //! |
68 | | |
69 | | // TODO: better docs |
70 | | |
71 | | #![cfg(feature = "database-sqlite")] |
72 | | #![cfg_attr(docsrs, doc(cfg(feature = "database-sqlite")))] |
73 | | |
74 | | use crate::{ |
75 | | chain::chain_information, |
76 | | executor::{self, host}, |
77 | | header, trie, |
78 | | }; |
79 | | |
80 | | use alloc::borrow::Cow; |
81 | | use core::{fmt, iter}; |
82 | | use parking_lot::Mutex; |
83 | | use rusqlite::OptionalExtension as _; |
84 | | |
85 | | pub use open::{open, Config, ConfigTy, DatabaseEmpty, DatabaseOpen}; |
86 | | |
87 | | mod open; |
88 | | mod tests; |
89 | | |
90 | | /// Returns an opaque string representing the version number of the SQLite library this binary |
91 | | /// is using. |
92 | 21 | pub fn sqlite_version() -> &'static str { |
93 | 21 | rusqlite::version() |
94 | 21 | } Unexecuted instantiation: _RNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite14sqlite_version _RNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite14sqlite_version Line | Count | Source | 92 | 21 | pub fn sqlite_version() -> &'static str { | 93 | 21 | rusqlite::version() | 94 | 21 | } |
|
95 | | |
96 | | /// An open database. Holds file descriptors. |
97 | | pub struct SqliteFullDatabase { |
98 | | /// The SQLite connection. |
99 | | /// |
100 | | /// The database is constantly within a transaction. |
101 | | /// When the database is opened, `BEGIN TRANSACTION` is immediately run. We periodically |
102 | | /// call `COMMIT; BEGIN_TRANSACTION` when deemed necessary. `COMMIT` is basically the |
103 | | /// equivalent of `fsync`, and must be called carefully in order to not lose too much speed. |
104 | | database: Mutex<rusqlite::Connection>, |
105 | | |
106 | | /// Number of bytes used to encode the block number. |
107 | | block_number_bytes: usize, |
108 | | } |
109 | | |
110 | | impl SqliteFullDatabase { |
111 | | /// Returns the hash of the block in the database whose storage is currently accessible. |
112 | 26 | pub fn best_block_hash(&self) -> Result<[u8; 32], CorruptedError> { |
113 | 26 | let connection = self.database.lock(); |
114 | | |
115 | 26 | let val = meta_get_blob(&connection, "best")?0 .ok_or(CorruptedError::MissingMetaKey)?0 ; |
116 | 26 | if val.len() == 32 { |
117 | 26 | let mut out = [0; 32]; |
118 | 26 | out.copy_from_slice(&val); |
119 | 26 | Ok(out) |
120 | | } else { |
121 | 0 | Err(CorruptedError::InvalidBlockHashLen) |
122 | | } |
123 | 26 | } Unexecuted instantiation: _RNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB2_18SqliteFullDatabase15best_block_hash _RNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB2_18SqliteFullDatabase15best_block_hash Line | Count | Source | 112 | 26 | pub fn best_block_hash(&self) -> Result<[u8; 32], CorruptedError> { | 113 | 26 | let connection = self.database.lock(); | 114 | | | 115 | 26 | let val = meta_get_blob(&connection, "best")?0 .ok_or(CorruptedError::MissingMetaKey)?0 ; | 116 | 26 | if val.len() == 32 { | 117 | 26 | let mut out = [0; 32]; | 118 | 26 | out.copy_from_slice(&val); | 119 | 26 | Ok(out) | 120 | | } else { | 121 | 0 | Err(CorruptedError::InvalidBlockHashLen) | 122 | | } | 123 | 26 | } |
|
124 | | |
125 | | /// Returns the hash of the finalized block in the database. |
126 | 1.10k | pub fn finalized_block_hash(&self) -> Result<[u8; 32], CorruptedError> { |
127 | 1.10k | let database = self.database.lock(); |
128 | 1.10k | finalized_hash(&database) |
129 | 1.10k | } _RNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB2_18SqliteFullDatabase20finalized_block_hash Line | Count | Source | 126 | 1.02k | pub fn finalized_block_hash(&self) -> Result<[u8; 32], CorruptedError> { | 127 | 1.02k | let database = self.database.lock(); | 128 | 1.02k | finalized_hash(&database) | 129 | 1.02k | } |
_RNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB2_18SqliteFullDatabase20finalized_block_hash Line | Count | Source | 126 | 84 | pub fn finalized_block_hash(&self) -> Result<[u8; 32], CorruptedError> { | 127 | 84 | let database = self.database.lock(); | 128 | 84 | finalized_hash(&database) | 129 | 84 | } |
|
130 | | |
131 | | /// Returns the SCALE-encoded header of the given block, or `None` if the block is unknown. |
132 | | /// |
133 | | /// > **Note**: If this method is called twice times in a row with the same block hash, it |
134 | | /// > is possible for the first time to return `Some` and the second time to return |
135 | | /// > `None`, in case the block has since been removed from the database. |
136 | 129 | pub fn block_scale_encoded_header( |
137 | 129 | &self, |
138 | 129 | block_hash: &[u8; 32], |
139 | 129 | ) -> Result<Option<Vec<u8>>, CorruptedError> { |
140 | 129 | let connection = self.database.lock(); |
141 | | |
142 | 129 | let out = connection |
143 | 129 | .prepare_cached(r#"SELECT header FROM blocks WHERE hash = ?"#) |
144 | 129 | .map_err(|err| CorruptedError::Internal(InternalError(err))0 )?0 Unexecuted instantiation: _RNCNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase26block_scale_encoded_header0B8_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase26block_scale_encoded_header0B8_ |
145 | 129 | .query_row((&block_hash[..],), |row| row.get::<_, Vec<u8>>(0)128 ) Unexecuted instantiation: _RNCNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase26block_scale_encoded_headers_0B8_ _RNCNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase26block_scale_encoded_headers_0B8_ Line | Count | Source | 145 | 128 | .query_row((&block_hash[..],), |row| row.get::<_, Vec<u8>>(0)) |
|
146 | 129 | .optional() |
147 | 129 | .map_err(|err| CorruptedError::Internal(InternalError(err))0 )?0 ; Unexecuted instantiation: _RNCNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase26block_scale_encoded_headers0_0B8_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase26block_scale_encoded_headers0_0B8_ |
148 | | |
149 | 129 | Ok(out) |
150 | 129 | } Unexecuted instantiation: _RNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB2_18SqliteFullDatabase26block_scale_encoded_header _RNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB2_18SqliteFullDatabase26block_scale_encoded_header Line | Count | Source | 136 | 129 | pub fn block_scale_encoded_header( | 137 | 129 | &self, | 138 | 129 | block_hash: &[u8; 32], | 139 | 129 | ) -> Result<Option<Vec<u8>>, CorruptedError> { | 140 | 129 | let connection = self.database.lock(); | 141 | | | 142 | 129 | let out = connection | 143 | 129 | .prepare_cached(r#"SELECT header FROM blocks WHERE hash = ?"#) | 144 | 129 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 | 145 | 129 | .query_row((&block_hash[..],), |row| row.get::<_, Vec<u8>>(0)) | 146 | 129 | .optional() | 147 | 129 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 148 | | | 149 | 129 | Ok(out) | 150 | 129 | } |
|
151 | | |
152 | | /// Returns the hash of the parent of the given block, or `None` if the block is unknown. |
153 | | /// |
154 | | /// > **Note**: If this method is called twice times in a row with the same block hash, it |
155 | | /// > is possible for the first time to return `Some` and the second time to return |
156 | | /// > `None`, in case the block has since been removed from the database. |
157 | 0 | pub fn block_parent(&self, block_hash: &[u8; 32]) -> Result<Option<[u8; 32]>, CorruptedError> { |
158 | 0 | let connection = self.database.lock(); |
159 | | |
160 | 0 | let out = connection |
161 | 0 | .prepare_cached(r#"SELECT parent_hash FROM blocks WHERE hash = ?"#) |
162 | 0 | .map_err(|err| CorruptedError::Internal(InternalError(err)))? Unexecuted instantiation: _RNCNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase12block_parent0B8_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase12block_parent0B8_ |
163 | 0 | .query_row((&block_hash[..],), |row| row.get::<_, [u8; 32]>(0)) Unexecuted instantiation: _RNCNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase12block_parents_0B8_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase12block_parents_0B8_ |
164 | 0 | .optional() |
165 | 0 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?; Unexecuted instantiation: _RNCNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase12block_parents0_0B8_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase12block_parents0_0B8_ |
166 | | |
167 | 0 | Ok(out) |
168 | 0 | } Unexecuted instantiation: _RNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB2_18SqliteFullDatabase12block_parent Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB2_18SqliteFullDatabase12block_parent |
169 | | |
170 | | /// Returns the list of extrinsics of the given block, or `None` if the block is unknown. |
171 | | /// |
172 | | /// > **Note**: The list of extrinsics of a block is also known as its *body*. |
173 | | /// |
174 | | /// > **Note**: If this method is called twice times in a row with the same block hash, it |
175 | | /// > is possible for the first time to return `Some` and the second time to return |
176 | | /// > `None`, in case the block has since been removed from the database. |
177 | 0 | pub fn block_extrinsics( |
178 | 0 | &self, |
179 | 0 | block_hash: &[u8; 32], |
180 | 0 | ) -> Result<Option<impl ExactSizeIterator<Item = Vec<u8>>>, CorruptedError> { |
181 | 0 | let connection = self.database.lock(); |
182 | | |
183 | | // TODO: doesn't detect if block is absent |
184 | | |
185 | 0 | let result = connection |
186 | 0 | .prepare_cached(r#"SELECT extrinsic FROM blocks_body WHERE hash = ? ORDER BY idx ASC"#) |
187 | 0 | .map_err(|err| CorruptedError::Internal(InternalError(err)))? Unexecuted instantiation: _RNCNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase16block_extrinsics0B8_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase16block_extrinsics0B8_ |
188 | 0 | .query_map((&block_hash[..],), |row| row.get::<_, Vec<u8>>(0)) Unexecuted instantiation: _RNCNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase16block_extrinsicss_0B8_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase16block_extrinsicss_0B8_ |
189 | 0 | .map_err(|err| CorruptedError::Internal(InternalError(err)))? Unexecuted instantiation: _RNCNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase16block_extrinsicss0_0B8_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase16block_extrinsicss0_0B8_ |
190 | 0 | .collect::<Result<Vec<_>, _>>() |
191 | 0 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?; Unexecuted instantiation: _RNCNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase16block_extrinsicss1_0B8_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase16block_extrinsicss1_0B8_ |
192 | | |
193 | 0 | Ok(Some(result.into_iter())) |
194 | 0 | } Unexecuted instantiation: _RNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB2_18SqliteFullDatabase16block_extrinsics Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB2_18SqliteFullDatabase16block_extrinsics |
195 | | |
196 | | /// Returns the hashes of the blocks given a block number. |
197 | 45 | pub fn block_hash_by_number( |
198 | 45 | &self, |
199 | 45 | block_number: u64, |
200 | 45 | ) -> Result<impl ExactSizeIterator<Item = [u8; 32]>, CorruptedError> { |
201 | 45 | let connection = self.database.lock(); |
202 | 45 | let result = block_hashes_by_number(&connection, block_number)?0 ; |
203 | 45 | Ok(result.into_iter()) |
204 | 45 | } _RNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB2_18SqliteFullDatabase20block_hash_by_number Line | Count | Source | 197 | 45 | pub fn block_hash_by_number( | 198 | 45 | &self, | 199 | 45 | block_number: u64, | 200 | 45 | ) -> Result<impl ExactSizeIterator<Item = [u8; 32]>, CorruptedError> { | 201 | 45 | let connection = self.database.lock(); | 202 | 45 | let result = block_hashes_by_number(&connection, block_number)?0 ; | 203 | 45 | Ok(result.into_iter()) | 204 | 45 | } |
Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB2_18SqliteFullDatabase20block_hash_by_number |
205 | | |
206 | | /// Returns the hash of the block of the best chain given a block number. |
207 | 1 | pub fn best_block_hash_by_number( |
208 | 1 | &self, |
209 | 1 | block_number: u64, |
210 | 1 | ) -> Result<Option<[u8; 32]>, CorruptedError> { |
211 | 1 | let connection = self.database.lock(); |
212 | | |
213 | 1 | let block_number = match i64::try_from(block_number) { |
214 | 1 | Ok(n) => n, |
215 | 0 | Err(_) => return Ok(None), |
216 | | }; |
217 | | |
218 | 1 | let result = connection |
219 | 1 | .prepare_cached(r#"SELECT hash FROM blocks WHERE number = ? AND is_best_chain = TRUE"#) |
220 | 1 | .map_err(|err| CorruptedError::Internal(InternalError(err))0 )?0 Unexecuted instantiation: _RNCNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase25best_block_hash_by_number0B8_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase25best_block_hash_by_number0B8_ |
221 | 1 | .query_row((block_number,), |row| row.get::<_, Vec<u8>>(0)0 ) Unexecuted instantiation: _RNCNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase25best_block_hash_by_numbers_0B8_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase25best_block_hash_by_numbers_0B8_ |
222 | 1 | .optional() |
223 | 1 | .map_err(|err| CorruptedError::Internal(InternalError(err))0 ) Unexecuted instantiation: _RNCNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase25best_block_hash_by_numbers0_0B8_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase25best_block_hash_by_numbers0_0B8_ |
224 | 1 | .and_then(|value| { |
225 | 1 | let Some(value0 ) = value else { return Ok(None) }; |
226 | | Ok(Some( |
227 | 0 | <[u8; 32]>::try_from(&value[..]) |
228 | 0 | .map_err(|_| CorruptedError::InvalidBlockHashLen)?, Unexecuted instantiation: _RNCNCNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB6_18SqliteFullDatabase25best_block_hash_by_numbers1_00Ba_ Unexecuted instantiation: _RNCNCNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB6_18SqliteFullDatabase25best_block_hash_by_numbers1_00Ba_ |
229 | | )) |
230 | 1 | })?0 ; Unexecuted instantiation: _RNCNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase25best_block_hash_by_numbers1_0B8_ _RNCNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase25best_block_hash_by_numbers1_0B8_ Line | Count | Source | 224 | 1 | .and_then(|value| { | 225 | 1 | let Some(value0 ) = value else { return Ok(None) }; | 226 | | Ok(Some( | 227 | 0 | <[u8; 32]>::try_from(&value[..]) | 228 | 0 | .map_err(|_| CorruptedError::InvalidBlockHashLen)?, | 229 | | )) | 230 | 1 | })?; |
|
231 | | |
232 | 1 | Ok(result) |
233 | 1 | } Unexecuted instantiation: _RNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB2_18SqliteFullDatabase25best_block_hash_by_number _RNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB2_18SqliteFullDatabase25best_block_hash_by_number Line | Count | Source | 207 | 1 | pub fn best_block_hash_by_number( | 208 | 1 | &self, | 209 | 1 | block_number: u64, | 210 | 1 | ) -> Result<Option<[u8; 32]>, CorruptedError> { | 211 | 1 | let connection = self.database.lock(); | 212 | | | 213 | 1 | let block_number = match i64::try_from(block_number) { | 214 | 1 | Ok(n) => n, | 215 | 0 | Err(_) => return Ok(None), | 216 | | }; | 217 | | | 218 | 1 | let result = connection | 219 | 1 | .prepare_cached(r#"SELECT hash FROM blocks WHERE number = ? AND is_best_chain = TRUE"#) | 220 | 1 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 | 221 | 1 | .query_row((block_number,), |row| row.get::<_, Vec<u8>>(0)) | 222 | 1 | .optional() | 223 | 1 | .map_err(|err| CorruptedError::Internal(InternalError(err))) | 224 | 1 | .and_then(|value| { | 225 | | let Some(value) = value else { return Ok(None) }; | 226 | | Ok(Some( | 227 | | <[u8; 32]>::try_from(&value[..]) | 228 | | .map_err(|_| CorruptedError::InvalidBlockHashLen)?, | 229 | | )) | 230 | 1 | })?0 ; | 231 | | | 232 | 1 | Ok(result) | 233 | 1 | } |
|
234 | | |
235 | | /// Returns a [`chain_information::ChainInformation`] struct containing the information about |
236 | | /// the current finalized state of the chain. |
237 | | /// |
238 | | /// This method is relatively expensive and should preferably not be called repeatedly. |
239 | | /// |
240 | | /// In order to avoid race conditions, the known finalized block hash must be passed as |
241 | | /// parameter. If the finalized block in the database doesn't match the hash passed as |
242 | | /// parameter, most likely because it has been updated in a parallel thread, a |
243 | | /// [`StorageAccessError::IncompleteStorage`] error is returned. |
244 | | // TODO: an IncompleteStorage error doesn't seem appropriate; also, why is it even a problem given that the chain information contains the finalized block anyway |
245 | 21 | pub fn to_chain_information( |
246 | 21 | &self, |
247 | 21 | finalized_block_hash: &[u8; 32], |
248 | 21 | ) -> Result<chain_information::ValidChainInformation, StorageAccessError> { |
249 | 21 | if finalized_hash(&self.database.lock())?0 != *finalized_block_hash { |
250 | 0 | return Err(StorageAccessError::IncompleteStorage); |
251 | 21 | } |
252 | | |
253 | 21 | let mut builder = chain_information::build::ChainInformationBuild::new( |
254 | | chain_information::build::Config { |
255 | | finalized_block_header: chain_information::build::ConfigFinalizedBlockHeader::Any { |
256 | 21 | scale_encoded_header: self |
257 | 21 | .block_scale_encoded_header(finalized_block_hash)?0 |
258 | 21 | .ok_or(StorageAccessError::UnknownBlock)?0 , // TODO: inappropriate error |
259 | 21 | known_finality: None, |
260 | | }, |
261 | | runtime: { |
262 | 21 | let code = match self.block_storage_get( |
263 | 21 | finalized_block_hash, |
264 | 21 | iter::empty::<iter::Empty<_>>(), |
265 | 21 | trie::bytes_to_nibbles(b":code".iter().copied()).map(u8::from), |
266 | 21 | )?0 { |
267 | 21 | Some((code, _)) => code, |
268 | 0 | None => todo!(), |
269 | | }; |
270 | 21 | let heap_pages = match self.block_storage_get( |
271 | 21 | &finalized_block_hash, |
272 | 21 | iter::empty::<iter::Empty<_>>(), |
273 | 21 | trie::bytes_to_nibbles(b":heappages".iter().copied()).map(u8::from), |
274 | 21 | )?0 { |
275 | 0 | Some((hp, _)) => Some(hp), |
276 | 21 | None => None, |
277 | | }; |
278 | 21 | let Ok(heap_pages) = |
279 | 21 | executor::storage_heap_pages_to_value(heap_pages.as_deref()) |
280 | | else { |
281 | 0 | todo!() |
282 | | }; |
283 | 21 | let Ok(runtime) = host::HostVmPrototype::new(host::Config { |
284 | 21 | module: code, |
285 | 21 | heap_pages, |
286 | 21 | exec_hint: |
287 | 21 | executor::vm::ExecHint::ExecuteOnceWithNonDeterministicValidation, |
288 | 21 | allow_unresolved_imports: true, |
289 | 21 | }) else { |
290 | 0 | todo!() |
291 | | }; |
292 | 21 | runtime |
293 | 21 | }, |
294 | 21 | block_number_bytes: self.block_number_bytes, |
295 | | }, |
296 | | ); |
297 | | |
298 | | // TODO: this whole code is racy because the database isn't locked |
299 | | loop { |
300 | 63 | match builder { |
301 | | chain_information::build::ChainInformationBuild::Finished { |
302 | 21 | result: Ok(chain_information), |
303 | 21 | .. // TODO: runtime thrown away |
304 | 21 | } => return Ok(chain_information), |
305 | | chain_information::build::ChainInformationBuild::Finished { |
306 | | result: Err(_), |
307 | | .. // TODO: runtime thrown away |
308 | 0 | } => todo!(), |
309 | | chain_information::build::ChainInformationBuild::InProgress( |
310 | 42 | chain_information::build::InProgress::StorageGet(val), |
311 | | ) => { |
312 | | // TODO: child trie support |
313 | 42 | let value = self.block_storage_get(finalized_block_hash, iter::empty::<iter::Empty<_>>(), trie::bytes_to_nibbles(val.key().as_ref().iter().copied()).map(u8::from))?0 ; |
314 | 42 | let value = match value { |
315 | 42 | Some((val, vers)) => { |
316 | 42 | Some((iter::once(val), chain_information::build::TrieEntryVersion::try_from(vers).map_err(|_| StorageAccessError::Corrupted(CorruptedError::InvalidTrieEntryVersion)0 )?0 )) Unexecuted instantiation: _RNCNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase20to_chain_information0B8_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase20to_chain_information0B8_ |
317 | | } |
318 | 0 | None => None |
319 | | }; |
320 | 42 | builder = val.inject_value(value); |
321 | | } |
322 | | chain_information::build::ChainInformationBuild::InProgress( |
323 | 0 | chain_information::build::InProgress::NextKey(val), |
324 | 0 | ) => { |
325 | 0 | // TODO: child trie support |
326 | 0 | let nk = self.block_storage_next_key(finalized_block_hash, iter::empty::<iter::Empty<_>>(), val.key().map(u8::from),val.prefix().map(u8::from), val.branch_nodes())?; |
327 | 0 | builder = val.inject_key(nk.map(|nibbles| nibbles.into_iter().map(|n| trie::Nibble::try_from(n).unwrap()))); Unexecuted instantiation: _RNCNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase20to_chain_informations_0B8_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase20to_chain_informations_0B8_ Unexecuted instantiation: _RNCNCNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB6_18SqliteFullDatabase20to_chain_informations_00Ba_ Unexecuted instantiation: _RNCNCNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB6_18SqliteFullDatabase20to_chain_informations_00Ba_ |
328 | | } |
329 | | chain_information::build::ChainInformationBuild::InProgress( |
330 | 0 | chain_information::build::InProgress::ClosestDescendantMerkleValue(val), |
331 | 0 | ) => { |
332 | 0 | // TODO: child trie support |
333 | 0 | let mv = self.block_storage_closest_descendant_merkle_value(finalized_block_hash, iter::empty::<iter::Empty<_>>(), val.key().map(u8::from))?; |
334 | 0 | builder = val.inject_merkle_value(mv.as_deref()); |
335 | | } |
336 | | } |
337 | | } |
338 | 21 | } Unexecuted instantiation: _RNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB2_18SqliteFullDatabase20to_chain_information _RNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB2_18SqliteFullDatabase20to_chain_information Line | Count | Source | 245 | 21 | pub fn to_chain_information( | 246 | 21 | &self, | 247 | 21 | finalized_block_hash: &[u8; 32], | 248 | 21 | ) -> Result<chain_information::ValidChainInformation, StorageAccessError> { | 249 | 21 | if finalized_hash(&self.database.lock())?0 != *finalized_block_hash { | 250 | 0 | return Err(StorageAccessError::IncompleteStorage); | 251 | 21 | } | 252 | | | 253 | 21 | let mut builder = chain_information::build::ChainInformationBuild::new( | 254 | | chain_information::build::Config { | 255 | | finalized_block_header: chain_information::build::ConfigFinalizedBlockHeader::Any { | 256 | 21 | scale_encoded_header: self | 257 | 21 | .block_scale_encoded_header(finalized_block_hash)?0 | 258 | 21 | .ok_or(StorageAccessError::UnknownBlock)?0 , // TODO: inappropriate error | 259 | 21 | known_finality: None, | 260 | | }, | 261 | | runtime: { | 262 | 21 | let code = match self.block_storage_get( | 263 | 21 | finalized_block_hash, | 264 | 21 | iter::empty::<iter::Empty<_>>(), | 265 | 21 | trie::bytes_to_nibbles(b":code".iter().copied()).map(u8::from), | 266 | 21 | )?0 { | 267 | 21 | Some((code, _)) => code, | 268 | 0 | None => todo!(), | 269 | | }; | 270 | 21 | let heap_pages = match self.block_storage_get( | 271 | 21 | &finalized_block_hash, | 272 | 21 | iter::empty::<iter::Empty<_>>(), | 273 | 21 | trie::bytes_to_nibbles(b":heappages".iter().copied()).map(u8::from), | 274 | 21 | )?0 { | 275 | 0 | Some((hp, _)) => Some(hp), | 276 | 21 | None => None, | 277 | | }; | 278 | 21 | let Ok(heap_pages) = | 279 | 21 | executor::storage_heap_pages_to_value(heap_pages.as_deref()) | 280 | | else { | 281 | 0 | todo!() | 282 | | }; | 283 | 21 | let Ok(runtime) = host::HostVmPrototype::new(host::Config { | 284 | 21 | module: code, | 285 | 21 | heap_pages, | 286 | 21 | exec_hint: | 287 | 21 | executor::vm::ExecHint::ExecuteOnceWithNonDeterministicValidation, | 288 | 21 | allow_unresolved_imports: true, | 289 | 21 | }) else { | 290 | 0 | todo!() | 291 | | }; | 292 | 21 | runtime | 293 | 21 | }, | 294 | 21 | block_number_bytes: self.block_number_bytes, | 295 | | }, | 296 | | ); | 297 | | | 298 | | // TODO: this whole code is racy because the database isn't locked | 299 | | loop { | 300 | 63 | match builder { | 301 | | chain_information::build::ChainInformationBuild::Finished { | 302 | 21 | result: Ok(chain_information), | 303 | 21 | .. // TODO: runtime thrown away | 304 | 21 | } => return Ok(chain_information), | 305 | | chain_information::build::ChainInformationBuild::Finished { | 306 | | result: Err(_), | 307 | | .. // TODO: runtime thrown away | 308 | 0 | } => todo!(), | 309 | | chain_information::build::ChainInformationBuild::InProgress( | 310 | 42 | chain_information::build::InProgress::StorageGet(val), | 311 | | ) => { | 312 | | // TODO: child trie support | 313 | 42 | let value = self.block_storage_get(finalized_block_hash, iter::empty::<iter::Empty<_>>(), trie::bytes_to_nibbles(val.key().as_ref().iter().copied()).map(u8::from))?0 ; | 314 | 42 | let value = match value { | 315 | 42 | Some((val, vers)) => { | 316 | 42 | Some((iter::once(val), chain_information::build::TrieEntryVersion::try_from(vers).map_err(|_| StorageAccessError::Corrupted(CorruptedError::InvalidTrieEntryVersion))?0 )) | 317 | | } | 318 | 0 | None => None | 319 | | }; | 320 | 42 | builder = val.inject_value(value); | 321 | | } | 322 | | chain_information::build::ChainInformationBuild::InProgress( | 323 | 0 | chain_information::build::InProgress::NextKey(val), | 324 | 0 | ) => { | 325 | 0 | // TODO: child trie support | 326 | 0 | let nk = self.block_storage_next_key(finalized_block_hash, iter::empty::<iter::Empty<_>>(), val.key().map(u8::from),val.prefix().map(u8::from), val.branch_nodes())?; | 327 | 0 | builder = val.inject_key(nk.map(|nibbles| nibbles.into_iter().map(|n| trie::Nibble::try_from(n).unwrap()))); | 328 | | } | 329 | | chain_information::build::ChainInformationBuild::InProgress( | 330 | 0 | chain_information::build::InProgress::ClosestDescendantMerkleValue(val), | 331 | 0 | ) => { | 332 | 0 | // TODO: child trie support | 333 | 0 | let mv = self.block_storage_closest_descendant_merkle_value(finalized_block_hash, iter::empty::<iter::Empty<_>>(), val.key().map(u8::from))?; | 334 | 0 | builder = val.inject_merkle_value(mv.as_deref()); | 335 | | } | 336 | | } | 337 | | } | 338 | 21 | } |
|
339 | | |
340 | | /// Insert a new block in the database. |
341 | | /// |
342 | | /// Must pass the header and body of the block. |
343 | | /// |
344 | | /// Blocks must be inserted in the correct order. An error is returned if the parent of the |
345 | | /// newly-inserted block isn't present in the database. |
346 | | /// |
347 | | /// > **Note**: It is not necessary for the newly-inserted block to be a descendant of the |
348 | | /// > finalized block, unless `is_new_best` is true. |
349 | | /// |
350 | 0 | pub fn insert<'a>( |
351 | 0 | &self, |
352 | 0 | scale_encoded_header: &[u8], |
353 | 0 | is_new_best: bool, |
354 | 0 | body: impl ExactSizeIterator<Item = impl AsRef<[u8]>>, |
355 | 0 | ) -> Result<(), InsertError> { |
356 | 0 | // Calculate the hash of the new best block. |
357 | 0 | let block_hash = header::hash_from_scale_encoded_header(scale_encoded_header); |
358 | | |
359 | | // Decode the header, as we will need various information from it. |
360 | | // TODO: this module shouldn't decode headers |
361 | 0 | let header = header::decode(scale_encoded_header, self.block_number_bytes) |
362 | 0 | .map_err(InsertError::BadHeader)?; |
363 | | |
364 | | // Locking is performed as late as possible. |
365 | 0 | let mut database = self.database.lock(); |
366 | | |
367 | | // Start a transaction to insert everything at once. |
368 | 0 | let transaction = database |
369 | 0 | .transaction() |
370 | 0 | .map_err(|err| InsertError::Corrupted(CorruptedError::Internal(InternalError(err))))?; Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase6insertppE0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase6insertppE0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase6insertINtNtCsdZExvAaxgia_5alloc3vec3VechEINtNtB1q_9into_iter8IntoIterB1n_EE0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase6insertINtNtCsdZExvAaxgia_5alloc3vec3VechEINtNtB1q_9into_iter8IntoIterB1n_EE0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase6insertINtNtCsdZExvAaxgia_5alloc3vec3VechEINtNtB1q_9into_iter8IntoIterB1n_EE0CsibGXYHQB8Ea_25json_rpc_general_requests |
371 | | |
372 | | // Make sure that the block to insert isn't already in the database. |
373 | 0 | if has_block(&transaction, &block_hash)? { |
374 | 0 | return Err(InsertError::Duplicate); |
375 | 0 | } |
376 | 0 |
|
377 | 0 | // Make sure that the parent of the block to insert is in the database. |
378 | 0 | if !has_block(&transaction, header.parent_hash)? { |
379 | 0 | return Err(InsertError::MissingParent); |
380 | 0 | } |
381 | 0 |
|
382 | 0 | transaction |
383 | 0 | .prepare_cached( |
384 | 0 | "INSERT INTO blocks(number, hash, parent_hash, state_trie_root_hash, header, is_best_chain, justification) VALUES (?, ?, ?, ?, ?, FALSE, NULL)", |
385 | 0 | ) |
386 | 0 | .map_err(|err| CorruptedError::Internal(InternalError(err)))? Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase6insertppEs_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase6insertppEs_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase6insertINtNtCsdZExvAaxgia_5alloc3vec3VechEINtNtB1q_9into_iter8IntoIterB1n_EEs_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase6insertINtNtCsdZExvAaxgia_5alloc3vec3VechEINtNtB1q_9into_iter8IntoIterB1n_EEs_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase6insertINtNtCsdZExvAaxgia_5alloc3vec3VechEINtNtB1q_9into_iter8IntoIterB1n_EEs_0CsibGXYHQB8Ea_25json_rpc_general_requests |
387 | 0 | .execute(( |
388 | 0 | i64::try_from(header.number).unwrap(), |
389 | 0 | &block_hash[..], |
390 | 0 | &header.parent_hash[..], |
391 | 0 | &header.state_root[..], |
392 | 0 | scale_encoded_header |
393 | 0 | )) |
394 | 0 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?; Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase6insertppEs0_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase6insertppEs0_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase6insertINtNtCsdZExvAaxgia_5alloc3vec3VechEINtNtB1q_9into_iter8IntoIterB1n_EEs0_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase6insertINtNtCsdZExvAaxgia_5alloc3vec3VechEINtNtB1q_9into_iter8IntoIterB1n_EEs0_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase6insertINtNtCsdZExvAaxgia_5alloc3vec3VechEINtNtB1q_9into_iter8IntoIterB1n_EEs0_0CsibGXYHQB8Ea_25json_rpc_general_requests |
395 | | |
396 | | { |
397 | 0 | let mut statement = transaction |
398 | 0 | .prepare_cached("INSERT INTO blocks_body(hash, idx, extrinsic) VALUES (?, ?, ?)") |
399 | 0 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?; Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase6insertppEs1_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase6insertppEs1_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase6insertINtNtCsdZExvAaxgia_5alloc3vec3VechEINtNtB1q_9into_iter8IntoIterB1n_EEs1_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase6insertINtNtCsdZExvAaxgia_5alloc3vec3VechEINtNtB1q_9into_iter8IntoIterB1n_EEs1_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase6insertINtNtCsdZExvAaxgia_5alloc3vec3VechEINtNtB1q_9into_iter8IntoIterB1n_EEs1_0CsibGXYHQB8Ea_25json_rpc_general_requests |
400 | 0 | for (index, item) in body.enumerate() { |
401 | 0 | statement |
402 | 0 | .execute(( |
403 | 0 | &block_hash[..], |
404 | 0 | i64::try_from(index).unwrap(), |
405 | 0 | item.as_ref(), |
406 | 0 | )) |
407 | 0 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?; Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase6insertppEs2_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase6insertppEs2_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase6insertINtNtCsdZExvAaxgia_5alloc3vec3VechEINtNtB1q_9into_iter8IntoIterB1n_EEs2_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase6insertINtNtCsdZExvAaxgia_5alloc3vec3VechEINtNtB1q_9into_iter8IntoIterB1n_EEs2_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase6insertINtNtCsdZExvAaxgia_5alloc3vec3VechEINtNtB1q_9into_iter8IntoIterB1n_EEs2_0CsibGXYHQB8Ea_25json_rpc_general_requests |
408 | | } |
409 | | } |
410 | | |
411 | | // Change the best chain to be the new block. |
412 | 0 | if is_new_best { |
413 | | // It would be illegal to change the best chain to not overlay with the |
414 | | // finalized chain. |
415 | 0 | if header.number <= finalized_num(&transaction)? { |
416 | 0 | return Err(InsertError::BestNotInFinalizedChain); |
417 | 0 | } |
418 | 0 |
|
419 | 0 | set_best_chain(&transaction, &block_hash)?; |
420 | 0 | } |
421 | | |
422 | | // If everything is successful, we commit. |
423 | 0 | transaction |
424 | 0 | .commit() |
425 | 0 | .map_err(|err| InsertError::Corrupted(CorruptedError::Internal(InternalError(err))))?; Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase6insertppEs3_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase6insertppEs3_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase6insertINtNtCsdZExvAaxgia_5alloc3vec3VechEINtNtB1q_9into_iter8IntoIterB1n_EEs3_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase6insertINtNtCsdZExvAaxgia_5alloc3vec3VechEINtNtB1q_9into_iter8IntoIterB1n_EEs3_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase6insertINtNtCsdZExvAaxgia_5alloc3vec3VechEINtNtB1q_9into_iter8IntoIterB1n_EEs3_0CsibGXYHQB8Ea_25json_rpc_general_requests |
426 | | |
427 | 0 | Ok(()) |
428 | 0 | } Unexecuted instantiation: _RINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase6insertppEB7_ Unexecuted instantiation: _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase6insertppEB7_ Unexecuted instantiation: _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase6insertINtNtCsdZExvAaxgia_5alloc3vec3VechEINtNtB1o_9into_iter8IntoIterB1l_EECsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase6insertINtNtCsdZExvAaxgia_5alloc3vec3VechEINtNtB1o_9into_iter8IntoIterB1l_EECscDgN54JpMGG_6author Unexecuted instantiation: _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase6insertINtNtCsdZExvAaxgia_5alloc3vec3VechEINtNtB1o_9into_iter8IntoIterB1l_EECsibGXYHQB8Ea_25json_rpc_general_requests |
429 | | |
430 | | // TODO: needs documentation |
431 | | // TODO: should we refuse inserting disjoint storage nodes? |
432 | 1.05k | pub fn insert_trie_nodes<'a>( |
433 | 1.05k | &self, |
434 | 1.05k | new_trie_nodes: impl Iterator<Item = InsertTrieNode<'a>>, |
435 | 1.05k | trie_entries_version: u8, |
436 | 1.05k | ) -> Result<(), CorruptedError> { |
437 | 1.05k | let mut database = self.database.lock(); |
438 | | |
439 | 1.05k | let transaction = database |
440 | 1.05k | .transaction() |
441 | 1.05k | .map_err(|err| CorruptedError::Internal(InternalError(err))0 )?0 ; Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtCsaYZPK01V26L_4core5array4iter8IntoIterNtB5_14InsertTrieNodeKj1_EE0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtNtNtB9_4trie14trie_structure9NodeIndexENCNvNtB5_5testss_30empty_database_fill_then_querys0_0EE0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodespE0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtB5_14InsertTrieNodeEE0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter7sources4once4OnceNtB5_14InsertTrieNodeEE0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtNtNtB9_4trie14trie_structure9NodeIndexENCNCNvCsiUjFBJteJ7x_17smoldot_full_node13open_database0s_0EE0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtB5_14InsertTrieNodeEE0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter7sources4once4OnceNtB5_14InsertTrieNodeEE0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtNtNtB9_4trie14trie_structure9NodeIndexENCNCNvCsiUjFBJteJ7x_17smoldot_full_node13open_database0s_0EE0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtB5_14InsertTrieNodeEE0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter7sources4once4OnceNtB5_14InsertTrieNodeEE0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtNtNtB9_4trie14trie_structure9NodeIndexENCNCNvCsiUjFBJteJ7x_17smoldot_full_node13open_database0s_0EE0CsibGXYHQB8Ea_25json_rpc_general_requests |
442 | | |
443 | | { |
444 | | // TODO: should check whether the existing merkle values that are referenced from inserted nodes exist in the parent's storage |
445 | | // TODO: is it correct to have OR IGNORE everywhere? |
446 | 1.05k | let mut insert_node_statement = transaction |
447 | 1.05k | .prepare_cached("INSERT OR IGNORE INTO trie_node(hash, partial_key) VALUES(?, ?)") |
448 | 1.05k | .map_err(|err| CorruptedError::Internal(InternalError(err))0 )?0 ; Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtCsaYZPK01V26L_4core5array4iter8IntoIterNtB5_14InsertTrieNodeKj1_EEs_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtNtNtB9_4trie14trie_structure9NodeIndexENCNvNtB5_5testss_30empty_database_fill_then_querys0_0EEs_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodespEs_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtB5_14InsertTrieNodeEEs_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter7sources4once4OnceNtB5_14InsertTrieNodeEEs_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtNtNtB9_4trie14trie_structure9NodeIndexENCNCNvCsiUjFBJteJ7x_17smoldot_full_node13open_database0s_0EEs_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtB5_14InsertTrieNodeEEs_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter7sources4once4OnceNtB5_14InsertTrieNodeEEs_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtNtNtB9_4trie14trie_structure9NodeIndexENCNCNvCsiUjFBJteJ7x_17smoldot_full_node13open_database0s_0EEs_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtB5_14InsertTrieNodeEEs_0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter7sources4once4OnceNtB5_14InsertTrieNodeEEs_0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtNtNtB9_4trie14trie_structure9NodeIndexENCNCNvCsiUjFBJteJ7x_17smoldot_full_node13open_database0s_0EEs_0CsibGXYHQB8Ea_25json_rpc_general_requests |
449 | 1.05k | let mut insert_node_storage_statement = transaction |
450 | 1.05k | .prepare_cached("INSERT OR IGNORE INTO trie_node_storage(node_hash, value, trie_root_ref, trie_entry_version) VALUES(?, ?, ?, ?)") |
451 | 1.05k | .map_err(|err| CorruptedError::Internal(InternalError(err))0 )?0 ; Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtCsaYZPK01V26L_4core5array4iter8IntoIterNtB5_14InsertTrieNodeKj1_EEs0_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtNtNtB9_4trie14trie_structure9NodeIndexENCNvNtB5_5testss_30empty_database_fill_then_querys0_0EEs0_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodespEs0_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtB5_14InsertTrieNodeEEs0_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter7sources4once4OnceNtB5_14InsertTrieNodeEEs0_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtNtNtB9_4trie14trie_structure9NodeIndexENCNCNvCsiUjFBJteJ7x_17smoldot_full_node13open_database0s_0EEs0_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtB5_14InsertTrieNodeEEs0_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter7sources4once4OnceNtB5_14InsertTrieNodeEEs0_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtNtNtB9_4trie14trie_structure9NodeIndexENCNCNvCsiUjFBJteJ7x_17smoldot_full_node13open_database0s_0EEs0_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtB5_14InsertTrieNodeEEs0_0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter7sources4once4OnceNtB5_14InsertTrieNodeEEs0_0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtNtNtB9_4trie14trie_structure9NodeIndexENCNCNvCsiUjFBJteJ7x_17smoldot_full_node13open_database0s_0EEs0_0CsibGXYHQB8Ea_25json_rpc_general_requests |
452 | 1.05k | let mut insert_child_statement = transaction |
453 | 1.05k | .prepare_cached( |
454 | 1.05k | "INSERT OR IGNORE INTO trie_node_child(hash, child_num, child_hash) VALUES(?, ?, ?)", |
455 | 1.05k | ) |
456 | 1.05k | .map_err(|err| CorruptedError::Internal(InternalError(err))0 )?0 ; Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtCsaYZPK01V26L_4core5array4iter8IntoIterNtB5_14InsertTrieNodeKj1_EEs1_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtNtNtB9_4trie14trie_structure9NodeIndexENCNvNtB5_5testss_30empty_database_fill_then_querys0_0EEs1_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodespEs1_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtB5_14InsertTrieNodeEEs1_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter7sources4once4OnceNtB5_14InsertTrieNodeEEs1_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtNtNtB9_4trie14trie_structure9NodeIndexENCNCNvCsiUjFBJteJ7x_17smoldot_full_node13open_database0s_0EEs1_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtB5_14InsertTrieNodeEEs1_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter7sources4once4OnceNtB5_14InsertTrieNodeEEs1_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtNtNtB9_4trie14trie_structure9NodeIndexENCNCNvCsiUjFBJteJ7x_17smoldot_full_node13open_database0s_0EEs1_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtB5_14InsertTrieNodeEEs1_0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter7sources4once4OnceNtB5_14InsertTrieNodeEEs1_0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtNtNtB9_4trie14trie_structure9NodeIndexENCNCNvCsiUjFBJteJ7x_17smoldot_full_node13open_database0s_0EEs1_0CsibGXYHQB8Ea_25json_rpc_general_requests |
457 | | // TODO: if the iterator's `next()` function accesses the database, we deadlock |
458 | 4.65k | for trie_node3.59k in new_trie_nodes { |
459 | 50.2k | assert!(trie_node.partial_key_nibbles.iter().all(3.59k |n| *n < 16))3.59k ; // TODO: document _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtCsaYZPK01V26L_4core5array4iter8IntoIterNtB5_14InsertTrieNodeKj1_EEs6_0B9_ Line | Count | Source | 459 | 13 | assert!(trie_node.partial_key_nibbles.iter().all(|n| *n < 16)); // TODO: document |
_RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtNtNtB9_4trie14trie_structure9NodeIndexENCNvNtB5_5testss_30empty_database_fill_then_querys0_0EEs6_0B9_ Line | Count | Source | 459 | 4.26k | assert!(trie_node.partial_key_nibbles.iter().all(|n| *n < 16)); // TODO: document |
Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodespEs6_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtB5_14InsertTrieNodeEEs6_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter7sources4once4OnceNtB5_14InsertTrieNodeEEs6_0CsiLzmwikkc22_14json_rpc_basic _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtNtNtB9_4trie14trie_structure9NodeIndexENCNCNvCsiUjFBJteJ7x_17smoldot_full_node13open_database0s_0EEs6_0CsiLzmwikkc22_14json_rpc_basic Line | Count | Source | 459 | 4.38k | assert!(trie_node.partial_key_nibbles.iter().all(|n| *n < 16)); // TODO: document |
Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtB5_14InsertTrieNodeEEs6_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter7sources4once4OnceNtB5_14InsertTrieNodeEEs6_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtNtNtB9_4trie14trie_structure9NodeIndexENCNCNvCsiUjFBJteJ7x_17smoldot_full_node13open_database0s_0EEs6_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtB5_14InsertTrieNodeEEs6_0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter7sources4once4OnceNtB5_14InsertTrieNodeEEs6_0CsibGXYHQB8Ea_25json_rpc_general_requests _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtNtNtB9_4trie14trie_structure9NodeIndexENCNCNvCsiUjFBJteJ7x_17smoldot_full_node13open_database0s_0EEs6_0CsibGXYHQB8Ea_25json_rpc_general_requests Line | Count | Source | 459 | 41.6k | assert!(trie_node.partial_key_nibbles.iter().all(|n| *n < 16)); // TODO: document |
|
460 | 3.59k | insert_node_statement |
461 | 3.59k | .execute((&trie_node.merkle_value, trie_node.partial_key_nibbles)) |
462 | 3.59k | .map_err(|err: rusqlite::Error| CorruptedError::Internal(InternalError(err))0 )?0 ; Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtCsaYZPK01V26L_4core5array4iter8IntoIterNtB5_14InsertTrieNodeKj1_EEs2_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtNtNtB9_4trie14trie_structure9NodeIndexENCNvNtB5_5testss_30empty_database_fill_then_querys0_0EEs2_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodespEs2_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtB5_14InsertTrieNodeEEs2_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter7sources4once4OnceNtB5_14InsertTrieNodeEEs2_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtNtNtB9_4trie14trie_structure9NodeIndexENCNCNvCsiUjFBJteJ7x_17smoldot_full_node13open_database0s_0EEs2_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtB5_14InsertTrieNodeEEs2_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter7sources4once4OnceNtB5_14InsertTrieNodeEEs2_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtNtNtB9_4trie14trie_structure9NodeIndexENCNCNvCsiUjFBJteJ7x_17smoldot_full_node13open_database0s_0EEs2_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtB5_14InsertTrieNodeEEs2_0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter7sources4once4OnceNtB5_14InsertTrieNodeEEs2_0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtNtNtB9_4trie14trie_structure9NodeIndexENCNCNvCsiUjFBJteJ7x_17smoldot_full_node13open_database0s_0EEs2_0CsibGXYHQB8Ea_25json_rpc_general_requests |
463 | 3.59k | match trie_node.storage_value { |
464 | | InsertTrieNodeStorageValue::Value { |
465 | 3.25k | value, |
466 | 3.25k | references_merkle_value, |
467 | 3.25k | } => { |
468 | 3.25k | insert_node_storage_statement |
469 | 3.25k | .execute(( |
470 | 3.25k | &trie_node.merkle_value, |
471 | 3.25k | if !references_merkle_value { |
472 | 3.25k | Some(&value) |
473 | | } else { |
474 | 0 | None |
475 | | }, |
476 | 3.25k | if references_merkle_value { |
477 | 0 | Some(&value) |
478 | | } else { |
479 | 3.25k | None |
480 | | }, |
481 | 3.25k | trie_entries_version, |
482 | 3.25k | )) |
483 | 3.25k | .map_err(|err| CorruptedError::Internal(InternalError(err))0 )?0 ; Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtCsaYZPK01V26L_4core5array4iter8IntoIterNtB5_14InsertTrieNodeKj1_EEs3_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtNtNtB9_4trie14trie_structure9NodeIndexENCNvNtB5_5testss_30empty_database_fill_then_querys0_0EEs3_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodespEs3_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtB5_14InsertTrieNodeEEs3_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter7sources4once4OnceNtB5_14InsertTrieNodeEEs3_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtNtNtB9_4trie14trie_structure9NodeIndexENCNCNvCsiUjFBJteJ7x_17smoldot_full_node13open_database0s_0EEs3_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtB5_14InsertTrieNodeEEs3_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter7sources4once4OnceNtB5_14InsertTrieNodeEEs3_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtNtNtB9_4trie14trie_structure9NodeIndexENCNCNvCsiUjFBJteJ7x_17smoldot_full_node13open_database0s_0EEs3_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtB5_14InsertTrieNodeEEs3_0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter7sources4once4OnceNtB5_14InsertTrieNodeEEs3_0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtNtNtB9_4trie14trie_structure9NodeIndexENCNCNvCsiUjFBJteJ7x_17smoldot_full_node13open_database0s_0EEs3_0CsibGXYHQB8Ea_25json_rpc_general_requests |
484 | | } |
485 | 342 | InsertTrieNodeStorageValue::NoValue => {} |
486 | | } |
487 | 57.5k | for (child_num, child) in trie_node.children_merkle_values.iter().enumerate()3.59k { |
488 | 57.5k | if let Some(child2.55k ) = child { |
489 | 2.55k | let child_num = |
490 | 2.55k | vec![u8::try_from(child_num).unwrap_or_else(|_| unreachable!()0 )]; Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtCsaYZPK01V26L_4core5array4iter8IntoIterNtB5_14InsertTrieNodeKj1_EEs7_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtNtNtB9_4trie14trie_structure9NodeIndexENCNvNtB5_5testss_30empty_database_fill_then_querys0_0EEs7_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodespEs7_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtB5_14InsertTrieNodeEEs7_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter7sources4once4OnceNtB5_14InsertTrieNodeEEs7_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtNtNtB9_4trie14trie_structure9NodeIndexENCNCNvCsiUjFBJteJ7x_17smoldot_full_node13open_database0s_0EEs7_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtB5_14InsertTrieNodeEEs7_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter7sources4once4OnceNtB5_14InsertTrieNodeEEs7_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtNtNtB9_4trie14trie_structure9NodeIndexENCNCNvCsiUjFBJteJ7x_17smoldot_full_node13open_database0s_0EEs7_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtB5_14InsertTrieNodeEEs7_0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter7sources4once4OnceNtB5_14InsertTrieNodeEEs7_0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtNtNtB9_4trie14trie_structure9NodeIndexENCNCNvCsiUjFBJteJ7x_17smoldot_full_node13open_database0s_0EEs7_0CsibGXYHQB8Ea_25json_rpc_general_requests |
491 | 2.55k | insert_child_statement |
492 | 2.55k | .execute((&trie_node.merkle_value, child_num, child)) |
493 | 2.55k | .map_err(|err| CorruptedError::Internal(InternalError(err))0 )?0 ; Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtCsaYZPK01V26L_4core5array4iter8IntoIterNtB5_14InsertTrieNodeKj1_EEs4_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtNtNtB9_4trie14trie_structure9NodeIndexENCNvNtB5_5testss_30empty_database_fill_then_querys0_0EEs4_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodespEs4_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtB5_14InsertTrieNodeEEs4_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter7sources4once4OnceNtB5_14InsertTrieNodeEEs4_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtNtNtB9_4trie14trie_structure9NodeIndexENCNCNvCsiUjFBJteJ7x_17smoldot_full_node13open_database0s_0EEs4_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtB5_14InsertTrieNodeEEs4_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter7sources4once4OnceNtB5_14InsertTrieNodeEEs4_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtNtNtB9_4trie14trie_structure9NodeIndexENCNCNvCsiUjFBJteJ7x_17smoldot_full_node13open_database0s_0EEs4_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtB5_14InsertTrieNodeEEs4_0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter7sources4once4OnceNtB5_14InsertTrieNodeEEs4_0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtNtNtB9_4trie14trie_structure9NodeIndexENCNCNvCsiUjFBJteJ7x_17smoldot_full_node13open_database0s_0EEs4_0CsibGXYHQB8Ea_25json_rpc_general_requests |
494 | 55.0k | } |
495 | | } |
496 | | } |
497 | | } |
498 | | |
499 | 1.05k | transaction |
500 | 1.05k | .commit() |
501 | 1.05k | .map_err(|err| CorruptedError::Internal(InternalError(err))0 )?0 ; Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtCsaYZPK01V26L_4core5array4iter8IntoIterNtB5_14InsertTrieNodeKj1_EEs5_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtNtNtB9_4trie14trie_structure9NodeIndexENCNvNtB5_5testss_30empty_database_fill_then_querys0_0EEs5_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodespEs5_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtB5_14InsertTrieNodeEEs5_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter7sources4once4OnceNtB5_14InsertTrieNodeEEs5_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtNtNtB9_4trie14trie_structure9NodeIndexENCNCNvCsiUjFBJteJ7x_17smoldot_full_node13open_database0s_0EEs5_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtB5_14InsertTrieNodeEEs5_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter7sources4once4OnceNtB5_14InsertTrieNodeEEs5_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtNtNtB9_4trie14trie_structure9NodeIndexENCNCNvCsiUjFBJteJ7x_17smoldot_full_node13open_database0s_0EEs5_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtB5_14InsertTrieNodeEEs5_0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter7sources4once4OnceNtB5_14InsertTrieNodeEEs5_0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtNtNtB9_4trie14trie_structure9NodeIndexENCNCNvCsiUjFBJteJ7x_17smoldot_full_node13open_database0s_0EEs5_0CsibGXYHQB8Ea_25json_rpc_general_requests |
502 | | |
503 | 1.05k | Ok(()) |
504 | 1.05k | } _RINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase17insert_trie_nodesINtNtNtCsaYZPK01V26L_4core5array4iter8IntoIterNtB3_14InsertTrieNodeKj1_EEB7_ Line | Count | Source | 432 | 7 | pub fn insert_trie_nodes<'a>( | 433 | 7 | &self, | 434 | 7 | new_trie_nodes: impl Iterator<Item = InsertTrieNode<'a>>, | 435 | 7 | trie_entries_version: u8, | 436 | 7 | ) -> Result<(), CorruptedError> { | 437 | 7 | let mut database = self.database.lock(); | 438 | | | 439 | 7 | let transaction = database | 440 | 7 | .transaction() | 441 | 7 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 442 | | | 443 | | { | 444 | | // TODO: should check whether the existing merkle values that are referenced from inserted nodes exist in the parent's storage | 445 | | // TODO: is it correct to have OR IGNORE everywhere? | 446 | 7 | let mut insert_node_statement = transaction | 447 | 7 | .prepare_cached("INSERT OR IGNORE INTO trie_node(hash, partial_key) VALUES(?, ?)") | 448 | 7 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 449 | 7 | let mut insert_node_storage_statement = transaction | 450 | 7 | .prepare_cached("INSERT OR IGNORE INTO trie_node_storage(node_hash, value, trie_root_ref, trie_entry_version) VALUES(?, ?, ?, ?)") | 451 | 7 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 452 | 7 | let mut insert_child_statement = transaction | 453 | 7 | .prepare_cached( | 454 | 7 | "INSERT OR IGNORE INTO trie_node_child(hash, child_num, child_hash) VALUES(?, ?, ?)", | 455 | 7 | ) | 456 | 7 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 457 | | // TODO: if the iterator's `next()` function accesses the database, we deadlock | 458 | 14 | for trie_node7 in new_trie_nodes { | 459 | 7 | assert!(trie_node.partial_key_nibbles.iter().all(|n| *n < 16)); // TODO: document | 460 | 7 | insert_node_statement | 461 | 7 | .execute((&trie_node.merkle_value, trie_node.partial_key_nibbles)) | 462 | 7 | .map_err(|err: rusqlite::Error| CorruptedError::Internal(InternalError(err)))?0 ; | 463 | 7 | match trie_node.storage_value { | 464 | | InsertTrieNodeStorageValue::Value { | 465 | 6 | value, | 466 | 6 | references_merkle_value, | 467 | 6 | } => { | 468 | 6 | insert_node_storage_statement | 469 | 6 | .execute(( | 470 | 6 | &trie_node.merkle_value, | 471 | 6 | if !references_merkle_value { | 472 | 6 | Some(&value) | 473 | | } else { | 474 | 0 | None | 475 | | }, | 476 | 6 | if references_merkle_value { | 477 | 0 | Some(&value) | 478 | | } else { | 479 | 6 | None | 480 | | }, | 481 | 6 | trie_entries_version, | 482 | 6 | )) | 483 | 6 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 484 | | } | 485 | 1 | InsertTrieNodeStorageValue::NoValue => {} | 486 | | } | 487 | 112 | for (child_num, child) in trie_node.children_merkle_values.iter().enumerate()7 { | 488 | 112 | if let Some(child4 ) = child { | 489 | 4 | let child_num = | 490 | 4 | vec![u8::try_from(child_num).unwrap_or_else(|_| unreachable!())]; | 491 | 4 | insert_child_statement | 492 | 4 | .execute((&trie_node.merkle_value, child_num, child)) | 493 | 4 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 494 | 108 | } | 495 | | } | 496 | | } | 497 | | } | 498 | | | 499 | 7 | transaction | 500 | 7 | .commit() | 501 | 7 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 502 | | | 503 | 7 | Ok(()) | 504 | 7 | } |
_RINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtNtNtB7_4trie14trie_structure9NodeIndexENCNvNtB3_5testss_30empty_database_fill_then_querys0_0EEB7_ Line | Count | Source | 432 | 1.02k | pub fn insert_trie_nodes<'a>( | 433 | 1.02k | &self, | 434 | 1.02k | new_trie_nodes: impl Iterator<Item = InsertTrieNode<'a>>, | 435 | 1.02k | trie_entries_version: u8, | 436 | 1.02k | ) -> Result<(), CorruptedError> { | 437 | 1.02k | let mut database = self.database.lock(); | 438 | | | 439 | 1.02k | let transaction = database | 440 | 1.02k | .transaction() | 441 | 1.02k | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 442 | | | 443 | | { | 444 | | // TODO: should check whether the existing merkle values that are referenced from inserted nodes exist in the parent's storage | 445 | | // TODO: is it correct to have OR IGNORE everywhere? | 446 | 1.02k | let mut insert_node_statement = transaction | 447 | 1.02k | .prepare_cached("INSERT OR IGNORE INTO trie_node(hash, partial_key) VALUES(?, ?)") | 448 | 1.02k | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 449 | 1.02k | let mut insert_node_storage_statement = transaction | 450 | 1.02k | .prepare_cached("INSERT OR IGNORE INTO trie_node_storage(node_hash, value, trie_root_ref, trie_entry_version) VALUES(?, ?, ?, ?)") | 451 | 1.02k | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 452 | 1.02k | let mut insert_child_statement = transaction | 453 | 1.02k | .prepare_cached( | 454 | 1.02k | "INSERT OR IGNORE INTO trie_node_child(hash, child_num, child_hash) VALUES(?, ?, ?)", | 455 | 1.02k | ) | 456 | 1.02k | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 457 | | // TODO: if the iterator's `next()` function accesses the database, we deadlock | 458 | 3.60k | for trie_node2.58k in new_trie_nodes { | 459 | 2.58k | assert!(trie_node.partial_key_nibbles.iter().all(|n| *n < 16)); // TODO: document | 460 | 2.58k | insert_node_statement | 461 | 2.58k | .execute((&trie_node.merkle_value, trie_node.partial_key_nibbles)) | 462 | 2.58k | .map_err(|err: rusqlite::Error| CorruptedError::Internal(InternalError(err)))?0 ; | 463 | 2.58k | match trie_node.storage_value { | 464 | | InsertTrieNodeStorageValue::Value { | 465 | 2.51k | value, | 466 | 2.51k | references_merkle_value, | 467 | 2.51k | } => { | 468 | 2.51k | insert_node_storage_statement | 469 | 2.51k | .execute(( | 470 | 2.51k | &trie_node.merkle_value, | 471 | 2.51k | if !references_merkle_value { | 472 | 2.51k | Some(&value) | 473 | | } else { | 474 | 0 | None | 475 | | }, | 476 | 2.51k | if references_merkle_value { | 477 | 0 | Some(&value) | 478 | | } else { | 479 | 2.51k | None | 480 | | }, | 481 | 2.51k | trie_entries_version, | 482 | 2.51k | )) | 483 | 2.51k | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 484 | | } | 485 | 68 | InsertTrieNodeStorageValue::NoValue => {} | 486 | | } | 487 | 41.3k | for (child_num, child) in trie_node.children_merkle_values.iter().enumerate()2.58k { | 488 | 41.3k | if let Some(child1.56k ) = child { | 489 | 1.56k | let child_num = | 490 | 1.56k | vec![u8::try_from(child_num).unwrap_or_else(|_| unreachable!())]; | 491 | 1.56k | insert_child_statement | 492 | 1.56k | .execute((&trie_node.merkle_value, child_num, child)) | 493 | 1.56k | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 494 | 39.7k | } | 495 | | } | 496 | | } | 497 | | } | 498 | | | 499 | 1.02k | transaction | 500 | 1.02k | .commit() | 501 | 1.02k | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 502 | | | 503 | 1.02k | Ok(()) | 504 | 1.02k | } |
Unexecuted instantiation: _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase17insert_trie_nodespEB7_ Unexecuted instantiation: _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase17insert_trie_nodesINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtB3_14InsertTrieNodeEECsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter7sources4once4OnceNtB3_14InsertTrieNodeEECsiLzmwikkc22_14json_rpc_basic _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtNtNtB7_4trie14trie_structure9NodeIndexENCNCNvCsiUjFBJteJ7x_17smoldot_full_node13open_database0s_0EECsiLzmwikkc22_14json_rpc_basic Line | Count | Source | 432 | 2 | pub fn insert_trie_nodes<'a>( | 433 | 2 | &self, | 434 | 2 | new_trie_nodes: impl Iterator<Item = InsertTrieNode<'a>>, | 435 | 2 | trie_entries_version: u8, | 436 | 2 | ) -> Result<(), CorruptedError> { | 437 | 2 | let mut database = self.database.lock(); | 438 | | | 439 | 2 | let transaction = database | 440 | 2 | .transaction() | 441 | 2 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 442 | | | 443 | | { | 444 | | // TODO: should check whether the existing merkle values that are referenced from inserted nodes exist in the parent's storage | 445 | | // TODO: is it correct to have OR IGNORE everywhere? | 446 | 2 | let mut insert_node_statement = transaction | 447 | 2 | .prepare_cached("INSERT OR IGNORE INTO trie_node(hash, partial_key) VALUES(?, ?)") | 448 | 2 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 449 | 2 | let mut insert_node_storage_statement = transaction | 450 | 2 | .prepare_cached("INSERT OR IGNORE INTO trie_node_storage(node_hash, value, trie_root_ref, trie_entry_version) VALUES(?, ?, ?, ?)") | 451 | 2 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 452 | 2 | let mut insert_child_statement = transaction | 453 | 2 | .prepare_cached( | 454 | 2 | "INSERT OR IGNORE INTO trie_node_child(hash, child_num, child_hash) VALUES(?, ?, ?)", | 455 | 2 | ) | 456 | 2 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 457 | | // TODO: if the iterator's `next()` function accesses the database, we deadlock | 458 | 98 | for trie_node96 in new_trie_nodes { | 459 | 96 | assert!(trie_node.partial_key_nibbles.iter().all(|n| *n < 16)); // TODO: document | 460 | 96 | insert_node_statement | 461 | 96 | .execute((&trie_node.merkle_value, trie_node.partial_key_nibbles)) | 462 | 96 | .map_err(|err: rusqlite::Error| CorruptedError::Internal(InternalError(err)))?0 ; | 463 | 96 | match trie_node.storage_value { | 464 | | InsertTrieNodeStorageValue::Value { | 465 | 70 | value, | 466 | 70 | references_merkle_value, | 467 | 70 | } => { | 468 | 70 | insert_node_storage_statement | 469 | 70 | .execute(( | 470 | 70 | &trie_node.merkle_value, | 471 | 70 | if !references_merkle_value { | 472 | 70 | Some(&value) | 473 | | } else { | 474 | 0 | None | 475 | | }, | 476 | 70 | if references_merkle_value { | 477 | 0 | Some(&value) | 478 | | } else { | 479 | 70 | None | 480 | | }, | 481 | 70 | trie_entries_version, | 482 | 70 | )) | 483 | 70 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 484 | | } | 485 | 26 | InsertTrieNodeStorageValue::NoValue => {} | 486 | | } | 487 | 1.53k | for (child_num, child) in trie_node.children_merkle_values.iter().enumerate()96 { | 488 | 1.53k | if let Some(child94 ) = child { | 489 | 94 | let child_num = | 490 | 94 | vec![u8::try_from(child_num).unwrap_or_else(|_| unreachable!())]; | 491 | 94 | insert_child_statement | 492 | 94 | .execute((&trie_node.merkle_value, child_num, child)) | 493 | 94 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 494 | 1.44k | } | 495 | | } | 496 | | } | 497 | | } | 498 | | | 499 | 2 | transaction | 500 | 2 | .commit() | 501 | 2 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 502 | | | 503 | 2 | Ok(()) | 504 | 2 | } |
Unexecuted instantiation: _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase17insert_trie_nodesINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtB3_14InsertTrieNodeEECscDgN54JpMGG_6author Unexecuted instantiation: _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter7sources4once4OnceNtB3_14InsertTrieNodeEECscDgN54JpMGG_6author Unexecuted instantiation: _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtNtNtB7_4trie14trie_structure9NodeIndexENCNCNvCsiUjFBJteJ7x_17smoldot_full_node13open_database0s_0EECscDgN54JpMGG_6author Unexecuted instantiation: _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase17insert_trie_nodesINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtB3_14InsertTrieNodeEECsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter7sources4once4OnceNtB3_14InsertTrieNodeEECsibGXYHQB8Ea_25json_rpc_general_requests _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase17insert_trie_nodesINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtNtNtB7_4trie14trie_structure9NodeIndexENCNCNvCsiUjFBJteJ7x_17smoldot_full_node13open_database0s_0EECsibGXYHQB8Ea_25json_rpc_general_requests Line | Count | Source | 432 | 19 | pub fn insert_trie_nodes<'a>( | 433 | 19 | &self, | 434 | 19 | new_trie_nodes: impl Iterator<Item = InsertTrieNode<'a>>, | 435 | 19 | trie_entries_version: u8, | 436 | 19 | ) -> Result<(), CorruptedError> { | 437 | 19 | let mut database = self.database.lock(); | 438 | | | 439 | 19 | let transaction = database | 440 | 19 | .transaction() | 441 | 19 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 442 | | | 443 | | { | 444 | | // TODO: should check whether the existing merkle values that are referenced from inserted nodes exist in the parent's storage | 445 | | // TODO: is it correct to have OR IGNORE everywhere? | 446 | 19 | let mut insert_node_statement = transaction | 447 | 19 | .prepare_cached("INSERT OR IGNORE INTO trie_node(hash, partial_key) VALUES(?, ?)") | 448 | 19 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 449 | 19 | let mut insert_node_storage_statement = transaction | 450 | 19 | .prepare_cached("INSERT OR IGNORE INTO trie_node_storage(node_hash, value, trie_root_ref, trie_entry_version) VALUES(?, ?, ?, ?)") | 451 | 19 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 452 | 19 | let mut insert_child_statement = transaction | 453 | 19 | .prepare_cached( | 454 | 19 | "INSERT OR IGNORE INTO trie_node_child(hash, child_num, child_hash) VALUES(?, ?, ?)", | 455 | 19 | ) | 456 | 19 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 457 | | // TODO: if the iterator's `next()` function accesses the database, we deadlock | 458 | 931 | for trie_node912 in new_trie_nodes { | 459 | 912 | assert!(trie_node.partial_key_nibbles.iter().all(|n| *n < 16)); // TODO: document | 460 | 912 | insert_node_statement | 461 | 912 | .execute((&trie_node.merkle_value, trie_node.partial_key_nibbles)) | 462 | 912 | .map_err(|err: rusqlite::Error| CorruptedError::Internal(InternalError(err)))?0 ; | 463 | 912 | match trie_node.storage_value { | 464 | | InsertTrieNodeStorageValue::Value { | 465 | 665 | value, | 466 | 665 | references_merkle_value, | 467 | 665 | } => { | 468 | 665 | insert_node_storage_statement | 469 | 665 | .execute(( | 470 | 665 | &trie_node.merkle_value, | 471 | 665 | if !references_merkle_value { | 472 | 665 | Some(&value) | 473 | | } else { | 474 | 0 | None | 475 | | }, | 476 | 665 | if references_merkle_value { | 477 | 0 | Some(&value) | 478 | | } else { | 479 | 665 | None | 480 | | }, | 481 | 665 | trie_entries_version, | 482 | 665 | )) | 483 | 665 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 484 | | } | 485 | 247 | InsertTrieNodeStorageValue::NoValue => {} | 486 | | } | 487 | 14.5k | for (child_num, child) in trie_node.children_merkle_values.iter().enumerate()912 { | 488 | 14.5k | if let Some(child893 ) = child { | 489 | 893 | let child_num = | 490 | 893 | vec![u8::try_from(child_num).unwrap_or_else(|_| unreachable!())]; | 491 | 893 | insert_child_statement | 492 | 893 | .execute((&trie_node.merkle_value, child_num, child)) | 493 | 893 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 494 | 13.6k | } | 495 | | } | 496 | | } | 497 | | } | 498 | | | 499 | 19 | transaction | 500 | 19 | .commit() | 501 | 19 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 502 | | | 503 | 19 | Ok(()) | 504 | 19 | } |
|
505 | | |
506 | | /// Returns a list of trie nodes that are missing from the database and that belong to the |
507 | | /// state of a block whose number is superior or equal to the finalized block. |
508 | | /// |
509 | | /// The ordering of the returned trie nodes is unspecified. |
510 | | /// |
511 | | /// > **Note**: This function call is relatively expensive, and the API user is expected to |
512 | | /// > cache the return value. |
513 | 42 | pub fn finalized_and_above_missing_trie_nodes_unordered( |
514 | 42 | &self, |
515 | 42 | ) -> Result<Vec<MissingTrieNode>, CorruptedError> { |
516 | 42 | let database = self.database.lock(); |
517 | | |
518 | 42 | let mut statement = database |
519 | 42 | .prepare_cached( |
520 | 42 | r#" |
521 | 42 | WITH RECURSIVE |
522 | 42 | -- List of all block hashes that are equal to the finalized block or above. |
523 | 42 | finalized_and_above_blocks(block_hash) AS ( |
524 | 42 | SELECT blocks.hash |
525 | 42 | FROM blocks |
526 | 42 | JOIN meta ON meta.key = "finalized" |
527 | 42 | WHERE blocks.number >= meta.value_number |
528 | 42 | ), |
529 | 42 | |
530 | 42 | -- List of all trie nodes for these blocks. |
531 | 42 | trie_nodes(block_hash, node_hash, node_key, is_present) AS ( |
532 | 42 | SELECT blocks.hash, blocks.state_trie_root_hash, |
533 | 42 | CASE WHEN trie_node.partial_key IS NULL THEN X'' ELSE trie_node.partial_key END, |
534 | 42 | trie_node.hash IS NOT NULL |
535 | 42 | FROM blocks |
536 | 42 | JOIN finalized_and_above_blocks |
537 | 42 | ON blocks.hash = finalized_and_above_blocks.block_hash |
538 | 42 | LEFT JOIN trie_node |
539 | 42 | ON trie_node.hash = blocks.state_trie_root_hash |
540 | 42 | |
541 | 42 | UNION ALL |
542 | 42 | SELECT trie_nodes.block_hash, trie_node_child.child_hash, |
543 | 42 | CASE WHEN trie_node.hash IS NULL THEN CAST(trie_nodes.node_key || trie_node_child.child_num AS BLOB) |
544 | 42 | ELSE CAST(trie_nodes.node_key || trie_node_child.child_num || trie_node.partial_key AS BLOB) END, |
545 | 42 | trie_node.hash IS NOT NULL |
546 | 42 | FROM trie_nodes |
547 | 42 | JOIN trie_node_child |
548 | 42 | ON trie_nodes.node_hash = trie_node_child.hash |
549 | 42 | LEFT JOIN trie_node |
550 | 42 | ON trie_node.hash = trie_node_child.child_hash |
551 | 42 | WHERE trie_nodes.is_present |
552 | 42 | |
553 | 42 | UNION ALL |
554 | 42 | SELECT trie_nodes.block_hash, trie_node_storage.trie_root_ref, |
555 | 42 | CASE WHEN trie_node.hash IS NULL THEN CAST(trie_nodes.node_key || X'10' AS BLOB) |
556 | 42 | ELSE CAST(trie_nodes.node_key || X'10' || trie_node.partial_key AS BLOB) END, |
557 | 42 | trie_node.hash IS NOT NULL |
558 | 42 | FROM trie_nodes |
559 | 42 | JOIN trie_node_storage |
560 | 42 | ON trie_nodes.node_hash = trie_node_storage.node_hash AND trie_node_storage.trie_root_ref IS NOT NULL |
561 | 42 | LEFT JOIN trie_node |
562 | 42 | ON trie_node.hash = trie_node_storage.trie_root_ref |
563 | 42 | WHERE trie_nodes.is_present |
564 | 42 | ) |
565 | 42 | |
566 | 42 | SELECT group_concat(HEX(trie_nodes.block_hash)), group_concat(CAST(blocks.number as TEXT)), trie_nodes.node_hash, group_concat(HEX(trie_nodes.node_key)) |
567 | 42 | FROM trie_nodes |
568 | 42 | JOIN blocks ON blocks.hash = trie_nodes.block_hash |
569 | 42 | WHERE is_present = false |
570 | 42 | GROUP BY trie_nodes.node_hash |
571 | 42 | "#) |
572 | 42 | .map_err(|err| { |
573 | 0 | CorruptedError::Internal( |
574 | 0 | InternalError(err), |
575 | 0 | ) |
576 | 42 | })?0 ; Unexecuted instantiation: _RNCNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase48finalized_and_above_missing_trie_nodes_unordered0B8_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase48finalized_and_above_missing_trie_nodes_unordered0B8_ |
577 | | |
578 | 42 | let results = statement |
579 | 42 | .query_map((), |row| {0 |
580 | 0 | let block_hashes = row.get::<_, String>(0)?; |
581 | 0 | let block_numbers = row.get::<_, String>(1)?; |
582 | 0 | let node_hash = row.get::<_, Vec<u8>>(2)?; |
583 | 0 | let node_keys = row.get::<_, String>(3)?; |
584 | 0 | Ok((block_hashes, block_numbers, node_hash, node_keys)) |
585 | 42 | }0 ) Unexecuted instantiation: _RNCNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase48finalized_and_above_missing_trie_nodes_unordereds_0B8_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase48finalized_and_above_missing_trie_nodes_unordereds_0B8_ |
586 | 42 | .map_err(|err| CorruptedError::Internal(InternalError(err))0 )?0 Unexecuted instantiation: _RNCNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase48finalized_and_above_missing_trie_nodes_unordereds0_0B8_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase48finalized_and_above_missing_trie_nodes_unordereds0_0B8_ |
587 | 42 | .map(|row| {0 |
588 | 0 | let (block_hashes, block_numbers, trie_node_hash, node_keys) = match row { |
589 | 0 | Ok(r) => r, |
590 | 0 | Err(err) => return Err(CorruptedError::Internal(InternalError(err))), |
591 | | }; |
592 | | |
593 | | // The SQL query above uses `group_concat` and `hex` to convert a list of blobs |
594 | | // into a string containing all these blobs. We now convert them back into lists |
595 | | // of blobs. |
596 | | // A panic here indicates a bug in SQLite. |
597 | 0 | let mut block_hashes_iter = block_hashes |
598 | 0 | .split(',') |
599 | 0 | .map(|hash| hex::decode(hash).unwrap_or_else(|_| unreachable!())); Unexecuted instantiation: _RNCNCNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB6_18SqliteFullDatabase48finalized_and_above_missing_trie_nodes_unordereds1_00Ba_ Unexecuted instantiation: _RNCNCNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB6_18SqliteFullDatabase48finalized_and_above_missing_trie_nodes_unordereds1_00Ba_ Unexecuted instantiation: _RNCNCNCNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB8_18SqliteFullDatabase48finalized_and_above_missing_trie_nodes_unordereds1_000Bc_ Unexecuted instantiation: _RNCNCNCNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB8_18SqliteFullDatabase48finalized_and_above_missing_trie_nodes_unordereds1_000Bc_ |
600 | 0 | let mut block_numbers_iter = block_numbers.split(',').map(|n| { |
601 | 0 | <u64 as core::str::FromStr>::from_str(n).unwrap_or_else(|_| unreachable!()) Unexecuted instantiation: _RNCNCNCNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB8_18SqliteFullDatabase48finalized_and_above_missing_trie_nodes_unordereds1_0s_00Bc_ Unexecuted instantiation: _RNCNCNCNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB8_18SqliteFullDatabase48finalized_and_above_missing_trie_nodes_unordereds1_0s_00Bc_ |
602 | 0 | }); Unexecuted instantiation: _RNCNCNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB6_18SqliteFullDatabase48finalized_and_above_missing_trie_nodes_unordereds1_0s_0Ba_ Unexecuted instantiation: _RNCNCNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB6_18SqliteFullDatabase48finalized_and_above_missing_trie_nodes_unordereds1_0s_0Ba_ |
603 | 0 | let mut node_keys_iter = node_keys |
604 | 0 | .split(',') |
605 | 0 | .map(|hash| hex::decode(hash).unwrap_or_else(|_| unreachable!())); Unexecuted instantiation: _RNCNCNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB6_18SqliteFullDatabase48finalized_and_above_missing_trie_nodes_unordereds1_0s0_0Ba_ Unexecuted instantiation: _RNCNCNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB6_18SqliteFullDatabase48finalized_and_above_missing_trie_nodes_unordereds1_0s0_0Ba_ Unexecuted instantiation: _RNCNCNCNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB8_18SqliteFullDatabase48finalized_and_above_missing_trie_nodes_unordereds1_0s0_00Bc_ Unexecuted instantiation: _RNCNCNCNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB8_18SqliteFullDatabase48finalized_and_above_missing_trie_nodes_unordereds1_0s0_00Bc_ |
606 | 0 |
|
607 | 0 | let mut blocks = Vec::with_capacity(32); |
608 | | loop { |
609 | | match ( |
610 | 0 | block_hashes_iter.next(), |
611 | 0 | block_numbers_iter.next(), |
612 | 0 | node_keys_iter.next(), |
613 | | ) { |
614 | 0 | (Some(hash), Some(number), Some(node_key)) => { |
615 | 0 | let hash = <[u8; 32]>::try_from(hash) |
616 | 0 | .map_err(|_| CorruptedError::InvalidBlockHashLen)?; Unexecuted instantiation: _RNCNCNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB6_18SqliteFullDatabase48finalized_and_above_missing_trie_nodes_unordereds1_0s1_0Ba_ Unexecuted instantiation: _RNCNCNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB6_18SqliteFullDatabase48finalized_and_above_missing_trie_nodes_unordereds1_0s1_0Ba_ |
617 | 0 | let mut trie_node_key_nibbles = Vec::with_capacity(node_key.len()); |
618 | 0 | let mut parent_tries_paths_nibbles = Vec::with_capacity(node_key.len()); |
619 | 0 | for nibble in node_key { |
620 | 0 | debug_assert!(nibble <= 16); |
621 | 0 | if nibble == 16 { |
622 | 0 | parent_tries_paths_nibbles.push(trie_node_key_nibbles.clone()); |
623 | 0 | trie_node_key_nibbles.clear(); |
624 | 0 | } else { |
625 | 0 | trie_node_key_nibbles.push(nibble); |
626 | 0 | } |
627 | | } |
628 | | |
629 | 0 | blocks.push(MissingTrieNodeBlock { |
630 | 0 | hash, |
631 | 0 | number, |
632 | 0 | parent_tries_paths_nibbles, |
633 | 0 | trie_node_key_nibbles, |
634 | 0 | }) |
635 | | } |
636 | 0 | (None, None, None) => break, |
637 | | _ => { |
638 | | // The iterators are supposed to have the same number of elements. |
639 | 0 | debug_assert!(false); |
640 | 0 | break; |
641 | | } |
642 | | } |
643 | | } |
644 | | |
645 | 0 | let trie_node_hash = <[u8; 32]>::try_from(trie_node_hash) |
646 | 0 | .map_err(|_| CorruptedError::InvalidTrieHashLen)?; Unexecuted instantiation: _RNCNCNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB6_18SqliteFullDatabase48finalized_and_above_missing_trie_nodes_unordereds1_0s2_0Ba_ Unexecuted instantiation: _RNCNCNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB6_18SqliteFullDatabase48finalized_and_above_missing_trie_nodes_unordereds1_0s2_0Ba_ |
647 | | |
648 | 0 | debug_assert!(!blocks.is_empty()); |
649 | | |
650 | 0 | Ok(MissingTrieNode { |
651 | 0 | blocks, |
652 | 0 | trie_node_hash, |
653 | 0 | }) |
654 | 42 | }0 ) Unexecuted instantiation: _RNCNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase48finalized_and_above_missing_trie_nodes_unordereds1_0B8_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase48finalized_and_above_missing_trie_nodes_unordereds1_0B8_ |
655 | 42 | .collect::<Result<Vec<_>, _>>()?0 ; |
656 | | |
657 | 42 | Ok(results) |
658 | 42 | } Unexecuted instantiation: _RNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB2_18SqliteFullDatabase48finalized_and_above_missing_trie_nodes_unordered _RNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB2_18SqliteFullDatabase48finalized_and_above_missing_trie_nodes_unordered Line | Count | Source | 513 | 42 | pub fn finalized_and_above_missing_trie_nodes_unordered( | 514 | 42 | &self, | 515 | 42 | ) -> Result<Vec<MissingTrieNode>, CorruptedError> { | 516 | 42 | let database = self.database.lock(); | 517 | | | 518 | 42 | let mut statement = database | 519 | 42 | .prepare_cached( | 520 | 42 | r#" | 521 | 42 | WITH RECURSIVE | 522 | 42 | -- List of all block hashes that are equal to the finalized block or above. | 523 | 42 | finalized_and_above_blocks(block_hash) AS ( | 524 | 42 | SELECT blocks.hash | 525 | 42 | FROM blocks | 526 | 42 | JOIN meta ON meta.key = "finalized" | 527 | 42 | WHERE blocks.number >= meta.value_number | 528 | 42 | ), | 529 | 42 | | 530 | 42 | -- List of all trie nodes for these blocks. | 531 | 42 | trie_nodes(block_hash, node_hash, node_key, is_present) AS ( | 532 | 42 | SELECT blocks.hash, blocks.state_trie_root_hash, | 533 | 42 | CASE WHEN trie_node.partial_key IS NULL THEN X'' ELSE trie_node.partial_key END, | 534 | 42 | trie_node.hash IS NOT NULL | 535 | 42 | FROM blocks | 536 | 42 | JOIN finalized_and_above_blocks | 537 | 42 | ON blocks.hash = finalized_and_above_blocks.block_hash | 538 | 42 | LEFT JOIN trie_node | 539 | 42 | ON trie_node.hash = blocks.state_trie_root_hash | 540 | 42 | | 541 | 42 | UNION ALL | 542 | 42 | SELECT trie_nodes.block_hash, trie_node_child.child_hash, | 543 | 42 | CASE WHEN trie_node.hash IS NULL THEN CAST(trie_nodes.node_key || trie_node_child.child_num AS BLOB) | 544 | 42 | ELSE CAST(trie_nodes.node_key || trie_node_child.child_num || trie_node.partial_key AS BLOB) END, | 545 | 42 | trie_node.hash IS NOT NULL | 546 | 42 | FROM trie_nodes | 547 | 42 | JOIN trie_node_child | 548 | 42 | ON trie_nodes.node_hash = trie_node_child.hash | 549 | 42 | LEFT JOIN trie_node | 550 | 42 | ON trie_node.hash = trie_node_child.child_hash | 551 | 42 | WHERE trie_nodes.is_present | 552 | 42 | | 553 | 42 | UNION ALL | 554 | 42 | SELECT trie_nodes.block_hash, trie_node_storage.trie_root_ref, | 555 | 42 | CASE WHEN trie_node.hash IS NULL THEN CAST(trie_nodes.node_key || X'10' AS BLOB) | 556 | 42 | ELSE CAST(trie_nodes.node_key || X'10' || trie_node.partial_key AS BLOB) END, | 557 | 42 | trie_node.hash IS NOT NULL | 558 | 42 | FROM trie_nodes | 559 | 42 | JOIN trie_node_storage | 560 | 42 | ON trie_nodes.node_hash = trie_node_storage.node_hash AND trie_node_storage.trie_root_ref IS NOT NULL | 561 | 42 | LEFT JOIN trie_node | 562 | 42 | ON trie_node.hash = trie_node_storage.trie_root_ref | 563 | 42 | WHERE trie_nodes.is_present | 564 | 42 | ) | 565 | 42 | | 566 | 42 | SELECT group_concat(HEX(trie_nodes.block_hash)), group_concat(CAST(blocks.number as TEXT)), trie_nodes.node_hash, group_concat(HEX(trie_nodes.node_key)) | 567 | 42 | FROM trie_nodes | 568 | 42 | JOIN blocks ON blocks.hash = trie_nodes.block_hash | 569 | 42 | WHERE is_present = false | 570 | 42 | GROUP BY trie_nodes.node_hash | 571 | 42 | "#) | 572 | 42 | .map_err(|err| { | 573 | | CorruptedError::Internal( | 574 | | InternalError(err), | 575 | | ) | 576 | 42 | })?0 ; | 577 | | | 578 | 42 | let results = statement | 579 | 42 | .query_map((), |row| { | 580 | | let block_hashes = row.get::<_, String>(0)?; | 581 | | let block_numbers = row.get::<_, String>(1)?; | 582 | | let node_hash = row.get::<_, Vec<u8>>(2)?; | 583 | | let node_keys = row.get::<_, String>(3)?; | 584 | | Ok((block_hashes, block_numbers, node_hash, node_keys)) | 585 | 42 | }) | 586 | 42 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 | 587 | 42 | .map(|row| { | 588 | | let (block_hashes, block_numbers, trie_node_hash, node_keys) = match row { | 589 | | Ok(r) => r, | 590 | | Err(err) => return Err(CorruptedError::Internal(InternalError(err))), | 591 | | }; | 592 | | | 593 | | // The SQL query above uses `group_concat` and `hex` to convert a list of blobs | 594 | | // into a string containing all these blobs. We now convert them back into lists | 595 | | // of blobs. | 596 | | // A panic here indicates a bug in SQLite. | 597 | | let mut block_hashes_iter = block_hashes | 598 | | .split(',') | 599 | | .map(|hash| hex::decode(hash).unwrap_or_else(|_| unreachable!())); | 600 | | let mut block_numbers_iter = block_numbers.split(',').map(|n| { | 601 | | <u64 as core::str::FromStr>::from_str(n).unwrap_or_else(|_| unreachable!()) | 602 | | }); | 603 | | let mut node_keys_iter = node_keys | 604 | | .split(',') | 605 | | .map(|hash| hex::decode(hash).unwrap_or_else(|_| unreachable!())); | 606 | | | 607 | | let mut blocks = Vec::with_capacity(32); | 608 | | loop { | 609 | | match ( | 610 | | block_hashes_iter.next(), | 611 | | block_numbers_iter.next(), | 612 | | node_keys_iter.next(), | 613 | | ) { | 614 | | (Some(hash), Some(number), Some(node_key)) => { | 615 | | let hash = <[u8; 32]>::try_from(hash) | 616 | | .map_err(|_| CorruptedError::InvalidBlockHashLen)?; | 617 | | let mut trie_node_key_nibbles = Vec::with_capacity(node_key.len()); | 618 | | let mut parent_tries_paths_nibbles = Vec::with_capacity(node_key.len()); | 619 | | for nibble in node_key { | 620 | | debug_assert!(nibble <= 16); | 621 | | if nibble == 16 { | 622 | | parent_tries_paths_nibbles.push(trie_node_key_nibbles.clone()); | 623 | | trie_node_key_nibbles.clear(); | 624 | | } else { | 625 | | trie_node_key_nibbles.push(nibble); | 626 | | } | 627 | | } | 628 | | | 629 | | blocks.push(MissingTrieNodeBlock { | 630 | | hash, | 631 | | number, | 632 | | parent_tries_paths_nibbles, | 633 | | trie_node_key_nibbles, | 634 | | }) | 635 | | } | 636 | | (None, None, None) => break, | 637 | | _ => { | 638 | | // The iterators are supposed to have the same number of elements. | 639 | | debug_assert!(false); | 640 | | break; | 641 | | } | 642 | | } | 643 | | } | 644 | | | 645 | | let trie_node_hash = <[u8; 32]>::try_from(trie_node_hash) | 646 | | .map_err(|_| CorruptedError::InvalidTrieHashLen)?; | 647 | | | 648 | | debug_assert!(!blocks.is_empty()); | 649 | | | 650 | | Ok(MissingTrieNode { | 651 | | blocks, | 652 | | trie_node_hash, | 653 | | }) | 654 | 42 | }) | 655 | 42 | .collect::<Result<Vec<_>, _>>()?0 ; | 656 | | | 657 | 42 | Ok(results) | 658 | 42 | } |
|
659 | | |
660 | | /// Changes the finalized block to the given one. |
661 | | /// |
662 | | /// The block must have been previously inserted using [`SqliteFullDatabase::insert`], |
663 | | /// otherwise an error is returned. |
664 | | /// |
665 | | /// Blocks are expected to be valid in context of the chain. Inserting an invalid block can |
666 | | /// result in the database being corrupted. |
667 | | /// |
668 | | /// The block must be a descendant of the current finalized block. Reverting finalization is |
669 | | /// forbidden, as the database intentionally discards some information when finality is |
670 | | /// applied. |
671 | | /// |
672 | | /// > **Note**: This function doesn't remove any block from the database but simply moves |
673 | | /// > the finalized block "cursor". |
674 | | /// |
675 | 0 | pub fn set_finalized( |
676 | 0 | &self, |
677 | 0 | new_finalized_block_hash: &[u8; 32], |
678 | 0 | ) -> Result<(), SetFinalizedError> { |
679 | 0 | let mut database = self.database.lock(); |
680 | | |
681 | | // Start a transaction to insert everything at once. |
682 | 0 | let transaction = database.transaction().map_err(|err| { |
683 | 0 | SetFinalizedError::Corrupted(CorruptedError::Internal(InternalError(err))) |
684 | 0 | })?; Unexecuted instantiation: _RNCNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase13set_finalized0B8_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase13set_finalized0B8_ |
685 | | |
686 | | // Fetch the header of the block to finalize. |
687 | 0 | let new_finalized_header = block_header(&transaction, new_finalized_block_hash)? |
688 | 0 | .ok_or(SetFinalizedError::UnknownBlock)?; |
689 | 0 | let new_finalized_header = header::decode(&new_finalized_header, self.block_number_bytes) |
690 | 0 | .map_err(CorruptedError::BlockHeaderCorrupted) |
691 | 0 | .map_err(SetFinalizedError::Corrupted)?; |
692 | | |
693 | | // Fetch the current finalized block. |
694 | 0 | let current_finalized = finalized_num(&transaction)?; |
695 | | |
696 | | // If the block to finalize is at the same height as the already-finalized |
697 | | // block, considering that the database only contains one block per height on |
698 | | // the finalized chain, and that the presence of the block to finalize in |
699 | | // the database has already been verified, it is guaranteed that the block |
700 | | // to finalize is already the one already finalized. |
701 | | // TODO: this comment is obsolete ^, should also compare the block hashes |
702 | 0 | if new_finalized_header.number == current_finalized { |
703 | 0 | return Ok(()); |
704 | 0 | } |
705 | 0 |
|
706 | 0 | // Cannot set the finalized block to a past block. The database can't support |
707 | 0 | // reverting finalization. |
708 | 0 | if new_finalized_header.number < current_finalized { |
709 | 0 | return Err(SetFinalizedError::RevertForbidden); |
710 | 0 | } |
711 | 0 |
|
712 | 0 | // At this point, we are sure that the operation will succeed unless the database is |
713 | 0 | // corrupted. |
714 | 0 | // Update the finalized block in meta. |
715 | 0 | meta_set_number(&transaction, "finalized", new_finalized_header.number)?; |
716 | | |
717 | | // It is possible that the best block has been pruned. |
718 | | // TODO: ^ yeah, how do we handle that exactly ^ ? |
719 | | |
720 | | // If everything went well up to this point, commit the transaction. |
721 | 0 | transaction.commit().map_err(|err| { |
722 | 0 | SetFinalizedError::Corrupted(CorruptedError::Internal(InternalError(err))) |
723 | 0 | })?; Unexecuted instantiation: _RNCNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase13set_finalizeds_0B8_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase13set_finalizeds_0B8_ |
724 | | |
725 | 0 | Ok(()) |
726 | 0 | } Unexecuted instantiation: _RNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB2_18SqliteFullDatabase13set_finalized Unexecuted instantiation: _RNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB2_18SqliteFullDatabase13set_finalized |
727 | | |
728 | | /// Removes from the database all blocks that aren't a descendant of the current finalized |
729 | | /// block. |
730 | 21 | pub fn purge_finality_orphans(&self) -> Result<(), CorruptedError> { |
731 | 21 | let mut database = self.database.lock(); |
732 | | |
733 | | // TODO: untested |
734 | | |
735 | 21 | let transaction = database |
736 | 21 | .transaction() |
737 | 21 | .map_err(|err| CorruptedError::Internal(InternalError(err))0 )?0 ; Unexecuted instantiation: _RNCNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase22purge_finality_orphans0B8_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase22purge_finality_orphans0B8_ |
738 | | |
739 | | // Temporarily disable foreign key checks in order to make the insertion easier, as we |
740 | | // don't have to make sure that trie nodes are sorted. |
741 | | // Note that this is immediately disabled again when we `COMMIT` later down below. |
742 | | // TODO: is this really necessary? |
743 | 21 | transaction |
744 | 21 | .execute("PRAGMA defer_foreign_keys = ON", ()) |
745 | 21 | .map_err(|err| CorruptedError::Internal(InternalError(err))0 )?0 ; Unexecuted instantiation: _RNCNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase22purge_finality_orphanss_0B8_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase22purge_finality_orphanss_0B8_ |
746 | | |
747 | 21 | let current_finalized = finalized_num(&transaction)?0 ; |
748 | | |
749 | 21 | let blocks = transaction |
750 | 21 | .prepare_cached( |
751 | 21 | r#"SELECT hash FROM blocks WHERE number <= ? AND is_best_chain = FALSE"#, |
752 | 21 | ) |
753 | 21 | .map_err(|err| CorruptedError::Internal(InternalError(err))0 )?0 Unexecuted instantiation: _RNCNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase22purge_finality_orphanss0_0B8_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase22purge_finality_orphanss0_0B8_ |
754 | 21 | .query_map((current_finalized,), |row| row.get::<_, Vec<u8>>(0)0 ) Unexecuted instantiation: _RNCNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase22purge_finality_orphanss1_0B8_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase22purge_finality_orphanss1_0B8_ |
755 | 21 | .map_err(|err| CorruptedError::Internal(InternalError(err))0 )?0 Unexecuted instantiation: _RNCNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase22purge_finality_orphanss2_0B8_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase22purge_finality_orphanss2_0B8_ |
756 | 21 | .collect::<Result<Vec<_>, _>>() |
757 | 21 | .map_err(|err| CorruptedError::Internal(InternalError(err))0 )?0 ; Unexecuted instantiation: _RNCNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase22purge_finality_orphanss3_0B8_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase22purge_finality_orphanss3_0B8_ |
758 | | |
759 | 21 | for block0 in blocks { |
760 | 0 | purge_block(&transaction, &block)?; |
761 | | } |
762 | | |
763 | | // If everything went well up to this point, commit the transaction. |
764 | 21 | transaction |
765 | 21 | .commit() |
766 | 21 | .map_err(|err| CorruptedError::Internal(InternalError(err))0 )?0 ; Unexecuted instantiation: _RNCNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase22purge_finality_orphanss4_0B8_ Unexecuted instantiation: _RNCNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabase22purge_finality_orphanss4_0B8_ |
767 | | |
768 | 21 | Ok(()) |
769 | 21 | } Unexecuted instantiation: _RNvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB2_18SqliteFullDatabase22purge_finality_orphans _RNvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB2_18SqliteFullDatabase22purge_finality_orphans Line | Count | Source | 730 | 21 | pub fn purge_finality_orphans(&self) -> Result<(), CorruptedError> { | 731 | 21 | let mut database = self.database.lock(); | 732 | | | 733 | | // TODO: untested | 734 | | | 735 | 21 | let transaction = database | 736 | 21 | .transaction() | 737 | 21 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 738 | | | 739 | | // Temporarily disable foreign key checks in order to make the insertion easier, as we | 740 | | // don't have to make sure that trie nodes are sorted. | 741 | | // Note that this is immediately disabled again when we `COMMIT` later down below. | 742 | | // TODO: is this really necessary? | 743 | 21 | transaction | 744 | 21 | .execute("PRAGMA defer_foreign_keys = ON", ()) | 745 | 21 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 746 | | | 747 | 21 | let current_finalized = finalized_num(&transaction)?0 ; | 748 | | | 749 | 21 | let blocks = transaction | 750 | 21 | .prepare_cached( | 751 | 21 | r#"SELECT hash FROM blocks WHERE number <= ? AND is_best_chain = FALSE"#, | 752 | 21 | ) | 753 | 21 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 | 754 | 21 | .query_map((current_finalized,), |row| row.get::<_, Vec<u8>>(0)) | 755 | 21 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 | 756 | 21 | .collect::<Result<Vec<_>, _>>() | 757 | 21 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 758 | | | 759 | 21 | for block0 in blocks { | 760 | 0 | purge_block(&transaction, &block)?; | 761 | | } | 762 | | | 763 | | // If everything went well up to this point, commit the transaction. | 764 | 21 | transaction | 765 | 21 | .commit() | 766 | 21 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 767 | | | 768 | 21 | Ok(()) | 769 | 21 | } |
|
770 | | |
771 | | /// Returns the value associated with a node of the trie of the given block. |
772 | | /// |
773 | | /// `parent_tries_paths_nibbles` is a list of keys to follow in order to find the root of the |
774 | | /// trie into which `key_nibbles` should be searched. |
775 | | /// |
776 | | /// Beware that both `parent_tries_paths_nibbles` and `key_nibbles` must yield *nibbles*, in |
777 | | /// other words values strictly inferior to 16. |
778 | | /// |
779 | | /// Returns an error if the block or its storage can't be found in the database. |
780 | | /// |
781 | | /// # Panic |
782 | | /// |
783 | | /// Panics if any of the values yielded by `parent_tries_paths_nibbles` or `key_nibbles` is |
784 | | /// superior or equal to 16. |
785 | | /// |
786 | 1.04M | pub fn block_storage_get( |
787 | 1.04M | &self, |
788 | 1.04M | block_hash: &[u8; 32], |
789 | 1.04M | parent_tries_paths_nibbles: impl Iterator<Item = impl Iterator<Item = u8>>, |
790 | 1.04M | key_nibbles: impl Iterator<Item = u8>, |
791 | 1.04M | ) -> Result<Option<(Vec<u8>, u8)>, StorageAccessError> { |
792 | 1.04M | // Process the iterators at the very beginning and before locking the database, in order |
793 | 1.04M | // to avoid a deadlock in case the `next()` function of one of the iterators accesses |
794 | 1.04M | // the database as well. |
795 | 1.04M | let key_vectored = parent_tries_paths_nibbles |
796 | 1.04M | .flat_map(|t| t.inspect(0 |n| assert!(*n < 160 )0 ).chain(iter::once(0x10))0 ) Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1z_B1y_EINtNtNtB1F_8adapters3map3MapINtNtNtB9_4trie6nibble14BytesToNibblesINtNtB2G_6copied6CopiedINtNtNtB1H_5slice4iter4IterhEEENvYhINtNtB1H_7convert4FromNtB36_6NibbleE4fromEE0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1z_B1y_EINtNtNtB1H_5array4iter8IntoIterhKj0_EE0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1z_B1y_EINtNtNtB1H_5array4iter8IntoIterhKj2_EE0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1z_B1y_EINtNtNtB1H_5array4iter8IntoIterhKj3_EE0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1z_B1y_EINtNtNtB1H_5array4iter8IntoIterhKj4_EE0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1z_B1y_EINtNtNtB1H_5array4iter8IntoIterhKj5_EE0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters3map3MapINtNtNtB9_4trie6nibble14BytesToNibblesINtNtB2H_6copied6CopiedINtNtNtB1I_5slice4iter4IterhEEENvYhINtNtB1I_7convert4FromNtB37_6NibbleE4fromEE0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2y_6option8IntoIterINtB1E_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB41_14SyncBackground12author_block0s_00EINtNtB2u_6copied6CopiedINtNtNtB2y_5slice4iter4IterhEEE0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2y_6option8IntoIterINtB1E_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s0_00EINtNtB2u_6copied6CopiedINtNtNtB2y_5slice4iter4IterhEEE0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters3map3MapINtNtNtB1I_5slice4iter4IterNtNtNtB9_4trie6nibble6NibbleENCNCNCNCINvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service24execute_block_and_insertRINtNtCsdZExvAaxgia_5alloc3vec3VechEIB35_B5s_EE0s3_00s0_0EE0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters3map3MapINtNtNtB9_4trie6nibble14BytesToNibblesINtNtB2H_6copied6CopiedINtNtNtB1I_5slice4iter4IterhEEENvYhINtNtB1I_7convert4FromNtB37_6NibbleE4fromEE0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2y_6option8IntoIterINtB1E_3VechEENCNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s7_00EINtNtB2u_6copied6CopiedINtNtNtB2y_5slice4iter4IterhEEE0B42_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters3map3MapINtNtNtB9_4trie6nibble14BytesToNibblesINtNtB2H_6copied6CopiedINtNtNtB1I_5slice4iter4IterhEEENvYhINtNtB1I_7convert4FromNtB37_6NibbleE4fromEE0CsiUjFBJteJ7x_17smoldot_full_node Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters6copied6CopiedINtNtNtB1I_5slice4iter4IterhEEE0CsiUjFBJteJ7x_17smoldot_full_node Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2y_6option8IntoIterINtB1E_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB41_14SyncBackground12author_block0s_00EINtNtB2u_6copied6CopiedINtNtNtB2y_5slice4iter4IterhEEE0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2y_6option8IntoIterINtB1E_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s0_00EINtNtB2u_6copied6CopiedINtNtNtB2y_5slice4iter4IterhEEE0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters3map3MapINtNtNtB1I_5slice4iter4IterNtNtNtB9_4trie6nibble6NibbleENCNCNCNCINvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service24execute_block_and_insertRINtNtCsdZExvAaxgia_5alloc3vec3VechEIB35_B5s_EE0s3_00s0_0EE0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters3map3MapINtNtNtB9_4trie6nibble14BytesToNibblesINtNtB2H_6copied6CopiedINtNtNtB1I_5slice4iter4IterhEEENvYhINtNtB1I_7convert4FromNtB37_6NibbleE4fromEE0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2y_6option8IntoIterINtB1E_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB41_14SyncBackground12author_block0s_00EINtNtB2u_6copied6CopiedINtNtNtB2y_5slice4iter4IterhEEE0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2y_6option8IntoIterINtB1E_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s0_00EINtNtB2u_6copied6CopiedINtNtNtB2y_5slice4iter4IterhEEE0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters3map3MapINtNtNtB1I_5slice4iter4IterNtNtNtB9_4trie6nibble6NibbleENCNCNCNCINvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service24execute_block_and_insertRINtNtCsdZExvAaxgia_5alloc3vec3VechEIB35_B5s_EE0s3_00s0_0EE0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters3map3MapINtNtNtB9_4trie6nibble14BytesToNibblesINtNtB2H_6copied6CopiedINtNtNtB1I_5slice4iter4IterhEEENvYhINtNtB1I_7convert4FromNtB37_6NibbleE4fromEE0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1B_B1A_EINtNtNtB1H_8adapters3map3MapINtNtNtBb_4trie6nibble14BytesToNibblesINtNtB2I_6copied6CopiedINtNtNtB1J_5slice4iter4IterhEEENvYhINtNtB1J_7convert4FromNtB38_6NibbleE4fromEE00Bb_ Unexecuted instantiation: _RNCNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1B_B1A_EINtNtNtB1J_5array4iter8IntoIterhKj0_EE00Bb_ Unexecuted instantiation: _RNCNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1B_B1A_EINtNtNtB1J_5array4iter8IntoIterhKj2_EE00Bb_ Unexecuted instantiation: _RNCNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1B_B1A_EINtNtNtB1J_5array4iter8IntoIterhKj3_EE00Bb_ Unexecuted instantiation: _RNCNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1B_B1A_EINtNtNtB1J_5array4iter8IntoIterhKj4_EE00Bb_ Unexecuted instantiation: _RNCNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1B_B1A_EINtNtNtB1J_5array4iter8IntoIterhKj5_EE00Bb_ Unexecuted instantiation: _RNCNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1C_B1B_EINtNtNtB1I_8adapters3map3MapINtNtNtBb_4trie6nibble14BytesToNibblesINtNtB2J_6copied6CopiedINtNtNtB1K_5slice4iter4IterhEEENvYhINtNtB1K_7convert4FromNtB39_6NibbleE4fromEE00Bb_ Unexecuted instantiation: _RNCNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2A_6option8IntoIterINtB1G_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB43_14SyncBackground12author_block0s_00EINtNtB2w_6copied6CopiedINtNtNtB2A_5slice4iter4IterhEEE00CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2A_6option8IntoIterINtB1G_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s0_00EINtNtB2w_6copied6CopiedINtNtNtB2A_5slice4iter4IterhEEE00CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1C_B1B_EINtNtNtB1I_8adapters3map3MapINtNtNtB1K_5slice4iter4IterNtNtNtBb_4trie6nibble6NibbleENCNCNCNCINvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service24execute_block_and_insertRINtNtCsdZExvAaxgia_5alloc3vec3VechEIB37_B5u_EE0s3_00s0_0EE00CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1C_B1B_EINtNtNtB1I_8adapters3map3MapINtNtNtBb_4trie6nibble14BytesToNibblesINtNtB2J_6copied6CopiedINtNtNtB1K_5slice4iter4IterhEEENvYhINtNtB1K_7convert4FromNtB39_6NibbleE4fromEE00CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2A_6option8IntoIterINtB1G_3VechEENCNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s7_00EINtNtB2w_6copied6CopiedINtNtNtB2A_5slice4iter4IterhEEE00B44_ Unexecuted instantiation: _RNCNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1C_B1B_EINtNtNtB1I_8adapters3map3MapINtNtNtBb_4trie6nibble14BytesToNibblesINtNtB2J_6copied6CopiedINtNtNtB1K_5slice4iter4IterhEEENvYhINtNtB1K_7convert4FromNtB39_6NibbleE4fromEE00CsiUjFBJteJ7x_17smoldot_full_node Unexecuted instantiation: _RNCNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1C_B1B_EINtNtNtB1I_8adapters6copied6CopiedINtNtNtB1K_5slice4iter4IterhEEE00CsiUjFBJteJ7x_17smoldot_full_node Unexecuted instantiation: _RNCNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2A_6option8IntoIterINtB1G_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB43_14SyncBackground12author_block0s_00EINtNtB2w_6copied6CopiedINtNtNtB2A_5slice4iter4IterhEEE00CscDgN54JpMGG_6author Unexecuted instantiation: _RNCNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2A_6option8IntoIterINtB1G_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s0_00EINtNtB2w_6copied6CopiedINtNtNtB2A_5slice4iter4IterhEEE00CscDgN54JpMGG_6author Unexecuted instantiation: _RNCNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1C_B1B_EINtNtNtB1I_8adapters3map3MapINtNtNtB1K_5slice4iter4IterNtNtNtBb_4trie6nibble6NibbleENCNCNCNCINvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service24execute_block_and_insertRINtNtCsdZExvAaxgia_5alloc3vec3VechEIB37_B5u_EE0s3_00s0_0EE00CscDgN54JpMGG_6author Unexecuted instantiation: _RNCNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1C_B1B_EINtNtNtB1I_8adapters3map3MapINtNtNtBb_4trie6nibble14BytesToNibblesINtNtB2J_6copied6CopiedINtNtNtB1K_5slice4iter4IterhEEENvYhINtNtB1K_7convert4FromNtB39_6NibbleE4fromEE00CscDgN54JpMGG_6author Unexecuted instantiation: _RNCNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2A_6option8IntoIterINtB1G_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB43_14SyncBackground12author_block0s_00EINtNtB2w_6copied6CopiedINtNtNtB2A_5slice4iter4IterhEEE00CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2A_6option8IntoIterINtB1G_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s0_00EINtNtB2w_6copied6CopiedINtNtNtB2A_5slice4iter4IterhEEE00CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1C_B1B_EINtNtNtB1I_8adapters3map3MapINtNtNtB1K_5slice4iter4IterNtNtNtBb_4trie6nibble6NibbleENCNCNCNCINvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service24execute_block_and_insertRINtNtCsdZExvAaxgia_5alloc3vec3VechEIB37_B5u_EE0s3_00s0_0EE00CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1C_B1B_EINtNtNtB1I_8adapters3map3MapINtNtNtBb_4trie6nibble14BytesToNibblesINtNtB2J_6copied6CopiedINtNtNtB1K_5slice4iter4IterhEEENvYhINtNtB1K_7convert4FromNtB39_6NibbleE4fromEE00CsibGXYHQB8Ea_25json_rpc_general_requests |
797 | 4.19M | .chain(key_nibbles.inspect(|n| assert!(*n < 16))) _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1z_B1y_EINtNtNtB1F_8adapters3map3MapINtNtNtB9_4trie6nibble14BytesToNibblesINtNtB2G_6copied6CopiedINtNtNtB1H_5slice4iter4IterhEEENvYhINtNtB1H_7convert4FromNtB36_6NibbleE4fromEEs_0B9_ Line | Count | Source | 797 | 4.19M | .chain(key_nibbles.inspect(|n| assert!(*n < 16))) |
Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1z_B1y_EINtNtNtB1H_5array4iter8IntoIterhKj0_EEs_0B9_ _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1z_B1y_EINtNtNtB1H_5array4iter8IntoIterhKj2_EEs_0B9_ Line | Count | Source | 797 | 6 | .chain(key_nibbles.inspect(|n| assert!(*n < 16))) |
_RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1z_B1y_EINtNtNtB1H_5array4iter8IntoIterhKj3_EEs_0B9_ Line | Count | Source | 797 | 9 | .chain(key_nibbles.inspect(|n| assert!(*n < 16))) |
_RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1z_B1y_EINtNtNtB1H_5array4iter8IntoIterhKj4_EEs_0B9_ Line | Count | Source | 797 | 12 | .chain(key_nibbles.inspect(|n| assert!(*n < 16))) |
_RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1z_B1y_EINtNtNtB1H_5array4iter8IntoIterhKj5_EEs_0B9_ Line | Count | Source | 797 | 15 | .chain(key_nibbles.inspect(|n| assert!(*n < 16))) |
_RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters3map3MapINtNtNtB9_4trie6nibble14BytesToNibblesINtNtB2H_6copied6CopiedINtNtNtB1I_5slice4iter4IterhEEENvYhINtNtB1I_7convert4FromNtB37_6NibbleE4fromEEs_0B9_ Line | Count | Source | 797 | 2.81k | .chain(key_nibbles.inspect(|n| assert!(*n < 16))) |
Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2y_6option8IntoIterINtB1E_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB41_14SyncBackground12author_block0s_00EINtNtB2u_6copied6CopiedINtNtNtB2y_5slice4iter4IterhEEEs_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2y_6option8IntoIterINtB1E_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s0_00EINtNtB2u_6copied6CopiedINtNtNtB2y_5slice4iter4IterhEEEs_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters3map3MapINtNtNtB1I_5slice4iter4IterNtNtNtB9_4trie6nibble6NibbleENCNCNCNCINvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service24execute_block_and_insertRINtNtCsdZExvAaxgia_5alloc3vec3VechEIB35_B5s_EE0s3_00s0_0EEs_0CsiLzmwikkc22_14json_rpc_basic _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters3map3MapINtNtNtB9_4trie6nibble14BytesToNibblesINtNtB2H_6copied6CopiedINtNtNtB1I_5slice4iter4IterhEEENvYhINtNtB1I_7convert4FromNtB37_6NibbleE4fromEEs_0CsiLzmwikkc22_14json_rpc_basic Line | Count | Source | 797 | 60 | .chain(key_nibbles.inspect(|n| assert!(*n < 16))) |
Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2y_6option8IntoIterINtB1E_3VechEENCNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s7_00EINtNtB2u_6copied6CopiedINtNtNtB2y_5slice4iter4IterhEEEs_0B42_ _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters3map3MapINtNtNtB9_4trie6nibble14BytesToNibblesINtNtB2H_6copied6CopiedINtNtNtB1I_5slice4iter4IterhEEENvYhINtNtB1I_7convert4FromNtB37_6NibbleE4fromEEs_0CsiUjFBJteJ7x_17smoldot_full_node Line | Count | Source | 797 | 60 | .chain(key_nibbles.inspect(|n| assert!(*n < 16))) |
Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters6copied6CopiedINtNtNtB1I_5slice4iter4IterhEEEs_0CsiUjFBJteJ7x_17smoldot_full_node Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2y_6option8IntoIterINtB1E_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB41_14SyncBackground12author_block0s_00EINtNtB2u_6copied6CopiedINtNtNtB2y_5slice4iter4IterhEEEs_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2y_6option8IntoIterINtB1E_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s0_00EINtNtB2u_6copied6CopiedINtNtNtB2y_5slice4iter4IterhEEEs_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters3map3MapINtNtNtB1I_5slice4iter4IterNtNtNtB9_4trie6nibble6NibbleENCNCNCNCINvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service24execute_block_and_insertRINtNtCsdZExvAaxgia_5alloc3vec3VechEIB35_B5s_EE0s3_00s0_0EEs_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters3map3MapINtNtNtB9_4trie6nibble14BytesToNibblesINtNtB2H_6copied6CopiedINtNtNtB1I_5slice4iter4IterhEEENvYhINtNtB1I_7convert4FromNtB37_6NibbleE4fromEEs_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2y_6option8IntoIterINtB1E_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB41_14SyncBackground12author_block0s_00EINtNtB2u_6copied6CopiedINtNtNtB2y_5slice4iter4IterhEEEs_0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2y_6option8IntoIterINtB1E_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s0_00EINtNtB2u_6copied6CopiedINtNtNtB2y_5slice4iter4IterhEEEs_0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters3map3MapINtNtNtB1I_5slice4iter4IterNtNtNtB9_4trie6nibble6NibbleENCNCNCNCINvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service24execute_block_and_insertRINtNtCsdZExvAaxgia_5alloc3vec3VechEIB35_B5s_EE0s3_00s0_0EEs_0CsibGXYHQB8Ea_25json_rpc_general_requests _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters3map3MapINtNtNtB9_4trie6nibble14BytesToNibblesINtNtB2H_6copied6CopiedINtNtNtB1I_5slice4iter4IterhEEENvYhINtNtB1I_7convert4FromNtB37_6NibbleE4fromEEs_0CsibGXYHQB8Ea_25json_rpc_general_requests Line | Count | Source | 797 | 570 | .chain(key_nibbles.inspect(|n| assert!(*n < 16))) |
|
798 | 1.04M | .collect::<Vec<_>>(); |
799 | 1.04M | |
800 | 1.04M | let connection = self.database.lock(); |
801 | | |
802 | | // TODO: could be optimized by having a different request when `parent_tries_paths_nibbles` is empty and when it isn't |
803 | | // TODO: trie_root_ref system untested |
804 | | // TODO: infinite loop if there's a loop in the trie; detect this |
805 | 1.04M | let mut statement = connection |
806 | 1.04M | .prepare_cached( |
807 | 1.04M | r#" |
808 | 1.04M | WITH RECURSIVE |
809 | 1.04M | -- At the end of the recursive statement, `node_with_key` must always contain |
810 | 1.04M | -- one and exactly one item where `search_remain` is either empty or null. Empty |
811 | 1.04M | -- indicates that we have found a match, while null means that the search has |
812 | 1.04M | -- been interrupted due to a storage entry not being in the database. If |
813 | 1.04M | -- `search_remain` is empty, then `node_hash` is either a hash in case of a match |
814 | 1.04M | -- or null in case there is no entry with the requested key. If `search_remain` |
815 | 1.04M | -- is null, then `node_hash` is irrelevant. |
816 | 1.04M | -- |
817 | 1.04M | -- In order to properly handle the situation where the key is empty, the initial |
818 | 1.04M | -- request of the recursive table building must check whether the partial key of |
819 | 1.04M | -- the root matches. In other words, all the entries of `node_with_key` (where |
820 | 1.04M | -- `node_hash` is non-null) contain entries that are known to be in the database |
821 | 1.04M | -- and after the partial key has already been verified to be correct. |
822 | 1.04M | node_with_key(node_hash, search_remain) AS ( |
823 | 1.04M | SELECT |
824 | 1.04M | IIF(COALESCE(SUBSTR(:key, 1, LENGTH(trie_node.partial_key)), X'') = trie_node.partial_key, trie_node.hash, NULL), |
825 | 1.04M | IIF(trie_node.partial_key IS NULL, NULL, COALESCE(SUBSTR(:key, 1 + LENGTH(trie_node.partial_key)), X'')) |
826 | 1.04M | FROM blocks |
827 | 1.04M | LEFT JOIN trie_node ON blocks.state_trie_root_hash = trie_node.hash |
828 | 1.04M | WHERE blocks.hash = :block_hash |
829 | 1.04M | UNION ALL |
830 | 1.04M | SELECT |
831 | 1.04M | CASE |
832 | 1.04M | WHEN HEX(SUBSTR(node_with_key.search_remain, 1, 1)) = '10' THEN trie_node_storage.trie_root_ref |
833 | 1.04M | WHEN SUBSTR(node_with_key.search_remain, 2, LENGTH(trie_node.partial_key)) = trie_node.partial_key THEN trie_node_child.child_hash |
834 | 1.04M | ELSE NULL END, |
835 | 1.04M | CASE |
836 | 1.04M | WHEN HEX(SUBSTR(node_with_key.search_remain, 1, 1)) = '10' THEN SUBSTR(node_with_key.search_remain, 1) |
837 | 1.04M | WHEN trie_node_child.child_hash IS NULL THEN X'' |
838 | 1.04M | WHEN trie_node.partial_key IS NULL THEN NULL |
839 | 1.04M | WHEN SUBSTR(node_with_key.search_remain, 2, LENGTH(trie_node.partial_key)) = trie_node.partial_key THEN SUBSTR(node_with_key.search_remain, 2 + LENGTH(trie_node.partial_key)) |
840 | 1.04M | ELSE X'' END |
841 | 1.04M | FROM node_with_key |
842 | 1.04M | LEFT JOIN trie_node_child |
843 | 1.04M | ON node_with_key.node_hash = trie_node_child.hash |
844 | 1.04M | AND SUBSTR(node_with_key.search_remain, 1, 1) = trie_node_child.child_num |
845 | 1.04M | LEFT JOIN trie_node |
846 | 1.04M | ON trie_node.hash = trie_node_child.child_hash |
847 | 1.04M | LEFT JOIN trie_node_storage |
848 | 1.04M | ON node_with_key.node_hash = trie_node_storage.node_hash |
849 | 1.04M | WHERE LENGTH(node_with_key.search_remain) >= 1 |
850 | 1.04M | ) |
851 | 1.04M | SELECT COUNT(blocks.hash) >= 1, node_with_key.search_remain IS NULL, COALESCE(trie_node_storage.value, trie_node_storage.trie_root_ref), trie_node_storage.trie_entry_version |
852 | 1.04M | FROM blocks |
853 | 1.04M | JOIN node_with_key ON LENGTH(node_with_key.search_remain) = 0 OR node_with_key.search_remain IS NULL |
854 | 1.04M | LEFT JOIN trie_node_storage ON node_with_key.node_hash = trie_node_storage.node_hash AND node_with_key.search_remain IS NOT NULL |
855 | 1.04M | WHERE blocks.hash = :block_hash; |
856 | 1.04M | "#) |
857 | 1.04M | .map_err(|err| { |
858 | 0 | StorageAccessError::Corrupted(CorruptedError::Internal( |
859 | 0 | InternalError(err), |
860 | 0 | )) |
861 | 1.04M | })?0 ; Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1z_B1y_EINtNtNtB1F_8adapters3map3MapINtNtNtB9_4trie6nibble14BytesToNibblesINtNtB2G_6copied6CopiedINtNtNtB1H_5slice4iter4IterhEEENvYhINtNtB1H_7convert4FromNtB36_6NibbleE4fromEEs0_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1z_B1y_EINtNtNtB1H_5array4iter8IntoIterhKj0_EEs0_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1z_B1y_EINtNtNtB1H_5array4iter8IntoIterhKj2_EEs0_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1z_B1y_EINtNtNtB1H_5array4iter8IntoIterhKj3_EEs0_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1z_B1y_EINtNtNtB1H_5array4iter8IntoIterhKj4_EEs0_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1z_B1y_EINtNtNtB1H_5array4iter8IntoIterhKj5_EEs0_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters3map3MapINtNtNtB9_4trie6nibble14BytesToNibblesINtNtB2H_6copied6CopiedINtNtNtB1I_5slice4iter4IterhEEENvYhINtNtB1I_7convert4FromNtB37_6NibbleE4fromEEs0_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2y_6option8IntoIterINtB1E_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB41_14SyncBackground12author_block0s_00EINtNtB2u_6copied6CopiedINtNtNtB2y_5slice4iter4IterhEEEs0_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2y_6option8IntoIterINtB1E_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s0_00EINtNtB2u_6copied6CopiedINtNtNtB2y_5slice4iter4IterhEEEs0_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters3map3MapINtNtNtB1I_5slice4iter4IterNtNtNtB9_4trie6nibble6NibbleENCNCNCNCINvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service24execute_block_and_insertRINtNtCsdZExvAaxgia_5alloc3vec3VechEIB35_B5s_EE0s3_00s0_0EEs0_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters3map3MapINtNtNtB9_4trie6nibble14BytesToNibblesINtNtB2H_6copied6CopiedINtNtNtB1I_5slice4iter4IterhEEENvYhINtNtB1I_7convert4FromNtB37_6NibbleE4fromEEs0_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2y_6option8IntoIterINtB1E_3VechEENCNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s7_00EINtNtB2u_6copied6CopiedINtNtNtB2y_5slice4iter4IterhEEEs0_0B42_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters3map3MapINtNtNtB9_4trie6nibble14BytesToNibblesINtNtB2H_6copied6CopiedINtNtNtB1I_5slice4iter4IterhEEENvYhINtNtB1I_7convert4FromNtB37_6NibbleE4fromEEs0_0CsiUjFBJteJ7x_17smoldot_full_node Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters6copied6CopiedINtNtNtB1I_5slice4iter4IterhEEEs0_0CsiUjFBJteJ7x_17smoldot_full_node Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2y_6option8IntoIterINtB1E_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB41_14SyncBackground12author_block0s_00EINtNtB2u_6copied6CopiedINtNtNtB2y_5slice4iter4IterhEEEs0_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2y_6option8IntoIterINtB1E_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s0_00EINtNtB2u_6copied6CopiedINtNtNtB2y_5slice4iter4IterhEEEs0_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters3map3MapINtNtNtB1I_5slice4iter4IterNtNtNtB9_4trie6nibble6NibbleENCNCNCNCINvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service24execute_block_and_insertRINtNtCsdZExvAaxgia_5alloc3vec3VechEIB35_B5s_EE0s3_00s0_0EEs0_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters3map3MapINtNtNtB9_4trie6nibble14BytesToNibblesINtNtB2H_6copied6CopiedINtNtNtB1I_5slice4iter4IterhEEENvYhINtNtB1I_7convert4FromNtB37_6NibbleE4fromEEs0_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2y_6option8IntoIterINtB1E_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB41_14SyncBackground12author_block0s_00EINtNtB2u_6copied6CopiedINtNtNtB2y_5slice4iter4IterhEEEs0_0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2y_6option8IntoIterINtB1E_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s0_00EINtNtB2u_6copied6CopiedINtNtNtB2y_5slice4iter4IterhEEEs0_0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters3map3MapINtNtNtB1I_5slice4iter4IterNtNtNtB9_4trie6nibble6NibbleENCNCNCNCINvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service24execute_block_and_insertRINtNtCsdZExvAaxgia_5alloc3vec3VechEIB35_B5s_EE0s3_00s0_0EEs0_0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters3map3MapINtNtNtB9_4trie6nibble14BytesToNibblesINtNtB2H_6copied6CopiedINtNtNtB1I_5slice4iter4IterhEEENvYhINtNtB1I_7convert4FromNtB37_6NibbleE4fromEEs0_0CsibGXYHQB8Ea_25json_rpc_general_requests |
862 | | |
863 | | // In order to debug the SQL query above (for example in case of a failing test), |
864 | | // uncomment this block: |
865 | | // |
866 | | /*println!("{:?}", { |
867 | | let mut statement = connection |
868 | | .prepare_cached( |
869 | | r#" |
870 | | WITH RECURSIVE |
871 | | copy-paste the definition of node_with_key here |
872 | | |
873 | | SELECT * FROM node_with_key"#).unwrap(); |
874 | | statement |
875 | | .query_map( |
876 | | rusqlite::named_params! { |
877 | | ":block_hash": &block_hash[..], |
878 | | ":key": key_vectored, |
879 | | }, |
880 | | |row| { |
881 | | let node_hash = row.get::<_, Option<Vec<u8>>>(0)?.map(hex::encode); |
882 | | let search_remain = row.get::<_, Option<Vec<u8>>>(1)?; |
883 | | Ok((node_hash, search_remain)) |
884 | | }, |
885 | | ) |
886 | | .unwrap() |
887 | | .collect::<Vec<_>>() |
888 | | });*/ |
889 | | |
890 | 1.04M | let (has_block, incomplete_storage, value, trie_entry_version) = statement |
891 | 1.04M | .query_row( |
892 | 1.04M | rusqlite::named_params! { |
893 | 1.04M | ":block_hash": &block_hash[..], |
894 | 1.04M | ":key": key_vectored, |
895 | 1.04M | }, |
896 | 1.04M | |row| { |
897 | 1.04M | let has_block = row.get::<_, i64>(0)?0 != 0; |
898 | 1.04M | let incomplete_storage = row.get::<_, i64>(1)?0 != 0; |
899 | 1.04M | let value = row.get::<_, Option<Vec<u8>>>(2)?0 ; |
900 | 1.04M | let trie_entry_version = row.get::<_, Option<i64>>(3)?0 ; |
901 | 1.04M | Ok((has_block, incomplete_storage, value, trie_entry_version)) |
902 | 1.04M | }, _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1z_B1y_EINtNtNtB1F_8adapters3map3MapINtNtNtB9_4trie6nibble14BytesToNibblesINtNtB2G_6copied6CopiedINtNtNtB1H_5slice4iter4IterhEEENvYhINtNtB1H_7convert4FromNtB36_6NibbleE4fromEEs1_0B9_ Line | Count | Source | 896 | 1.04M | |row| { | 897 | 1.04M | let has_block = row.get::<_, i64>(0)?0 != 0; | 898 | 1.04M | let incomplete_storage = row.get::<_, i64>(1)?0 != 0; | 899 | 1.04M | let value = row.get::<_, Option<Vec<u8>>>(2)?0 ; | 900 | 1.04M | let trie_entry_version = row.get::<_, Option<i64>>(3)?0 ; | 901 | 1.04M | Ok((has_block, incomplete_storage, value, trie_entry_version)) | 902 | 1.04M | }, |
_RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1z_B1y_EINtNtNtB1H_5array4iter8IntoIterhKj0_EEs1_0B9_ Line | Count | Source | 896 | 3 | |row| { | 897 | 3 | let has_block = row.get::<_, i64>(0)?0 != 0; | 898 | 3 | let incomplete_storage = row.get::<_, i64>(1)?0 != 0; | 899 | 3 | let value = row.get::<_, Option<Vec<u8>>>(2)?0 ; | 900 | 3 | let trie_entry_version = row.get::<_, Option<i64>>(3)?0 ; | 901 | 3 | Ok((has_block, incomplete_storage, value, trie_entry_version)) | 902 | 3 | }, |
_RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1z_B1y_EINtNtNtB1H_5array4iter8IntoIterhKj2_EEs1_0B9_ Line | Count | Source | 896 | 3 | |row| { | 897 | 3 | let has_block = row.get::<_, i64>(0)?0 != 0; | 898 | 3 | let incomplete_storage = row.get::<_, i64>(1)?0 != 0; | 899 | 3 | let value = row.get::<_, Option<Vec<u8>>>(2)?0 ; | 900 | 3 | let trie_entry_version = row.get::<_, Option<i64>>(3)?0 ; | 901 | 3 | Ok((has_block, incomplete_storage, value, trie_entry_version)) | 902 | 3 | }, |
_RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1z_B1y_EINtNtNtB1H_5array4iter8IntoIterhKj3_EEs1_0B9_ Line | Count | Source | 896 | 3 | |row| { | 897 | 3 | let has_block = row.get::<_, i64>(0)?0 != 0; | 898 | 3 | let incomplete_storage = row.get::<_, i64>(1)?0 != 0; | 899 | 3 | let value = row.get::<_, Option<Vec<u8>>>(2)?0 ; | 900 | 3 | let trie_entry_version = row.get::<_, Option<i64>>(3)?0 ; | 901 | 3 | Ok((has_block, incomplete_storage, value, trie_entry_version)) | 902 | 3 | }, |
_RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1z_B1y_EINtNtNtB1H_5array4iter8IntoIterhKj4_EEs1_0B9_ Line | Count | Source | 896 | 3 | |row| { | 897 | 3 | let has_block = row.get::<_, i64>(0)?0 != 0; | 898 | 3 | let incomplete_storage = row.get::<_, i64>(1)?0 != 0; | 899 | 3 | let value = row.get::<_, Option<Vec<u8>>>(2)?0 ; | 900 | 3 | let trie_entry_version = row.get::<_, Option<i64>>(3)?0 ; | 901 | 3 | Ok((has_block, incomplete_storage, value, trie_entry_version)) | 902 | 3 | }, |
_RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1z_B1y_EINtNtNtB1H_5array4iter8IntoIterhKj5_EEs1_0B9_ Line | Count | Source | 896 | 3 | |row| { | 897 | 3 | let has_block = row.get::<_, i64>(0)?0 != 0; | 898 | 3 | let incomplete_storage = row.get::<_, i64>(1)?0 != 0; | 899 | 3 | let value = row.get::<_, Option<Vec<u8>>>(2)?0 ; | 900 | 3 | let trie_entry_version = row.get::<_, Option<i64>>(3)?0 ; | 901 | 3 | Ok((has_block, incomplete_storage, value, trie_entry_version)) | 902 | 3 | }, |
_RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters3map3MapINtNtNtB9_4trie6nibble14BytesToNibblesINtNtB2H_6copied6CopiedINtNtNtB1I_5slice4iter4IterhEEENvYhINtNtB1I_7convert4FromNtB37_6NibbleE4fromEEs1_0B9_ Line | Count | Source | 896 | 84 | |row| { | 897 | 84 | let has_block = row.get::<_, i64>(0)?0 != 0; | 898 | 84 | let incomplete_storage = row.get::<_, i64>(1)?0 != 0; | 899 | 84 | let value = row.get::<_, Option<Vec<u8>>>(2)?0 ; | 900 | 84 | let trie_entry_version = row.get::<_, Option<i64>>(3)?0 ; | 901 | 84 | Ok((has_block, incomplete_storage, value, trie_entry_version)) | 902 | 84 | }, |
Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2y_6option8IntoIterINtB1E_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB41_14SyncBackground12author_block0s_00EINtNtB2u_6copied6CopiedINtNtNtB2y_5slice4iter4IterhEEEs1_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2y_6option8IntoIterINtB1E_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s0_00EINtNtB2u_6copied6CopiedINtNtNtB2y_5slice4iter4IterhEEEs1_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters3map3MapINtNtNtB1I_5slice4iter4IterNtNtNtB9_4trie6nibble6NibbleENCNCNCNCINvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service24execute_block_and_insertRINtNtCsdZExvAaxgia_5alloc3vec3VechEIB35_B5s_EE0s3_00s0_0EEs1_0CsiLzmwikkc22_14json_rpc_basic _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters3map3MapINtNtNtB9_4trie6nibble14BytesToNibblesINtNtB2H_6copied6CopiedINtNtNtB1I_5slice4iter4IterhEEENvYhINtNtB1I_7convert4FromNtB37_6NibbleE4fromEEs1_0CsiLzmwikkc22_14json_rpc_basic Line | Count | Source | 896 | 4 | |row| { | 897 | 4 | let has_block = row.get::<_, i64>(0)?0 != 0; | 898 | 4 | let incomplete_storage = row.get::<_, i64>(1)?0 != 0; | 899 | 4 | let value = row.get::<_, Option<Vec<u8>>>(2)?0 ; | 900 | 4 | let trie_entry_version = row.get::<_, Option<i64>>(3)?0 ; | 901 | 4 | Ok((has_block, incomplete_storage, value, trie_entry_version)) | 902 | 4 | }, |
Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2y_6option8IntoIterINtB1E_3VechEENCNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s7_00EINtNtB2u_6copied6CopiedINtNtNtB2y_5slice4iter4IterhEEEs1_0B42_ _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters3map3MapINtNtNtB9_4trie6nibble14BytesToNibblesINtNtB2H_6copied6CopiedINtNtNtB1I_5slice4iter4IterhEEENvYhINtNtB1I_7convert4FromNtB37_6NibbleE4fromEEs1_0CsiUjFBJteJ7x_17smoldot_full_node Line | Count | Source | 896 | 4 | |row| { | 897 | 4 | let has_block = row.get::<_, i64>(0)?0 != 0; | 898 | 4 | let incomplete_storage = row.get::<_, i64>(1)?0 != 0; | 899 | 4 | let value = row.get::<_, Option<Vec<u8>>>(2)?0 ; | 900 | 4 | let trie_entry_version = row.get::<_, Option<i64>>(3)?0 ; | 901 | 4 | Ok((has_block, incomplete_storage, value, trie_entry_version)) | 902 | 4 | }, |
Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters6copied6CopiedINtNtNtB1I_5slice4iter4IterhEEEs1_0CsiUjFBJteJ7x_17smoldot_full_node Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2y_6option8IntoIterINtB1E_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB41_14SyncBackground12author_block0s_00EINtNtB2u_6copied6CopiedINtNtNtB2y_5slice4iter4IterhEEEs1_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2y_6option8IntoIterINtB1E_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s0_00EINtNtB2u_6copied6CopiedINtNtNtB2y_5slice4iter4IterhEEEs1_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters3map3MapINtNtNtB1I_5slice4iter4IterNtNtNtB9_4trie6nibble6NibbleENCNCNCNCINvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service24execute_block_and_insertRINtNtCsdZExvAaxgia_5alloc3vec3VechEIB35_B5s_EE0s3_00s0_0EEs1_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters3map3MapINtNtNtB9_4trie6nibble14BytesToNibblesINtNtB2H_6copied6CopiedINtNtNtB1I_5slice4iter4IterhEEENvYhINtNtB1I_7convert4FromNtB37_6NibbleE4fromEEs1_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2y_6option8IntoIterINtB1E_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB41_14SyncBackground12author_block0s_00EINtNtB2u_6copied6CopiedINtNtNtB2y_5slice4iter4IterhEEEs1_0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2y_6option8IntoIterINtB1E_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s0_00EINtNtB2u_6copied6CopiedINtNtNtB2y_5slice4iter4IterhEEEs1_0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters3map3MapINtNtNtB1I_5slice4iter4IterNtNtNtB9_4trie6nibble6NibbleENCNCNCNCINvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service24execute_block_and_insertRINtNtCsdZExvAaxgia_5alloc3vec3VechEIB35_B5s_EE0s3_00s0_0EEs1_0CsibGXYHQB8Ea_25json_rpc_general_requests _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters3map3MapINtNtNtB9_4trie6nibble14BytesToNibblesINtNtB2H_6copied6CopiedINtNtNtB1I_5slice4iter4IterhEEENvYhINtNtB1I_7convert4FromNtB37_6NibbleE4fromEEs1_0CsibGXYHQB8Ea_25json_rpc_general_requests Line | Count | Source | 896 | 38 | |row| { | 897 | 38 | let has_block = row.get::<_, i64>(0)?0 != 0; | 898 | 38 | let incomplete_storage = row.get::<_, i64>(1)?0 != 0; | 899 | 38 | let value = row.get::<_, Option<Vec<u8>>>(2)?0 ; | 900 | 38 | let trie_entry_version = row.get::<_, Option<i64>>(3)?0 ; | 901 | 38 | Ok((has_block, incomplete_storage, value, trie_entry_version)) | 902 | 38 | }, |
|
903 | 1.04M | ) |
904 | 1.04M | .map_err(|err| { |
905 | 0 | StorageAccessError::Corrupted(CorruptedError::Internal(InternalError(err))) |
906 | 1.04M | })?0 ; Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1z_B1y_EINtNtNtB1F_8adapters3map3MapINtNtNtB9_4trie6nibble14BytesToNibblesINtNtB2G_6copied6CopiedINtNtNtB1H_5slice4iter4IterhEEENvYhINtNtB1H_7convert4FromNtB36_6NibbleE4fromEEs2_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1z_B1y_EINtNtNtB1H_5array4iter8IntoIterhKj0_EEs2_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1z_B1y_EINtNtNtB1H_5array4iter8IntoIterhKj2_EEs2_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1z_B1y_EINtNtNtB1H_5array4iter8IntoIterhKj3_EEs2_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1z_B1y_EINtNtNtB1H_5array4iter8IntoIterhKj4_EEs2_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1z_B1y_EINtNtNtB1H_5array4iter8IntoIterhKj5_EEs2_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters3map3MapINtNtNtB9_4trie6nibble14BytesToNibblesINtNtB2H_6copied6CopiedINtNtNtB1I_5slice4iter4IterhEEENvYhINtNtB1I_7convert4FromNtB37_6NibbleE4fromEEs2_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2y_6option8IntoIterINtB1E_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB41_14SyncBackground12author_block0s_00EINtNtB2u_6copied6CopiedINtNtNtB2y_5slice4iter4IterhEEEs2_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2y_6option8IntoIterINtB1E_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s0_00EINtNtB2u_6copied6CopiedINtNtNtB2y_5slice4iter4IterhEEEs2_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters3map3MapINtNtNtB1I_5slice4iter4IterNtNtNtB9_4trie6nibble6NibbleENCNCNCNCINvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service24execute_block_and_insertRINtNtCsdZExvAaxgia_5alloc3vec3VechEIB35_B5s_EE0s3_00s0_0EEs2_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters3map3MapINtNtNtB9_4trie6nibble14BytesToNibblesINtNtB2H_6copied6CopiedINtNtNtB1I_5slice4iter4IterhEEENvYhINtNtB1I_7convert4FromNtB37_6NibbleE4fromEEs2_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2y_6option8IntoIterINtB1E_3VechEENCNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s7_00EINtNtB2u_6copied6CopiedINtNtNtB2y_5slice4iter4IterhEEEs2_0B42_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters3map3MapINtNtNtB9_4trie6nibble14BytesToNibblesINtNtB2H_6copied6CopiedINtNtNtB1I_5slice4iter4IterhEEENvYhINtNtB1I_7convert4FromNtB37_6NibbleE4fromEEs2_0CsiUjFBJteJ7x_17smoldot_full_node Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters6copied6CopiedINtNtNtB1I_5slice4iter4IterhEEEs2_0CsiUjFBJteJ7x_17smoldot_full_node Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2y_6option8IntoIterINtB1E_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB41_14SyncBackground12author_block0s_00EINtNtB2u_6copied6CopiedINtNtNtB2y_5slice4iter4IterhEEEs2_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2y_6option8IntoIterINtB1E_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s0_00EINtNtB2u_6copied6CopiedINtNtNtB2y_5slice4iter4IterhEEEs2_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters3map3MapINtNtNtB1I_5slice4iter4IterNtNtNtB9_4trie6nibble6NibbleENCNCNCNCINvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service24execute_block_and_insertRINtNtCsdZExvAaxgia_5alloc3vec3VechEIB35_B5s_EE0s3_00s0_0EEs2_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters3map3MapINtNtNtB9_4trie6nibble14BytesToNibblesINtNtB2H_6copied6CopiedINtNtNtB1I_5slice4iter4IterhEEENvYhINtNtB1I_7convert4FromNtB37_6NibbleE4fromEEs2_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2y_6option8IntoIterINtB1E_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB41_14SyncBackground12author_block0s_00EINtNtB2u_6copied6CopiedINtNtNtB2y_5slice4iter4IterhEEEs2_0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2y_6option8IntoIterINtB1E_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s0_00EINtNtB2u_6copied6CopiedINtNtNtB2y_5slice4iter4IterhEEEs2_0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters3map3MapINtNtNtB1I_5slice4iter4IterNtNtNtB9_4trie6nibble6NibbleENCNCNCNCINvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service24execute_block_and_insertRINtNtCsdZExvAaxgia_5alloc3vec3VechEIB35_B5s_EE0s3_00s0_0EEs2_0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters3map3MapINtNtNtB9_4trie6nibble14BytesToNibblesINtNtB2H_6copied6CopiedINtNtNtB1I_5slice4iter4IterhEEENvYhINtNtB1I_7convert4FromNtB37_6NibbleE4fromEEs2_0CsibGXYHQB8Ea_25json_rpc_general_requests |
907 | | |
908 | 1.04M | if !has_block { |
909 | 1 | return Err(StorageAccessError::UnknownBlock); |
910 | 1.04M | } |
911 | 1.04M | |
912 | 1.04M | if incomplete_storage { |
913 | 7 | return Err(StorageAccessError::IncompleteStorage); |
914 | 1.04M | } |
915 | | |
916 | 1.04M | let Some(value209k ) = value else { return Ok(None)838k }; |
917 | | |
918 | 209k | let trie_entry_version = u8::try_from(trie_entry_version.unwrap()) |
919 | 209k | .map_err(|_| CorruptedError::InvalidTrieEntryVersion0 ) Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1z_B1y_EINtNtNtB1F_8adapters3map3MapINtNtNtB9_4trie6nibble14BytesToNibblesINtNtB2G_6copied6CopiedINtNtNtB1H_5slice4iter4IterhEEENvYhINtNtB1H_7convert4FromNtB36_6NibbleE4fromEEs3_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1z_B1y_EINtNtNtB1H_5array4iter8IntoIterhKj0_EEs3_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1z_B1y_EINtNtNtB1H_5array4iter8IntoIterhKj2_EEs3_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1z_B1y_EINtNtNtB1H_5array4iter8IntoIterhKj3_EEs3_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1z_B1y_EINtNtNtB1H_5array4iter8IntoIterhKj4_EEs3_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1z_B1y_EINtNtNtB1H_5array4iter8IntoIterhKj5_EEs3_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters3map3MapINtNtNtB9_4trie6nibble14BytesToNibblesINtNtB2H_6copied6CopiedINtNtNtB1I_5slice4iter4IterhEEENvYhINtNtB1I_7convert4FromNtB37_6NibbleE4fromEEs3_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2y_6option8IntoIterINtB1E_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB41_14SyncBackground12author_block0s_00EINtNtB2u_6copied6CopiedINtNtNtB2y_5slice4iter4IterhEEEs3_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2y_6option8IntoIterINtB1E_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s0_00EINtNtB2u_6copied6CopiedINtNtNtB2y_5slice4iter4IterhEEEs3_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters3map3MapINtNtNtB1I_5slice4iter4IterNtNtNtB9_4trie6nibble6NibbleENCNCNCNCINvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service24execute_block_and_insertRINtNtCsdZExvAaxgia_5alloc3vec3VechEIB35_B5s_EE0s3_00s0_0EEs3_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters3map3MapINtNtNtB9_4trie6nibble14BytesToNibblesINtNtB2H_6copied6CopiedINtNtNtB1I_5slice4iter4IterhEEENvYhINtNtB1I_7convert4FromNtB37_6NibbleE4fromEEs3_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2y_6option8IntoIterINtB1E_3VechEENCNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s7_00EINtNtB2u_6copied6CopiedINtNtNtB2y_5slice4iter4IterhEEEs3_0B42_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters3map3MapINtNtNtB9_4trie6nibble14BytesToNibblesINtNtB2H_6copied6CopiedINtNtNtB1I_5slice4iter4IterhEEENvYhINtNtB1I_7convert4FromNtB37_6NibbleE4fromEEs3_0CsiUjFBJteJ7x_17smoldot_full_node Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters6copied6CopiedINtNtNtB1I_5slice4iter4IterhEEEs3_0CsiUjFBJteJ7x_17smoldot_full_node Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2y_6option8IntoIterINtB1E_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB41_14SyncBackground12author_block0s_00EINtNtB2u_6copied6CopiedINtNtNtB2y_5slice4iter4IterhEEEs3_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2y_6option8IntoIterINtB1E_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s0_00EINtNtB2u_6copied6CopiedINtNtNtB2y_5slice4iter4IterhEEEs3_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters3map3MapINtNtNtB1I_5slice4iter4IterNtNtNtB9_4trie6nibble6NibbleENCNCNCNCINvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service24execute_block_and_insertRINtNtCsdZExvAaxgia_5alloc3vec3VechEIB35_B5s_EE0s3_00s0_0EEs3_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters3map3MapINtNtNtB9_4trie6nibble14BytesToNibblesINtNtB2H_6copied6CopiedINtNtNtB1I_5slice4iter4IterhEEENvYhINtNtB1I_7convert4FromNtB37_6NibbleE4fromEEs3_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2y_6option8IntoIterINtB1E_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB41_14SyncBackground12author_block0s_00EINtNtB2u_6copied6CopiedINtNtNtB2y_5slice4iter4IterhEEEs3_0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2y_6option8IntoIterINtB1E_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s0_00EINtNtB2u_6copied6CopiedINtNtNtB2y_5slice4iter4IterhEEEs3_0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters3map3MapINtNtNtB1I_5slice4iter4IterNtNtNtB9_4trie6nibble6NibbleENCNCNCNCINvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service24execute_block_and_insertRINtNtCsdZExvAaxgia_5alloc3vec3VechEIB35_B5s_EE0s3_00s0_0EEs3_0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1A_B1z_EINtNtNtB1G_8adapters3map3MapINtNtNtB9_4trie6nibble14BytesToNibblesINtNtB2H_6copied6CopiedINtNtNtB1I_5slice4iter4IterhEEENvYhINtNtB1I_7convert4FromNtB37_6NibbleE4fromEEs3_0CsibGXYHQB8Ea_25json_rpc_general_requests |
920 | 209k | .map_err(StorageAccessError::Corrupted)?0 ; |
921 | 209k | Ok(Some((value, trie_entry_version))) |
922 | 1.04M | } _RINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1x_B1w_EINtNtNtB1D_8adapters3map3MapINtNtNtB7_4trie6nibble14BytesToNibblesINtNtB2E_6copied6CopiedINtNtNtB1F_5slice4iter4IterhEEENvYhINtNtB1F_7convert4FromNtB34_6NibbleE4fromEEB7_ Line | Count | Source | 786 | 1.04M | pub fn block_storage_get( | 787 | 1.04M | &self, | 788 | 1.04M | block_hash: &[u8; 32], | 789 | 1.04M | parent_tries_paths_nibbles: impl Iterator<Item = impl Iterator<Item = u8>>, | 790 | 1.04M | key_nibbles: impl Iterator<Item = u8>, | 791 | 1.04M | ) -> Result<Option<(Vec<u8>, u8)>, StorageAccessError> { | 792 | 1.04M | // Process the iterators at the very beginning and before locking the database, in order | 793 | 1.04M | // to avoid a deadlock in case the `next()` function of one of the iterators accesses | 794 | 1.04M | // the database as well. | 795 | 1.04M | let key_vectored = parent_tries_paths_nibbles | 796 | 1.04M | .flat_map(|t| t.inspect(|n| assert!(*n < 16)).chain(iter::once(0x10))) | 797 | 1.04M | .chain(key_nibbles.inspect(|n| assert!(*n < 16))) | 798 | 1.04M | .collect::<Vec<_>>(); | 799 | 1.04M | | 800 | 1.04M | let connection = self.database.lock(); | 801 | | | 802 | | // TODO: could be optimized by having a different request when `parent_tries_paths_nibbles` is empty and when it isn't | 803 | | // TODO: trie_root_ref system untested | 804 | | // TODO: infinite loop if there's a loop in the trie; detect this | 805 | 1.04M | let mut statement = connection | 806 | 1.04M | .prepare_cached( | 807 | 1.04M | r#" | 808 | 1.04M | WITH RECURSIVE | 809 | 1.04M | -- At the end of the recursive statement, `node_with_key` must always contain | 810 | 1.04M | -- one and exactly one item where `search_remain` is either empty or null. Empty | 811 | 1.04M | -- indicates that we have found a match, while null means that the search has | 812 | 1.04M | -- been interrupted due to a storage entry not being in the database. If | 813 | 1.04M | -- `search_remain` is empty, then `node_hash` is either a hash in case of a match | 814 | 1.04M | -- or null in case there is no entry with the requested key. If `search_remain` | 815 | 1.04M | -- is null, then `node_hash` is irrelevant. | 816 | 1.04M | -- | 817 | 1.04M | -- In order to properly handle the situation where the key is empty, the initial | 818 | 1.04M | -- request of the recursive table building must check whether the partial key of | 819 | 1.04M | -- the root matches. In other words, all the entries of `node_with_key` (where | 820 | 1.04M | -- `node_hash` is non-null) contain entries that are known to be in the database | 821 | 1.04M | -- and after the partial key has already been verified to be correct. | 822 | 1.04M | node_with_key(node_hash, search_remain) AS ( | 823 | 1.04M | SELECT | 824 | 1.04M | IIF(COALESCE(SUBSTR(:key, 1, LENGTH(trie_node.partial_key)), X'') = trie_node.partial_key, trie_node.hash, NULL), | 825 | 1.04M | IIF(trie_node.partial_key IS NULL, NULL, COALESCE(SUBSTR(:key, 1 + LENGTH(trie_node.partial_key)), X'')) | 826 | 1.04M | FROM blocks | 827 | 1.04M | LEFT JOIN trie_node ON blocks.state_trie_root_hash = trie_node.hash | 828 | 1.04M | WHERE blocks.hash = :block_hash | 829 | 1.04M | UNION ALL | 830 | 1.04M | SELECT | 831 | 1.04M | CASE | 832 | 1.04M | WHEN HEX(SUBSTR(node_with_key.search_remain, 1, 1)) = '10' THEN trie_node_storage.trie_root_ref | 833 | 1.04M | WHEN SUBSTR(node_with_key.search_remain, 2, LENGTH(trie_node.partial_key)) = trie_node.partial_key THEN trie_node_child.child_hash | 834 | 1.04M | ELSE NULL END, | 835 | 1.04M | CASE | 836 | 1.04M | WHEN HEX(SUBSTR(node_with_key.search_remain, 1, 1)) = '10' THEN SUBSTR(node_with_key.search_remain, 1) | 837 | 1.04M | WHEN trie_node_child.child_hash IS NULL THEN X'' | 838 | 1.04M | WHEN trie_node.partial_key IS NULL THEN NULL | 839 | 1.04M | WHEN SUBSTR(node_with_key.search_remain, 2, LENGTH(trie_node.partial_key)) = trie_node.partial_key THEN SUBSTR(node_with_key.search_remain, 2 + LENGTH(trie_node.partial_key)) | 840 | 1.04M | ELSE X'' END | 841 | 1.04M | FROM node_with_key | 842 | 1.04M | LEFT JOIN trie_node_child | 843 | 1.04M | ON node_with_key.node_hash = trie_node_child.hash | 844 | 1.04M | AND SUBSTR(node_with_key.search_remain, 1, 1) = trie_node_child.child_num | 845 | 1.04M | LEFT JOIN trie_node | 846 | 1.04M | ON trie_node.hash = trie_node_child.child_hash | 847 | 1.04M | LEFT JOIN trie_node_storage | 848 | 1.04M | ON node_with_key.node_hash = trie_node_storage.node_hash | 849 | 1.04M | WHERE LENGTH(node_with_key.search_remain) >= 1 | 850 | 1.04M | ) | 851 | 1.04M | SELECT COUNT(blocks.hash) >= 1, node_with_key.search_remain IS NULL, COALESCE(trie_node_storage.value, trie_node_storage.trie_root_ref), trie_node_storage.trie_entry_version | 852 | 1.04M | FROM blocks | 853 | 1.04M | JOIN node_with_key ON LENGTH(node_with_key.search_remain) = 0 OR node_with_key.search_remain IS NULL | 854 | 1.04M | LEFT JOIN trie_node_storage ON node_with_key.node_hash = trie_node_storage.node_hash AND node_with_key.search_remain IS NOT NULL | 855 | 1.04M | WHERE blocks.hash = :block_hash; | 856 | 1.04M | "#) | 857 | 1.04M | .map_err(|err| { | 858 | | StorageAccessError::Corrupted(CorruptedError::Internal( | 859 | | InternalError(err), | 860 | | )) | 861 | 1.04M | })?0 ; | 862 | | | 863 | | // In order to debug the SQL query above (for example in case of a failing test), | 864 | | // uncomment this block: | 865 | | // | 866 | | /*println!("{:?}", { | 867 | | let mut statement = connection | 868 | | .prepare_cached( | 869 | | r#" | 870 | | WITH RECURSIVE | 871 | | copy-paste the definition of node_with_key here | 872 | | | 873 | | SELECT * FROM node_with_key"#).unwrap(); | 874 | | statement | 875 | | .query_map( | 876 | | rusqlite::named_params! { | 877 | | ":block_hash": &block_hash[..], | 878 | | ":key": key_vectored, | 879 | | }, | 880 | | |row| { | 881 | | let node_hash = row.get::<_, Option<Vec<u8>>>(0)?.map(hex::encode); | 882 | | let search_remain = row.get::<_, Option<Vec<u8>>>(1)?; | 883 | | Ok((node_hash, search_remain)) | 884 | | }, | 885 | | ) | 886 | | .unwrap() | 887 | | .collect::<Vec<_>>() | 888 | | });*/ | 889 | | | 890 | 1.04M | let (has_block, incomplete_storage, value, trie_entry_version) = statement | 891 | 1.04M | .query_row( | 892 | 1.04M | rusqlite::named_params! { | 893 | 1.04M | ":block_hash": &block_hash[..], | 894 | 1.04M | ":key": key_vectored, | 895 | 1.04M | }, | 896 | 1.04M | |row| { | 897 | | let has_block = row.get::<_, i64>(0)? != 0; | 898 | | let incomplete_storage = row.get::<_, i64>(1)? != 0; | 899 | | let value = row.get::<_, Option<Vec<u8>>>(2)?; | 900 | | let trie_entry_version = row.get::<_, Option<i64>>(3)?; | 901 | | Ok((has_block, incomplete_storage, value, trie_entry_version)) | 902 | 1.04M | }, | 903 | 1.04M | ) | 904 | 1.04M | .map_err(|err| { | 905 | | StorageAccessError::Corrupted(CorruptedError::Internal(InternalError(err))) | 906 | 1.04M | })?0 ; | 907 | | | 908 | 1.04M | if !has_block { | 909 | 0 | return Err(StorageAccessError::UnknownBlock); | 910 | 1.04M | } | 911 | 1.04M | | 912 | 1.04M | if incomplete_storage { | 913 | 0 | return Err(StorageAccessError::IncompleteStorage); | 914 | 1.04M | } | 915 | | | 916 | 1.04M | let Some(value209k ) = value else { return Ok(None)838k }; | 917 | | | 918 | 209k | let trie_entry_version = u8::try_from(trie_entry_version.unwrap()) | 919 | 209k | .map_err(|_| CorruptedError::InvalidTrieEntryVersion) | 920 | 209k | .map_err(StorageAccessError::Corrupted)?0 ; | 921 | 209k | Ok(Some((value, trie_entry_version))) | 922 | 1.04M | } |
_RINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1x_B1w_EINtNtNtB1F_5array4iter8IntoIterhKj0_EEB7_ Line | Count | Source | 786 | 3 | pub fn block_storage_get( | 787 | 3 | &self, | 788 | 3 | block_hash: &[u8; 32], | 789 | 3 | parent_tries_paths_nibbles: impl Iterator<Item = impl Iterator<Item = u8>>, | 790 | 3 | key_nibbles: impl Iterator<Item = u8>, | 791 | 3 | ) -> Result<Option<(Vec<u8>, u8)>, StorageAccessError> { | 792 | 3 | // Process the iterators at the very beginning and before locking the database, in order | 793 | 3 | // to avoid a deadlock in case the `next()` function of one of the iterators accesses | 794 | 3 | // the database as well. | 795 | 3 | let key_vectored = parent_tries_paths_nibbles | 796 | 3 | .flat_map(|t| t.inspect(|n| assert!(*n < 16)).chain(iter::once(0x10))) | 797 | 3 | .chain(key_nibbles.inspect(|n| assert!(*n < 16))) | 798 | 3 | .collect::<Vec<_>>(); | 799 | 3 | | 800 | 3 | let connection = self.database.lock(); | 801 | | | 802 | | // TODO: could be optimized by having a different request when `parent_tries_paths_nibbles` is empty and when it isn't | 803 | | // TODO: trie_root_ref system untested | 804 | | // TODO: infinite loop if there's a loop in the trie; detect this | 805 | 3 | let mut statement = connection | 806 | 3 | .prepare_cached( | 807 | 3 | r#" | 808 | 3 | WITH RECURSIVE | 809 | 3 | -- At the end of the recursive statement, `node_with_key` must always contain | 810 | 3 | -- one and exactly one item where `search_remain` is either empty or null. Empty | 811 | 3 | -- indicates that we have found a match, while null means that the search has | 812 | 3 | -- been interrupted due to a storage entry not being in the database. If | 813 | 3 | -- `search_remain` is empty, then `node_hash` is either a hash in case of a match | 814 | 3 | -- or null in case there is no entry with the requested key. If `search_remain` | 815 | 3 | -- is null, then `node_hash` is irrelevant. | 816 | 3 | -- | 817 | 3 | -- In order to properly handle the situation where the key is empty, the initial | 818 | 3 | -- request of the recursive table building must check whether the partial key of | 819 | 3 | -- the root matches. In other words, all the entries of `node_with_key` (where | 820 | 3 | -- `node_hash` is non-null) contain entries that are known to be in the database | 821 | 3 | -- and after the partial key has already been verified to be correct. | 822 | 3 | node_with_key(node_hash, search_remain) AS ( | 823 | 3 | SELECT | 824 | 3 | IIF(COALESCE(SUBSTR(:key, 1, LENGTH(trie_node.partial_key)), X'') = trie_node.partial_key, trie_node.hash, NULL), | 825 | 3 | IIF(trie_node.partial_key IS NULL, NULL, COALESCE(SUBSTR(:key, 1 + LENGTH(trie_node.partial_key)), X'')) | 826 | 3 | FROM blocks | 827 | 3 | LEFT JOIN trie_node ON blocks.state_trie_root_hash = trie_node.hash | 828 | 3 | WHERE blocks.hash = :block_hash | 829 | 3 | UNION ALL | 830 | 3 | SELECT | 831 | 3 | CASE | 832 | 3 | WHEN HEX(SUBSTR(node_with_key.search_remain, 1, 1)) = '10' THEN trie_node_storage.trie_root_ref | 833 | 3 | WHEN SUBSTR(node_with_key.search_remain, 2, LENGTH(trie_node.partial_key)) = trie_node.partial_key THEN trie_node_child.child_hash | 834 | 3 | ELSE NULL END, | 835 | 3 | CASE | 836 | 3 | WHEN HEX(SUBSTR(node_with_key.search_remain, 1, 1)) = '10' THEN SUBSTR(node_with_key.search_remain, 1) | 837 | 3 | WHEN trie_node_child.child_hash IS NULL THEN X'' | 838 | 3 | WHEN trie_node.partial_key IS NULL THEN NULL | 839 | 3 | WHEN SUBSTR(node_with_key.search_remain, 2, LENGTH(trie_node.partial_key)) = trie_node.partial_key THEN SUBSTR(node_with_key.search_remain, 2 + LENGTH(trie_node.partial_key)) | 840 | 3 | ELSE X'' END | 841 | 3 | FROM node_with_key | 842 | 3 | LEFT JOIN trie_node_child | 843 | 3 | ON node_with_key.node_hash = trie_node_child.hash | 844 | 3 | AND SUBSTR(node_with_key.search_remain, 1, 1) = trie_node_child.child_num | 845 | 3 | LEFT JOIN trie_node | 846 | 3 | ON trie_node.hash = trie_node_child.child_hash | 847 | 3 | LEFT JOIN trie_node_storage | 848 | 3 | ON node_with_key.node_hash = trie_node_storage.node_hash | 849 | 3 | WHERE LENGTH(node_with_key.search_remain) >= 1 | 850 | 3 | ) | 851 | 3 | SELECT COUNT(blocks.hash) >= 1, node_with_key.search_remain IS NULL, COALESCE(trie_node_storage.value, trie_node_storage.trie_root_ref), trie_node_storage.trie_entry_version | 852 | 3 | FROM blocks | 853 | 3 | JOIN node_with_key ON LENGTH(node_with_key.search_remain) = 0 OR node_with_key.search_remain IS NULL | 854 | 3 | LEFT JOIN trie_node_storage ON node_with_key.node_hash = trie_node_storage.node_hash AND node_with_key.search_remain IS NOT NULL | 855 | 3 | WHERE blocks.hash = :block_hash; | 856 | 3 | "#) | 857 | 3 | .map_err(|err| { | 858 | | StorageAccessError::Corrupted(CorruptedError::Internal( | 859 | | InternalError(err), | 860 | | )) | 861 | 3 | })?0 ; | 862 | | | 863 | | // In order to debug the SQL query above (for example in case of a failing test), | 864 | | // uncomment this block: | 865 | | // | 866 | | /*println!("{:?}", { | 867 | | let mut statement = connection | 868 | | .prepare_cached( | 869 | | r#" | 870 | | WITH RECURSIVE | 871 | | copy-paste the definition of node_with_key here | 872 | | | 873 | | SELECT * FROM node_with_key"#).unwrap(); | 874 | | statement | 875 | | .query_map( | 876 | | rusqlite::named_params! { | 877 | | ":block_hash": &block_hash[..], | 878 | | ":key": key_vectored, | 879 | | }, | 880 | | |row| { | 881 | | let node_hash = row.get::<_, Option<Vec<u8>>>(0)?.map(hex::encode); | 882 | | let search_remain = row.get::<_, Option<Vec<u8>>>(1)?; | 883 | | Ok((node_hash, search_remain)) | 884 | | }, | 885 | | ) | 886 | | .unwrap() | 887 | | .collect::<Vec<_>>() | 888 | | });*/ | 889 | | | 890 | 3 | let (has_block, incomplete_storage, value, trie_entry_version) = statement | 891 | 3 | .query_row( | 892 | 3 | rusqlite::named_params! { | 893 | 3 | ":block_hash": &block_hash[..], | 894 | 3 | ":key": key_vectored, | 895 | 3 | }, | 896 | 3 | |row| { | 897 | | let has_block = row.get::<_, i64>(0)? != 0; | 898 | | let incomplete_storage = row.get::<_, i64>(1)? != 0; | 899 | | let value = row.get::<_, Option<Vec<u8>>>(2)?; | 900 | | let trie_entry_version = row.get::<_, Option<i64>>(3)?; | 901 | | Ok((has_block, incomplete_storage, value, trie_entry_version)) | 902 | 3 | }, | 903 | 3 | ) | 904 | 3 | .map_err(|err| { | 905 | | StorageAccessError::Corrupted(CorruptedError::Internal(InternalError(err))) | 906 | 3 | })?0 ; | 907 | | | 908 | 3 | if !has_block { | 909 | 1 | return Err(StorageAccessError::UnknownBlock); | 910 | 2 | } | 911 | 2 | | 912 | 2 | if incomplete_storage { | 913 | 1 | return Err(StorageAccessError::IncompleteStorage); | 914 | 1 | } | 915 | | | 916 | 1 | let Some(value0 ) = value else { return Ok(None) }; | 917 | | | 918 | 0 | let trie_entry_version = u8::try_from(trie_entry_version.unwrap()) | 919 | 0 | .map_err(|_| CorruptedError::InvalidTrieEntryVersion) | 920 | 0 | .map_err(StorageAccessError::Corrupted)?; | 921 | 0 | Ok(Some((value, trie_entry_version))) | 922 | 3 | } |
_RINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1x_B1w_EINtNtNtB1F_5array4iter8IntoIterhKj2_EEB7_ Line | Count | Source | 786 | 3 | pub fn block_storage_get( | 787 | 3 | &self, | 788 | 3 | block_hash: &[u8; 32], | 789 | 3 | parent_tries_paths_nibbles: impl Iterator<Item = impl Iterator<Item = u8>>, | 790 | 3 | key_nibbles: impl Iterator<Item = u8>, | 791 | 3 | ) -> Result<Option<(Vec<u8>, u8)>, StorageAccessError> { | 792 | 3 | // Process the iterators at the very beginning and before locking the database, in order | 793 | 3 | // to avoid a deadlock in case the `next()` function of one of the iterators accesses | 794 | 3 | // the database as well. | 795 | 3 | let key_vectored = parent_tries_paths_nibbles | 796 | 3 | .flat_map(|t| t.inspect(|n| assert!(*n < 16)).chain(iter::once(0x10))) | 797 | 3 | .chain(key_nibbles.inspect(|n| assert!(*n < 16))) | 798 | 3 | .collect::<Vec<_>>(); | 799 | 3 | | 800 | 3 | let connection = self.database.lock(); | 801 | | | 802 | | // TODO: could be optimized by having a different request when `parent_tries_paths_nibbles` is empty and when it isn't | 803 | | // TODO: trie_root_ref system untested | 804 | | // TODO: infinite loop if there's a loop in the trie; detect this | 805 | 3 | let mut statement = connection | 806 | 3 | .prepare_cached( | 807 | 3 | r#" | 808 | 3 | WITH RECURSIVE | 809 | 3 | -- At the end of the recursive statement, `node_with_key` must always contain | 810 | 3 | -- one and exactly one item where `search_remain` is either empty or null. Empty | 811 | 3 | -- indicates that we have found a match, while null means that the search has | 812 | 3 | -- been interrupted due to a storage entry not being in the database. If | 813 | 3 | -- `search_remain` is empty, then `node_hash` is either a hash in case of a match | 814 | 3 | -- or null in case there is no entry with the requested key. If `search_remain` | 815 | 3 | -- is null, then `node_hash` is irrelevant. | 816 | 3 | -- | 817 | 3 | -- In order to properly handle the situation where the key is empty, the initial | 818 | 3 | -- request of the recursive table building must check whether the partial key of | 819 | 3 | -- the root matches. In other words, all the entries of `node_with_key` (where | 820 | 3 | -- `node_hash` is non-null) contain entries that are known to be in the database | 821 | 3 | -- and after the partial key has already been verified to be correct. | 822 | 3 | node_with_key(node_hash, search_remain) AS ( | 823 | 3 | SELECT | 824 | 3 | IIF(COALESCE(SUBSTR(:key, 1, LENGTH(trie_node.partial_key)), X'') = trie_node.partial_key, trie_node.hash, NULL), | 825 | 3 | IIF(trie_node.partial_key IS NULL, NULL, COALESCE(SUBSTR(:key, 1 + LENGTH(trie_node.partial_key)), X'')) | 826 | 3 | FROM blocks | 827 | 3 | LEFT JOIN trie_node ON blocks.state_trie_root_hash = trie_node.hash | 828 | 3 | WHERE blocks.hash = :block_hash | 829 | 3 | UNION ALL | 830 | 3 | SELECT | 831 | 3 | CASE | 832 | 3 | WHEN HEX(SUBSTR(node_with_key.search_remain, 1, 1)) = '10' THEN trie_node_storage.trie_root_ref | 833 | 3 | WHEN SUBSTR(node_with_key.search_remain, 2, LENGTH(trie_node.partial_key)) = trie_node.partial_key THEN trie_node_child.child_hash | 834 | 3 | ELSE NULL END, | 835 | 3 | CASE | 836 | 3 | WHEN HEX(SUBSTR(node_with_key.search_remain, 1, 1)) = '10' THEN SUBSTR(node_with_key.search_remain, 1) | 837 | 3 | WHEN trie_node_child.child_hash IS NULL THEN X'' | 838 | 3 | WHEN trie_node.partial_key IS NULL THEN NULL | 839 | 3 | WHEN SUBSTR(node_with_key.search_remain, 2, LENGTH(trie_node.partial_key)) = trie_node.partial_key THEN SUBSTR(node_with_key.search_remain, 2 + LENGTH(trie_node.partial_key)) | 840 | 3 | ELSE X'' END | 841 | 3 | FROM node_with_key | 842 | 3 | LEFT JOIN trie_node_child | 843 | 3 | ON node_with_key.node_hash = trie_node_child.hash | 844 | 3 | AND SUBSTR(node_with_key.search_remain, 1, 1) = trie_node_child.child_num | 845 | 3 | LEFT JOIN trie_node | 846 | 3 | ON trie_node.hash = trie_node_child.child_hash | 847 | 3 | LEFT JOIN trie_node_storage | 848 | 3 | ON node_with_key.node_hash = trie_node_storage.node_hash | 849 | 3 | WHERE LENGTH(node_with_key.search_remain) >= 1 | 850 | 3 | ) | 851 | 3 | SELECT COUNT(blocks.hash) >= 1, node_with_key.search_remain IS NULL, COALESCE(trie_node_storage.value, trie_node_storage.trie_root_ref), trie_node_storage.trie_entry_version | 852 | 3 | FROM blocks | 853 | 3 | JOIN node_with_key ON LENGTH(node_with_key.search_remain) = 0 OR node_with_key.search_remain IS NULL | 854 | 3 | LEFT JOIN trie_node_storage ON node_with_key.node_hash = trie_node_storage.node_hash AND node_with_key.search_remain IS NOT NULL | 855 | 3 | WHERE blocks.hash = :block_hash; | 856 | 3 | "#) | 857 | 3 | .map_err(|err| { | 858 | | StorageAccessError::Corrupted(CorruptedError::Internal( | 859 | | InternalError(err), | 860 | | )) | 861 | 3 | })?0 ; | 862 | | | 863 | | // In order to debug the SQL query above (for example in case of a failing test), | 864 | | // uncomment this block: | 865 | | // | 866 | | /*println!("{:?}", { | 867 | | let mut statement = connection | 868 | | .prepare_cached( | 869 | | r#" | 870 | | WITH RECURSIVE | 871 | | copy-paste the definition of node_with_key here | 872 | | | 873 | | SELECT * FROM node_with_key"#).unwrap(); | 874 | | statement | 875 | | .query_map( | 876 | | rusqlite::named_params! { | 877 | | ":block_hash": &block_hash[..], | 878 | | ":key": key_vectored, | 879 | | }, | 880 | | |row| { | 881 | | let node_hash = row.get::<_, Option<Vec<u8>>>(0)?.map(hex::encode); | 882 | | let search_remain = row.get::<_, Option<Vec<u8>>>(1)?; | 883 | | Ok((node_hash, search_remain)) | 884 | | }, | 885 | | ) | 886 | | .unwrap() | 887 | | .collect::<Vec<_>>() | 888 | | });*/ | 889 | | | 890 | 3 | let (has_block, incomplete_storage, value, trie_entry_version) = statement | 891 | 3 | .query_row( | 892 | 3 | rusqlite::named_params! { | 893 | 3 | ":block_hash": &block_hash[..], | 894 | 3 | ":key": key_vectored, | 895 | 3 | }, | 896 | 3 | |row| { | 897 | | let has_block = row.get::<_, i64>(0)? != 0; | 898 | | let incomplete_storage = row.get::<_, i64>(1)? != 0; | 899 | | let value = row.get::<_, Option<Vec<u8>>>(2)?; | 900 | | let trie_entry_version = row.get::<_, Option<i64>>(3)?; | 901 | | Ok((has_block, incomplete_storage, value, trie_entry_version)) | 902 | 3 | }, | 903 | 3 | ) | 904 | 3 | .map_err(|err| { | 905 | | StorageAccessError::Corrupted(CorruptedError::Internal(InternalError(err))) | 906 | 3 | })?0 ; | 907 | | | 908 | 3 | if !has_block { | 909 | 0 | return Err(StorageAccessError::UnknownBlock); | 910 | 3 | } | 911 | 3 | | 912 | 3 | if incomplete_storage { | 913 | 1 | return Err(StorageAccessError::IncompleteStorage); | 914 | 2 | } | 915 | | | 916 | 2 | let Some(value) = value else { return Ok(None)0 }; | 917 | | | 918 | 2 | let trie_entry_version = u8::try_from(trie_entry_version.unwrap()) | 919 | 2 | .map_err(|_| CorruptedError::InvalidTrieEntryVersion) | 920 | 2 | .map_err(StorageAccessError::Corrupted)?0 ; | 921 | 2 | Ok(Some((value, trie_entry_version))) | 922 | 3 | } |
_RINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1x_B1w_EINtNtNtB1F_5array4iter8IntoIterhKj3_EEB7_ Line | Count | Source | 786 | 3 | pub fn block_storage_get( | 787 | 3 | &self, | 788 | 3 | block_hash: &[u8; 32], | 789 | 3 | parent_tries_paths_nibbles: impl Iterator<Item = impl Iterator<Item = u8>>, | 790 | 3 | key_nibbles: impl Iterator<Item = u8>, | 791 | 3 | ) -> Result<Option<(Vec<u8>, u8)>, StorageAccessError> { | 792 | 3 | // Process the iterators at the very beginning and before locking the database, in order | 793 | 3 | // to avoid a deadlock in case the `next()` function of one of the iterators accesses | 794 | 3 | // the database as well. | 795 | 3 | let key_vectored = parent_tries_paths_nibbles | 796 | 3 | .flat_map(|t| t.inspect(|n| assert!(*n < 16)).chain(iter::once(0x10))) | 797 | 3 | .chain(key_nibbles.inspect(|n| assert!(*n < 16))) | 798 | 3 | .collect::<Vec<_>>(); | 799 | 3 | | 800 | 3 | let connection = self.database.lock(); | 801 | | | 802 | | // TODO: could be optimized by having a different request when `parent_tries_paths_nibbles` is empty and when it isn't | 803 | | // TODO: trie_root_ref system untested | 804 | | // TODO: infinite loop if there's a loop in the trie; detect this | 805 | 3 | let mut statement = connection | 806 | 3 | .prepare_cached( | 807 | 3 | r#" | 808 | 3 | WITH RECURSIVE | 809 | 3 | -- At the end of the recursive statement, `node_with_key` must always contain | 810 | 3 | -- one and exactly one item where `search_remain` is either empty or null. Empty | 811 | 3 | -- indicates that we have found a match, while null means that the search has | 812 | 3 | -- been interrupted due to a storage entry not being in the database. If | 813 | 3 | -- `search_remain` is empty, then `node_hash` is either a hash in case of a match | 814 | 3 | -- or null in case there is no entry with the requested key. If `search_remain` | 815 | 3 | -- is null, then `node_hash` is irrelevant. | 816 | 3 | -- | 817 | 3 | -- In order to properly handle the situation where the key is empty, the initial | 818 | 3 | -- request of the recursive table building must check whether the partial key of | 819 | 3 | -- the root matches. In other words, all the entries of `node_with_key` (where | 820 | 3 | -- `node_hash` is non-null) contain entries that are known to be in the database | 821 | 3 | -- and after the partial key has already been verified to be correct. | 822 | 3 | node_with_key(node_hash, search_remain) AS ( | 823 | 3 | SELECT | 824 | 3 | IIF(COALESCE(SUBSTR(:key, 1, LENGTH(trie_node.partial_key)), X'') = trie_node.partial_key, trie_node.hash, NULL), | 825 | 3 | IIF(trie_node.partial_key IS NULL, NULL, COALESCE(SUBSTR(:key, 1 + LENGTH(trie_node.partial_key)), X'')) | 826 | 3 | FROM blocks | 827 | 3 | LEFT JOIN trie_node ON blocks.state_trie_root_hash = trie_node.hash | 828 | 3 | WHERE blocks.hash = :block_hash | 829 | 3 | UNION ALL | 830 | 3 | SELECT | 831 | 3 | CASE | 832 | 3 | WHEN HEX(SUBSTR(node_with_key.search_remain, 1, 1)) = '10' THEN trie_node_storage.trie_root_ref | 833 | 3 | WHEN SUBSTR(node_with_key.search_remain, 2, LENGTH(trie_node.partial_key)) = trie_node.partial_key THEN trie_node_child.child_hash | 834 | 3 | ELSE NULL END, | 835 | 3 | CASE | 836 | 3 | WHEN HEX(SUBSTR(node_with_key.search_remain, 1, 1)) = '10' THEN SUBSTR(node_with_key.search_remain, 1) | 837 | 3 | WHEN trie_node_child.child_hash IS NULL THEN X'' | 838 | 3 | WHEN trie_node.partial_key IS NULL THEN NULL | 839 | 3 | WHEN SUBSTR(node_with_key.search_remain, 2, LENGTH(trie_node.partial_key)) = trie_node.partial_key THEN SUBSTR(node_with_key.search_remain, 2 + LENGTH(trie_node.partial_key)) | 840 | 3 | ELSE X'' END | 841 | 3 | FROM node_with_key | 842 | 3 | LEFT JOIN trie_node_child | 843 | 3 | ON node_with_key.node_hash = trie_node_child.hash | 844 | 3 | AND SUBSTR(node_with_key.search_remain, 1, 1) = trie_node_child.child_num | 845 | 3 | LEFT JOIN trie_node | 846 | 3 | ON trie_node.hash = trie_node_child.child_hash | 847 | 3 | LEFT JOIN trie_node_storage | 848 | 3 | ON node_with_key.node_hash = trie_node_storage.node_hash | 849 | 3 | WHERE LENGTH(node_with_key.search_remain) >= 1 | 850 | 3 | ) | 851 | 3 | SELECT COUNT(blocks.hash) >= 1, node_with_key.search_remain IS NULL, COALESCE(trie_node_storage.value, trie_node_storage.trie_root_ref), trie_node_storage.trie_entry_version | 852 | 3 | FROM blocks | 853 | 3 | JOIN node_with_key ON LENGTH(node_with_key.search_remain) = 0 OR node_with_key.search_remain IS NULL | 854 | 3 | LEFT JOIN trie_node_storage ON node_with_key.node_hash = trie_node_storage.node_hash AND node_with_key.search_remain IS NOT NULL | 855 | 3 | WHERE blocks.hash = :block_hash; | 856 | 3 | "#) | 857 | 3 | .map_err(|err| { | 858 | | StorageAccessError::Corrupted(CorruptedError::Internal( | 859 | | InternalError(err), | 860 | | )) | 861 | 3 | })?0 ; | 862 | | | 863 | | // In order to debug the SQL query above (for example in case of a failing test), | 864 | | // uncomment this block: | 865 | | // | 866 | | /*println!("{:?}", { | 867 | | let mut statement = connection | 868 | | .prepare_cached( | 869 | | r#" | 870 | | WITH RECURSIVE | 871 | | copy-paste the definition of node_with_key here | 872 | | | 873 | | SELECT * FROM node_with_key"#).unwrap(); | 874 | | statement | 875 | | .query_map( | 876 | | rusqlite::named_params! { | 877 | | ":block_hash": &block_hash[..], | 878 | | ":key": key_vectored, | 879 | | }, | 880 | | |row| { | 881 | | let node_hash = row.get::<_, Option<Vec<u8>>>(0)?.map(hex::encode); | 882 | | let search_remain = row.get::<_, Option<Vec<u8>>>(1)?; | 883 | | Ok((node_hash, search_remain)) | 884 | | }, | 885 | | ) | 886 | | .unwrap() | 887 | | .collect::<Vec<_>>() | 888 | | });*/ | 889 | | | 890 | 3 | let (has_block, incomplete_storage, value, trie_entry_version) = statement | 891 | 3 | .query_row( | 892 | 3 | rusqlite::named_params! { | 893 | 3 | ":block_hash": &block_hash[..], | 894 | 3 | ":key": key_vectored, | 895 | 3 | }, | 896 | 3 | |row| { | 897 | | let has_block = row.get::<_, i64>(0)? != 0; | 898 | | let incomplete_storage = row.get::<_, i64>(1)? != 0; | 899 | | let value = row.get::<_, Option<Vec<u8>>>(2)?; | 900 | | let trie_entry_version = row.get::<_, Option<i64>>(3)?; | 901 | | Ok((has_block, incomplete_storage, value, trie_entry_version)) | 902 | 3 | }, | 903 | 3 | ) | 904 | 3 | .map_err(|err| { | 905 | | StorageAccessError::Corrupted(CorruptedError::Internal(InternalError(err))) | 906 | 3 | })?0 ; | 907 | | | 908 | 3 | if !has_block { | 909 | 0 | return Err(StorageAccessError::UnknownBlock); | 910 | 3 | } | 911 | 3 | | 912 | 3 | if incomplete_storage { | 913 | 1 | return Err(StorageAccessError::IncompleteStorage); | 914 | 2 | } | 915 | | | 916 | 2 | let Some(value0 ) = value else { return Ok(None) }; | 917 | | | 918 | 0 | let trie_entry_version = u8::try_from(trie_entry_version.unwrap()) | 919 | 0 | .map_err(|_| CorruptedError::InvalidTrieEntryVersion) | 920 | 0 | .map_err(StorageAccessError::Corrupted)?; | 921 | 0 | Ok(Some((value, trie_entry_version))) | 922 | 3 | } |
_RINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1x_B1w_EINtNtNtB1F_5array4iter8IntoIterhKj4_EEB7_ Line | Count | Source | 786 | 3 | pub fn block_storage_get( | 787 | 3 | &self, | 788 | 3 | block_hash: &[u8; 32], | 789 | 3 | parent_tries_paths_nibbles: impl Iterator<Item = impl Iterator<Item = u8>>, | 790 | 3 | key_nibbles: impl Iterator<Item = u8>, | 791 | 3 | ) -> Result<Option<(Vec<u8>, u8)>, StorageAccessError> { | 792 | 3 | // Process the iterators at the very beginning and before locking the database, in order | 793 | 3 | // to avoid a deadlock in case the `next()` function of one of the iterators accesses | 794 | 3 | // the database as well. | 795 | 3 | let key_vectored = parent_tries_paths_nibbles | 796 | 3 | .flat_map(|t| t.inspect(|n| assert!(*n < 16)).chain(iter::once(0x10))) | 797 | 3 | .chain(key_nibbles.inspect(|n| assert!(*n < 16))) | 798 | 3 | .collect::<Vec<_>>(); | 799 | 3 | | 800 | 3 | let connection = self.database.lock(); | 801 | | | 802 | | // TODO: could be optimized by having a different request when `parent_tries_paths_nibbles` is empty and when it isn't | 803 | | // TODO: trie_root_ref system untested | 804 | | // TODO: infinite loop if there's a loop in the trie; detect this | 805 | 3 | let mut statement = connection | 806 | 3 | .prepare_cached( | 807 | 3 | r#" | 808 | 3 | WITH RECURSIVE | 809 | 3 | -- At the end of the recursive statement, `node_with_key` must always contain | 810 | 3 | -- one and exactly one item where `search_remain` is either empty or null. Empty | 811 | 3 | -- indicates that we have found a match, while null means that the search has | 812 | 3 | -- been interrupted due to a storage entry not being in the database. If | 813 | 3 | -- `search_remain` is empty, then `node_hash` is either a hash in case of a match | 814 | 3 | -- or null in case there is no entry with the requested key. If `search_remain` | 815 | 3 | -- is null, then `node_hash` is irrelevant. | 816 | 3 | -- | 817 | 3 | -- In order to properly handle the situation where the key is empty, the initial | 818 | 3 | -- request of the recursive table building must check whether the partial key of | 819 | 3 | -- the root matches. In other words, all the entries of `node_with_key` (where | 820 | 3 | -- `node_hash` is non-null) contain entries that are known to be in the database | 821 | 3 | -- and after the partial key has already been verified to be correct. | 822 | 3 | node_with_key(node_hash, search_remain) AS ( | 823 | 3 | SELECT | 824 | 3 | IIF(COALESCE(SUBSTR(:key, 1, LENGTH(trie_node.partial_key)), X'') = trie_node.partial_key, trie_node.hash, NULL), | 825 | 3 | IIF(trie_node.partial_key IS NULL, NULL, COALESCE(SUBSTR(:key, 1 + LENGTH(trie_node.partial_key)), X'')) | 826 | 3 | FROM blocks | 827 | 3 | LEFT JOIN trie_node ON blocks.state_trie_root_hash = trie_node.hash | 828 | 3 | WHERE blocks.hash = :block_hash | 829 | 3 | UNION ALL | 830 | 3 | SELECT | 831 | 3 | CASE | 832 | 3 | WHEN HEX(SUBSTR(node_with_key.search_remain, 1, 1)) = '10' THEN trie_node_storage.trie_root_ref | 833 | 3 | WHEN SUBSTR(node_with_key.search_remain, 2, LENGTH(trie_node.partial_key)) = trie_node.partial_key THEN trie_node_child.child_hash | 834 | 3 | ELSE NULL END, | 835 | 3 | CASE | 836 | 3 | WHEN HEX(SUBSTR(node_with_key.search_remain, 1, 1)) = '10' THEN SUBSTR(node_with_key.search_remain, 1) | 837 | 3 | WHEN trie_node_child.child_hash IS NULL THEN X'' | 838 | 3 | WHEN trie_node.partial_key IS NULL THEN NULL | 839 | 3 | WHEN SUBSTR(node_with_key.search_remain, 2, LENGTH(trie_node.partial_key)) = trie_node.partial_key THEN SUBSTR(node_with_key.search_remain, 2 + LENGTH(trie_node.partial_key)) | 840 | 3 | ELSE X'' END | 841 | 3 | FROM node_with_key | 842 | 3 | LEFT JOIN trie_node_child | 843 | 3 | ON node_with_key.node_hash = trie_node_child.hash | 844 | 3 | AND SUBSTR(node_with_key.search_remain, 1, 1) = trie_node_child.child_num | 845 | 3 | LEFT JOIN trie_node | 846 | 3 | ON trie_node.hash = trie_node_child.child_hash | 847 | 3 | LEFT JOIN trie_node_storage | 848 | 3 | ON node_with_key.node_hash = trie_node_storage.node_hash | 849 | 3 | WHERE LENGTH(node_with_key.search_remain) >= 1 | 850 | 3 | ) | 851 | 3 | SELECT COUNT(blocks.hash) >= 1, node_with_key.search_remain IS NULL, COALESCE(trie_node_storage.value, trie_node_storage.trie_root_ref), trie_node_storage.trie_entry_version | 852 | 3 | FROM blocks | 853 | 3 | JOIN node_with_key ON LENGTH(node_with_key.search_remain) = 0 OR node_with_key.search_remain IS NULL | 854 | 3 | LEFT JOIN trie_node_storage ON node_with_key.node_hash = trie_node_storage.node_hash AND node_with_key.search_remain IS NOT NULL | 855 | 3 | WHERE blocks.hash = :block_hash; | 856 | 3 | "#) | 857 | 3 | .map_err(|err| { | 858 | | StorageAccessError::Corrupted(CorruptedError::Internal( | 859 | | InternalError(err), | 860 | | )) | 861 | 3 | })?0 ; | 862 | | | 863 | | // In order to debug the SQL query above (for example in case of a failing test), | 864 | | // uncomment this block: | 865 | | // | 866 | | /*println!("{:?}", { | 867 | | let mut statement = connection | 868 | | .prepare_cached( | 869 | | r#" | 870 | | WITH RECURSIVE | 871 | | copy-paste the definition of node_with_key here | 872 | | | 873 | | SELECT * FROM node_with_key"#).unwrap(); | 874 | | statement | 875 | | .query_map( | 876 | | rusqlite::named_params! { | 877 | | ":block_hash": &block_hash[..], | 878 | | ":key": key_vectored, | 879 | | }, | 880 | | |row| { | 881 | | let node_hash = row.get::<_, Option<Vec<u8>>>(0)?.map(hex::encode); | 882 | | let search_remain = row.get::<_, Option<Vec<u8>>>(1)?; | 883 | | Ok((node_hash, search_remain)) | 884 | | }, | 885 | | ) | 886 | | .unwrap() | 887 | | .collect::<Vec<_>>() | 888 | | });*/ | 889 | | | 890 | 3 | let (has_block, incomplete_storage, value, trie_entry_version) = statement | 891 | 3 | .query_row( | 892 | 3 | rusqlite::named_params! { | 893 | 3 | ":block_hash": &block_hash[..], | 894 | 3 | ":key": key_vectored, | 895 | 3 | }, | 896 | 3 | |row| { | 897 | | let has_block = row.get::<_, i64>(0)? != 0; | 898 | | let incomplete_storage = row.get::<_, i64>(1)? != 0; | 899 | | let value = row.get::<_, Option<Vec<u8>>>(2)?; | 900 | | let trie_entry_version = row.get::<_, Option<i64>>(3)?; | 901 | | Ok((has_block, incomplete_storage, value, trie_entry_version)) | 902 | 3 | }, | 903 | 3 | ) | 904 | 3 | .map_err(|err| { | 905 | | StorageAccessError::Corrupted(CorruptedError::Internal(InternalError(err))) | 906 | 3 | })?0 ; | 907 | | | 908 | 3 | if !has_block { | 909 | 0 | return Err(StorageAccessError::UnknownBlock); | 910 | 3 | } | 911 | 3 | | 912 | 3 | if incomplete_storage { | 913 | 2 | return Err(StorageAccessError::IncompleteStorage); | 914 | 1 | } | 915 | | | 916 | 1 | let Some(value0 ) = value else { return Ok(None) }; | 917 | | | 918 | 0 | let trie_entry_version = u8::try_from(trie_entry_version.unwrap()) | 919 | 0 | .map_err(|_| CorruptedError::InvalidTrieEntryVersion) | 920 | 0 | .map_err(StorageAccessError::Corrupted)?; | 921 | 0 | Ok(Some((value, trie_entry_version))) | 922 | 3 | } |
_RINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1x_B1w_EINtNtNtB1F_5array4iter8IntoIterhKj5_EEB7_ Line | Count | Source | 786 | 3 | pub fn block_storage_get( | 787 | 3 | &self, | 788 | 3 | block_hash: &[u8; 32], | 789 | 3 | parent_tries_paths_nibbles: impl Iterator<Item = impl Iterator<Item = u8>>, | 790 | 3 | key_nibbles: impl Iterator<Item = u8>, | 791 | 3 | ) -> Result<Option<(Vec<u8>, u8)>, StorageAccessError> { | 792 | 3 | // Process the iterators at the very beginning and before locking the database, in order | 793 | 3 | // to avoid a deadlock in case the `next()` function of one of the iterators accesses | 794 | 3 | // the database as well. | 795 | 3 | let key_vectored = parent_tries_paths_nibbles | 796 | 3 | .flat_map(|t| t.inspect(|n| assert!(*n < 16)).chain(iter::once(0x10))) | 797 | 3 | .chain(key_nibbles.inspect(|n| assert!(*n < 16))) | 798 | 3 | .collect::<Vec<_>>(); | 799 | 3 | | 800 | 3 | let connection = self.database.lock(); | 801 | | | 802 | | // TODO: could be optimized by having a different request when `parent_tries_paths_nibbles` is empty and when it isn't | 803 | | // TODO: trie_root_ref system untested | 804 | | // TODO: infinite loop if there's a loop in the trie; detect this | 805 | 3 | let mut statement = connection | 806 | 3 | .prepare_cached( | 807 | 3 | r#" | 808 | 3 | WITH RECURSIVE | 809 | 3 | -- At the end of the recursive statement, `node_with_key` must always contain | 810 | 3 | -- one and exactly one item where `search_remain` is either empty or null. Empty | 811 | 3 | -- indicates that we have found a match, while null means that the search has | 812 | 3 | -- been interrupted due to a storage entry not being in the database. If | 813 | 3 | -- `search_remain` is empty, then `node_hash` is either a hash in case of a match | 814 | 3 | -- or null in case there is no entry with the requested key. If `search_remain` | 815 | 3 | -- is null, then `node_hash` is irrelevant. | 816 | 3 | -- | 817 | 3 | -- In order to properly handle the situation where the key is empty, the initial | 818 | 3 | -- request of the recursive table building must check whether the partial key of | 819 | 3 | -- the root matches. In other words, all the entries of `node_with_key` (where | 820 | 3 | -- `node_hash` is non-null) contain entries that are known to be in the database | 821 | 3 | -- and after the partial key has already been verified to be correct. | 822 | 3 | node_with_key(node_hash, search_remain) AS ( | 823 | 3 | SELECT | 824 | 3 | IIF(COALESCE(SUBSTR(:key, 1, LENGTH(trie_node.partial_key)), X'') = trie_node.partial_key, trie_node.hash, NULL), | 825 | 3 | IIF(trie_node.partial_key IS NULL, NULL, COALESCE(SUBSTR(:key, 1 + LENGTH(trie_node.partial_key)), X'')) | 826 | 3 | FROM blocks | 827 | 3 | LEFT JOIN trie_node ON blocks.state_trie_root_hash = trie_node.hash | 828 | 3 | WHERE blocks.hash = :block_hash | 829 | 3 | UNION ALL | 830 | 3 | SELECT | 831 | 3 | CASE | 832 | 3 | WHEN HEX(SUBSTR(node_with_key.search_remain, 1, 1)) = '10' THEN trie_node_storage.trie_root_ref | 833 | 3 | WHEN SUBSTR(node_with_key.search_remain, 2, LENGTH(trie_node.partial_key)) = trie_node.partial_key THEN trie_node_child.child_hash | 834 | 3 | ELSE NULL END, | 835 | 3 | CASE | 836 | 3 | WHEN HEX(SUBSTR(node_with_key.search_remain, 1, 1)) = '10' THEN SUBSTR(node_with_key.search_remain, 1) | 837 | 3 | WHEN trie_node_child.child_hash IS NULL THEN X'' | 838 | 3 | WHEN trie_node.partial_key IS NULL THEN NULL | 839 | 3 | WHEN SUBSTR(node_with_key.search_remain, 2, LENGTH(trie_node.partial_key)) = trie_node.partial_key THEN SUBSTR(node_with_key.search_remain, 2 + LENGTH(trie_node.partial_key)) | 840 | 3 | ELSE X'' END | 841 | 3 | FROM node_with_key | 842 | 3 | LEFT JOIN trie_node_child | 843 | 3 | ON node_with_key.node_hash = trie_node_child.hash | 844 | 3 | AND SUBSTR(node_with_key.search_remain, 1, 1) = trie_node_child.child_num | 845 | 3 | LEFT JOIN trie_node | 846 | 3 | ON trie_node.hash = trie_node_child.child_hash | 847 | 3 | LEFT JOIN trie_node_storage | 848 | 3 | ON node_with_key.node_hash = trie_node_storage.node_hash | 849 | 3 | WHERE LENGTH(node_with_key.search_remain) >= 1 | 850 | 3 | ) | 851 | 3 | SELECT COUNT(blocks.hash) >= 1, node_with_key.search_remain IS NULL, COALESCE(trie_node_storage.value, trie_node_storage.trie_root_ref), trie_node_storage.trie_entry_version | 852 | 3 | FROM blocks | 853 | 3 | JOIN node_with_key ON LENGTH(node_with_key.search_remain) = 0 OR node_with_key.search_remain IS NULL | 854 | 3 | LEFT JOIN trie_node_storage ON node_with_key.node_hash = trie_node_storage.node_hash AND node_with_key.search_remain IS NOT NULL | 855 | 3 | WHERE blocks.hash = :block_hash; | 856 | 3 | "#) | 857 | 3 | .map_err(|err| { | 858 | | StorageAccessError::Corrupted(CorruptedError::Internal( | 859 | | InternalError(err), | 860 | | )) | 861 | 3 | })?0 ; | 862 | | | 863 | | // In order to debug the SQL query above (for example in case of a failing test), | 864 | | // uncomment this block: | 865 | | // | 866 | | /*println!("{:?}", { | 867 | | let mut statement = connection | 868 | | .prepare_cached( | 869 | | r#" | 870 | | WITH RECURSIVE | 871 | | copy-paste the definition of node_with_key here | 872 | | | 873 | | SELECT * FROM node_with_key"#).unwrap(); | 874 | | statement | 875 | | .query_map( | 876 | | rusqlite::named_params! { | 877 | | ":block_hash": &block_hash[..], | 878 | | ":key": key_vectored, | 879 | | }, | 880 | | |row| { | 881 | | let node_hash = row.get::<_, Option<Vec<u8>>>(0)?.map(hex::encode); | 882 | | let search_remain = row.get::<_, Option<Vec<u8>>>(1)?; | 883 | | Ok((node_hash, search_remain)) | 884 | | }, | 885 | | ) | 886 | | .unwrap() | 887 | | .collect::<Vec<_>>() | 888 | | });*/ | 889 | | | 890 | 3 | let (has_block, incomplete_storage, value, trie_entry_version) = statement | 891 | 3 | .query_row( | 892 | 3 | rusqlite::named_params! { | 893 | 3 | ":block_hash": &block_hash[..], | 894 | 3 | ":key": key_vectored, | 895 | 3 | }, | 896 | 3 | |row| { | 897 | | let has_block = row.get::<_, i64>(0)? != 0; | 898 | | let incomplete_storage = row.get::<_, i64>(1)? != 0; | 899 | | let value = row.get::<_, Option<Vec<u8>>>(2)?; | 900 | | let trie_entry_version = row.get::<_, Option<i64>>(3)?; | 901 | | Ok((has_block, incomplete_storage, value, trie_entry_version)) | 902 | 3 | }, | 903 | 3 | ) | 904 | 3 | .map_err(|err| { | 905 | | StorageAccessError::Corrupted(CorruptedError::Internal(InternalError(err))) | 906 | 3 | })?0 ; | 907 | | | 908 | 3 | if !has_block { | 909 | 0 | return Err(StorageAccessError::UnknownBlock); | 910 | 3 | } | 911 | 3 | | 912 | 3 | if incomplete_storage { | 913 | 2 | return Err(StorageAccessError::IncompleteStorage); | 914 | 1 | } | 915 | | | 916 | 1 | let Some(value) = value else { return Ok(None)0 }; | 917 | | | 918 | 1 | let trie_entry_version = u8::try_from(trie_entry_version.unwrap()) | 919 | 1 | .map_err(|_| CorruptedError::InvalidTrieEntryVersion) | 920 | 1 | .map_err(StorageAccessError::Corrupted)?0 ; | 921 | 1 | Ok(Some((value, trie_entry_version))) | 922 | 3 | } |
_RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1y_B1x_EINtNtNtB1E_8adapters3map3MapINtNtNtB7_4trie6nibble14BytesToNibblesINtNtB2F_6copied6CopiedINtNtNtB1G_5slice4iter4IterhEEENvYhINtNtB1G_7convert4FromNtB35_6NibbleE4fromEEB7_ Line | Count | Source | 786 | 84 | pub fn block_storage_get( | 787 | 84 | &self, | 788 | 84 | block_hash: &[u8; 32], | 789 | 84 | parent_tries_paths_nibbles: impl Iterator<Item = impl Iterator<Item = u8>>, | 790 | 84 | key_nibbles: impl Iterator<Item = u8>, | 791 | 84 | ) -> Result<Option<(Vec<u8>, u8)>, StorageAccessError> { | 792 | 84 | // Process the iterators at the very beginning and before locking the database, in order | 793 | 84 | // to avoid a deadlock in case the `next()` function of one of the iterators accesses | 794 | 84 | // the database as well. | 795 | 84 | let key_vectored = parent_tries_paths_nibbles | 796 | 84 | .flat_map(|t| t.inspect(|n| assert!(*n < 16)).chain(iter::once(0x10))) | 797 | 84 | .chain(key_nibbles.inspect(|n| assert!(*n < 16))) | 798 | 84 | .collect::<Vec<_>>(); | 799 | 84 | | 800 | 84 | let connection = self.database.lock(); | 801 | | | 802 | | // TODO: could be optimized by having a different request when `parent_tries_paths_nibbles` is empty and when it isn't | 803 | | // TODO: trie_root_ref system untested | 804 | | // TODO: infinite loop if there's a loop in the trie; detect this | 805 | 84 | let mut statement = connection | 806 | 84 | .prepare_cached( | 807 | 84 | r#" | 808 | 84 | WITH RECURSIVE | 809 | 84 | -- At the end of the recursive statement, `node_with_key` must always contain | 810 | 84 | -- one and exactly one item where `search_remain` is either empty or null. Empty | 811 | 84 | -- indicates that we have found a match, while null means that the search has | 812 | 84 | -- been interrupted due to a storage entry not being in the database. If | 813 | 84 | -- `search_remain` is empty, then `node_hash` is either a hash in case of a match | 814 | 84 | -- or null in case there is no entry with the requested key. If `search_remain` | 815 | 84 | -- is null, then `node_hash` is irrelevant. | 816 | 84 | -- | 817 | 84 | -- In order to properly handle the situation where the key is empty, the initial | 818 | 84 | -- request of the recursive table building must check whether the partial key of | 819 | 84 | -- the root matches. In other words, all the entries of `node_with_key` (where | 820 | 84 | -- `node_hash` is non-null) contain entries that are known to be in the database | 821 | 84 | -- and after the partial key has already been verified to be correct. | 822 | 84 | node_with_key(node_hash, search_remain) AS ( | 823 | 84 | SELECT | 824 | 84 | IIF(COALESCE(SUBSTR(:key, 1, LENGTH(trie_node.partial_key)), X'') = trie_node.partial_key, trie_node.hash, NULL), | 825 | 84 | IIF(trie_node.partial_key IS NULL, NULL, COALESCE(SUBSTR(:key, 1 + LENGTH(trie_node.partial_key)), X'')) | 826 | 84 | FROM blocks | 827 | 84 | LEFT JOIN trie_node ON blocks.state_trie_root_hash = trie_node.hash | 828 | 84 | WHERE blocks.hash = :block_hash | 829 | 84 | UNION ALL | 830 | 84 | SELECT | 831 | 84 | CASE | 832 | 84 | WHEN HEX(SUBSTR(node_with_key.search_remain, 1, 1)) = '10' THEN trie_node_storage.trie_root_ref | 833 | 84 | WHEN SUBSTR(node_with_key.search_remain, 2, LENGTH(trie_node.partial_key)) = trie_node.partial_key THEN trie_node_child.child_hash | 834 | 84 | ELSE NULL END, | 835 | 84 | CASE | 836 | 84 | WHEN HEX(SUBSTR(node_with_key.search_remain, 1, 1)) = '10' THEN SUBSTR(node_with_key.search_remain, 1) | 837 | 84 | WHEN trie_node_child.child_hash IS NULL THEN X'' | 838 | 84 | WHEN trie_node.partial_key IS NULL THEN NULL | 839 | 84 | WHEN SUBSTR(node_with_key.search_remain, 2, LENGTH(trie_node.partial_key)) = trie_node.partial_key THEN SUBSTR(node_with_key.search_remain, 2 + LENGTH(trie_node.partial_key)) | 840 | 84 | ELSE X'' END | 841 | 84 | FROM node_with_key | 842 | 84 | LEFT JOIN trie_node_child | 843 | 84 | ON node_with_key.node_hash = trie_node_child.hash | 844 | 84 | AND SUBSTR(node_with_key.search_remain, 1, 1) = trie_node_child.child_num | 845 | 84 | LEFT JOIN trie_node | 846 | 84 | ON trie_node.hash = trie_node_child.child_hash | 847 | 84 | LEFT JOIN trie_node_storage | 848 | 84 | ON node_with_key.node_hash = trie_node_storage.node_hash | 849 | 84 | WHERE LENGTH(node_with_key.search_remain) >= 1 | 850 | 84 | ) | 851 | 84 | SELECT COUNT(blocks.hash) >= 1, node_with_key.search_remain IS NULL, COALESCE(trie_node_storage.value, trie_node_storage.trie_root_ref), trie_node_storage.trie_entry_version | 852 | 84 | FROM blocks | 853 | 84 | JOIN node_with_key ON LENGTH(node_with_key.search_remain) = 0 OR node_with_key.search_remain IS NULL | 854 | 84 | LEFT JOIN trie_node_storage ON node_with_key.node_hash = trie_node_storage.node_hash AND node_with_key.search_remain IS NOT NULL | 855 | 84 | WHERE blocks.hash = :block_hash; | 856 | 84 | "#) | 857 | 84 | .map_err(|err| { | 858 | | StorageAccessError::Corrupted(CorruptedError::Internal( | 859 | | InternalError(err), | 860 | | )) | 861 | 84 | })?0 ; | 862 | | | 863 | | // In order to debug the SQL query above (for example in case of a failing test), | 864 | | // uncomment this block: | 865 | | // | 866 | | /*println!("{:?}", { | 867 | | let mut statement = connection | 868 | | .prepare_cached( | 869 | | r#" | 870 | | WITH RECURSIVE | 871 | | copy-paste the definition of node_with_key here | 872 | | | 873 | | SELECT * FROM node_with_key"#).unwrap(); | 874 | | statement | 875 | | .query_map( | 876 | | rusqlite::named_params! { | 877 | | ":block_hash": &block_hash[..], | 878 | | ":key": key_vectored, | 879 | | }, | 880 | | |row| { | 881 | | let node_hash = row.get::<_, Option<Vec<u8>>>(0)?.map(hex::encode); | 882 | | let search_remain = row.get::<_, Option<Vec<u8>>>(1)?; | 883 | | Ok((node_hash, search_remain)) | 884 | | }, | 885 | | ) | 886 | | .unwrap() | 887 | | .collect::<Vec<_>>() | 888 | | });*/ | 889 | | | 890 | 84 | let (has_block, incomplete_storage, value, trie_entry_version) = statement | 891 | 84 | .query_row( | 892 | 84 | rusqlite::named_params! { | 893 | 84 | ":block_hash": &block_hash[..], | 894 | 84 | ":key": key_vectored, | 895 | 84 | }, | 896 | 84 | |row| { | 897 | | let has_block = row.get::<_, i64>(0)? != 0; | 898 | | let incomplete_storage = row.get::<_, i64>(1)? != 0; | 899 | | let value = row.get::<_, Option<Vec<u8>>>(2)?; | 900 | | let trie_entry_version = row.get::<_, Option<i64>>(3)?; | 901 | | Ok((has_block, incomplete_storage, value, trie_entry_version)) | 902 | 84 | }, | 903 | 84 | ) | 904 | 84 | .map_err(|err| { | 905 | | StorageAccessError::Corrupted(CorruptedError::Internal(InternalError(err))) | 906 | 84 | })?0 ; | 907 | | | 908 | 84 | if !has_block { | 909 | 0 | return Err(StorageAccessError::UnknownBlock); | 910 | 84 | } | 911 | 84 | | 912 | 84 | if incomplete_storage { | 913 | 0 | return Err(StorageAccessError::IncompleteStorage); | 914 | 84 | } | 915 | | | 916 | 84 | let Some(value63 ) = value else { return Ok(None)21 }; | 917 | | | 918 | 63 | let trie_entry_version = u8::try_from(trie_entry_version.unwrap()) | 919 | 63 | .map_err(|_| CorruptedError::InvalidTrieEntryVersion) | 920 | 63 | .map_err(StorageAccessError::Corrupted)?0 ; | 921 | 63 | Ok(Some((value, trie_entry_version))) | 922 | 84 | } |
Unexecuted instantiation: _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2w_6option8IntoIterINtB1C_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB3Z_14SyncBackground12author_block0s_00EINtNtB2s_6copied6CopiedINtNtNtB2w_5slice4iter4IterhEEECsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2w_6option8IntoIterINtB1C_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s0_00EINtNtB2s_6copied6CopiedINtNtNtB2w_5slice4iter4IterhEEECsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1y_B1x_EINtNtNtB1E_8adapters3map3MapINtNtNtB1G_5slice4iter4IterNtNtNtB7_4trie6nibble6NibbleENCNCNCNCINvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service24execute_block_and_insertRINtNtCsdZExvAaxgia_5alloc3vec3VechEIB33_B5q_EE0s3_00s0_0EECsiLzmwikkc22_14json_rpc_basic _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1y_B1x_EINtNtNtB1E_8adapters3map3MapINtNtNtB7_4trie6nibble14BytesToNibblesINtNtB2F_6copied6CopiedINtNtNtB1G_5slice4iter4IterhEEENvYhINtNtB1G_7convert4FromNtB35_6NibbleE4fromEECsiLzmwikkc22_14json_rpc_basic Line | Count | Source | 786 | 4 | pub fn block_storage_get( | 787 | 4 | &self, | 788 | 4 | block_hash: &[u8; 32], | 789 | 4 | parent_tries_paths_nibbles: impl Iterator<Item = impl Iterator<Item = u8>>, | 790 | 4 | key_nibbles: impl Iterator<Item = u8>, | 791 | 4 | ) -> Result<Option<(Vec<u8>, u8)>, StorageAccessError> { | 792 | 4 | // Process the iterators at the very beginning and before locking the database, in order | 793 | 4 | // to avoid a deadlock in case the `next()` function of one of the iterators accesses | 794 | 4 | // the database as well. | 795 | 4 | let key_vectored = parent_tries_paths_nibbles | 796 | 4 | .flat_map(|t| t.inspect(|n| assert!(*n < 16)).chain(iter::once(0x10))) | 797 | 4 | .chain(key_nibbles.inspect(|n| assert!(*n < 16))) | 798 | 4 | .collect::<Vec<_>>(); | 799 | 4 | | 800 | 4 | let connection = self.database.lock(); | 801 | | | 802 | | // TODO: could be optimized by having a different request when `parent_tries_paths_nibbles` is empty and when it isn't | 803 | | // TODO: trie_root_ref system untested | 804 | | // TODO: infinite loop if there's a loop in the trie; detect this | 805 | 4 | let mut statement = connection | 806 | 4 | .prepare_cached( | 807 | 4 | r#" | 808 | 4 | WITH RECURSIVE | 809 | 4 | -- At the end of the recursive statement, `node_with_key` must always contain | 810 | 4 | -- one and exactly one item where `search_remain` is either empty or null. Empty | 811 | 4 | -- indicates that we have found a match, while null means that the search has | 812 | 4 | -- been interrupted due to a storage entry not being in the database. If | 813 | 4 | -- `search_remain` is empty, then `node_hash` is either a hash in case of a match | 814 | 4 | -- or null in case there is no entry with the requested key. If `search_remain` | 815 | 4 | -- is null, then `node_hash` is irrelevant. | 816 | 4 | -- | 817 | 4 | -- In order to properly handle the situation where the key is empty, the initial | 818 | 4 | -- request of the recursive table building must check whether the partial key of | 819 | 4 | -- the root matches. In other words, all the entries of `node_with_key` (where | 820 | 4 | -- `node_hash` is non-null) contain entries that are known to be in the database | 821 | 4 | -- and after the partial key has already been verified to be correct. | 822 | 4 | node_with_key(node_hash, search_remain) AS ( | 823 | 4 | SELECT | 824 | 4 | IIF(COALESCE(SUBSTR(:key, 1, LENGTH(trie_node.partial_key)), X'') = trie_node.partial_key, trie_node.hash, NULL), | 825 | 4 | IIF(trie_node.partial_key IS NULL, NULL, COALESCE(SUBSTR(:key, 1 + LENGTH(trie_node.partial_key)), X'')) | 826 | 4 | FROM blocks | 827 | 4 | LEFT JOIN trie_node ON blocks.state_trie_root_hash = trie_node.hash | 828 | 4 | WHERE blocks.hash = :block_hash | 829 | 4 | UNION ALL | 830 | 4 | SELECT | 831 | 4 | CASE | 832 | 4 | WHEN HEX(SUBSTR(node_with_key.search_remain, 1, 1)) = '10' THEN trie_node_storage.trie_root_ref | 833 | 4 | WHEN SUBSTR(node_with_key.search_remain, 2, LENGTH(trie_node.partial_key)) = trie_node.partial_key THEN trie_node_child.child_hash | 834 | 4 | ELSE NULL END, | 835 | 4 | CASE | 836 | 4 | WHEN HEX(SUBSTR(node_with_key.search_remain, 1, 1)) = '10' THEN SUBSTR(node_with_key.search_remain, 1) | 837 | 4 | WHEN trie_node_child.child_hash IS NULL THEN X'' | 838 | 4 | WHEN trie_node.partial_key IS NULL THEN NULL | 839 | 4 | WHEN SUBSTR(node_with_key.search_remain, 2, LENGTH(trie_node.partial_key)) = trie_node.partial_key THEN SUBSTR(node_with_key.search_remain, 2 + LENGTH(trie_node.partial_key)) | 840 | 4 | ELSE X'' END | 841 | 4 | FROM node_with_key | 842 | 4 | LEFT JOIN trie_node_child | 843 | 4 | ON node_with_key.node_hash = trie_node_child.hash | 844 | 4 | AND SUBSTR(node_with_key.search_remain, 1, 1) = trie_node_child.child_num | 845 | 4 | LEFT JOIN trie_node | 846 | 4 | ON trie_node.hash = trie_node_child.child_hash | 847 | 4 | LEFT JOIN trie_node_storage | 848 | 4 | ON node_with_key.node_hash = trie_node_storage.node_hash | 849 | 4 | WHERE LENGTH(node_with_key.search_remain) >= 1 | 850 | 4 | ) | 851 | 4 | SELECT COUNT(blocks.hash) >= 1, node_with_key.search_remain IS NULL, COALESCE(trie_node_storage.value, trie_node_storage.trie_root_ref), trie_node_storage.trie_entry_version | 852 | 4 | FROM blocks | 853 | 4 | JOIN node_with_key ON LENGTH(node_with_key.search_remain) = 0 OR node_with_key.search_remain IS NULL | 854 | 4 | LEFT JOIN trie_node_storage ON node_with_key.node_hash = trie_node_storage.node_hash AND node_with_key.search_remain IS NOT NULL | 855 | 4 | WHERE blocks.hash = :block_hash; | 856 | 4 | "#) | 857 | 4 | .map_err(|err| { | 858 | | StorageAccessError::Corrupted(CorruptedError::Internal( | 859 | | InternalError(err), | 860 | | )) | 861 | 4 | })?0 ; | 862 | | | 863 | | // In order to debug the SQL query above (for example in case of a failing test), | 864 | | // uncomment this block: | 865 | | // | 866 | | /*println!("{:?}", { | 867 | | let mut statement = connection | 868 | | .prepare_cached( | 869 | | r#" | 870 | | WITH RECURSIVE | 871 | | copy-paste the definition of node_with_key here | 872 | | | 873 | | SELECT * FROM node_with_key"#).unwrap(); | 874 | | statement | 875 | | .query_map( | 876 | | rusqlite::named_params! { | 877 | | ":block_hash": &block_hash[..], | 878 | | ":key": key_vectored, | 879 | | }, | 880 | | |row| { | 881 | | let node_hash = row.get::<_, Option<Vec<u8>>>(0)?.map(hex::encode); | 882 | | let search_remain = row.get::<_, Option<Vec<u8>>>(1)?; | 883 | | Ok((node_hash, search_remain)) | 884 | | }, | 885 | | ) | 886 | | .unwrap() | 887 | | .collect::<Vec<_>>() | 888 | | });*/ | 889 | | | 890 | 4 | let (has_block, incomplete_storage, value, trie_entry_version) = statement | 891 | 4 | .query_row( | 892 | 4 | rusqlite::named_params! { | 893 | 4 | ":block_hash": &block_hash[..], | 894 | 4 | ":key": key_vectored, | 895 | 4 | }, | 896 | 4 | |row| { | 897 | | let has_block = row.get::<_, i64>(0)? != 0; | 898 | | let incomplete_storage = row.get::<_, i64>(1)? != 0; | 899 | | let value = row.get::<_, Option<Vec<u8>>>(2)?; | 900 | | let trie_entry_version = row.get::<_, Option<i64>>(3)?; | 901 | | Ok((has_block, incomplete_storage, value, trie_entry_version)) | 902 | 4 | }, | 903 | 4 | ) | 904 | 4 | .map_err(|err| { | 905 | | StorageAccessError::Corrupted(CorruptedError::Internal(InternalError(err))) | 906 | 4 | })?0 ; | 907 | | | 908 | 4 | if !has_block { | 909 | 0 | return Err(StorageAccessError::UnknownBlock); | 910 | 4 | } | 911 | 4 | | 912 | 4 | if incomplete_storage { | 913 | 0 | return Err(StorageAccessError::IncompleteStorage); | 914 | 4 | } | 915 | | | 916 | 4 | let Some(value2 ) = value else { return Ok(None)2 }; | 917 | | | 918 | 2 | let trie_entry_version = u8::try_from(trie_entry_version.unwrap()) | 919 | 2 | .map_err(|_| CorruptedError::InvalidTrieEntryVersion) | 920 | 2 | .map_err(StorageAccessError::Corrupted)?0 ; | 921 | 2 | Ok(Some((value, trie_entry_version))) | 922 | 4 | } |
Unexecuted instantiation: _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2w_6option8IntoIterINtB1C_3VechEENCNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s7_00EINtNtB2s_6copied6CopiedINtNtNtB2w_5slice4iter4IterhEEEB40_ _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1y_B1x_EINtNtNtB1E_8adapters3map3MapINtNtNtB7_4trie6nibble14BytesToNibblesINtNtB2F_6copied6CopiedINtNtNtB1G_5slice4iter4IterhEEENvYhINtNtB1G_7convert4FromNtB35_6NibbleE4fromEECsiUjFBJteJ7x_17smoldot_full_node Line | Count | Source | 786 | 4 | pub fn block_storage_get( | 787 | 4 | &self, | 788 | 4 | block_hash: &[u8; 32], | 789 | 4 | parent_tries_paths_nibbles: impl Iterator<Item = impl Iterator<Item = u8>>, | 790 | 4 | key_nibbles: impl Iterator<Item = u8>, | 791 | 4 | ) -> Result<Option<(Vec<u8>, u8)>, StorageAccessError> { | 792 | 4 | // Process the iterators at the very beginning and before locking the database, in order | 793 | 4 | // to avoid a deadlock in case the `next()` function of one of the iterators accesses | 794 | 4 | // the database as well. | 795 | 4 | let key_vectored = parent_tries_paths_nibbles | 796 | 4 | .flat_map(|t| t.inspect(|n| assert!(*n < 16)).chain(iter::once(0x10))) | 797 | 4 | .chain(key_nibbles.inspect(|n| assert!(*n < 16))) | 798 | 4 | .collect::<Vec<_>>(); | 799 | 4 | | 800 | 4 | let connection = self.database.lock(); | 801 | | | 802 | | // TODO: could be optimized by having a different request when `parent_tries_paths_nibbles` is empty and when it isn't | 803 | | // TODO: trie_root_ref system untested | 804 | | // TODO: infinite loop if there's a loop in the trie; detect this | 805 | 4 | let mut statement = connection | 806 | 4 | .prepare_cached( | 807 | 4 | r#" | 808 | 4 | WITH RECURSIVE | 809 | 4 | -- At the end of the recursive statement, `node_with_key` must always contain | 810 | 4 | -- one and exactly one item where `search_remain` is either empty or null. Empty | 811 | 4 | -- indicates that we have found a match, while null means that the search has | 812 | 4 | -- been interrupted due to a storage entry not being in the database. If | 813 | 4 | -- `search_remain` is empty, then `node_hash` is either a hash in case of a match | 814 | 4 | -- or null in case there is no entry with the requested key. If `search_remain` | 815 | 4 | -- is null, then `node_hash` is irrelevant. | 816 | 4 | -- | 817 | 4 | -- In order to properly handle the situation where the key is empty, the initial | 818 | 4 | -- request of the recursive table building must check whether the partial key of | 819 | 4 | -- the root matches. In other words, all the entries of `node_with_key` (where | 820 | 4 | -- `node_hash` is non-null) contain entries that are known to be in the database | 821 | 4 | -- and after the partial key has already been verified to be correct. | 822 | 4 | node_with_key(node_hash, search_remain) AS ( | 823 | 4 | SELECT | 824 | 4 | IIF(COALESCE(SUBSTR(:key, 1, LENGTH(trie_node.partial_key)), X'') = trie_node.partial_key, trie_node.hash, NULL), | 825 | 4 | IIF(trie_node.partial_key IS NULL, NULL, COALESCE(SUBSTR(:key, 1 + LENGTH(trie_node.partial_key)), X'')) | 826 | 4 | FROM blocks | 827 | 4 | LEFT JOIN trie_node ON blocks.state_trie_root_hash = trie_node.hash | 828 | 4 | WHERE blocks.hash = :block_hash | 829 | 4 | UNION ALL | 830 | 4 | SELECT | 831 | 4 | CASE | 832 | 4 | WHEN HEX(SUBSTR(node_with_key.search_remain, 1, 1)) = '10' THEN trie_node_storage.trie_root_ref | 833 | 4 | WHEN SUBSTR(node_with_key.search_remain, 2, LENGTH(trie_node.partial_key)) = trie_node.partial_key THEN trie_node_child.child_hash | 834 | 4 | ELSE NULL END, | 835 | 4 | CASE | 836 | 4 | WHEN HEX(SUBSTR(node_with_key.search_remain, 1, 1)) = '10' THEN SUBSTR(node_with_key.search_remain, 1) | 837 | 4 | WHEN trie_node_child.child_hash IS NULL THEN X'' | 838 | 4 | WHEN trie_node.partial_key IS NULL THEN NULL | 839 | 4 | WHEN SUBSTR(node_with_key.search_remain, 2, LENGTH(trie_node.partial_key)) = trie_node.partial_key THEN SUBSTR(node_with_key.search_remain, 2 + LENGTH(trie_node.partial_key)) | 840 | 4 | ELSE X'' END | 841 | 4 | FROM node_with_key | 842 | 4 | LEFT JOIN trie_node_child | 843 | 4 | ON node_with_key.node_hash = trie_node_child.hash | 844 | 4 | AND SUBSTR(node_with_key.search_remain, 1, 1) = trie_node_child.child_num | 845 | 4 | LEFT JOIN trie_node | 846 | 4 | ON trie_node.hash = trie_node_child.child_hash | 847 | 4 | LEFT JOIN trie_node_storage | 848 | 4 | ON node_with_key.node_hash = trie_node_storage.node_hash | 849 | 4 | WHERE LENGTH(node_with_key.search_remain) >= 1 | 850 | 4 | ) | 851 | 4 | SELECT COUNT(blocks.hash) >= 1, node_with_key.search_remain IS NULL, COALESCE(trie_node_storage.value, trie_node_storage.trie_root_ref), trie_node_storage.trie_entry_version | 852 | 4 | FROM blocks | 853 | 4 | JOIN node_with_key ON LENGTH(node_with_key.search_remain) = 0 OR node_with_key.search_remain IS NULL | 854 | 4 | LEFT JOIN trie_node_storage ON node_with_key.node_hash = trie_node_storage.node_hash AND node_with_key.search_remain IS NOT NULL | 855 | 4 | WHERE blocks.hash = :block_hash; | 856 | 4 | "#) | 857 | 4 | .map_err(|err| { | 858 | | StorageAccessError::Corrupted(CorruptedError::Internal( | 859 | | InternalError(err), | 860 | | )) | 861 | 4 | })?0 ; | 862 | | | 863 | | // In order to debug the SQL query above (for example in case of a failing test), | 864 | | // uncomment this block: | 865 | | // | 866 | | /*println!("{:?}", { | 867 | | let mut statement = connection | 868 | | .prepare_cached( | 869 | | r#" | 870 | | WITH RECURSIVE | 871 | | copy-paste the definition of node_with_key here | 872 | | | 873 | | SELECT * FROM node_with_key"#).unwrap(); | 874 | | statement | 875 | | .query_map( | 876 | | rusqlite::named_params! { | 877 | | ":block_hash": &block_hash[..], | 878 | | ":key": key_vectored, | 879 | | }, | 880 | | |row| { | 881 | | let node_hash = row.get::<_, Option<Vec<u8>>>(0)?.map(hex::encode); | 882 | | let search_remain = row.get::<_, Option<Vec<u8>>>(1)?; | 883 | | Ok((node_hash, search_remain)) | 884 | | }, | 885 | | ) | 886 | | .unwrap() | 887 | | .collect::<Vec<_>>() | 888 | | });*/ | 889 | | | 890 | 4 | let (has_block, incomplete_storage, value, trie_entry_version) = statement | 891 | 4 | .query_row( | 892 | 4 | rusqlite::named_params! { | 893 | 4 | ":block_hash": &block_hash[..], | 894 | 4 | ":key": key_vectored, | 895 | 4 | }, | 896 | 4 | |row| { | 897 | | let has_block = row.get::<_, i64>(0)? != 0; | 898 | | let incomplete_storage = row.get::<_, i64>(1)? != 0; | 899 | | let value = row.get::<_, Option<Vec<u8>>>(2)?; | 900 | | let trie_entry_version = row.get::<_, Option<i64>>(3)?; | 901 | | Ok((has_block, incomplete_storage, value, trie_entry_version)) | 902 | 4 | }, | 903 | 4 | ) | 904 | 4 | .map_err(|err| { | 905 | | StorageAccessError::Corrupted(CorruptedError::Internal(InternalError(err))) | 906 | 4 | })?0 ; | 907 | | | 908 | 4 | if !has_block { | 909 | 0 | return Err(StorageAccessError::UnknownBlock); | 910 | 4 | } | 911 | 4 | | 912 | 4 | if incomplete_storage { | 913 | 0 | return Err(StorageAccessError::IncompleteStorage); | 914 | 4 | } | 915 | | | 916 | 4 | let Some(value2 ) = value else { return Ok(None)2 }; | 917 | | | 918 | 2 | let trie_entry_version = u8::try_from(trie_entry_version.unwrap()) | 919 | 2 | .map_err(|_| CorruptedError::InvalidTrieEntryVersion) | 920 | 2 | .map_err(StorageAccessError::Corrupted)?0 ; | 921 | 2 | Ok(Some((value, trie_entry_version))) | 922 | 4 | } |
Unexecuted instantiation: _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1y_B1x_EINtNtNtB1E_8adapters6copied6CopiedINtNtNtB1G_5slice4iter4IterhEEECsiUjFBJteJ7x_17smoldot_full_node Unexecuted instantiation: _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2w_6option8IntoIterINtB1C_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB3Z_14SyncBackground12author_block0s_00EINtNtB2s_6copied6CopiedINtNtNtB2w_5slice4iter4IterhEEECscDgN54JpMGG_6author Unexecuted instantiation: _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2w_6option8IntoIterINtB1C_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s0_00EINtNtB2s_6copied6CopiedINtNtNtB2w_5slice4iter4IterhEEECscDgN54JpMGG_6author Unexecuted instantiation: _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1y_B1x_EINtNtNtB1E_8adapters3map3MapINtNtNtB1G_5slice4iter4IterNtNtNtB7_4trie6nibble6NibbleENCNCNCNCINvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service24execute_block_and_insertRINtNtCsdZExvAaxgia_5alloc3vec3VechEIB33_B5q_EE0s3_00s0_0EECscDgN54JpMGG_6author Unexecuted instantiation: _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1y_B1x_EINtNtNtB1E_8adapters3map3MapINtNtNtB7_4trie6nibble14BytesToNibblesINtNtB2F_6copied6CopiedINtNtNtB1G_5slice4iter4IterhEEENvYhINtNtB1G_7convert4FromNtB35_6NibbleE4fromEECscDgN54JpMGG_6author Unexecuted instantiation: _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2w_6option8IntoIterINtB1C_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB3Z_14SyncBackground12author_block0s_00EINtNtB2s_6copied6CopiedINtNtNtB2w_5slice4iter4IterhEEECsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase17block_storage_getINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2w_6option8IntoIterINtB1C_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s0_00EINtNtB2s_6copied6CopiedINtNtNtB2w_5slice4iter4IterhEEECsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1y_B1x_EINtNtNtB1E_8adapters3map3MapINtNtNtB1G_5slice4iter4IterNtNtNtB7_4trie6nibble6NibbleENCNCNCNCINvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service24execute_block_and_insertRINtNtCsdZExvAaxgia_5alloc3vec3VechEIB33_B5q_EE0s3_00s0_0EECsibGXYHQB8Ea_25json_rpc_general_requests _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase17block_storage_getINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1y_B1x_EINtNtNtB1E_8adapters3map3MapINtNtNtB7_4trie6nibble14BytesToNibblesINtNtB2F_6copied6CopiedINtNtNtB1G_5slice4iter4IterhEEENvYhINtNtB1G_7convert4FromNtB35_6NibbleE4fromEECsibGXYHQB8Ea_25json_rpc_general_requests Line | Count | Source | 786 | 38 | pub fn block_storage_get( | 787 | 38 | &self, | 788 | 38 | block_hash: &[u8; 32], | 789 | 38 | parent_tries_paths_nibbles: impl Iterator<Item = impl Iterator<Item = u8>>, | 790 | 38 | key_nibbles: impl Iterator<Item = u8>, | 791 | 38 | ) -> Result<Option<(Vec<u8>, u8)>, StorageAccessError> { | 792 | 38 | // Process the iterators at the very beginning and before locking the database, in order | 793 | 38 | // to avoid a deadlock in case the `next()` function of one of the iterators accesses | 794 | 38 | // the database as well. | 795 | 38 | let key_vectored = parent_tries_paths_nibbles | 796 | 38 | .flat_map(|t| t.inspect(|n| assert!(*n < 16)).chain(iter::once(0x10))) | 797 | 38 | .chain(key_nibbles.inspect(|n| assert!(*n < 16))) | 798 | 38 | .collect::<Vec<_>>(); | 799 | 38 | | 800 | 38 | let connection = self.database.lock(); | 801 | | | 802 | | // TODO: could be optimized by having a different request when `parent_tries_paths_nibbles` is empty and when it isn't | 803 | | // TODO: trie_root_ref system untested | 804 | | // TODO: infinite loop if there's a loop in the trie; detect this | 805 | 38 | let mut statement = connection | 806 | 38 | .prepare_cached( | 807 | 38 | r#" | 808 | 38 | WITH RECURSIVE | 809 | 38 | -- At the end of the recursive statement, `node_with_key` must always contain | 810 | 38 | -- one and exactly one item where `search_remain` is either empty or null. Empty | 811 | 38 | -- indicates that we have found a match, while null means that the search has | 812 | 38 | -- been interrupted due to a storage entry not being in the database. If | 813 | 38 | -- `search_remain` is empty, then `node_hash` is either a hash in case of a match | 814 | 38 | -- or null in case there is no entry with the requested key. If `search_remain` | 815 | 38 | -- is null, then `node_hash` is irrelevant. | 816 | 38 | -- | 817 | 38 | -- In order to properly handle the situation where the key is empty, the initial | 818 | 38 | -- request of the recursive table building must check whether the partial key of | 819 | 38 | -- the root matches. In other words, all the entries of `node_with_key` (where | 820 | 38 | -- `node_hash` is non-null) contain entries that are known to be in the database | 821 | 38 | -- and after the partial key has already been verified to be correct. | 822 | 38 | node_with_key(node_hash, search_remain) AS ( | 823 | 38 | SELECT | 824 | 38 | IIF(COALESCE(SUBSTR(:key, 1, LENGTH(trie_node.partial_key)), X'') = trie_node.partial_key, trie_node.hash, NULL), | 825 | 38 | IIF(trie_node.partial_key IS NULL, NULL, COALESCE(SUBSTR(:key, 1 + LENGTH(trie_node.partial_key)), X'')) | 826 | 38 | FROM blocks | 827 | 38 | LEFT JOIN trie_node ON blocks.state_trie_root_hash = trie_node.hash | 828 | 38 | WHERE blocks.hash = :block_hash | 829 | 38 | UNION ALL | 830 | 38 | SELECT | 831 | 38 | CASE | 832 | 38 | WHEN HEX(SUBSTR(node_with_key.search_remain, 1, 1)) = '10' THEN trie_node_storage.trie_root_ref | 833 | 38 | WHEN SUBSTR(node_with_key.search_remain, 2, LENGTH(trie_node.partial_key)) = trie_node.partial_key THEN trie_node_child.child_hash | 834 | 38 | ELSE NULL END, | 835 | 38 | CASE | 836 | 38 | WHEN HEX(SUBSTR(node_with_key.search_remain, 1, 1)) = '10' THEN SUBSTR(node_with_key.search_remain, 1) | 837 | 38 | WHEN trie_node_child.child_hash IS NULL THEN X'' | 838 | 38 | WHEN trie_node.partial_key IS NULL THEN NULL | 839 | 38 | WHEN SUBSTR(node_with_key.search_remain, 2, LENGTH(trie_node.partial_key)) = trie_node.partial_key THEN SUBSTR(node_with_key.search_remain, 2 + LENGTH(trie_node.partial_key)) | 840 | 38 | ELSE X'' END | 841 | 38 | FROM node_with_key | 842 | 38 | LEFT JOIN trie_node_child | 843 | 38 | ON node_with_key.node_hash = trie_node_child.hash | 844 | 38 | AND SUBSTR(node_with_key.search_remain, 1, 1) = trie_node_child.child_num | 845 | 38 | LEFT JOIN trie_node | 846 | 38 | ON trie_node.hash = trie_node_child.child_hash | 847 | 38 | LEFT JOIN trie_node_storage | 848 | 38 | ON node_with_key.node_hash = trie_node_storage.node_hash | 849 | 38 | WHERE LENGTH(node_with_key.search_remain) >= 1 | 850 | 38 | ) | 851 | 38 | SELECT COUNT(blocks.hash) >= 1, node_with_key.search_remain IS NULL, COALESCE(trie_node_storage.value, trie_node_storage.trie_root_ref), trie_node_storage.trie_entry_version | 852 | 38 | FROM blocks | 853 | 38 | JOIN node_with_key ON LENGTH(node_with_key.search_remain) = 0 OR node_with_key.search_remain IS NULL | 854 | 38 | LEFT JOIN trie_node_storage ON node_with_key.node_hash = trie_node_storage.node_hash AND node_with_key.search_remain IS NOT NULL | 855 | 38 | WHERE blocks.hash = :block_hash; | 856 | 38 | "#) | 857 | 38 | .map_err(|err| { | 858 | | StorageAccessError::Corrupted(CorruptedError::Internal( | 859 | | InternalError(err), | 860 | | )) | 861 | 38 | })?0 ; | 862 | | | 863 | | // In order to debug the SQL query above (for example in case of a failing test), | 864 | | // uncomment this block: | 865 | | // | 866 | | /*println!("{:?}", { | 867 | | let mut statement = connection | 868 | | .prepare_cached( | 869 | | r#" | 870 | | WITH RECURSIVE | 871 | | copy-paste the definition of node_with_key here | 872 | | | 873 | | SELECT * FROM node_with_key"#).unwrap(); | 874 | | statement | 875 | | .query_map( | 876 | | rusqlite::named_params! { | 877 | | ":block_hash": &block_hash[..], | 878 | | ":key": key_vectored, | 879 | | }, | 880 | | |row| { | 881 | | let node_hash = row.get::<_, Option<Vec<u8>>>(0)?.map(hex::encode); | 882 | | let search_remain = row.get::<_, Option<Vec<u8>>>(1)?; | 883 | | Ok((node_hash, search_remain)) | 884 | | }, | 885 | | ) | 886 | | .unwrap() | 887 | | .collect::<Vec<_>>() | 888 | | });*/ | 889 | | | 890 | 38 | let (has_block, incomplete_storage, value, trie_entry_version) = statement | 891 | 38 | .query_row( | 892 | 38 | rusqlite::named_params! { | 893 | 38 | ":block_hash": &block_hash[..], | 894 | 38 | ":key": key_vectored, | 895 | 38 | }, | 896 | 38 | |row| { | 897 | | let has_block = row.get::<_, i64>(0)? != 0; | 898 | | let incomplete_storage = row.get::<_, i64>(1)? != 0; | 899 | | let value = row.get::<_, Option<Vec<u8>>>(2)?; | 900 | | let trie_entry_version = row.get::<_, Option<i64>>(3)?; | 901 | | Ok((has_block, incomplete_storage, value, trie_entry_version)) | 902 | 38 | }, | 903 | 38 | ) | 904 | 38 | .map_err(|err| { | 905 | | StorageAccessError::Corrupted(CorruptedError::Internal(InternalError(err))) | 906 | 38 | })?0 ; | 907 | | | 908 | 38 | if !has_block { | 909 | 0 | return Err(StorageAccessError::UnknownBlock); | 910 | 38 | } | 911 | 38 | | 912 | 38 | if incomplete_storage { | 913 | 0 | return Err(StorageAccessError::IncompleteStorage); | 914 | 38 | } | 915 | | | 916 | 38 | let Some(value19 ) = value else { return Ok(None)19 }; | 917 | | | 918 | 19 | let trie_entry_version = u8::try_from(trie_entry_version.unwrap()) | 919 | 19 | .map_err(|_| CorruptedError::InvalidTrieEntryVersion) | 920 | 19 | .map_err(StorageAccessError::Corrupted)?0 ; | 921 | 19 | Ok(Some((value, trie_entry_version))) | 922 | 38 | } |
|
923 | | |
924 | | /// Returns the key in the storage that immediately follows or is equal to the key passed as |
925 | | /// parameter in the storage of the block. |
926 | | /// |
927 | | /// `key_nibbles` must be an iterator to the **nibbles** of the key. |
928 | | /// |
929 | | /// `prefix_nibbles` must be an iterator to nibbles. If the result of the function wouldn't |
930 | | /// start with this specific list of bytes, `None` is returned. |
931 | | /// |
932 | | /// `parent_tries_paths_nibbles` is a list of keys to follow in order to find the root of the |
933 | | /// trie into which `key_nibbles` should be searched. |
934 | | /// |
935 | | /// Returns `None` if `parent_tries_paths_nibbles` didn't lead to any trie, or if there is no |
936 | | /// next key. |
937 | | /// |
938 | | /// The key is returned in the same format as `key_nibbles`. |
939 | | /// |
940 | | /// If `branch_nodes` is `false`, then branch nodes (i.e. nodes with no value associated to |
941 | | /// them) are ignored during the search. |
942 | | /// |
943 | | /// > **Note**: Contrary to many other similar functions in smoldot, there is no `or_equal` |
944 | | /// > parameter to this function. Instead, `or_equal` is implicitly `true`, and a |
945 | | /// > value of `false` can be easily emulated by appending a `0` at the end |
946 | | /// > of `key_nibbles`. |
947 | | /// |
948 | | /// # Panics |
949 | | /// |
950 | | /// Panics if any of the values yielded by `parent_tries_paths_nibbles`, `key_nibbles`, or |
951 | | /// `prefix_nibbles` is superior or equal to 16. |
952 | | /// |
953 | 1.04M | pub fn block_storage_next_key( |
954 | 1.04M | &self, |
955 | 1.04M | block_hash: &[u8; 32], |
956 | 1.04M | parent_tries_paths_nibbles: impl Iterator<Item = impl Iterator<Item = u8>>, |
957 | 1.04M | key_nibbles: impl Iterator<Item = u8>, |
958 | 1.04M | prefix_nibbles: impl Iterator<Item = u8>, |
959 | 1.04M | branch_nodes: bool, |
960 | 1.04M | ) -> Result<Option<Vec<u8>>, StorageAccessError> { |
961 | 1.04M | // Process the iterators at the very beginning and before locking the database, in order |
962 | 1.04M | // to avoid a deadlock in case the `next()` function of one of the iterators accesses |
963 | 1.04M | // the database as well. |
964 | 1.04M | let parent_tries_paths_nibbles = parent_tries_paths_nibbles |
965 | 1.04M | .flat_map(|t| t.inspect(0 |n| assert!(*n < 160 )0 ).chain(iter::once(0x10))0 ) Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1K_8adapters3map3MapINtNtB2L_6copied6CopiedINtNtNtB1M_5slice4iter4IterNtNtNtB9_4trie6nibble6NibbleEENvYhINtNtB1M_7convert4FromB3W_E4fromEB2G_E0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1M_5array4iter8IntoIterhKj0_EB1D_E0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1M_5array4iter8IntoIterhKj0_EB2G_E0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1M_5array4iter8IntoIterhKj1_EB1D_E0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1M_5array4iter8IntoIterhKj2_EB1D_E0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1M_5array4iter8IntoIterhKj3_EB1D_E0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1M_5array4iter8IntoIterhKj3_EB2G_E0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1M_5array4iter8IntoIterhKj8_EB1D_E0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1F_B1E_EINtNtNtB1L_8adapters3map3MapINtCs1qmLyiTSqYF_6either6EitherINtNtNtB9_4trie6nibble14BytesToNibblesINtNtB2M_6copied6CopiedINtNtNtB1N_5slice4iter4IterhEEEIB3a_IB3F_INtNvNtB9_4util11as_ref_iter4IterIB3a_RShINtNvMs5_NtNtNtB9_8executor2vm11interpreterNtB66_11Interpreter11read_memory12AccessOffsetB5U_EEhEEINtNtB2M_7flatten7FlatMapINtNtB2M_5chain5ChainIB2I_IB7y_IB4E_NtNtB6a_20trie_root_calculator14InProgressNodeEIB7X_INtNtB1J_4once4OnceIB3a_ANtB3H_6Nibblej1_RINtNtCsdZExvAaxgia_5alloc3vec3VecB9L_EEEINtNtB1N_6option8IntoIterB9F_EENCNvMs3_B8y_NtB8y_5Inner21current_node_full_key0ENcNtIB3a_B9F_Ba1_E4Left0EIB9n_Bc2_EEIB5j_Bcs_B9L_EIB5l_B9L_Bcs_EEEENvYhINtNtB1N_7convert4FromB9L_E4fromEIB2I_IB3a_IB3F_IB5j_IB3a_B5P_B5U_EhEEIB3a_B39_IB1F_B9L_EEEBd3_EE0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2D_6option8IntoIterINtB1J_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB46_14SyncBackground12author_block0s5_00EINtNtB2z_6copied6CopiedINtNtNtB2D_5slice4iter4IterhEEB5F_E0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2D_6option8IntoIterINtB1J_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s6_00EINtNtB2z_6copied6CopiedINtNtNtB2D_5slice4iter4IterhEEB5g_E0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2D_6option8IntoIterINtB1J_3VechEENCNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sd_00EINtNtB2z_6copied6CopiedINtNtNtB2D_5slice4iter4IterhEEB5J_E0B47_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1F_B1E_EINtNtNtB1L_8adapters6copied6CopiedINtNtNtB1N_5slice4iter4IterhEEB2H_E0CsiUjFBJteJ7x_17smoldot_full_node Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2D_6option8IntoIterINtB1J_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB46_14SyncBackground12author_block0s5_00EINtNtB2z_6copied6CopiedINtNtNtB2D_5slice4iter4IterhEEB5F_E0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2D_6option8IntoIterINtB1J_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s6_00EINtNtB2z_6copied6CopiedINtNtNtB2D_5slice4iter4IterhEEB5g_E0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2D_6option8IntoIterINtB1J_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB46_14SyncBackground12author_block0s5_00EINtNtB2z_6copied6CopiedINtNtNtB2D_5slice4iter4IterhEEB5F_E0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2D_6option8IntoIterINtB1J_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s6_00EINtNtB2z_6copied6CopiedINtNtNtB2D_5slice4iter4IterhEEB5g_E0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1G_B1F_EINtNtNtB1M_8adapters3map3MapINtNtB2N_6copied6CopiedINtNtNtB1O_5slice4iter4IterNtNtNtBb_4trie6nibble6NibbleEENvYhINtNtB1O_7convert4FromB3Y_E4fromEB2I_E00Bb_ Unexecuted instantiation: _RNCNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1G_B1F_EINtNtNtB1O_5array4iter8IntoIterhKj0_EB1F_E00Bb_ Unexecuted instantiation: _RNCNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1G_B1F_EINtNtNtB1O_5array4iter8IntoIterhKj0_EB2I_E00Bb_ Unexecuted instantiation: _RNCNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1G_B1F_EINtNtNtB1O_5array4iter8IntoIterhKj1_EB1F_E00Bb_ Unexecuted instantiation: _RNCNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1G_B1F_EINtNtNtB1O_5array4iter8IntoIterhKj2_EB1F_E00Bb_ Unexecuted instantiation: _RNCNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1G_B1F_EINtNtNtB1O_5array4iter8IntoIterhKj3_EB1F_E00Bb_ Unexecuted instantiation: _RNCNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1G_B1F_EINtNtNtB1O_5array4iter8IntoIterhKj3_EB2I_E00Bb_ Unexecuted instantiation: _RNCNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1G_B1F_EINtNtNtB1O_5array4iter8IntoIterhKj8_EB1F_E00Bb_ Unexecuted instantiation: _RNCNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1H_B1G_EINtNtNtB1N_8adapters3map3MapINtCs1qmLyiTSqYF_6either6EitherINtNtNtBb_4trie6nibble14BytesToNibblesINtNtB2O_6copied6CopiedINtNtNtB1P_5slice4iter4IterhEEEIB3c_IB3H_INtNvNtBb_4util11as_ref_iter4IterIB3c_RShINtNvMs5_NtNtNtBb_8executor2vm11interpreterNtB68_11Interpreter11read_memory12AccessOffsetB5W_EEhEEINtNtB2O_7flatten7FlatMapINtNtB2O_5chain5ChainIB2K_IB7A_IB4G_NtNtB6c_20trie_root_calculator14InProgressNodeEIB7Z_INtNtB1L_4once4OnceIB3c_ANtB3J_6Nibblej1_RINtNtCsdZExvAaxgia_5alloc3vec3VecB9N_EEEINtNtB1P_6option8IntoIterB9H_EENCNvMs3_B8A_NtB8A_5Inner21current_node_full_key0ENcNtIB3c_B9H_Ba3_E4Left0EIB9p_Bc4_EEIB5l_Bcu_B9N_EIB5n_B9N_Bcu_EEEENvYhINtNtB1P_7convert4FromB9N_E4fromEIB2K_IB3c_IB3H_IB5l_IB3c_B5R_B5W_EhEEIB3c_B3b_IB1H_B9N_EEEBd5_EE00Bb_ Unexecuted instantiation: _RNCNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2F_6option8IntoIterINtB1L_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB48_14SyncBackground12author_block0s5_00EINtNtB2B_6copied6CopiedINtNtNtB2F_5slice4iter4IterhEEB5H_E00CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2F_6option8IntoIterINtB1L_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s6_00EINtNtB2B_6copied6CopiedINtNtNtB2F_5slice4iter4IterhEEB5i_E00CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2F_6option8IntoIterINtB1L_3VechEENCNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sd_00EINtNtB2B_6copied6CopiedINtNtNtB2F_5slice4iter4IterhEEB5L_E00B49_ Unexecuted instantiation: _RNCNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1H_B1G_EINtNtNtB1N_8adapters6copied6CopiedINtNtNtB1P_5slice4iter4IterhEEB2J_E00CsiUjFBJteJ7x_17smoldot_full_node Unexecuted instantiation: _RNCNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2F_6option8IntoIterINtB1L_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB48_14SyncBackground12author_block0s5_00EINtNtB2B_6copied6CopiedINtNtNtB2F_5slice4iter4IterhEEB5H_E00CscDgN54JpMGG_6author Unexecuted instantiation: _RNCNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2F_6option8IntoIterINtB1L_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s6_00EINtNtB2B_6copied6CopiedINtNtNtB2F_5slice4iter4IterhEEB5i_E00CscDgN54JpMGG_6author Unexecuted instantiation: _RNCNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2F_6option8IntoIterINtB1L_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB48_14SyncBackground12author_block0s5_00EINtNtB2B_6copied6CopiedINtNtNtB2F_5slice4iter4IterhEEB5H_E00CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2F_6option8IntoIterINtB1L_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s6_00EINtNtB2B_6copied6CopiedINtNtNtB2F_5slice4iter4IterhEEB5i_E00CsibGXYHQB8Ea_25json_rpc_general_requests |
966 | 1.04M | .collect::<Vec<_>>(); |
967 | 1.04M | let parent_tries_paths_nibbles_length = parent_tries_paths_nibbles.len(); |
968 | 1.04M | let key_nibbles = { |
969 | 1.04M | let mut v = parent_tries_paths_nibbles.clone(); |
970 | 4.20M | v.extend(key_nibbles.inspect(|n| assert!(*n < 16))); _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1K_8adapters3map3MapINtNtB2L_6copied6CopiedINtNtNtB1M_5slice4iter4IterNtNtNtB9_4trie6nibble6NibbleEENvYhINtNtB1M_7convert4FromB3W_E4fromEB2G_Es_0B9_ Line | Count | Source | 970 | 4.19M | v.extend(key_nibbles.inspect(|n| assert!(*n < 16))); |
Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1M_5array4iter8IntoIterhKj0_EB1D_Es_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1M_5array4iter8IntoIterhKj0_EB2G_Es_0B9_ _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1M_5array4iter8IntoIterhKj1_EB1D_Es_0B9_ Line | Count | Source | 970 | 1 | v.extend(key_nibbles.inspect(|n| assert!(*n < 16))); |
_RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1M_5array4iter8IntoIterhKj2_EB1D_Es_0B9_ Line | Count | Source | 970 | 8 | v.extend(key_nibbles.inspect(|n| assert!(*n < 16))); |
_RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1M_5array4iter8IntoIterhKj3_EB1D_Es_0B9_ Line | Count | Source | 970 | 15 | v.extend(key_nibbles.inspect(|n| assert!(*n < 16))); |
_RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1M_5array4iter8IntoIterhKj3_EB2G_Es_0B9_ Line | Count | Source | 970 | 3 | v.extend(key_nibbles.inspect(|n| assert!(*n < 16))); |
_RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1M_5array4iter8IntoIterhKj8_EB1D_Es_0B9_ Line | Count | Source | 970 | 16 | v.extend(key_nibbles.inspect(|n| assert!(*n < 16))); |
Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1F_B1E_EINtNtNtB1L_8adapters3map3MapINtCs1qmLyiTSqYF_6either6EitherINtNtNtB9_4trie6nibble14BytesToNibblesINtNtB2M_6copied6CopiedINtNtNtB1N_5slice4iter4IterhEEEIB3a_IB3F_INtNvNtB9_4util11as_ref_iter4IterIB3a_RShINtNvMs5_NtNtNtB9_8executor2vm11interpreterNtB66_11Interpreter11read_memory12AccessOffsetB5U_EEhEEINtNtB2M_7flatten7FlatMapINtNtB2M_5chain5ChainIB2I_IB7y_IB4E_NtNtB6a_20trie_root_calculator14InProgressNodeEIB7X_INtNtB1J_4once4OnceIB3a_ANtB3H_6Nibblej1_RINtNtCsdZExvAaxgia_5alloc3vec3VecB9L_EEEINtNtB1N_6option8IntoIterB9F_EENCNvMs3_B8y_NtB8y_5Inner21current_node_full_key0ENcNtIB3a_B9F_Ba1_E4Left0EIB9n_Bc2_EEIB5j_Bcs_B9L_EIB5l_B9L_Bcs_EEEENvYhINtNtB1N_7convert4FromB9L_E4fromEIB2I_IB3a_IB3F_IB5j_IB3a_B5P_B5U_EhEEIB3a_B39_IB1F_B9L_EEEBd3_EEs_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2D_6option8IntoIterINtB1J_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB46_14SyncBackground12author_block0s5_00EINtNtB2z_6copied6CopiedINtNtNtB2D_5slice4iter4IterhEEB5F_Es_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2D_6option8IntoIterINtB1J_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s6_00EINtNtB2z_6copied6CopiedINtNtNtB2D_5slice4iter4IterhEEB5g_Es_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2D_6option8IntoIterINtB1J_3VechEENCNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sd_00EINtNtB2z_6copied6CopiedINtNtNtB2D_5slice4iter4IterhEEB5J_Es_0B47_ _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1F_B1E_EINtNtNtB1L_8adapters6copied6CopiedINtNtNtB1N_5slice4iter4IterhEEB2H_Es_0CsiUjFBJteJ7x_17smoldot_full_node Line | Count | Source | 970 | 4.44k | v.extend(key_nibbles.inspect(|n| assert!(*n < 16))); |
Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2D_6option8IntoIterINtB1J_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB46_14SyncBackground12author_block0s5_00EINtNtB2z_6copied6CopiedINtNtNtB2D_5slice4iter4IterhEEB5F_Es_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2D_6option8IntoIterINtB1J_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s6_00EINtNtB2z_6copied6CopiedINtNtNtB2D_5slice4iter4IterhEEB5g_Es_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2D_6option8IntoIterINtB1J_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB46_14SyncBackground12author_block0s5_00EINtNtB2z_6copied6CopiedINtNtNtB2D_5slice4iter4IterhEEB5F_Es_0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2D_6option8IntoIterINtB1J_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s6_00EINtNtB2z_6copied6CopiedINtNtNtB2D_5slice4iter4IterhEEB5g_Es_0CsibGXYHQB8Ea_25json_rpc_general_requests |
971 | 1.04M | v |
972 | 1.04M | }; |
973 | 1.04M | let prefix_nibbles = { |
974 | 1.04M | let mut v = parent_tries_paths_nibbles; |
975 | 4.19M | v.extend(prefix_nibbles.inspect(|n| assert!(*n < 16))); _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1K_8adapters3map3MapINtNtB2L_6copied6CopiedINtNtNtB1M_5slice4iter4IterNtNtNtB9_4trie6nibble6NibbleEENvYhINtNtB1M_7convert4FromB3W_E4fromEB2G_Es0_0B9_ Line | Count | Source | 975 | 4.19M | v.extend(prefix_nibbles.inspect(|n| assert!(*n < 16))); |
Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1M_5array4iter8IntoIterhKj0_EB1D_Es0_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1M_5array4iter8IntoIterhKj0_EB2G_Es0_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1M_5array4iter8IntoIterhKj1_EB1D_Es0_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1M_5array4iter8IntoIterhKj2_EB1D_Es0_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1M_5array4iter8IntoIterhKj3_EB1D_Es0_0B9_ _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1M_5array4iter8IntoIterhKj3_EB2G_Es0_0B9_ Line | Count | Source | 975 | 3 | v.extend(prefix_nibbles.inspect(|n| assert!(*n < 16))); |
Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1M_5array4iter8IntoIterhKj8_EB1D_Es0_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1F_B1E_EINtNtNtB1L_8adapters3map3MapINtCs1qmLyiTSqYF_6either6EitherINtNtNtB9_4trie6nibble14BytesToNibblesINtNtB2M_6copied6CopiedINtNtNtB1N_5slice4iter4IterhEEEIB3a_IB3F_INtNvNtB9_4util11as_ref_iter4IterIB3a_RShINtNvMs5_NtNtNtB9_8executor2vm11interpreterNtB66_11Interpreter11read_memory12AccessOffsetB5U_EEhEEINtNtB2M_7flatten7FlatMapINtNtB2M_5chain5ChainIB2I_IB7y_IB4E_NtNtB6a_20trie_root_calculator14InProgressNodeEIB7X_INtNtB1J_4once4OnceIB3a_ANtB3H_6Nibblej1_RINtNtCsdZExvAaxgia_5alloc3vec3VecB9L_EEEINtNtB1N_6option8IntoIterB9F_EENCNvMs3_B8y_NtB8y_5Inner21current_node_full_key0ENcNtIB3a_B9F_Ba1_E4Left0EIB9n_Bc2_EEIB5j_Bcs_B9L_EIB5l_B9L_Bcs_EEEENvYhINtNtB1N_7convert4FromB9L_E4fromEIB2I_IB3a_IB3F_IB5j_IB3a_B5P_B5U_EhEEIB3a_B39_IB1F_B9L_EEEBd3_EEs0_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2D_6option8IntoIterINtB1J_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB46_14SyncBackground12author_block0s5_00EINtNtB2z_6copied6CopiedINtNtNtB2D_5slice4iter4IterhEEB5F_Es0_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2D_6option8IntoIterINtB1J_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s6_00EINtNtB2z_6copied6CopiedINtNtNtB2D_5slice4iter4IterhEEB5g_Es0_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2D_6option8IntoIterINtB1J_3VechEENCNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sd_00EINtNtB2z_6copied6CopiedINtNtNtB2D_5slice4iter4IterhEEB5J_Es0_0B47_ _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1F_B1E_EINtNtNtB1L_8adapters6copied6CopiedINtNtNtB1N_5slice4iter4IterhEEB2H_Es0_0CsiUjFBJteJ7x_17smoldot_full_node Line | Count | Source | 975 | 16 | v.extend(prefix_nibbles.inspect(|n| assert!(*n < 16))); |
Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2D_6option8IntoIterINtB1J_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB46_14SyncBackground12author_block0s5_00EINtNtB2z_6copied6CopiedINtNtNtB2D_5slice4iter4IterhEEB5F_Es0_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2D_6option8IntoIterINtB1J_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s6_00EINtNtB2z_6copied6CopiedINtNtNtB2D_5slice4iter4IterhEEB5g_Es0_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2D_6option8IntoIterINtB1J_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB46_14SyncBackground12author_block0s5_00EINtNtB2z_6copied6CopiedINtNtNtB2D_5slice4iter4IterhEEB5F_Es0_0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2D_6option8IntoIterINtB1J_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s6_00EINtNtB2z_6copied6CopiedINtNtNtB2D_5slice4iter4IterhEEB5g_Es0_0CsibGXYHQB8Ea_25json_rpc_general_requests |
976 | 1.04M | v |
977 | 1.04M | }; |
978 | 1.04M | |
979 | 1.04M | let connection = self.database.lock(); |
980 | | |
981 | | // Sorry for that extremely complicated SQL statement. While the logic isn't actually very |
982 | | // complicated, we have to jump through many hoops in order to go around quirks in the |
983 | | // SQL language. |
984 | | // If you want to work on this SQL code, there is no miracle: write tests, and if a test |
985 | | // fails debug the content of `next_key` to find out where the iteration doesn't behave |
986 | | // as expected. |
987 | | // TODO: this algorithm relies the fact that leaf nodes always have a storage value, which isn't exactly clear in the schema ; however not relying on this makes it way harder to write |
988 | | // TODO: trie_root_ref system untested and most likely not working |
989 | | // TODO: infinite loop if there's a loop in the trie; detect this |
990 | | // TODO: could also check the prefix while iterating instead of only at the very end, which could maybe save many lookups |
991 | 1.04M | let mut statement = connection |
992 | 1.04M | .prepare_cached( |
993 | 1.04M | r#" |
994 | 1.04M | WITH RECURSIVE |
995 | 1.04M | -- We build a temporary table `next_key`, inserting entries one after one as we |
996 | 1.04M | -- descend the trie by trying to match entries with `:key`. |
997 | 1.04M | -- At each iteration, `node_hash` is the root where to continue the search, |
998 | 1.04M | -- `node_is_branch` is true if `node_hash` is a branch node, `node_full_key` is |
999 | 1.04M | -- the key of `node_hash` (that we build along the way) and serves as the final |
1000 | 1.04M | -- result, and `key_search_remain` contains the `:key` that remains to be matched. |
1001 | 1.04M | -- Can also be NULL to indicate that the search ended because the node necessary to |
1002 | 1.04M | -- continue was missing from the database, in which case the values of |
1003 | 1.04M | -- `node_hash` and `node_is_branch` have irrelevant values, and the value of |
1004 | 1.04M | -- `node_full_key` is the "best known key". |
1005 | 1.04M | -- If `:skip_branches` is false, the search ends when `key_search_remain` is null |
1006 | 1.04M | -- or empty. If `:skip_branches` is true, the search ends when `key_search_remain` |
1007 | 1.04M | -- is null or empty and that `node_is_branch` is false. |
1008 | 1.04M | -- |
1009 | 1.04M | -- `next_key` has zero elements if the block can't be found in the database or if |
1010 | 1.04M | -- the trie has no next key at all. These two situations need to be differentiated |
1011 | 1.04M | -- in the final SELECT statement. |
1012 | 1.04M | -- |
1013 | 1.04M | -- When encountering a node, we follow both the child that exactly matches `:key` |
1014 | 1.04M | -- and also the first child that is strictly superior to `:key`. This is necessary |
1015 | 1.04M | -- because `:key` might be equal to something like `ffffffff...`, in which case the |
1016 | 1.04M | -- result will be after any equal match. |
1017 | 1.04M | -- This means that the number of entries in `next_key` at the end of the recursion |
1018 | 1.04M | -- is something like `2 * depth_in_trie(key)`. |
1019 | 1.04M | -- In order to obtain the final result, we take the entry in `next_key` with the |
1020 | 1.04M | -- minimal `node_full_key` amongst the ones that have finished the search. |
1021 | 1.04M | -- |
1022 | 1.04M | -- Note that in the code below we do a lot of `COALESCE(SUBSTR(...), X'')`. This |
1023 | 1.04M | -- is because, for some reason, `SUBSTR(X'', ...)` always produces `NULL`. For this |
1024 | 1.04M | -- reason, it is also not possible to automatically pass NULL values |
1025 | 1.04M | -- through `SUSBTR`, and we have to use CASE/IIFs instead. |
1026 | 1.04M | next_key(node_hash, node_is_branch, node_full_key, key_search_remain) AS ( |
1027 | 1.04M | SELECT |
1028 | 1.04M | CASE |
1029 | 1.04M | WHEN trie_node.hash IS NULL |
1030 | 1.04M | THEN NULL |
1031 | 1.04M | WHEN COALESCE(SUBSTR(:key, 1, LENGTH(trie_node.partial_key)), X'') <= trie_node.partial_key |
1032 | 1.04M | THEN trie_node.hash |
1033 | 1.04M | ELSE |
1034 | 1.04M | NULL |
1035 | 1.04M | END, |
1036 | 1.04M | trie_node_storage.value IS NULL AND trie_node_storage.trie_root_ref IS NULL, |
1037 | 1.04M | COALESCE(trie_node.partial_key, X''), |
1038 | 1.04M | CASE |
1039 | 1.04M | WHEN trie_node.partial_key IS NULL |
1040 | 1.04M | THEN NULL |
1041 | 1.04M | WHEN COALESCE(SUBSTR(:key, 1, LENGTH(trie_node.partial_key)), X'') <= trie_node.partial_key |
1042 | 1.04M | THEN COALESCE(SUBSTR(:key, 1 + LENGTH(trie_node.partial_key)), X'') |
1043 | 1.04M | ELSE |
1044 | 1.04M | X'' -- The partial key is strictly inferior to `:key` |
1045 | 1.04M | END |
1046 | 1.04M | FROM blocks |
1047 | 1.04M | LEFT JOIN trie_node ON trie_node.hash = blocks.state_trie_root_hash |
1048 | 1.04M | LEFT JOIN trie_node_storage ON trie_node_storage.node_hash = trie_node.hash |
1049 | 1.04M | WHERE blocks.hash = :block_hash |
1050 | 1.04M | |
1051 | 1.04M | UNION ALL |
1052 | 1.04M | SELECT |
1053 | 1.04M | COALESCE(trie_node.hash, trie_node_trieref.hash), |
1054 | 1.04M | trie_node_storage.value IS NULL AND trie_node_storage.trie_root_ref IS NULL, |
1055 | 1.04M | CASE |
1056 | 1.04M | WHEN trie_node_child.child_num IS NULL |
1057 | 1.04M | THEN next_key.node_full_key |
1058 | 1.04M | WHEN trie_node.partial_key IS NULL AND trie_node_trieref.partial_key IS NULL |
1059 | 1.04M | THEN CAST(next_key.node_full_key || trie_node_child.child_num AS BLOB) |
1060 | 1.04M | ELSE |
1061 | 1.04M | CAST(next_key.node_full_key || trie_node_child.child_num || COALESCE(trie_node.partial_key, trie_node_trieref.partial_key) AS BLOB) |
1062 | 1.04M | END, |
1063 | 1.04M | CASE |
1064 | 1.04M | WHEN trie_node_child.child_num IS NOT NULL AND trie_node.partial_key IS NULL |
1065 | 1.04M | THEN NULL -- Child exists but is missing from database |
1066 | 1.04M | WHEN HEX(SUBSTR(next_key.key_search_remain, 1, 1)) = '10' AND trie_node_trieref.hash IS NULL |
1067 | 1.04M | THEN NULL -- Trie reference exists but is missing from database |
1068 | 1.04M | WHEN SUBSTR(next_key.key_search_remain, 1, 1) = trie_node_child.child_num AND SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node.partial_key)) = trie_node.partial_key |
1069 | 1.04M | THEN SUBSTR(next_key.key_search_remain, 2 + LENGTH(trie_node.partial_key)) -- Equal match, continue iterating |
1070 | 1.04M | WHEN SUBSTR(next_key.key_search_remain, 1, 1) = trie_node_child.child_num AND SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node.partial_key)) < trie_node.partial_key |
1071 | 1.04M | THEN X'' -- Searched key is before the node we are iterating to, thus we cut the search short |
1072 | 1.04M | WHEN HEX(SUBSTR(next_key.key_search_remain, 1, 1)) = '10' AND COALESCE(SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node_trieref.partial_key)), X'') = trie_node_trieref.partial_key |
1073 | 1.04M | THEN COALESCE(SUBSTR(next_key.key_search_remain, 2 + LENGTH(trie_node_trieref.partial_key)), X'') |
1074 | 1.04M | ELSE |
1075 | 1.04M | X'' -- Shouldn't be reachable. |
1076 | 1.04M | END |
1077 | 1.04M | FROM next_key |
1078 | 1.04M | |
1079 | 1.04M | LEFT JOIN trie_node_child |
1080 | 1.04M | ON next_key.node_hash = trie_node_child.hash |
1081 | 1.04M | AND CASE WHEN LENGTH(next_key.key_search_remain) = 0 THEN TRUE |
1082 | 1.04M | ELSE SUBSTR(next_key.key_search_remain, 1, 1) <= trie_node_child.child_num END |
1083 | 1.04M | LEFT JOIN trie_node ON trie_node.hash = trie_node_child.child_hash |
1084 | 1.04M | |
1085 | 1.04M | -- We want to keep only situations where `trie_node_child` is either |
1086 | 1.04M | -- equal to the key, or the first child strictly superior to the key. In |
1087 | 1.04M | -- order to do that, we try to find another child that is strictly |
1088 | 1.04M | -- in-between the key and `trie_node_child`. In the `WHERE` clause at the |
1089 | 1.04M | -- bottom, we only keep rows where `trie_node_child_before` is NULL. |
1090 | 1.04M | LEFT JOIN trie_node_child AS trie_node_child_before |
1091 | 1.04M | ON next_key.node_hash = trie_node_child_before.hash |
1092 | 1.04M | AND trie_node_child_before.child_num < trie_node_child.child_num |
1093 | 1.04M | AND (next_key.key_search_remain = X'' OR trie_node_child_before.child_num > SUBSTR(next_key.key_search_remain, 1, 1)) |
1094 | 1.04M | |
1095 | 1.04M | LEFT JOIN trie_node_storage AS trie_node_storage_trieref |
1096 | 1.04M | ON HEX(SUBSTR(next_key.key_search_remain, 1, 1)) = '10' AND next_key.node_hash = trie_node_storage_trieref.node_hash AND trie_node_storage_trieref.trie_root_ref IS NOT NULL |
1097 | 1.04M | LEFT JOIN trie_node AS trie_node_trieref |
1098 | 1.04M | ON trie_node_trieref.hash = trie_node_storage_trieref.node_hash |
1099 | 1.04M | AND COALESCE(SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node_trieref.partial_key)), X'') <= trie_node_trieref.partial_key |
1100 | 1.04M | |
1101 | 1.04M | LEFT JOIN trie_node_storage |
1102 | 1.04M | ON trie_node_storage.node_hash = COALESCE(trie_node.hash, trie_node_trieref.hash) |
1103 | 1.04M | |
1104 | 1.04M | WHERE |
1105 | 1.04M | -- Don't pull items that have already finished searching. |
1106 | 1.04M | next_key.node_hash IS NOT NULL AND next_key.key_search_remain IS NOT NULL AND (next_key.key_search_remain != X'' OR (next_key.node_is_branch AND :skip_branches)) |
1107 | 1.04M | -- See explanation above. |
1108 | 1.04M | AND trie_node_child_before.hash IS NULL |
1109 | 1.04M | -- Don't generate an item if there's nowhere to go to. |
1110 | 1.04M | AND (HEX(SUBSTR(next_key.key_search_remain, 1, 1)) = '10' OR trie_node_child.child_num IS NOT NULL) |
1111 | 1.04M | -- Stop iterating if the child's partial key is before the searched key. |
1112 | 1.04M | AND (trie_node.hash IS NULL OR NOT (COALESCE(SUBSTR(next_key.key_search_remain, 1, 1), X'') = trie_node_child.child_num AND COALESCE(SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node.partial_key)), X'') > trie_node.partial_key)) |
1113 | 1.04M | ), |
1114 | 1.04M | |
1115 | 1.04M | -- Now keep only the entries of `next_key` which have finished iterating. |
1116 | 1.04M | terminal_next_key(incomplete_storage, node_full_key, output) AS ( |
1117 | 1.04M | SELECT |
1118 | 1.04M | CASE |
1119 | 1.04M | WHEN COALESCE(SUBSTR(node_full_key, 1, LENGTH(:prefix)), X'') != :prefix THEN FALSE |
1120 | 1.04M | ELSE key_search_remain IS NULL |
1121 | 1.04M | END, |
1122 | 1.04M | node_full_key, |
1123 | 1.04M | CASE |
1124 | 1.04M | WHEN node_hash IS NULL THEN NULL |
1125 | 1.04M | WHEN COALESCE(SUBSTR(node_full_key, 1, LENGTH(:prefix)), X'') = :prefix THEN node_full_key |
1126 | 1.04M | ELSE NULL |
1127 | 1.04M | END |
1128 | 1.04M | FROM next_key |
1129 | 1.04M | WHERE key_search_remain IS NULL OR (LENGTH(key_search_remain) = 0 AND (NOT :skip_branches OR NOT node_is_branch)) |
1130 | 1.04M | ) |
1131 | 1.04M | |
1132 | 1.04M | SELECT |
1133 | 1.04M | COUNT(blocks.hash) >= 1, |
1134 | 1.04M | COALESCE(terminal_next_key.incomplete_storage, FALSE), |
1135 | 1.04M | terminal_next_key.output |
1136 | 1.04M | FROM blocks |
1137 | 1.04M | LEFT JOIN terminal_next_key |
1138 | 1.04M | WHERE blocks.hash = :block_hash |
1139 | 1.04M | -- We pick the entry of `terminal_next_key` with the smallest full key. Note that |
1140 | 1.04M | -- it might seem like a good idea to not using any GROUP BY and instead just do |
1141 | 1.04M | -- `ORDER BY node_full_key ASC LIMIT 1`, but doing so sometimes leads to SQLite |
1142 | 1.04M | -- not picking the entry with the smallest full key for a reason I couldn't |
1143 | 1.04M | -- figure out. |
1144 | 1.04M | AND (terminal_next_key.node_full_key IS NULL OR terminal_next_key.node_full_key = (SELECT MIN(node_full_key) FROM terminal_next_key)) |
1145 | 1.04M | LIMIT 1"#, |
1146 | 1.04M | ) |
1147 | 1.04M | .map_err(|err| { |
1148 | 0 | StorageAccessError::Corrupted(CorruptedError::Internal( |
1149 | 0 | InternalError(err), |
1150 | 0 | )) |
1151 | 1.04M | })?0 ; Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1K_8adapters3map3MapINtNtB2L_6copied6CopiedINtNtNtB1M_5slice4iter4IterNtNtNtB9_4trie6nibble6NibbleEENvYhINtNtB1M_7convert4FromB3W_E4fromEB2G_Es1_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1M_5array4iter8IntoIterhKj0_EB1D_Es1_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1M_5array4iter8IntoIterhKj0_EB2G_Es1_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1M_5array4iter8IntoIterhKj1_EB1D_Es1_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1M_5array4iter8IntoIterhKj2_EB1D_Es1_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1M_5array4iter8IntoIterhKj3_EB1D_Es1_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1M_5array4iter8IntoIterhKj3_EB2G_Es1_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1M_5array4iter8IntoIterhKj8_EB1D_Es1_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1F_B1E_EINtNtNtB1L_8adapters3map3MapINtCs1qmLyiTSqYF_6either6EitherINtNtNtB9_4trie6nibble14BytesToNibblesINtNtB2M_6copied6CopiedINtNtNtB1N_5slice4iter4IterhEEEIB3a_IB3F_INtNvNtB9_4util11as_ref_iter4IterIB3a_RShINtNvMs5_NtNtNtB9_8executor2vm11interpreterNtB66_11Interpreter11read_memory12AccessOffsetB5U_EEhEEINtNtB2M_7flatten7FlatMapINtNtB2M_5chain5ChainIB2I_IB7y_IB4E_NtNtB6a_20trie_root_calculator14InProgressNodeEIB7X_INtNtB1J_4once4OnceIB3a_ANtB3H_6Nibblej1_RINtNtCsdZExvAaxgia_5alloc3vec3VecB9L_EEEINtNtB1N_6option8IntoIterB9F_EENCNvMs3_B8y_NtB8y_5Inner21current_node_full_key0ENcNtIB3a_B9F_Ba1_E4Left0EIB9n_Bc2_EEIB5j_Bcs_B9L_EIB5l_B9L_Bcs_EEEENvYhINtNtB1N_7convert4FromB9L_E4fromEIB2I_IB3a_IB3F_IB5j_IB3a_B5P_B5U_EhEEIB3a_B39_IB1F_B9L_EEEBd3_EEs1_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2D_6option8IntoIterINtB1J_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB46_14SyncBackground12author_block0s5_00EINtNtB2z_6copied6CopiedINtNtNtB2D_5slice4iter4IterhEEB5F_Es1_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2D_6option8IntoIterINtB1J_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s6_00EINtNtB2z_6copied6CopiedINtNtNtB2D_5slice4iter4IterhEEB5g_Es1_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2D_6option8IntoIterINtB1J_3VechEENCNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sd_00EINtNtB2z_6copied6CopiedINtNtNtB2D_5slice4iter4IterhEEB5J_Es1_0B47_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1F_B1E_EINtNtNtB1L_8adapters6copied6CopiedINtNtNtB1N_5slice4iter4IterhEEB2H_Es1_0CsiUjFBJteJ7x_17smoldot_full_node Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2D_6option8IntoIterINtB1J_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB46_14SyncBackground12author_block0s5_00EINtNtB2z_6copied6CopiedINtNtNtB2D_5slice4iter4IterhEEB5F_Es1_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2D_6option8IntoIterINtB1J_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s6_00EINtNtB2z_6copied6CopiedINtNtNtB2D_5slice4iter4IterhEEB5g_Es1_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2D_6option8IntoIterINtB1J_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB46_14SyncBackground12author_block0s5_00EINtNtB2z_6copied6CopiedINtNtNtB2D_5slice4iter4IterhEEB5F_Es1_0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2D_6option8IntoIterINtB1J_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s6_00EINtNtB2z_6copied6CopiedINtNtNtB2D_5slice4iter4IterhEEB5g_Es1_0CsibGXYHQB8Ea_25json_rpc_general_requests |
1152 | | |
1153 | | // In order to debug the SQL query above (for example in case of a failing test), |
1154 | | // uncomment this block: |
1155 | | // |
1156 | | /*println!("{:?}", { |
1157 | | let mut statement = connection |
1158 | | .prepare_cached( |
1159 | | r#" |
1160 | | WITH RECURSIVE |
1161 | | copy-paste the definition of next_key here |
1162 | | |
1163 | | SELECT * FROM next_key"#).unwrap(); |
1164 | | statement |
1165 | | .query_map( |
1166 | | rusqlite::named_params! { |
1167 | | ":block_hash": &block_hash[..], |
1168 | | ":key": key_nibbles, |
1169 | | //":prefix": prefix_nibbles, |
1170 | | ":skip_branches": !branch_nodes |
1171 | | }, |
1172 | | |row| { |
1173 | | let node_hash = row.get::<_, Option<Vec<u8>>>(0)?.map(hex::encode); |
1174 | | let node_is_branch = row.get::<_, Option<i64>>(1)?.map(|n| n != 0); |
1175 | | let node_full_key = row.get::<_, Option<Vec<u8>>>(2)?; |
1176 | | let search_remain = row.get::<_, Option<Vec<u8>>>(3)?; |
1177 | | Ok((node_hash, node_is_branch, node_full_key, search_remain)) |
1178 | | }, |
1179 | | ) |
1180 | | .unwrap() |
1181 | | .collect::<Vec<_>>() |
1182 | | });*/ |
1183 | | |
1184 | 1.04M | let result = statement |
1185 | 1.04M | .query_row( |
1186 | 1.04M | rusqlite::named_params! { |
1187 | 1.04M | ":block_hash": &block_hash[..], |
1188 | 1.04M | ":key": key_nibbles, |
1189 | 1.04M | ":prefix": prefix_nibbles, |
1190 | 1.04M | ":skip_branches": !branch_nodes |
1191 | 1.04M | }, |
1192 | 1.04M | |row| { |
1193 | 1.04M | let block_is_known = row.get::<_, i64>(0)?0 != 0; |
1194 | 1.04M | let incomplete_storage = row.get::<_, i64>(1)?0 != 0; |
1195 | 1.04M | let next_key = row.get::<_, Option<Vec<u8>>>(2)?0 ; |
1196 | 1.04M | Ok((block_is_known, incomplete_storage, next_key)) |
1197 | 1.04M | }, _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1K_8adapters3map3MapINtNtB2L_6copied6CopiedINtNtNtB1M_5slice4iter4IterNtNtNtB9_4trie6nibble6NibbleEENvYhINtNtB1M_7convert4FromB3W_E4fromEB2G_Es2_0B9_ Line | Count | Source | 1192 | 1.04M | |row| { | 1193 | 1.04M | let block_is_known = row.get::<_, i64>(0)?0 != 0; | 1194 | 1.04M | let incomplete_storage = row.get::<_, i64>(1)?0 != 0; | 1195 | 1.04M | let next_key = row.get::<_, Option<Vec<u8>>>(2)?0 ; | 1196 | 1.04M | Ok((block_is_known, incomplete_storage, next_key)) | 1197 | 1.04M | }, |
_RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1M_5array4iter8IntoIterhKj0_EB1D_Es2_0B9_ Line | Count | Source | 1192 | 2 | |row| { | 1193 | 2 | let block_is_known = row.get::<_, i64>(0)?0 != 0; | 1194 | 2 | let incomplete_storage = row.get::<_, i64>(1)?0 != 0; | 1195 | 2 | let next_key = row.get::<_, Option<Vec<u8>>>(2)?0 ; | 1196 | 2 | Ok((block_is_known, incomplete_storage, next_key)) | 1197 | 2 | }, |
_RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1M_5array4iter8IntoIterhKj0_EB2G_Es2_0B9_ Line | Count | Source | 1192 | 2 | |row| { | 1193 | 2 | let block_is_known = row.get::<_, i64>(0)?0 != 0; | 1194 | 2 | let incomplete_storage = row.get::<_, i64>(1)?0 != 0; | 1195 | 2 | let next_key = row.get::<_, Option<Vec<u8>>>(2)?0 ; | 1196 | 2 | Ok((block_is_known, incomplete_storage, next_key)) | 1197 | 2 | }, |
_RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1M_5array4iter8IntoIterhKj1_EB1D_Es2_0B9_ Line | Count | Source | 1192 | 1 | |row| { | 1193 | 1 | let block_is_known = row.get::<_, i64>(0)?0 != 0; | 1194 | 1 | let incomplete_storage = row.get::<_, i64>(1)?0 != 0; | 1195 | 1 | let next_key = row.get::<_, Option<Vec<u8>>>(2)?0 ; | 1196 | 1 | Ok((block_is_known, incomplete_storage, next_key)) | 1197 | 1 | }, |
_RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1M_5array4iter8IntoIterhKj2_EB1D_Es2_0B9_ Line | Count | Source | 1192 | 4 | |row| { | 1193 | 4 | let block_is_known = row.get::<_, i64>(0)?0 != 0; | 1194 | 4 | let incomplete_storage = row.get::<_, i64>(1)?0 != 0; | 1195 | 4 | let next_key = row.get::<_, Option<Vec<u8>>>(2)?0 ; | 1196 | 4 | Ok((block_is_known, incomplete_storage, next_key)) | 1197 | 4 | }, |
_RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1M_5array4iter8IntoIterhKj3_EB1D_Es2_0B9_ Line | Count | Source | 1192 | 5 | |row| { | 1193 | 5 | let block_is_known = row.get::<_, i64>(0)?0 != 0; | 1194 | 5 | let incomplete_storage = row.get::<_, i64>(1)?0 != 0; | 1195 | 5 | let next_key = row.get::<_, Option<Vec<u8>>>(2)?0 ; | 1196 | 5 | Ok((block_is_known, incomplete_storage, next_key)) | 1197 | 5 | }, |
_RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1M_5array4iter8IntoIterhKj3_EB2G_Es2_0B9_ Line | Count | Source | 1192 | 1 | |row| { | 1193 | 1 | let block_is_known = row.get::<_, i64>(0)?0 != 0; | 1194 | 1 | let incomplete_storage = row.get::<_, i64>(1)?0 != 0; | 1195 | 1 | let next_key = row.get::<_, Option<Vec<u8>>>(2)?0 ; | 1196 | 1 | Ok((block_is_known, incomplete_storage, next_key)) | 1197 | 1 | }, |
_RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1M_5array4iter8IntoIterhKj8_EB1D_Es2_0B9_ Line | Count | Source | 1192 | 2 | |row| { | 1193 | 2 | let block_is_known = row.get::<_, i64>(0)?0 != 0; | 1194 | 2 | let incomplete_storage = row.get::<_, i64>(1)?0 != 0; | 1195 | 2 | let next_key = row.get::<_, Option<Vec<u8>>>(2)?0 ; | 1196 | 2 | Ok((block_is_known, incomplete_storage, next_key)) | 1197 | 2 | }, |
Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1F_B1E_EINtNtNtB1L_8adapters3map3MapINtCs1qmLyiTSqYF_6either6EitherINtNtNtB9_4trie6nibble14BytesToNibblesINtNtB2M_6copied6CopiedINtNtNtB1N_5slice4iter4IterhEEEIB3a_IB3F_INtNvNtB9_4util11as_ref_iter4IterIB3a_RShINtNvMs5_NtNtNtB9_8executor2vm11interpreterNtB66_11Interpreter11read_memory12AccessOffsetB5U_EEhEEINtNtB2M_7flatten7FlatMapINtNtB2M_5chain5ChainIB2I_IB7y_IB4E_NtNtB6a_20trie_root_calculator14InProgressNodeEIB7X_INtNtB1J_4once4OnceIB3a_ANtB3H_6Nibblej1_RINtNtCsdZExvAaxgia_5alloc3vec3VecB9L_EEEINtNtB1N_6option8IntoIterB9F_EENCNvMs3_B8y_NtB8y_5Inner21current_node_full_key0ENcNtIB3a_B9F_Ba1_E4Left0EIB9n_Bc2_EEIB5j_Bcs_B9L_EIB5l_B9L_Bcs_EEEENvYhINtNtB1N_7convert4FromB9L_E4fromEIB2I_IB3a_IB3F_IB5j_IB3a_B5P_B5U_EhEEIB3a_B39_IB1F_B9L_EEEBd3_EEs2_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2D_6option8IntoIterINtB1J_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB46_14SyncBackground12author_block0s5_00EINtNtB2z_6copied6CopiedINtNtNtB2D_5slice4iter4IterhEEB5F_Es2_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2D_6option8IntoIterINtB1J_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s6_00EINtNtB2z_6copied6CopiedINtNtNtB2D_5slice4iter4IterhEEB5g_Es2_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2D_6option8IntoIterINtB1J_3VechEENCNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sd_00EINtNtB2z_6copied6CopiedINtNtNtB2D_5slice4iter4IterhEEB5J_Es2_0B47_ _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1F_B1E_EINtNtNtB1L_8adapters6copied6CopiedINtNtNtB1N_5slice4iter4IterhEEB2H_Es2_0CsiUjFBJteJ7x_17smoldot_full_node Line | Count | Source | 1192 | 51 | |row| { | 1193 | 51 | let block_is_known = row.get::<_, i64>(0)?0 != 0; | 1194 | 51 | let incomplete_storage = row.get::<_, i64>(1)?0 != 0; | 1195 | 51 | let next_key = row.get::<_, Option<Vec<u8>>>(2)?0 ; | 1196 | 51 | Ok((block_is_known, incomplete_storage, next_key)) | 1197 | 51 | }, |
Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2D_6option8IntoIterINtB1J_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB46_14SyncBackground12author_block0s5_00EINtNtB2z_6copied6CopiedINtNtNtB2D_5slice4iter4IterhEEB5F_Es2_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2D_6option8IntoIterINtB1J_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s6_00EINtNtB2z_6copied6CopiedINtNtNtB2D_5slice4iter4IterhEEB5g_Es2_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2D_6option8IntoIterINtB1J_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB46_14SyncBackground12author_block0s5_00EINtNtB2z_6copied6CopiedINtNtNtB2D_5slice4iter4IterhEEB5F_Es2_0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2D_6option8IntoIterINtB1J_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s6_00EINtNtB2z_6copied6CopiedINtNtNtB2D_5slice4iter4IterhEEB5g_Es2_0CsibGXYHQB8Ea_25json_rpc_general_requests |
1198 | 1.04M | ) |
1199 | 1.04M | .optional() |
1200 | 1.04M | .map_err(|err| { |
1201 | 0 | StorageAccessError::Corrupted(CorruptedError::Internal(InternalError(err))) |
1202 | 1.04M | })?0 ; Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1K_8adapters3map3MapINtNtB2L_6copied6CopiedINtNtNtB1M_5slice4iter4IterNtNtNtB9_4trie6nibble6NibbleEENvYhINtNtB1M_7convert4FromB3W_E4fromEB2G_Es3_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1M_5array4iter8IntoIterhKj0_EB1D_Es3_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1M_5array4iter8IntoIterhKj0_EB2G_Es3_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1M_5array4iter8IntoIterhKj1_EB1D_Es3_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1M_5array4iter8IntoIterhKj2_EB1D_Es3_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1M_5array4iter8IntoIterhKj3_EB1D_Es3_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1M_5array4iter8IntoIterhKj3_EB2G_Es3_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1M_5array4iter8IntoIterhKj8_EB1D_Es3_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1F_B1E_EINtNtNtB1L_8adapters3map3MapINtCs1qmLyiTSqYF_6either6EitherINtNtNtB9_4trie6nibble14BytesToNibblesINtNtB2M_6copied6CopiedINtNtNtB1N_5slice4iter4IterhEEEIB3a_IB3F_INtNvNtB9_4util11as_ref_iter4IterIB3a_RShINtNvMs5_NtNtNtB9_8executor2vm11interpreterNtB66_11Interpreter11read_memory12AccessOffsetB5U_EEhEEINtNtB2M_7flatten7FlatMapINtNtB2M_5chain5ChainIB2I_IB7y_IB4E_NtNtB6a_20trie_root_calculator14InProgressNodeEIB7X_INtNtB1J_4once4OnceIB3a_ANtB3H_6Nibblej1_RINtNtCsdZExvAaxgia_5alloc3vec3VecB9L_EEEINtNtB1N_6option8IntoIterB9F_EENCNvMs3_B8y_NtB8y_5Inner21current_node_full_key0ENcNtIB3a_B9F_Ba1_E4Left0EIB9n_Bc2_EEIB5j_Bcs_B9L_EIB5l_B9L_Bcs_EEEENvYhINtNtB1N_7convert4FromB9L_E4fromEIB2I_IB3a_IB3F_IB5j_IB3a_B5P_B5U_EhEEIB3a_B39_IB1F_B9L_EEEBd3_EEs3_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2D_6option8IntoIterINtB1J_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB46_14SyncBackground12author_block0s5_00EINtNtB2z_6copied6CopiedINtNtNtB2D_5slice4iter4IterhEEB5F_Es3_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2D_6option8IntoIterINtB1J_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s6_00EINtNtB2z_6copied6CopiedINtNtNtB2D_5slice4iter4IterhEEB5g_Es3_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2D_6option8IntoIterINtB1J_3VechEENCNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sd_00EINtNtB2z_6copied6CopiedINtNtNtB2D_5slice4iter4IterhEEB5J_Es3_0B47_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1F_B1E_EINtNtNtB1L_8adapters6copied6CopiedINtNtNtB1N_5slice4iter4IterhEEB2H_Es3_0CsiUjFBJteJ7x_17smoldot_full_node Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2D_6option8IntoIterINtB1J_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB46_14SyncBackground12author_block0s5_00EINtNtB2z_6copied6CopiedINtNtNtB2D_5slice4iter4IterhEEB5F_Es3_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2D_6option8IntoIterINtB1J_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s6_00EINtNtB2z_6copied6CopiedINtNtNtB2D_5slice4iter4IterhEEB5g_Es3_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2D_6option8IntoIterINtB1J_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB46_14SyncBackground12author_block0s5_00EINtNtB2z_6copied6CopiedINtNtNtB2D_5slice4iter4IterhEEB5F_Es3_0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2D_6option8IntoIterINtB1J_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s6_00EINtNtB2z_6copied6CopiedINtNtNtB2D_5slice4iter4IterhEEB5g_Es3_0CsibGXYHQB8Ea_25json_rpc_general_requests |
1203 | | |
1204 | 1.04M | let Some((block_is_known, incomplete_storage, mut next_key)) = result else { |
1205 | 0 | return Ok(None); |
1206 | | }; |
1207 | | |
1208 | 1.04M | if !block_is_known { |
1209 | 3 | return Err(StorageAccessError::UnknownBlock); |
1210 | 1.04M | } |
1211 | 1.04M | |
1212 | 1.04M | if incomplete_storage { |
1213 | 6 | return Err(StorageAccessError::IncompleteStorage); |
1214 | 1.04M | } |
1215 | 1.04M | |
1216 | 1.04M | if parent_tries_paths_nibbles_length != 0 { |
1217 | 0 | next_key = next_key.map(|nk| nk[parent_tries_paths_nibbles_length..].to_vec()); Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1K_8adapters3map3MapINtNtB2L_6copied6CopiedINtNtNtB1M_5slice4iter4IterNtNtNtB9_4trie6nibble6NibbleEENvYhINtNtB1M_7convert4FromB3W_E4fromEB2G_Es4_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1M_5array4iter8IntoIterhKj0_EB1D_Es4_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1M_5array4iter8IntoIterhKj0_EB2G_Es4_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1M_5array4iter8IntoIterhKj1_EB1D_Es4_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1M_5array4iter8IntoIterhKj2_EB1D_Es4_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1M_5array4iter8IntoIterhKj3_EB1D_Es4_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1M_5array4iter8IntoIterhKj3_EB2G_Es4_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1E_B1D_EINtNtNtB1M_5array4iter8IntoIterhKj8_EB1D_Es4_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1F_B1E_EINtNtNtB1L_8adapters3map3MapINtCs1qmLyiTSqYF_6either6EitherINtNtNtB9_4trie6nibble14BytesToNibblesINtNtB2M_6copied6CopiedINtNtNtB1N_5slice4iter4IterhEEEIB3a_IB3F_INtNvNtB9_4util11as_ref_iter4IterIB3a_RShINtNvMs5_NtNtNtB9_8executor2vm11interpreterNtB66_11Interpreter11read_memory12AccessOffsetB5U_EEhEEINtNtB2M_7flatten7FlatMapINtNtB2M_5chain5ChainIB2I_IB7y_IB4E_NtNtB6a_20trie_root_calculator14InProgressNodeEIB7X_INtNtB1J_4once4OnceIB3a_ANtB3H_6Nibblej1_RINtNtCsdZExvAaxgia_5alloc3vec3VecB9L_EEEINtNtB1N_6option8IntoIterB9F_EENCNvMs3_B8y_NtB8y_5Inner21current_node_full_key0ENcNtIB3a_B9F_Ba1_E4Left0EIB9n_Bc2_EEIB5j_Bcs_B9L_EIB5l_B9L_Bcs_EEEENvYhINtNtB1N_7convert4FromB9L_E4fromEIB2I_IB3a_IB3F_IB5j_IB3a_B5P_B5U_EhEEIB3a_B39_IB1F_B9L_EEEBd3_EEs4_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2D_6option8IntoIterINtB1J_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB46_14SyncBackground12author_block0s5_00EINtNtB2z_6copied6CopiedINtNtNtB2D_5slice4iter4IterhEEB5F_Es4_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2D_6option8IntoIterINtB1J_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s6_00EINtNtB2z_6copied6CopiedINtNtNtB2D_5slice4iter4IterhEEB5g_Es4_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2D_6option8IntoIterINtB1J_3VechEENCNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sd_00EINtNtB2z_6copied6CopiedINtNtNtB2D_5slice4iter4IterhEEB5J_Es4_0B47_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1F_B1E_EINtNtNtB1L_8adapters6copied6CopiedINtNtNtB1N_5slice4iter4IterhEEB2H_Es4_0CsiUjFBJteJ7x_17smoldot_full_node Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2D_6option8IntoIterINtB1J_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB46_14SyncBackground12author_block0s5_00EINtNtB2z_6copied6CopiedINtNtNtB2D_5slice4iter4IterhEEB5F_Es4_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2D_6option8IntoIterINtB1J_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s6_00EINtNtB2z_6copied6CopiedINtNtNtB2D_5slice4iter4IterhEEB5g_Es4_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2D_6option8IntoIterINtB1J_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB46_14SyncBackground12author_block0s5_00EINtNtB2z_6copied6CopiedINtNtNtB2D_5slice4iter4IterhEEB5F_Es4_0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2D_6option8IntoIterINtB1J_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s6_00EINtNtB2z_6copied6CopiedINtNtNtB2D_5slice4iter4IterhEEB5g_Es4_0CsibGXYHQB8Ea_25json_rpc_general_requests |
1218 | 1.04M | } |
1219 | | |
1220 | 1.04M | Ok(next_key) |
1221 | 1.04M | } _RINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1C_B1B_EINtNtNtB1I_8adapters3map3MapINtNtB2J_6copied6CopiedINtNtNtB1K_5slice4iter4IterNtNtNtB7_4trie6nibble6NibbleEENvYhINtNtB1K_7convert4FromB3U_E4fromEB2E_EB7_ Line | Count | Source | 953 | 1.04M | pub fn block_storage_next_key( | 954 | 1.04M | &self, | 955 | 1.04M | block_hash: &[u8; 32], | 956 | 1.04M | parent_tries_paths_nibbles: impl Iterator<Item = impl Iterator<Item = u8>>, | 957 | 1.04M | key_nibbles: impl Iterator<Item = u8>, | 958 | 1.04M | prefix_nibbles: impl Iterator<Item = u8>, | 959 | 1.04M | branch_nodes: bool, | 960 | 1.04M | ) -> Result<Option<Vec<u8>>, StorageAccessError> { | 961 | 1.04M | // Process the iterators at the very beginning and before locking the database, in order | 962 | 1.04M | // to avoid a deadlock in case the `next()` function of one of the iterators accesses | 963 | 1.04M | // the database as well. | 964 | 1.04M | let parent_tries_paths_nibbles = parent_tries_paths_nibbles | 965 | 1.04M | .flat_map(|t| t.inspect(|n| assert!(*n < 16)).chain(iter::once(0x10))) | 966 | 1.04M | .collect::<Vec<_>>(); | 967 | 1.04M | let parent_tries_paths_nibbles_length = parent_tries_paths_nibbles.len(); | 968 | 1.04M | let key_nibbles = { | 969 | 1.04M | let mut v = parent_tries_paths_nibbles.clone(); | 970 | 1.04M | v.extend(key_nibbles.inspect(|n| assert!(*n < 16))); | 971 | 1.04M | v | 972 | 1.04M | }; | 973 | 1.04M | let prefix_nibbles = { | 974 | 1.04M | let mut v = parent_tries_paths_nibbles; | 975 | 1.04M | v.extend(prefix_nibbles.inspect(|n| assert!(*n < 16))); | 976 | 1.04M | v | 977 | 1.04M | }; | 978 | 1.04M | | 979 | 1.04M | let connection = self.database.lock(); | 980 | | | 981 | | // Sorry for that extremely complicated SQL statement. While the logic isn't actually very | 982 | | // complicated, we have to jump through many hoops in order to go around quirks in the | 983 | | // SQL language. | 984 | | // If you want to work on this SQL code, there is no miracle: write tests, and if a test | 985 | | // fails debug the content of `next_key` to find out where the iteration doesn't behave | 986 | | // as expected. | 987 | | // TODO: this algorithm relies the fact that leaf nodes always have a storage value, which isn't exactly clear in the schema ; however not relying on this makes it way harder to write | 988 | | // TODO: trie_root_ref system untested and most likely not working | 989 | | // TODO: infinite loop if there's a loop in the trie; detect this | 990 | | // TODO: could also check the prefix while iterating instead of only at the very end, which could maybe save many lookups | 991 | 1.04M | let mut statement = connection | 992 | 1.04M | .prepare_cached( | 993 | 1.04M | r#" | 994 | 1.04M | WITH RECURSIVE | 995 | 1.04M | -- We build a temporary table `next_key`, inserting entries one after one as we | 996 | 1.04M | -- descend the trie by trying to match entries with `:key`. | 997 | 1.04M | -- At each iteration, `node_hash` is the root where to continue the search, | 998 | 1.04M | -- `node_is_branch` is true if `node_hash` is a branch node, `node_full_key` is | 999 | 1.04M | -- the key of `node_hash` (that we build along the way) and serves as the final | 1000 | 1.04M | -- result, and `key_search_remain` contains the `:key` that remains to be matched. | 1001 | 1.04M | -- Can also be NULL to indicate that the search ended because the node necessary to | 1002 | 1.04M | -- continue was missing from the database, in which case the values of | 1003 | 1.04M | -- `node_hash` and `node_is_branch` have irrelevant values, and the value of | 1004 | 1.04M | -- `node_full_key` is the "best known key". | 1005 | 1.04M | -- If `:skip_branches` is false, the search ends when `key_search_remain` is null | 1006 | 1.04M | -- or empty. If `:skip_branches` is true, the search ends when `key_search_remain` | 1007 | 1.04M | -- is null or empty and that `node_is_branch` is false. | 1008 | 1.04M | -- | 1009 | 1.04M | -- `next_key` has zero elements if the block can't be found in the database or if | 1010 | 1.04M | -- the trie has no next key at all. These two situations need to be differentiated | 1011 | 1.04M | -- in the final SELECT statement. | 1012 | 1.04M | -- | 1013 | 1.04M | -- When encountering a node, we follow both the child that exactly matches `:key` | 1014 | 1.04M | -- and also the first child that is strictly superior to `:key`. This is necessary | 1015 | 1.04M | -- because `:key` might be equal to something like `ffffffff...`, in which case the | 1016 | 1.04M | -- result will be after any equal match. | 1017 | 1.04M | -- This means that the number of entries in `next_key` at the end of the recursion | 1018 | 1.04M | -- is something like `2 * depth_in_trie(key)`. | 1019 | 1.04M | -- In order to obtain the final result, we take the entry in `next_key` with the | 1020 | 1.04M | -- minimal `node_full_key` amongst the ones that have finished the search. | 1021 | 1.04M | -- | 1022 | 1.04M | -- Note that in the code below we do a lot of `COALESCE(SUBSTR(...), X'')`. This | 1023 | 1.04M | -- is because, for some reason, `SUBSTR(X'', ...)` always produces `NULL`. For this | 1024 | 1.04M | -- reason, it is also not possible to automatically pass NULL values | 1025 | 1.04M | -- through `SUSBTR`, and we have to use CASE/IIFs instead. | 1026 | 1.04M | next_key(node_hash, node_is_branch, node_full_key, key_search_remain) AS ( | 1027 | 1.04M | SELECT | 1028 | 1.04M | CASE | 1029 | 1.04M | WHEN trie_node.hash IS NULL | 1030 | 1.04M | THEN NULL | 1031 | 1.04M | WHEN COALESCE(SUBSTR(:key, 1, LENGTH(trie_node.partial_key)), X'') <= trie_node.partial_key | 1032 | 1.04M | THEN trie_node.hash | 1033 | 1.04M | ELSE | 1034 | 1.04M | NULL | 1035 | 1.04M | END, | 1036 | 1.04M | trie_node_storage.value IS NULL AND trie_node_storage.trie_root_ref IS NULL, | 1037 | 1.04M | COALESCE(trie_node.partial_key, X''), | 1038 | 1.04M | CASE | 1039 | 1.04M | WHEN trie_node.partial_key IS NULL | 1040 | 1.04M | THEN NULL | 1041 | 1.04M | WHEN COALESCE(SUBSTR(:key, 1, LENGTH(trie_node.partial_key)), X'') <= trie_node.partial_key | 1042 | 1.04M | THEN COALESCE(SUBSTR(:key, 1 + LENGTH(trie_node.partial_key)), X'') | 1043 | 1.04M | ELSE | 1044 | 1.04M | X'' -- The partial key is strictly inferior to `:key` | 1045 | 1.04M | END | 1046 | 1.04M | FROM blocks | 1047 | 1.04M | LEFT JOIN trie_node ON trie_node.hash = blocks.state_trie_root_hash | 1048 | 1.04M | LEFT JOIN trie_node_storage ON trie_node_storage.node_hash = trie_node.hash | 1049 | 1.04M | WHERE blocks.hash = :block_hash | 1050 | 1.04M | | 1051 | 1.04M | UNION ALL | 1052 | 1.04M | SELECT | 1053 | 1.04M | COALESCE(trie_node.hash, trie_node_trieref.hash), | 1054 | 1.04M | trie_node_storage.value IS NULL AND trie_node_storage.trie_root_ref IS NULL, | 1055 | 1.04M | CASE | 1056 | 1.04M | WHEN trie_node_child.child_num IS NULL | 1057 | 1.04M | THEN next_key.node_full_key | 1058 | 1.04M | WHEN trie_node.partial_key IS NULL AND trie_node_trieref.partial_key IS NULL | 1059 | 1.04M | THEN CAST(next_key.node_full_key || trie_node_child.child_num AS BLOB) | 1060 | 1.04M | ELSE | 1061 | 1.04M | CAST(next_key.node_full_key || trie_node_child.child_num || COALESCE(trie_node.partial_key, trie_node_trieref.partial_key) AS BLOB) | 1062 | 1.04M | END, | 1063 | 1.04M | CASE | 1064 | 1.04M | WHEN trie_node_child.child_num IS NOT NULL AND trie_node.partial_key IS NULL | 1065 | 1.04M | THEN NULL -- Child exists but is missing from database | 1066 | 1.04M | WHEN HEX(SUBSTR(next_key.key_search_remain, 1, 1)) = '10' AND trie_node_trieref.hash IS NULL | 1067 | 1.04M | THEN NULL -- Trie reference exists but is missing from database | 1068 | 1.04M | WHEN SUBSTR(next_key.key_search_remain, 1, 1) = trie_node_child.child_num AND SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node.partial_key)) = trie_node.partial_key | 1069 | 1.04M | THEN SUBSTR(next_key.key_search_remain, 2 + LENGTH(trie_node.partial_key)) -- Equal match, continue iterating | 1070 | 1.04M | WHEN SUBSTR(next_key.key_search_remain, 1, 1) = trie_node_child.child_num AND SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node.partial_key)) < trie_node.partial_key | 1071 | 1.04M | THEN X'' -- Searched key is before the node we are iterating to, thus we cut the search short | 1072 | 1.04M | WHEN HEX(SUBSTR(next_key.key_search_remain, 1, 1)) = '10' AND COALESCE(SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node_trieref.partial_key)), X'') = trie_node_trieref.partial_key | 1073 | 1.04M | THEN COALESCE(SUBSTR(next_key.key_search_remain, 2 + LENGTH(trie_node_trieref.partial_key)), X'') | 1074 | 1.04M | ELSE | 1075 | 1.04M | X'' -- Shouldn't be reachable. | 1076 | 1.04M | END | 1077 | 1.04M | FROM next_key | 1078 | 1.04M | | 1079 | 1.04M | LEFT JOIN trie_node_child | 1080 | 1.04M | ON next_key.node_hash = trie_node_child.hash | 1081 | 1.04M | AND CASE WHEN LENGTH(next_key.key_search_remain) = 0 THEN TRUE | 1082 | 1.04M | ELSE SUBSTR(next_key.key_search_remain, 1, 1) <= trie_node_child.child_num END | 1083 | 1.04M | LEFT JOIN trie_node ON trie_node.hash = trie_node_child.child_hash | 1084 | 1.04M | | 1085 | 1.04M | -- We want to keep only situations where `trie_node_child` is either | 1086 | 1.04M | -- equal to the key, or the first child strictly superior to the key. In | 1087 | 1.04M | -- order to do that, we try to find another child that is strictly | 1088 | 1.04M | -- in-between the key and `trie_node_child`. In the `WHERE` clause at the | 1089 | 1.04M | -- bottom, we only keep rows where `trie_node_child_before` is NULL. | 1090 | 1.04M | LEFT JOIN trie_node_child AS trie_node_child_before | 1091 | 1.04M | ON next_key.node_hash = trie_node_child_before.hash | 1092 | 1.04M | AND trie_node_child_before.child_num < trie_node_child.child_num | 1093 | 1.04M | AND (next_key.key_search_remain = X'' OR trie_node_child_before.child_num > SUBSTR(next_key.key_search_remain, 1, 1)) | 1094 | 1.04M | | 1095 | 1.04M | LEFT JOIN trie_node_storage AS trie_node_storage_trieref | 1096 | 1.04M | ON HEX(SUBSTR(next_key.key_search_remain, 1, 1)) = '10' AND next_key.node_hash = trie_node_storage_trieref.node_hash AND trie_node_storage_trieref.trie_root_ref IS NOT NULL | 1097 | 1.04M | LEFT JOIN trie_node AS trie_node_trieref | 1098 | 1.04M | ON trie_node_trieref.hash = trie_node_storage_trieref.node_hash | 1099 | 1.04M | AND COALESCE(SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node_trieref.partial_key)), X'') <= trie_node_trieref.partial_key | 1100 | 1.04M | | 1101 | 1.04M | LEFT JOIN trie_node_storage | 1102 | 1.04M | ON trie_node_storage.node_hash = COALESCE(trie_node.hash, trie_node_trieref.hash) | 1103 | 1.04M | | 1104 | 1.04M | WHERE | 1105 | 1.04M | -- Don't pull items that have already finished searching. | 1106 | 1.04M | next_key.node_hash IS NOT NULL AND next_key.key_search_remain IS NOT NULL AND (next_key.key_search_remain != X'' OR (next_key.node_is_branch AND :skip_branches)) | 1107 | 1.04M | -- See explanation above. | 1108 | 1.04M | AND trie_node_child_before.hash IS NULL | 1109 | 1.04M | -- Don't generate an item if there's nowhere to go to. | 1110 | 1.04M | AND (HEX(SUBSTR(next_key.key_search_remain, 1, 1)) = '10' OR trie_node_child.child_num IS NOT NULL) | 1111 | 1.04M | -- Stop iterating if the child's partial key is before the searched key. | 1112 | 1.04M | AND (trie_node.hash IS NULL OR NOT (COALESCE(SUBSTR(next_key.key_search_remain, 1, 1), X'') = trie_node_child.child_num AND COALESCE(SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node.partial_key)), X'') > trie_node.partial_key)) | 1113 | 1.04M | ), | 1114 | 1.04M | | 1115 | 1.04M | -- Now keep only the entries of `next_key` which have finished iterating. | 1116 | 1.04M | terminal_next_key(incomplete_storage, node_full_key, output) AS ( | 1117 | 1.04M | SELECT | 1118 | 1.04M | CASE | 1119 | 1.04M | WHEN COALESCE(SUBSTR(node_full_key, 1, LENGTH(:prefix)), X'') != :prefix THEN FALSE | 1120 | 1.04M | ELSE key_search_remain IS NULL | 1121 | 1.04M | END, | 1122 | 1.04M | node_full_key, | 1123 | 1.04M | CASE | 1124 | 1.04M | WHEN node_hash IS NULL THEN NULL | 1125 | 1.04M | WHEN COALESCE(SUBSTR(node_full_key, 1, LENGTH(:prefix)), X'') = :prefix THEN node_full_key | 1126 | 1.04M | ELSE NULL | 1127 | 1.04M | END | 1128 | 1.04M | FROM next_key | 1129 | 1.04M | WHERE key_search_remain IS NULL OR (LENGTH(key_search_remain) = 0 AND (NOT :skip_branches OR NOT node_is_branch)) | 1130 | 1.04M | ) | 1131 | 1.04M | | 1132 | 1.04M | SELECT | 1133 | 1.04M | COUNT(blocks.hash) >= 1, | 1134 | 1.04M | COALESCE(terminal_next_key.incomplete_storage, FALSE), | 1135 | 1.04M | terminal_next_key.output | 1136 | 1.04M | FROM blocks | 1137 | 1.04M | LEFT JOIN terminal_next_key | 1138 | 1.04M | WHERE blocks.hash = :block_hash | 1139 | 1.04M | -- We pick the entry of `terminal_next_key` with the smallest full key. Note that | 1140 | 1.04M | -- it might seem like a good idea to not using any GROUP BY and instead just do | 1141 | 1.04M | -- `ORDER BY node_full_key ASC LIMIT 1`, but doing so sometimes leads to SQLite | 1142 | 1.04M | -- not picking the entry with the smallest full key for a reason I couldn't | 1143 | 1.04M | -- figure out. | 1144 | 1.04M | AND (terminal_next_key.node_full_key IS NULL OR terminal_next_key.node_full_key = (SELECT MIN(node_full_key) FROM terminal_next_key)) | 1145 | 1.04M | LIMIT 1"#, | 1146 | 1.04M | ) | 1147 | 1.04M | .map_err(|err| { | 1148 | | StorageAccessError::Corrupted(CorruptedError::Internal( | 1149 | | InternalError(err), | 1150 | | )) | 1151 | 1.04M | })?0 ; | 1152 | | | 1153 | | // In order to debug the SQL query above (for example in case of a failing test), | 1154 | | // uncomment this block: | 1155 | | // | 1156 | | /*println!("{:?}", { | 1157 | | let mut statement = connection | 1158 | | .prepare_cached( | 1159 | | r#" | 1160 | | WITH RECURSIVE | 1161 | | copy-paste the definition of next_key here | 1162 | | | 1163 | | SELECT * FROM next_key"#).unwrap(); | 1164 | | statement | 1165 | | .query_map( | 1166 | | rusqlite::named_params! { | 1167 | | ":block_hash": &block_hash[..], | 1168 | | ":key": key_nibbles, | 1169 | | //":prefix": prefix_nibbles, | 1170 | | ":skip_branches": !branch_nodes | 1171 | | }, | 1172 | | |row| { | 1173 | | let node_hash = row.get::<_, Option<Vec<u8>>>(0)?.map(hex::encode); | 1174 | | let node_is_branch = row.get::<_, Option<i64>>(1)?.map(|n| n != 0); | 1175 | | let node_full_key = row.get::<_, Option<Vec<u8>>>(2)?; | 1176 | | let search_remain = row.get::<_, Option<Vec<u8>>>(3)?; | 1177 | | Ok((node_hash, node_is_branch, node_full_key, search_remain)) | 1178 | | }, | 1179 | | ) | 1180 | | .unwrap() | 1181 | | .collect::<Vec<_>>() | 1182 | | });*/ | 1183 | | | 1184 | 1.04M | let result = statement | 1185 | 1.04M | .query_row( | 1186 | 1.04M | rusqlite::named_params! { | 1187 | 1.04M | ":block_hash": &block_hash[..], | 1188 | 1.04M | ":key": key_nibbles, | 1189 | 1.04M | ":prefix": prefix_nibbles, | 1190 | 1.04M | ":skip_branches": !branch_nodes | 1191 | 1.04M | }, | 1192 | 1.04M | |row| { | 1193 | | let block_is_known = row.get::<_, i64>(0)? != 0; | 1194 | | let incomplete_storage = row.get::<_, i64>(1)? != 0; | 1195 | | let next_key = row.get::<_, Option<Vec<u8>>>(2)?; | 1196 | | Ok((block_is_known, incomplete_storage, next_key)) | 1197 | 1.04M | }, | 1198 | 1.04M | ) | 1199 | 1.04M | .optional() | 1200 | 1.04M | .map_err(|err| { | 1201 | | StorageAccessError::Corrupted(CorruptedError::Internal(InternalError(err))) | 1202 | 1.04M | })?0 ; | 1203 | | | 1204 | 1.04M | let Some((block_is_known, incomplete_storage, mut next_key)) = result else { | 1205 | 0 | return Ok(None); | 1206 | | }; | 1207 | | | 1208 | 1.04M | if !block_is_known { | 1209 | 0 | return Err(StorageAccessError::UnknownBlock); | 1210 | 1.04M | } | 1211 | 1.04M | | 1212 | 1.04M | if incomplete_storage { | 1213 | 0 | return Err(StorageAccessError::IncompleteStorage); | 1214 | 1.04M | } | 1215 | 1.04M | | 1216 | 1.04M | if parent_tries_paths_nibbles_length != 0 { | 1217 | 0 | next_key = next_key.map(|nk| nk[parent_tries_paths_nibbles_length..].to_vec()); | 1218 | 1.04M | } | 1219 | | | 1220 | 1.04M | Ok(next_key) | 1221 | 1.04M | } |
_RINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1C_B1B_EINtNtNtB1K_5array4iter8IntoIterhKj0_EB1B_EB7_ Line | Count | Source | 953 | 2 | pub fn block_storage_next_key( | 954 | 2 | &self, | 955 | 2 | block_hash: &[u8; 32], | 956 | 2 | parent_tries_paths_nibbles: impl Iterator<Item = impl Iterator<Item = u8>>, | 957 | 2 | key_nibbles: impl Iterator<Item = u8>, | 958 | 2 | prefix_nibbles: impl Iterator<Item = u8>, | 959 | 2 | branch_nodes: bool, | 960 | 2 | ) -> Result<Option<Vec<u8>>, StorageAccessError> { | 961 | 2 | // Process the iterators at the very beginning and before locking the database, in order | 962 | 2 | // to avoid a deadlock in case the `next()` function of one of the iterators accesses | 963 | 2 | // the database as well. | 964 | 2 | let parent_tries_paths_nibbles = parent_tries_paths_nibbles | 965 | 2 | .flat_map(|t| t.inspect(|n| assert!(*n < 16)).chain(iter::once(0x10))) | 966 | 2 | .collect::<Vec<_>>(); | 967 | 2 | let parent_tries_paths_nibbles_length = parent_tries_paths_nibbles.len(); | 968 | 2 | let key_nibbles = { | 969 | 2 | let mut v = parent_tries_paths_nibbles.clone(); | 970 | 2 | v.extend(key_nibbles.inspect(|n| assert!(*n < 16))); | 971 | 2 | v | 972 | 2 | }; | 973 | 2 | let prefix_nibbles = { | 974 | 2 | let mut v = parent_tries_paths_nibbles; | 975 | 2 | v.extend(prefix_nibbles.inspect(|n| assert!(*n < 16))); | 976 | 2 | v | 977 | 2 | }; | 978 | 2 | | 979 | 2 | let connection = self.database.lock(); | 980 | | | 981 | | // Sorry for that extremely complicated SQL statement. While the logic isn't actually very | 982 | | // complicated, we have to jump through many hoops in order to go around quirks in the | 983 | | // SQL language. | 984 | | // If you want to work on this SQL code, there is no miracle: write tests, and if a test | 985 | | // fails debug the content of `next_key` to find out where the iteration doesn't behave | 986 | | // as expected. | 987 | | // TODO: this algorithm relies the fact that leaf nodes always have a storage value, which isn't exactly clear in the schema ; however not relying on this makes it way harder to write | 988 | | // TODO: trie_root_ref system untested and most likely not working | 989 | | // TODO: infinite loop if there's a loop in the trie; detect this | 990 | | // TODO: could also check the prefix while iterating instead of only at the very end, which could maybe save many lookups | 991 | 2 | let mut statement = connection | 992 | 2 | .prepare_cached( | 993 | 2 | r#" | 994 | 2 | WITH RECURSIVE | 995 | 2 | -- We build a temporary table `next_key`, inserting entries one after one as we | 996 | 2 | -- descend the trie by trying to match entries with `:key`. | 997 | 2 | -- At each iteration, `node_hash` is the root where to continue the search, | 998 | 2 | -- `node_is_branch` is true if `node_hash` is a branch node, `node_full_key` is | 999 | 2 | -- the key of `node_hash` (that we build along the way) and serves as the final | 1000 | 2 | -- result, and `key_search_remain` contains the `:key` that remains to be matched. | 1001 | 2 | -- Can also be NULL to indicate that the search ended because the node necessary to | 1002 | 2 | -- continue was missing from the database, in which case the values of | 1003 | 2 | -- `node_hash` and `node_is_branch` have irrelevant values, and the value of | 1004 | 2 | -- `node_full_key` is the "best known key". | 1005 | 2 | -- If `:skip_branches` is false, the search ends when `key_search_remain` is null | 1006 | 2 | -- or empty. If `:skip_branches` is true, the search ends when `key_search_remain` | 1007 | 2 | -- is null or empty and that `node_is_branch` is false. | 1008 | 2 | -- | 1009 | 2 | -- `next_key` has zero elements if the block can't be found in the database or if | 1010 | 2 | -- the trie has no next key at all. These two situations need to be differentiated | 1011 | 2 | -- in the final SELECT statement. | 1012 | 2 | -- | 1013 | 2 | -- When encountering a node, we follow both the child that exactly matches `:key` | 1014 | 2 | -- and also the first child that is strictly superior to `:key`. This is necessary | 1015 | 2 | -- because `:key` might be equal to something like `ffffffff...`, in which case the | 1016 | 2 | -- result will be after any equal match. | 1017 | 2 | -- This means that the number of entries in `next_key` at the end of the recursion | 1018 | 2 | -- is something like `2 * depth_in_trie(key)`. | 1019 | 2 | -- In order to obtain the final result, we take the entry in `next_key` with the | 1020 | 2 | -- minimal `node_full_key` amongst the ones that have finished the search. | 1021 | 2 | -- | 1022 | 2 | -- Note that in the code below we do a lot of `COALESCE(SUBSTR(...), X'')`. This | 1023 | 2 | -- is because, for some reason, `SUBSTR(X'', ...)` always produces `NULL`. For this | 1024 | 2 | -- reason, it is also not possible to automatically pass NULL values | 1025 | 2 | -- through `SUSBTR`, and we have to use CASE/IIFs instead. | 1026 | 2 | next_key(node_hash, node_is_branch, node_full_key, key_search_remain) AS ( | 1027 | 2 | SELECT | 1028 | 2 | CASE | 1029 | 2 | WHEN trie_node.hash IS NULL | 1030 | 2 | THEN NULL | 1031 | 2 | WHEN COALESCE(SUBSTR(:key, 1, LENGTH(trie_node.partial_key)), X'') <= trie_node.partial_key | 1032 | 2 | THEN trie_node.hash | 1033 | 2 | ELSE | 1034 | 2 | NULL | 1035 | 2 | END, | 1036 | 2 | trie_node_storage.value IS NULL AND trie_node_storage.trie_root_ref IS NULL, | 1037 | 2 | COALESCE(trie_node.partial_key, X''), | 1038 | 2 | CASE | 1039 | 2 | WHEN trie_node.partial_key IS NULL | 1040 | 2 | THEN NULL | 1041 | 2 | WHEN COALESCE(SUBSTR(:key, 1, LENGTH(trie_node.partial_key)), X'') <= trie_node.partial_key | 1042 | 2 | THEN COALESCE(SUBSTR(:key, 1 + LENGTH(trie_node.partial_key)), X'') | 1043 | 2 | ELSE | 1044 | 2 | X'' -- The partial key is strictly inferior to `:key` | 1045 | 2 | END | 1046 | 2 | FROM blocks | 1047 | 2 | LEFT JOIN trie_node ON trie_node.hash = blocks.state_trie_root_hash | 1048 | 2 | LEFT JOIN trie_node_storage ON trie_node_storage.node_hash = trie_node.hash | 1049 | 2 | WHERE blocks.hash = :block_hash | 1050 | 2 | | 1051 | 2 | UNION ALL | 1052 | 2 | SELECT | 1053 | 2 | COALESCE(trie_node.hash, trie_node_trieref.hash), | 1054 | 2 | trie_node_storage.value IS NULL AND trie_node_storage.trie_root_ref IS NULL, | 1055 | 2 | CASE | 1056 | 2 | WHEN trie_node_child.child_num IS NULL | 1057 | 2 | THEN next_key.node_full_key | 1058 | 2 | WHEN trie_node.partial_key IS NULL AND trie_node_trieref.partial_key IS NULL | 1059 | 2 | THEN CAST(next_key.node_full_key || trie_node_child.child_num AS BLOB) | 1060 | 2 | ELSE | 1061 | 2 | CAST(next_key.node_full_key || trie_node_child.child_num || COALESCE(trie_node.partial_key, trie_node_trieref.partial_key) AS BLOB) | 1062 | 2 | END, | 1063 | 2 | CASE | 1064 | 2 | WHEN trie_node_child.child_num IS NOT NULL AND trie_node.partial_key IS NULL | 1065 | 2 | THEN NULL -- Child exists but is missing from database | 1066 | 2 | WHEN HEX(SUBSTR(next_key.key_search_remain, 1, 1)) = '10' AND trie_node_trieref.hash IS NULL | 1067 | 2 | THEN NULL -- Trie reference exists but is missing from database | 1068 | 2 | WHEN SUBSTR(next_key.key_search_remain, 1, 1) = trie_node_child.child_num AND SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node.partial_key)) = trie_node.partial_key | 1069 | 2 | THEN SUBSTR(next_key.key_search_remain, 2 + LENGTH(trie_node.partial_key)) -- Equal match, continue iterating | 1070 | 2 | WHEN SUBSTR(next_key.key_search_remain, 1, 1) = trie_node_child.child_num AND SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node.partial_key)) < trie_node.partial_key | 1071 | 2 | THEN X'' -- Searched key is before the node we are iterating to, thus we cut the search short | 1072 | 2 | WHEN HEX(SUBSTR(next_key.key_search_remain, 1, 1)) = '10' AND COALESCE(SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node_trieref.partial_key)), X'') = trie_node_trieref.partial_key | 1073 | 2 | THEN COALESCE(SUBSTR(next_key.key_search_remain, 2 + LENGTH(trie_node_trieref.partial_key)), X'') | 1074 | 2 | ELSE | 1075 | 2 | X'' -- Shouldn't be reachable. | 1076 | 2 | END | 1077 | 2 | FROM next_key | 1078 | 2 | | 1079 | 2 | LEFT JOIN trie_node_child | 1080 | 2 | ON next_key.node_hash = trie_node_child.hash | 1081 | 2 | AND CASE WHEN LENGTH(next_key.key_search_remain) = 0 THEN TRUE | 1082 | 2 | ELSE SUBSTR(next_key.key_search_remain, 1, 1) <= trie_node_child.child_num END | 1083 | 2 | LEFT JOIN trie_node ON trie_node.hash = trie_node_child.child_hash | 1084 | 2 | | 1085 | 2 | -- We want to keep only situations where `trie_node_child` is either | 1086 | 2 | -- equal to the key, or the first child strictly superior to the key. In | 1087 | 2 | -- order to do that, we try to find another child that is strictly | 1088 | 2 | -- in-between the key and `trie_node_child`. In the `WHERE` clause at the | 1089 | 2 | -- bottom, we only keep rows where `trie_node_child_before` is NULL. | 1090 | 2 | LEFT JOIN trie_node_child AS trie_node_child_before | 1091 | 2 | ON next_key.node_hash = trie_node_child_before.hash | 1092 | 2 | AND trie_node_child_before.child_num < trie_node_child.child_num | 1093 | 2 | AND (next_key.key_search_remain = X'' OR trie_node_child_before.child_num > SUBSTR(next_key.key_search_remain, 1, 1)) | 1094 | 2 | | 1095 | 2 | LEFT JOIN trie_node_storage AS trie_node_storage_trieref | 1096 | 2 | ON HEX(SUBSTR(next_key.key_search_remain, 1, 1)) = '10' AND next_key.node_hash = trie_node_storage_trieref.node_hash AND trie_node_storage_trieref.trie_root_ref IS NOT NULL | 1097 | 2 | LEFT JOIN trie_node AS trie_node_trieref | 1098 | 2 | ON trie_node_trieref.hash = trie_node_storage_trieref.node_hash | 1099 | 2 | AND COALESCE(SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node_trieref.partial_key)), X'') <= trie_node_trieref.partial_key | 1100 | 2 | | 1101 | 2 | LEFT JOIN trie_node_storage | 1102 | 2 | ON trie_node_storage.node_hash = COALESCE(trie_node.hash, trie_node_trieref.hash) | 1103 | 2 | | 1104 | 2 | WHERE | 1105 | 2 | -- Don't pull items that have already finished searching. | 1106 | 2 | next_key.node_hash IS NOT NULL AND next_key.key_search_remain IS NOT NULL AND (next_key.key_search_remain != X'' OR (next_key.node_is_branch AND :skip_branches)) | 1107 | 2 | -- See explanation above. | 1108 | 2 | AND trie_node_child_before.hash IS NULL | 1109 | 2 | -- Don't generate an item if there's nowhere to go to. | 1110 | 2 | AND (HEX(SUBSTR(next_key.key_search_remain, 1, 1)) = '10' OR trie_node_child.child_num IS NOT NULL) | 1111 | 2 | -- Stop iterating if the child's partial key is before the searched key. | 1112 | 2 | AND (trie_node.hash IS NULL OR NOT (COALESCE(SUBSTR(next_key.key_search_remain, 1, 1), X'') = trie_node_child.child_num AND COALESCE(SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node.partial_key)), X'') > trie_node.partial_key)) | 1113 | 2 | ), | 1114 | 2 | | 1115 | 2 | -- Now keep only the entries of `next_key` which have finished iterating. | 1116 | 2 | terminal_next_key(incomplete_storage, node_full_key, output) AS ( | 1117 | 2 | SELECT | 1118 | 2 | CASE | 1119 | 2 | WHEN COALESCE(SUBSTR(node_full_key, 1, LENGTH(:prefix)), X'') != :prefix THEN FALSE | 1120 | 2 | ELSE key_search_remain IS NULL | 1121 | 2 | END, | 1122 | 2 | node_full_key, | 1123 | 2 | CASE | 1124 | 2 | WHEN node_hash IS NULL THEN NULL | 1125 | 2 | WHEN COALESCE(SUBSTR(node_full_key, 1, LENGTH(:prefix)), X'') = :prefix THEN node_full_key | 1126 | 2 | ELSE NULL | 1127 | 2 | END | 1128 | 2 | FROM next_key | 1129 | 2 | WHERE key_search_remain IS NULL OR (LENGTH(key_search_remain) = 0 AND (NOT :skip_branches OR NOT node_is_branch)) | 1130 | 2 | ) | 1131 | 2 | | 1132 | 2 | SELECT | 1133 | 2 | COUNT(blocks.hash) >= 1, | 1134 | 2 | COALESCE(terminal_next_key.incomplete_storage, FALSE), | 1135 | 2 | terminal_next_key.output | 1136 | 2 | FROM blocks | 1137 | 2 | LEFT JOIN terminal_next_key | 1138 | 2 | WHERE blocks.hash = :block_hash | 1139 | 2 | -- We pick the entry of `terminal_next_key` with the smallest full key. Note that | 1140 | 2 | -- it might seem like a good idea to not using any GROUP BY and instead just do | 1141 | 2 | -- `ORDER BY node_full_key ASC LIMIT 1`, but doing so sometimes leads to SQLite | 1142 | 2 | -- not picking the entry with the smallest full key for a reason I couldn't | 1143 | 2 | -- figure out. | 1144 | 2 | AND (terminal_next_key.node_full_key IS NULL OR terminal_next_key.node_full_key = (SELECT MIN(node_full_key) FROM terminal_next_key)) | 1145 | 2 | LIMIT 1"#, | 1146 | 2 | ) | 1147 | 2 | .map_err(|err| { | 1148 | | StorageAccessError::Corrupted(CorruptedError::Internal( | 1149 | | InternalError(err), | 1150 | | )) | 1151 | 2 | })?0 ; | 1152 | | | 1153 | | // In order to debug the SQL query above (for example in case of a failing test), | 1154 | | // uncomment this block: | 1155 | | // | 1156 | | /*println!("{:?}", { | 1157 | | let mut statement = connection | 1158 | | .prepare_cached( | 1159 | | r#" | 1160 | | WITH RECURSIVE | 1161 | | copy-paste the definition of next_key here | 1162 | | | 1163 | | SELECT * FROM next_key"#).unwrap(); | 1164 | | statement | 1165 | | .query_map( | 1166 | | rusqlite::named_params! { | 1167 | | ":block_hash": &block_hash[..], | 1168 | | ":key": key_nibbles, | 1169 | | //":prefix": prefix_nibbles, | 1170 | | ":skip_branches": !branch_nodes | 1171 | | }, | 1172 | | |row| { | 1173 | | let node_hash = row.get::<_, Option<Vec<u8>>>(0)?.map(hex::encode); | 1174 | | let node_is_branch = row.get::<_, Option<i64>>(1)?.map(|n| n != 0); | 1175 | | let node_full_key = row.get::<_, Option<Vec<u8>>>(2)?; | 1176 | | let search_remain = row.get::<_, Option<Vec<u8>>>(3)?; | 1177 | | Ok((node_hash, node_is_branch, node_full_key, search_remain)) | 1178 | | }, | 1179 | | ) | 1180 | | .unwrap() | 1181 | | .collect::<Vec<_>>() | 1182 | | });*/ | 1183 | | | 1184 | 2 | let result = statement | 1185 | 2 | .query_row( | 1186 | 2 | rusqlite::named_params! { | 1187 | 2 | ":block_hash": &block_hash[..], | 1188 | 2 | ":key": key_nibbles, | 1189 | 2 | ":prefix": prefix_nibbles, | 1190 | 2 | ":skip_branches": !branch_nodes | 1191 | 2 | }, | 1192 | 2 | |row| { | 1193 | | let block_is_known = row.get::<_, i64>(0)? != 0; | 1194 | | let incomplete_storage = row.get::<_, i64>(1)? != 0; | 1195 | | let next_key = row.get::<_, Option<Vec<u8>>>(2)?; | 1196 | | Ok((block_is_known, incomplete_storage, next_key)) | 1197 | 2 | }, | 1198 | 2 | ) | 1199 | 2 | .optional() | 1200 | 2 | .map_err(|err| { | 1201 | | StorageAccessError::Corrupted(CorruptedError::Internal(InternalError(err))) | 1202 | 2 | })?0 ; | 1203 | | | 1204 | 2 | let Some((block_is_known, incomplete_storage, mut next_key)) = result else { | 1205 | 0 | return Ok(None); | 1206 | | }; | 1207 | | | 1208 | 2 | if !block_is_known { | 1209 | 0 | return Err(StorageAccessError::UnknownBlock); | 1210 | 2 | } | 1211 | 2 | | 1212 | 2 | if incomplete_storage { | 1213 | 1 | return Err(StorageAccessError::IncompleteStorage); | 1214 | 1 | } | 1215 | 1 | | 1216 | 1 | if parent_tries_paths_nibbles_length != 0 { | 1217 | 0 | next_key = next_key.map(|nk| nk[parent_tries_paths_nibbles_length..].to_vec()); | 1218 | 1 | } | 1219 | | | 1220 | 1 | Ok(next_key) | 1221 | 2 | } |
_RINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1C_B1B_EINtNtNtB1K_5array4iter8IntoIterhKj0_EB2E_EB7_ Line | Count | Source | 953 | 2 | pub fn block_storage_next_key( | 954 | 2 | &self, | 955 | 2 | block_hash: &[u8; 32], | 956 | 2 | parent_tries_paths_nibbles: impl Iterator<Item = impl Iterator<Item = u8>>, | 957 | 2 | key_nibbles: impl Iterator<Item = u8>, | 958 | 2 | prefix_nibbles: impl Iterator<Item = u8>, | 959 | 2 | branch_nodes: bool, | 960 | 2 | ) -> Result<Option<Vec<u8>>, StorageAccessError> { | 961 | 2 | // Process the iterators at the very beginning and before locking the database, in order | 962 | 2 | // to avoid a deadlock in case the `next()` function of one of the iterators accesses | 963 | 2 | // the database as well. | 964 | 2 | let parent_tries_paths_nibbles = parent_tries_paths_nibbles | 965 | 2 | .flat_map(|t| t.inspect(|n| assert!(*n < 16)).chain(iter::once(0x10))) | 966 | 2 | .collect::<Vec<_>>(); | 967 | 2 | let parent_tries_paths_nibbles_length = parent_tries_paths_nibbles.len(); | 968 | 2 | let key_nibbles = { | 969 | 2 | let mut v = parent_tries_paths_nibbles.clone(); | 970 | 2 | v.extend(key_nibbles.inspect(|n| assert!(*n < 16))); | 971 | 2 | v | 972 | 2 | }; | 973 | 2 | let prefix_nibbles = { | 974 | 2 | let mut v = parent_tries_paths_nibbles; | 975 | 2 | v.extend(prefix_nibbles.inspect(|n| assert!(*n < 16))); | 976 | 2 | v | 977 | 2 | }; | 978 | 2 | | 979 | 2 | let connection = self.database.lock(); | 980 | | | 981 | | // Sorry for that extremely complicated SQL statement. While the logic isn't actually very | 982 | | // complicated, we have to jump through many hoops in order to go around quirks in the | 983 | | // SQL language. | 984 | | // If you want to work on this SQL code, there is no miracle: write tests, and if a test | 985 | | // fails debug the content of `next_key` to find out where the iteration doesn't behave | 986 | | // as expected. | 987 | | // TODO: this algorithm relies the fact that leaf nodes always have a storage value, which isn't exactly clear in the schema ; however not relying on this makes it way harder to write | 988 | | // TODO: trie_root_ref system untested and most likely not working | 989 | | // TODO: infinite loop if there's a loop in the trie; detect this | 990 | | // TODO: could also check the prefix while iterating instead of only at the very end, which could maybe save many lookups | 991 | 2 | let mut statement = connection | 992 | 2 | .prepare_cached( | 993 | 2 | r#" | 994 | 2 | WITH RECURSIVE | 995 | 2 | -- We build a temporary table `next_key`, inserting entries one after one as we | 996 | 2 | -- descend the trie by trying to match entries with `:key`. | 997 | 2 | -- At each iteration, `node_hash` is the root where to continue the search, | 998 | 2 | -- `node_is_branch` is true if `node_hash` is a branch node, `node_full_key` is | 999 | 2 | -- the key of `node_hash` (that we build along the way) and serves as the final | 1000 | 2 | -- result, and `key_search_remain` contains the `:key` that remains to be matched. | 1001 | 2 | -- Can also be NULL to indicate that the search ended because the node necessary to | 1002 | 2 | -- continue was missing from the database, in which case the values of | 1003 | 2 | -- `node_hash` and `node_is_branch` have irrelevant values, and the value of | 1004 | 2 | -- `node_full_key` is the "best known key". | 1005 | 2 | -- If `:skip_branches` is false, the search ends when `key_search_remain` is null | 1006 | 2 | -- or empty. If `:skip_branches` is true, the search ends when `key_search_remain` | 1007 | 2 | -- is null or empty and that `node_is_branch` is false. | 1008 | 2 | -- | 1009 | 2 | -- `next_key` has zero elements if the block can't be found in the database or if | 1010 | 2 | -- the trie has no next key at all. These two situations need to be differentiated | 1011 | 2 | -- in the final SELECT statement. | 1012 | 2 | -- | 1013 | 2 | -- When encountering a node, we follow both the child that exactly matches `:key` | 1014 | 2 | -- and also the first child that is strictly superior to `:key`. This is necessary | 1015 | 2 | -- because `:key` might be equal to something like `ffffffff...`, in which case the | 1016 | 2 | -- result will be after any equal match. | 1017 | 2 | -- This means that the number of entries in `next_key` at the end of the recursion | 1018 | 2 | -- is something like `2 * depth_in_trie(key)`. | 1019 | 2 | -- In order to obtain the final result, we take the entry in `next_key` with the | 1020 | 2 | -- minimal `node_full_key` amongst the ones that have finished the search. | 1021 | 2 | -- | 1022 | 2 | -- Note that in the code below we do a lot of `COALESCE(SUBSTR(...), X'')`. This | 1023 | 2 | -- is because, for some reason, `SUBSTR(X'', ...)` always produces `NULL`. For this | 1024 | 2 | -- reason, it is also not possible to automatically pass NULL values | 1025 | 2 | -- through `SUSBTR`, and we have to use CASE/IIFs instead. | 1026 | 2 | next_key(node_hash, node_is_branch, node_full_key, key_search_remain) AS ( | 1027 | 2 | SELECT | 1028 | 2 | CASE | 1029 | 2 | WHEN trie_node.hash IS NULL | 1030 | 2 | THEN NULL | 1031 | 2 | WHEN COALESCE(SUBSTR(:key, 1, LENGTH(trie_node.partial_key)), X'') <= trie_node.partial_key | 1032 | 2 | THEN trie_node.hash | 1033 | 2 | ELSE | 1034 | 2 | NULL | 1035 | 2 | END, | 1036 | 2 | trie_node_storage.value IS NULL AND trie_node_storage.trie_root_ref IS NULL, | 1037 | 2 | COALESCE(trie_node.partial_key, X''), | 1038 | 2 | CASE | 1039 | 2 | WHEN trie_node.partial_key IS NULL | 1040 | 2 | THEN NULL | 1041 | 2 | WHEN COALESCE(SUBSTR(:key, 1, LENGTH(trie_node.partial_key)), X'') <= trie_node.partial_key | 1042 | 2 | THEN COALESCE(SUBSTR(:key, 1 + LENGTH(trie_node.partial_key)), X'') | 1043 | 2 | ELSE | 1044 | 2 | X'' -- The partial key is strictly inferior to `:key` | 1045 | 2 | END | 1046 | 2 | FROM blocks | 1047 | 2 | LEFT JOIN trie_node ON trie_node.hash = blocks.state_trie_root_hash | 1048 | 2 | LEFT JOIN trie_node_storage ON trie_node_storage.node_hash = trie_node.hash | 1049 | 2 | WHERE blocks.hash = :block_hash | 1050 | 2 | | 1051 | 2 | UNION ALL | 1052 | 2 | SELECT | 1053 | 2 | COALESCE(trie_node.hash, trie_node_trieref.hash), | 1054 | 2 | trie_node_storage.value IS NULL AND trie_node_storage.trie_root_ref IS NULL, | 1055 | 2 | CASE | 1056 | 2 | WHEN trie_node_child.child_num IS NULL | 1057 | 2 | THEN next_key.node_full_key | 1058 | 2 | WHEN trie_node.partial_key IS NULL AND trie_node_trieref.partial_key IS NULL | 1059 | 2 | THEN CAST(next_key.node_full_key || trie_node_child.child_num AS BLOB) | 1060 | 2 | ELSE | 1061 | 2 | CAST(next_key.node_full_key || trie_node_child.child_num || COALESCE(trie_node.partial_key, trie_node_trieref.partial_key) AS BLOB) | 1062 | 2 | END, | 1063 | 2 | CASE | 1064 | 2 | WHEN trie_node_child.child_num IS NOT NULL AND trie_node.partial_key IS NULL | 1065 | 2 | THEN NULL -- Child exists but is missing from database | 1066 | 2 | WHEN HEX(SUBSTR(next_key.key_search_remain, 1, 1)) = '10' AND trie_node_trieref.hash IS NULL | 1067 | 2 | THEN NULL -- Trie reference exists but is missing from database | 1068 | 2 | WHEN SUBSTR(next_key.key_search_remain, 1, 1) = trie_node_child.child_num AND SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node.partial_key)) = trie_node.partial_key | 1069 | 2 | THEN SUBSTR(next_key.key_search_remain, 2 + LENGTH(trie_node.partial_key)) -- Equal match, continue iterating | 1070 | 2 | WHEN SUBSTR(next_key.key_search_remain, 1, 1) = trie_node_child.child_num AND SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node.partial_key)) < trie_node.partial_key | 1071 | 2 | THEN X'' -- Searched key is before the node we are iterating to, thus we cut the search short | 1072 | 2 | WHEN HEX(SUBSTR(next_key.key_search_remain, 1, 1)) = '10' AND COALESCE(SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node_trieref.partial_key)), X'') = trie_node_trieref.partial_key | 1073 | 2 | THEN COALESCE(SUBSTR(next_key.key_search_remain, 2 + LENGTH(trie_node_trieref.partial_key)), X'') | 1074 | 2 | ELSE | 1075 | 2 | X'' -- Shouldn't be reachable. | 1076 | 2 | END | 1077 | 2 | FROM next_key | 1078 | 2 | | 1079 | 2 | LEFT JOIN trie_node_child | 1080 | 2 | ON next_key.node_hash = trie_node_child.hash | 1081 | 2 | AND CASE WHEN LENGTH(next_key.key_search_remain) = 0 THEN TRUE | 1082 | 2 | ELSE SUBSTR(next_key.key_search_remain, 1, 1) <= trie_node_child.child_num END | 1083 | 2 | LEFT JOIN trie_node ON trie_node.hash = trie_node_child.child_hash | 1084 | 2 | | 1085 | 2 | -- We want to keep only situations where `trie_node_child` is either | 1086 | 2 | -- equal to the key, or the first child strictly superior to the key. In | 1087 | 2 | -- order to do that, we try to find another child that is strictly | 1088 | 2 | -- in-between the key and `trie_node_child`. In the `WHERE` clause at the | 1089 | 2 | -- bottom, we only keep rows where `trie_node_child_before` is NULL. | 1090 | 2 | LEFT JOIN trie_node_child AS trie_node_child_before | 1091 | 2 | ON next_key.node_hash = trie_node_child_before.hash | 1092 | 2 | AND trie_node_child_before.child_num < trie_node_child.child_num | 1093 | 2 | AND (next_key.key_search_remain = X'' OR trie_node_child_before.child_num > SUBSTR(next_key.key_search_remain, 1, 1)) | 1094 | 2 | | 1095 | 2 | LEFT JOIN trie_node_storage AS trie_node_storage_trieref | 1096 | 2 | ON HEX(SUBSTR(next_key.key_search_remain, 1, 1)) = '10' AND next_key.node_hash = trie_node_storage_trieref.node_hash AND trie_node_storage_trieref.trie_root_ref IS NOT NULL | 1097 | 2 | LEFT JOIN trie_node AS trie_node_trieref | 1098 | 2 | ON trie_node_trieref.hash = trie_node_storage_trieref.node_hash | 1099 | 2 | AND COALESCE(SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node_trieref.partial_key)), X'') <= trie_node_trieref.partial_key | 1100 | 2 | | 1101 | 2 | LEFT JOIN trie_node_storage | 1102 | 2 | ON trie_node_storage.node_hash = COALESCE(trie_node.hash, trie_node_trieref.hash) | 1103 | 2 | | 1104 | 2 | WHERE | 1105 | 2 | -- Don't pull items that have already finished searching. | 1106 | 2 | next_key.node_hash IS NOT NULL AND next_key.key_search_remain IS NOT NULL AND (next_key.key_search_remain != X'' OR (next_key.node_is_branch AND :skip_branches)) | 1107 | 2 | -- See explanation above. | 1108 | 2 | AND trie_node_child_before.hash IS NULL | 1109 | 2 | -- Don't generate an item if there's nowhere to go to. | 1110 | 2 | AND (HEX(SUBSTR(next_key.key_search_remain, 1, 1)) = '10' OR trie_node_child.child_num IS NOT NULL) | 1111 | 2 | -- Stop iterating if the child's partial key is before the searched key. | 1112 | 2 | AND (trie_node.hash IS NULL OR NOT (COALESCE(SUBSTR(next_key.key_search_remain, 1, 1), X'') = trie_node_child.child_num AND COALESCE(SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node.partial_key)), X'') > trie_node.partial_key)) | 1113 | 2 | ), | 1114 | 2 | | 1115 | 2 | -- Now keep only the entries of `next_key` which have finished iterating. | 1116 | 2 | terminal_next_key(incomplete_storage, node_full_key, output) AS ( | 1117 | 2 | SELECT | 1118 | 2 | CASE | 1119 | 2 | WHEN COALESCE(SUBSTR(node_full_key, 1, LENGTH(:prefix)), X'') != :prefix THEN FALSE | 1120 | 2 | ELSE key_search_remain IS NULL | 1121 | 2 | END, | 1122 | 2 | node_full_key, | 1123 | 2 | CASE | 1124 | 2 | WHEN node_hash IS NULL THEN NULL | 1125 | 2 | WHEN COALESCE(SUBSTR(node_full_key, 1, LENGTH(:prefix)), X'') = :prefix THEN node_full_key | 1126 | 2 | ELSE NULL | 1127 | 2 | END | 1128 | 2 | FROM next_key | 1129 | 2 | WHERE key_search_remain IS NULL OR (LENGTH(key_search_remain) = 0 AND (NOT :skip_branches OR NOT node_is_branch)) | 1130 | 2 | ) | 1131 | 2 | | 1132 | 2 | SELECT | 1133 | 2 | COUNT(blocks.hash) >= 1, | 1134 | 2 | COALESCE(terminal_next_key.incomplete_storage, FALSE), | 1135 | 2 | terminal_next_key.output | 1136 | 2 | FROM blocks | 1137 | 2 | LEFT JOIN terminal_next_key | 1138 | 2 | WHERE blocks.hash = :block_hash | 1139 | 2 | -- We pick the entry of `terminal_next_key` with the smallest full key. Note that | 1140 | 2 | -- it might seem like a good idea to not using any GROUP BY and instead just do | 1141 | 2 | -- `ORDER BY node_full_key ASC LIMIT 1`, but doing so sometimes leads to SQLite | 1142 | 2 | -- not picking the entry with the smallest full key for a reason I couldn't | 1143 | 2 | -- figure out. | 1144 | 2 | AND (terminal_next_key.node_full_key IS NULL OR terminal_next_key.node_full_key = (SELECT MIN(node_full_key) FROM terminal_next_key)) | 1145 | 2 | LIMIT 1"#, | 1146 | 2 | ) | 1147 | 2 | .map_err(|err| { | 1148 | | StorageAccessError::Corrupted(CorruptedError::Internal( | 1149 | | InternalError(err), | 1150 | | )) | 1151 | 2 | })?0 ; | 1152 | | | 1153 | | // In order to debug the SQL query above (for example in case of a failing test), | 1154 | | // uncomment this block: | 1155 | | // | 1156 | | /*println!("{:?}", { | 1157 | | let mut statement = connection | 1158 | | .prepare_cached( | 1159 | | r#" | 1160 | | WITH RECURSIVE | 1161 | | copy-paste the definition of next_key here | 1162 | | | 1163 | | SELECT * FROM next_key"#).unwrap(); | 1164 | | statement | 1165 | | .query_map( | 1166 | | rusqlite::named_params! { | 1167 | | ":block_hash": &block_hash[..], | 1168 | | ":key": key_nibbles, | 1169 | | //":prefix": prefix_nibbles, | 1170 | | ":skip_branches": !branch_nodes | 1171 | | }, | 1172 | | |row| { | 1173 | | let node_hash = row.get::<_, Option<Vec<u8>>>(0)?.map(hex::encode); | 1174 | | let node_is_branch = row.get::<_, Option<i64>>(1)?.map(|n| n != 0); | 1175 | | let node_full_key = row.get::<_, Option<Vec<u8>>>(2)?; | 1176 | | let search_remain = row.get::<_, Option<Vec<u8>>>(3)?; | 1177 | | Ok((node_hash, node_is_branch, node_full_key, search_remain)) | 1178 | | }, | 1179 | | ) | 1180 | | .unwrap() | 1181 | | .collect::<Vec<_>>() | 1182 | | });*/ | 1183 | | | 1184 | 2 | let result = statement | 1185 | 2 | .query_row( | 1186 | 2 | rusqlite::named_params! { | 1187 | 2 | ":block_hash": &block_hash[..], | 1188 | 2 | ":key": key_nibbles, | 1189 | 2 | ":prefix": prefix_nibbles, | 1190 | 2 | ":skip_branches": !branch_nodes | 1191 | 2 | }, | 1192 | 2 | |row| { | 1193 | | let block_is_known = row.get::<_, i64>(0)? != 0; | 1194 | | let incomplete_storage = row.get::<_, i64>(1)? != 0; | 1195 | | let next_key = row.get::<_, Option<Vec<u8>>>(2)?; | 1196 | | Ok((block_is_known, incomplete_storage, next_key)) | 1197 | 2 | }, | 1198 | 2 | ) | 1199 | 2 | .optional() | 1200 | 2 | .map_err(|err| { | 1201 | | StorageAccessError::Corrupted(CorruptedError::Internal(InternalError(err))) | 1202 | 2 | })?0 ; | 1203 | | | 1204 | 2 | let Some((block_is_known, incomplete_storage, mut next_key)) = result else { | 1205 | 0 | return Ok(None); | 1206 | | }; | 1207 | | | 1208 | 2 | if !block_is_known { | 1209 | 2 | return Err(StorageAccessError::UnknownBlock); | 1210 | 0 | } | 1211 | 0 |
| 1212 | 0 | if incomplete_storage { | 1213 | 0 | return Err(StorageAccessError::IncompleteStorage); | 1214 | 0 | } | 1215 | 0 |
| 1216 | 0 | if parent_tries_paths_nibbles_length != 0 { | 1217 | 0 | next_key = next_key.map(|nk| nk[parent_tries_paths_nibbles_length..].to_vec()); | 1218 | 0 | } | 1219 | | | 1220 | 0 | Ok(next_key) | 1221 | 2 | } |
_RINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1C_B1B_EINtNtNtB1K_5array4iter8IntoIterhKj1_EB1B_EB7_ Line | Count | Source | 953 | 1 | pub fn block_storage_next_key( | 954 | 1 | &self, | 955 | 1 | block_hash: &[u8; 32], | 956 | 1 | parent_tries_paths_nibbles: impl Iterator<Item = impl Iterator<Item = u8>>, | 957 | 1 | key_nibbles: impl Iterator<Item = u8>, | 958 | 1 | prefix_nibbles: impl Iterator<Item = u8>, | 959 | 1 | branch_nodes: bool, | 960 | 1 | ) -> Result<Option<Vec<u8>>, StorageAccessError> { | 961 | 1 | // Process the iterators at the very beginning and before locking the database, in order | 962 | 1 | // to avoid a deadlock in case the `next()` function of one of the iterators accesses | 963 | 1 | // the database as well. | 964 | 1 | let parent_tries_paths_nibbles = parent_tries_paths_nibbles | 965 | 1 | .flat_map(|t| t.inspect(|n| assert!(*n < 16)).chain(iter::once(0x10))) | 966 | 1 | .collect::<Vec<_>>(); | 967 | 1 | let parent_tries_paths_nibbles_length = parent_tries_paths_nibbles.len(); | 968 | 1 | let key_nibbles = { | 969 | 1 | let mut v = parent_tries_paths_nibbles.clone(); | 970 | 1 | v.extend(key_nibbles.inspect(|n| assert!(*n < 16))); | 971 | 1 | v | 972 | 1 | }; | 973 | 1 | let prefix_nibbles = { | 974 | 1 | let mut v = parent_tries_paths_nibbles; | 975 | 1 | v.extend(prefix_nibbles.inspect(|n| assert!(*n < 16))); | 976 | 1 | v | 977 | 1 | }; | 978 | 1 | | 979 | 1 | let connection = self.database.lock(); | 980 | | | 981 | | // Sorry for that extremely complicated SQL statement. While the logic isn't actually very | 982 | | // complicated, we have to jump through many hoops in order to go around quirks in the | 983 | | // SQL language. | 984 | | // If you want to work on this SQL code, there is no miracle: write tests, and if a test | 985 | | // fails debug the content of `next_key` to find out where the iteration doesn't behave | 986 | | // as expected. | 987 | | // TODO: this algorithm relies the fact that leaf nodes always have a storage value, which isn't exactly clear in the schema ; however not relying on this makes it way harder to write | 988 | | // TODO: trie_root_ref system untested and most likely not working | 989 | | // TODO: infinite loop if there's a loop in the trie; detect this | 990 | | // TODO: could also check the prefix while iterating instead of only at the very end, which could maybe save many lookups | 991 | 1 | let mut statement = connection | 992 | 1 | .prepare_cached( | 993 | 1 | r#" | 994 | 1 | WITH RECURSIVE | 995 | 1 | -- We build a temporary table `next_key`, inserting entries one after one as we | 996 | 1 | -- descend the trie by trying to match entries with `:key`. | 997 | 1 | -- At each iteration, `node_hash` is the root where to continue the search, | 998 | 1 | -- `node_is_branch` is true if `node_hash` is a branch node, `node_full_key` is | 999 | 1 | -- the key of `node_hash` (that we build along the way) and serves as the final | 1000 | 1 | -- result, and `key_search_remain` contains the `:key` that remains to be matched. | 1001 | 1 | -- Can also be NULL to indicate that the search ended because the node necessary to | 1002 | 1 | -- continue was missing from the database, in which case the values of | 1003 | 1 | -- `node_hash` and `node_is_branch` have irrelevant values, and the value of | 1004 | 1 | -- `node_full_key` is the "best known key". | 1005 | 1 | -- If `:skip_branches` is false, the search ends when `key_search_remain` is null | 1006 | 1 | -- or empty. If `:skip_branches` is true, the search ends when `key_search_remain` | 1007 | 1 | -- is null or empty and that `node_is_branch` is false. | 1008 | 1 | -- | 1009 | 1 | -- `next_key` has zero elements if the block can't be found in the database or if | 1010 | 1 | -- the trie has no next key at all. These two situations need to be differentiated | 1011 | 1 | -- in the final SELECT statement. | 1012 | 1 | -- | 1013 | 1 | -- When encountering a node, we follow both the child that exactly matches `:key` | 1014 | 1 | -- and also the first child that is strictly superior to `:key`. This is necessary | 1015 | 1 | -- because `:key` might be equal to something like `ffffffff...`, in which case the | 1016 | 1 | -- result will be after any equal match. | 1017 | 1 | -- This means that the number of entries in `next_key` at the end of the recursion | 1018 | 1 | -- is something like `2 * depth_in_trie(key)`. | 1019 | 1 | -- In order to obtain the final result, we take the entry in `next_key` with the | 1020 | 1 | -- minimal `node_full_key` amongst the ones that have finished the search. | 1021 | 1 | -- | 1022 | 1 | -- Note that in the code below we do a lot of `COALESCE(SUBSTR(...), X'')`. This | 1023 | 1 | -- is because, for some reason, `SUBSTR(X'', ...)` always produces `NULL`. For this | 1024 | 1 | -- reason, it is also not possible to automatically pass NULL values | 1025 | 1 | -- through `SUSBTR`, and we have to use CASE/IIFs instead. | 1026 | 1 | next_key(node_hash, node_is_branch, node_full_key, key_search_remain) AS ( | 1027 | 1 | SELECT | 1028 | 1 | CASE | 1029 | 1 | WHEN trie_node.hash IS NULL | 1030 | 1 | THEN NULL | 1031 | 1 | WHEN COALESCE(SUBSTR(:key, 1, LENGTH(trie_node.partial_key)), X'') <= trie_node.partial_key | 1032 | 1 | THEN trie_node.hash | 1033 | 1 | ELSE | 1034 | 1 | NULL | 1035 | 1 | END, | 1036 | 1 | trie_node_storage.value IS NULL AND trie_node_storage.trie_root_ref IS NULL, | 1037 | 1 | COALESCE(trie_node.partial_key, X''), | 1038 | 1 | CASE | 1039 | 1 | WHEN trie_node.partial_key IS NULL | 1040 | 1 | THEN NULL | 1041 | 1 | WHEN COALESCE(SUBSTR(:key, 1, LENGTH(trie_node.partial_key)), X'') <= trie_node.partial_key | 1042 | 1 | THEN COALESCE(SUBSTR(:key, 1 + LENGTH(trie_node.partial_key)), X'') | 1043 | 1 | ELSE | 1044 | 1 | X'' -- The partial key is strictly inferior to `:key` | 1045 | 1 | END | 1046 | 1 | FROM blocks | 1047 | 1 | LEFT JOIN trie_node ON trie_node.hash = blocks.state_trie_root_hash | 1048 | 1 | LEFT JOIN trie_node_storage ON trie_node_storage.node_hash = trie_node.hash | 1049 | 1 | WHERE blocks.hash = :block_hash | 1050 | 1 | | 1051 | 1 | UNION ALL | 1052 | 1 | SELECT | 1053 | 1 | COALESCE(trie_node.hash, trie_node_trieref.hash), | 1054 | 1 | trie_node_storage.value IS NULL AND trie_node_storage.trie_root_ref IS NULL, | 1055 | 1 | CASE | 1056 | 1 | WHEN trie_node_child.child_num IS NULL | 1057 | 1 | THEN next_key.node_full_key | 1058 | 1 | WHEN trie_node.partial_key IS NULL AND trie_node_trieref.partial_key IS NULL | 1059 | 1 | THEN CAST(next_key.node_full_key || trie_node_child.child_num AS BLOB) | 1060 | 1 | ELSE | 1061 | 1 | CAST(next_key.node_full_key || trie_node_child.child_num || COALESCE(trie_node.partial_key, trie_node_trieref.partial_key) AS BLOB) | 1062 | 1 | END, | 1063 | 1 | CASE | 1064 | 1 | WHEN trie_node_child.child_num IS NOT NULL AND trie_node.partial_key IS NULL | 1065 | 1 | THEN NULL -- Child exists but is missing from database | 1066 | 1 | WHEN HEX(SUBSTR(next_key.key_search_remain, 1, 1)) = '10' AND trie_node_trieref.hash IS NULL | 1067 | 1 | THEN NULL -- Trie reference exists but is missing from database | 1068 | 1 | WHEN SUBSTR(next_key.key_search_remain, 1, 1) = trie_node_child.child_num AND SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node.partial_key)) = trie_node.partial_key | 1069 | 1 | THEN SUBSTR(next_key.key_search_remain, 2 + LENGTH(trie_node.partial_key)) -- Equal match, continue iterating | 1070 | 1 | WHEN SUBSTR(next_key.key_search_remain, 1, 1) = trie_node_child.child_num AND SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node.partial_key)) < trie_node.partial_key | 1071 | 1 | THEN X'' -- Searched key is before the node we are iterating to, thus we cut the search short | 1072 | 1 | WHEN HEX(SUBSTR(next_key.key_search_remain, 1, 1)) = '10' AND COALESCE(SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node_trieref.partial_key)), X'') = trie_node_trieref.partial_key | 1073 | 1 | THEN COALESCE(SUBSTR(next_key.key_search_remain, 2 + LENGTH(trie_node_trieref.partial_key)), X'') | 1074 | 1 | ELSE | 1075 | 1 | X'' -- Shouldn't be reachable. | 1076 | 1 | END | 1077 | 1 | FROM next_key | 1078 | 1 | | 1079 | 1 | LEFT JOIN trie_node_child | 1080 | 1 | ON next_key.node_hash = trie_node_child.hash | 1081 | 1 | AND CASE WHEN LENGTH(next_key.key_search_remain) = 0 THEN TRUE | 1082 | 1 | ELSE SUBSTR(next_key.key_search_remain, 1, 1) <= trie_node_child.child_num END | 1083 | 1 | LEFT JOIN trie_node ON trie_node.hash = trie_node_child.child_hash | 1084 | 1 | | 1085 | 1 | -- We want to keep only situations where `trie_node_child` is either | 1086 | 1 | -- equal to the key, or the first child strictly superior to the key. In | 1087 | 1 | -- order to do that, we try to find another child that is strictly | 1088 | 1 | -- in-between the key and `trie_node_child`. In the `WHERE` clause at the | 1089 | 1 | -- bottom, we only keep rows where `trie_node_child_before` is NULL. | 1090 | 1 | LEFT JOIN trie_node_child AS trie_node_child_before | 1091 | 1 | ON next_key.node_hash = trie_node_child_before.hash | 1092 | 1 | AND trie_node_child_before.child_num < trie_node_child.child_num | 1093 | 1 | AND (next_key.key_search_remain = X'' OR trie_node_child_before.child_num > SUBSTR(next_key.key_search_remain, 1, 1)) | 1094 | 1 | | 1095 | 1 | LEFT JOIN trie_node_storage AS trie_node_storage_trieref | 1096 | 1 | ON HEX(SUBSTR(next_key.key_search_remain, 1, 1)) = '10' AND next_key.node_hash = trie_node_storage_trieref.node_hash AND trie_node_storage_trieref.trie_root_ref IS NOT NULL | 1097 | 1 | LEFT JOIN trie_node AS trie_node_trieref | 1098 | 1 | ON trie_node_trieref.hash = trie_node_storage_trieref.node_hash | 1099 | 1 | AND COALESCE(SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node_trieref.partial_key)), X'') <= trie_node_trieref.partial_key | 1100 | 1 | | 1101 | 1 | LEFT JOIN trie_node_storage | 1102 | 1 | ON trie_node_storage.node_hash = COALESCE(trie_node.hash, trie_node_trieref.hash) | 1103 | 1 | | 1104 | 1 | WHERE | 1105 | 1 | -- Don't pull items that have already finished searching. | 1106 | 1 | next_key.node_hash IS NOT NULL AND next_key.key_search_remain IS NOT NULL AND (next_key.key_search_remain != X'' OR (next_key.node_is_branch AND :skip_branches)) | 1107 | 1 | -- See explanation above. | 1108 | 1 | AND trie_node_child_before.hash IS NULL | 1109 | 1 | -- Don't generate an item if there's nowhere to go to. | 1110 | 1 | AND (HEX(SUBSTR(next_key.key_search_remain, 1, 1)) = '10' OR trie_node_child.child_num IS NOT NULL) | 1111 | 1 | -- Stop iterating if the child's partial key is before the searched key. | 1112 | 1 | AND (trie_node.hash IS NULL OR NOT (COALESCE(SUBSTR(next_key.key_search_remain, 1, 1), X'') = trie_node_child.child_num AND COALESCE(SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node.partial_key)), X'') > trie_node.partial_key)) | 1113 | 1 | ), | 1114 | 1 | | 1115 | 1 | -- Now keep only the entries of `next_key` which have finished iterating. | 1116 | 1 | terminal_next_key(incomplete_storage, node_full_key, output) AS ( | 1117 | 1 | SELECT | 1118 | 1 | CASE | 1119 | 1 | WHEN COALESCE(SUBSTR(node_full_key, 1, LENGTH(:prefix)), X'') != :prefix THEN FALSE | 1120 | 1 | ELSE key_search_remain IS NULL | 1121 | 1 | END, | 1122 | 1 | node_full_key, | 1123 | 1 | CASE | 1124 | 1 | WHEN node_hash IS NULL THEN NULL | 1125 | 1 | WHEN COALESCE(SUBSTR(node_full_key, 1, LENGTH(:prefix)), X'') = :prefix THEN node_full_key | 1126 | 1 | ELSE NULL | 1127 | 1 | END | 1128 | 1 | FROM next_key | 1129 | 1 | WHERE key_search_remain IS NULL OR (LENGTH(key_search_remain) = 0 AND (NOT :skip_branches OR NOT node_is_branch)) | 1130 | 1 | ) | 1131 | 1 | | 1132 | 1 | SELECT | 1133 | 1 | COUNT(blocks.hash) >= 1, | 1134 | 1 | COALESCE(terminal_next_key.incomplete_storage, FALSE), | 1135 | 1 | terminal_next_key.output | 1136 | 1 | FROM blocks | 1137 | 1 | LEFT JOIN terminal_next_key | 1138 | 1 | WHERE blocks.hash = :block_hash | 1139 | 1 | -- We pick the entry of `terminal_next_key` with the smallest full key. Note that | 1140 | 1 | -- it might seem like a good idea to not using any GROUP BY and instead just do | 1141 | 1 | -- `ORDER BY node_full_key ASC LIMIT 1`, but doing so sometimes leads to SQLite | 1142 | 1 | -- not picking the entry with the smallest full key for a reason I couldn't | 1143 | 1 | -- figure out. | 1144 | 1 | AND (terminal_next_key.node_full_key IS NULL OR terminal_next_key.node_full_key = (SELECT MIN(node_full_key) FROM terminal_next_key)) | 1145 | 1 | LIMIT 1"#, | 1146 | 1 | ) | 1147 | 1 | .map_err(|err| { | 1148 | | StorageAccessError::Corrupted(CorruptedError::Internal( | 1149 | | InternalError(err), | 1150 | | )) | 1151 | 1 | })?0 ; | 1152 | | | 1153 | | // In order to debug the SQL query above (for example in case of a failing test), | 1154 | | // uncomment this block: | 1155 | | // | 1156 | | /*println!("{:?}", { | 1157 | | let mut statement = connection | 1158 | | .prepare_cached( | 1159 | | r#" | 1160 | | WITH RECURSIVE | 1161 | | copy-paste the definition of next_key here | 1162 | | | 1163 | | SELECT * FROM next_key"#).unwrap(); | 1164 | | statement | 1165 | | .query_map( | 1166 | | rusqlite::named_params! { | 1167 | | ":block_hash": &block_hash[..], | 1168 | | ":key": key_nibbles, | 1169 | | //":prefix": prefix_nibbles, | 1170 | | ":skip_branches": !branch_nodes | 1171 | | }, | 1172 | | |row| { | 1173 | | let node_hash = row.get::<_, Option<Vec<u8>>>(0)?.map(hex::encode); | 1174 | | let node_is_branch = row.get::<_, Option<i64>>(1)?.map(|n| n != 0); | 1175 | | let node_full_key = row.get::<_, Option<Vec<u8>>>(2)?; | 1176 | | let search_remain = row.get::<_, Option<Vec<u8>>>(3)?; | 1177 | | Ok((node_hash, node_is_branch, node_full_key, search_remain)) | 1178 | | }, | 1179 | | ) | 1180 | | .unwrap() | 1181 | | .collect::<Vec<_>>() | 1182 | | });*/ | 1183 | | | 1184 | 1 | let result = statement | 1185 | 1 | .query_row( | 1186 | 1 | rusqlite::named_params! { | 1187 | 1 | ":block_hash": &block_hash[..], | 1188 | 1 | ":key": key_nibbles, | 1189 | 1 | ":prefix": prefix_nibbles, | 1190 | 1 | ":skip_branches": !branch_nodes | 1191 | 1 | }, | 1192 | 1 | |row| { | 1193 | | let block_is_known = row.get::<_, i64>(0)? != 0; | 1194 | | let incomplete_storage = row.get::<_, i64>(1)? != 0; | 1195 | | let next_key = row.get::<_, Option<Vec<u8>>>(2)?; | 1196 | | Ok((block_is_known, incomplete_storage, next_key)) | 1197 | 1 | }, | 1198 | 1 | ) | 1199 | 1 | .optional() | 1200 | 1 | .map_err(|err| { | 1201 | | StorageAccessError::Corrupted(CorruptedError::Internal(InternalError(err))) | 1202 | 1 | })?0 ; | 1203 | | | 1204 | 1 | let Some((block_is_known, incomplete_storage, mut next_key)) = result else { | 1205 | 0 | return Ok(None); | 1206 | | }; | 1207 | | | 1208 | 1 | if !block_is_known { | 1209 | 0 | return Err(StorageAccessError::UnknownBlock); | 1210 | 1 | } | 1211 | 1 | | 1212 | 1 | if incomplete_storage { | 1213 | 0 | return Err(StorageAccessError::IncompleteStorage); | 1214 | 1 | } | 1215 | 1 | | 1216 | 1 | if parent_tries_paths_nibbles_length != 0 { | 1217 | 0 | next_key = next_key.map(|nk| nk[parent_tries_paths_nibbles_length..].to_vec()); | 1218 | 1 | } | 1219 | | | 1220 | 1 | Ok(next_key) | 1221 | 1 | } |
_RINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1C_B1B_EINtNtNtB1K_5array4iter8IntoIterhKj2_EB1B_EB7_ Line | Count | Source | 953 | 4 | pub fn block_storage_next_key( | 954 | 4 | &self, | 955 | 4 | block_hash: &[u8; 32], | 956 | 4 | parent_tries_paths_nibbles: impl Iterator<Item = impl Iterator<Item = u8>>, | 957 | 4 | key_nibbles: impl Iterator<Item = u8>, | 958 | 4 | prefix_nibbles: impl Iterator<Item = u8>, | 959 | 4 | branch_nodes: bool, | 960 | 4 | ) -> Result<Option<Vec<u8>>, StorageAccessError> { | 961 | 4 | // Process the iterators at the very beginning and before locking the database, in order | 962 | 4 | // to avoid a deadlock in case the `next()` function of one of the iterators accesses | 963 | 4 | // the database as well. | 964 | 4 | let parent_tries_paths_nibbles = parent_tries_paths_nibbles | 965 | 4 | .flat_map(|t| t.inspect(|n| assert!(*n < 16)).chain(iter::once(0x10))) | 966 | 4 | .collect::<Vec<_>>(); | 967 | 4 | let parent_tries_paths_nibbles_length = parent_tries_paths_nibbles.len(); | 968 | 4 | let key_nibbles = { | 969 | 4 | let mut v = parent_tries_paths_nibbles.clone(); | 970 | 4 | v.extend(key_nibbles.inspect(|n| assert!(*n < 16))); | 971 | 4 | v | 972 | 4 | }; | 973 | 4 | let prefix_nibbles = { | 974 | 4 | let mut v = parent_tries_paths_nibbles; | 975 | 4 | v.extend(prefix_nibbles.inspect(|n| assert!(*n < 16))); | 976 | 4 | v | 977 | 4 | }; | 978 | 4 | | 979 | 4 | let connection = self.database.lock(); | 980 | | | 981 | | // Sorry for that extremely complicated SQL statement. While the logic isn't actually very | 982 | | // complicated, we have to jump through many hoops in order to go around quirks in the | 983 | | // SQL language. | 984 | | // If you want to work on this SQL code, there is no miracle: write tests, and if a test | 985 | | // fails debug the content of `next_key` to find out where the iteration doesn't behave | 986 | | // as expected. | 987 | | // TODO: this algorithm relies the fact that leaf nodes always have a storage value, which isn't exactly clear in the schema ; however not relying on this makes it way harder to write | 988 | | // TODO: trie_root_ref system untested and most likely not working | 989 | | // TODO: infinite loop if there's a loop in the trie; detect this | 990 | | // TODO: could also check the prefix while iterating instead of only at the very end, which could maybe save many lookups | 991 | 4 | let mut statement = connection | 992 | 4 | .prepare_cached( | 993 | 4 | r#" | 994 | 4 | WITH RECURSIVE | 995 | 4 | -- We build a temporary table `next_key`, inserting entries one after one as we | 996 | 4 | -- descend the trie by trying to match entries with `:key`. | 997 | 4 | -- At each iteration, `node_hash` is the root where to continue the search, | 998 | 4 | -- `node_is_branch` is true if `node_hash` is a branch node, `node_full_key` is | 999 | 4 | -- the key of `node_hash` (that we build along the way) and serves as the final | 1000 | 4 | -- result, and `key_search_remain` contains the `:key` that remains to be matched. | 1001 | 4 | -- Can also be NULL to indicate that the search ended because the node necessary to | 1002 | 4 | -- continue was missing from the database, in which case the values of | 1003 | 4 | -- `node_hash` and `node_is_branch` have irrelevant values, and the value of | 1004 | 4 | -- `node_full_key` is the "best known key". | 1005 | 4 | -- If `:skip_branches` is false, the search ends when `key_search_remain` is null | 1006 | 4 | -- or empty. If `:skip_branches` is true, the search ends when `key_search_remain` | 1007 | 4 | -- is null or empty and that `node_is_branch` is false. | 1008 | 4 | -- | 1009 | 4 | -- `next_key` has zero elements if the block can't be found in the database or if | 1010 | 4 | -- the trie has no next key at all. These two situations need to be differentiated | 1011 | 4 | -- in the final SELECT statement. | 1012 | 4 | -- | 1013 | 4 | -- When encountering a node, we follow both the child that exactly matches `:key` | 1014 | 4 | -- and also the first child that is strictly superior to `:key`. This is necessary | 1015 | 4 | -- because `:key` might be equal to something like `ffffffff...`, in which case the | 1016 | 4 | -- result will be after any equal match. | 1017 | 4 | -- This means that the number of entries in `next_key` at the end of the recursion | 1018 | 4 | -- is something like `2 * depth_in_trie(key)`. | 1019 | 4 | -- In order to obtain the final result, we take the entry in `next_key` with the | 1020 | 4 | -- minimal `node_full_key` amongst the ones that have finished the search. | 1021 | 4 | -- | 1022 | 4 | -- Note that in the code below we do a lot of `COALESCE(SUBSTR(...), X'')`. This | 1023 | 4 | -- is because, for some reason, `SUBSTR(X'', ...)` always produces `NULL`. For this | 1024 | 4 | -- reason, it is also not possible to automatically pass NULL values | 1025 | 4 | -- through `SUSBTR`, and we have to use CASE/IIFs instead. | 1026 | 4 | next_key(node_hash, node_is_branch, node_full_key, key_search_remain) AS ( | 1027 | 4 | SELECT | 1028 | 4 | CASE | 1029 | 4 | WHEN trie_node.hash IS NULL | 1030 | 4 | THEN NULL | 1031 | 4 | WHEN COALESCE(SUBSTR(:key, 1, LENGTH(trie_node.partial_key)), X'') <= trie_node.partial_key | 1032 | 4 | THEN trie_node.hash | 1033 | 4 | ELSE | 1034 | 4 | NULL | 1035 | 4 | END, | 1036 | 4 | trie_node_storage.value IS NULL AND trie_node_storage.trie_root_ref IS NULL, | 1037 | 4 | COALESCE(trie_node.partial_key, X''), | 1038 | 4 | CASE | 1039 | 4 | WHEN trie_node.partial_key IS NULL | 1040 | 4 | THEN NULL | 1041 | 4 | WHEN COALESCE(SUBSTR(:key, 1, LENGTH(trie_node.partial_key)), X'') <= trie_node.partial_key | 1042 | 4 | THEN COALESCE(SUBSTR(:key, 1 + LENGTH(trie_node.partial_key)), X'') | 1043 | 4 | ELSE | 1044 | 4 | X'' -- The partial key is strictly inferior to `:key` | 1045 | 4 | END | 1046 | 4 | FROM blocks | 1047 | 4 | LEFT JOIN trie_node ON trie_node.hash = blocks.state_trie_root_hash | 1048 | 4 | LEFT JOIN trie_node_storage ON trie_node_storage.node_hash = trie_node.hash | 1049 | 4 | WHERE blocks.hash = :block_hash | 1050 | 4 | | 1051 | 4 | UNION ALL | 1052 | 4 | SELECT | 1053 | 4 | COALESCE(trie_node.hash, trie_node_trieref.hash), | 1054 | 4 | trie_node_storage.value IS NULL AND trie_node_storage.trie_root_ref IS NULL, | 1055 | 4 | CASE | 1056 | 4 | WHEN trie_node_child.child_num IS NULL | 1057 | 4 | THEN next_key.node_full_key | 1058 | 4 | WHEN trie_node.partial_key IS NULL AND trie_node_trieref.partial_key IS NULL | 1059 | 4 | THEN CAST(next_key.node_full_key || trie_node_child.child_num AS BLOB) | 1060 | 4 | ELSE | 1061 | 4 | CAST(next_key.node_full_key || trie_node_child.child_num || COALESCE(trie_node.partial_key, trie_node_trieref.partial_key) AS BLOB) | 1062 | 4 | END, | 1063 | 4 | CASE | 1064 | 4 | WHEN trie_node_child.child_num IS NOT NULL AND trie_node.partial_key IS NULL | 1065 | 4 | THEN NULL -- Child exists but is missing from database | 1066 | 4 | WHEN HEX(SUBSTR(next_key.key_search_remain, 1, 1)) = '10' AND trie_node_trieref.hash IS NULL | 1067 | 4 | THEN NULL -- Trie reference exists but is missing from database | 1068 | 4 | WHEN SUBSTR(next_key.key_search_remain, 1, 1) = trie_node_child.child_num AND SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node.partial_key)) = trie_node.partial_key | 1069 | 4 | THEN SUBSTR(next_key.key_search_remain, 2 + LENGTH(trie_node.partial_key)) -- Equal match, continue iterating | 1070 | 4 | WHEN SUBSTR(next_key.key_search_remain, 1, 1) = trie_node_child.child_num AND SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node.partial_key)) < trie_node.partial_key | 1071 | 4 | THEN X'' -- Searched key is before the node we are iterating to, thus we cut the search short | 1072 | 4 | WHEN HEX(SUBSTR(next_key.key_search_remain, 1, 1)) = '10' AND COALESCE(SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node_trieref.partial_key)), X'') = trie_node_trieref.partial_key | 1073 | 4 | THEN COALESCE(SUBSTR(next_key.key_search_remain, 2 + LENGTH(trie_node_trieref.partial_key)), X'') | 1074 | 4 | ELSE | 1075 | 4 | X'' -- Shouldn't be reachable. | 1076 | 4 | END | 1077 | 4 | FROM next_key | 1078 | 4 | | 1079 | 4 | LEFT JOIN trie_node_child | 1080 | 4 | ON next_key.node_hash = trie_node_child.hash | 1081 | 4 | AND CASE WHEN LENGTH(next_key.key_search_remain) = 0 THEN TRUE | 1082 | 4 | ELSE SUBSTR(next_key.key_search_remain, 1, 1) <= trie_node_child.child_num END | 1083 | 4 | LEFT JOIN trie_node ON trie_node.hash = trie_node_child.child_hash | 1084 | 4 | | 1085 | 4 | -- We want to keep only situations where `trie_node_child` is either | 1086 | 4 | -- equal to the key, or the first child strictly superior to the key. In | 1087 | 4 | -- order to do that, we try to find another child that is strictly | 1088 | 4 | -- in-between the key and `trie_node_child`. In the `WHERE` clause at the | 1089 | 4 | -- bottom, we only keep rows where `trie_node_child_before` is NULL. | 1090 | 4 | LEFT JOIN trie_node_child AS trie_node_child_before | 1091 | 4 | ON next_key.node_hash = trie_node_child_before.hash | 1092 | 4 | AND trie_node_child_before.child_num < trie_node_child.child_num | 1093 | 4 | AND (next_key.key_search_remain = X'' OR trie_node_child_before.child_num > SUBSTR(next_key.key_search_remain, 1, 1)) | 1094 | 4 | | 1095 | 4 | LEFT JOIN trie_node_storage AS trie_node_storage_trieref | 1096 | 4 | ON HEX(SUBSTR(next_key.key_search_remain, 1, 1)) = '10' AND next_key.node_hash = trie_node_storage_trieref.node_hash AND trie_node_storage_trieref.trie_root_ref IS NOT NULL | 1097 | 4 | LEFT JOIN trie_node AS trie_node_trieref | 1098 | 4 | ON trie_node_trieref.hash = trie_node_storage_trieref.node_hash | 1099 | 4 | AND COALESCE(SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node_trieref.partial_key)), X'') <= trie_node_trieref.partial_key | 1100 | 4 | | 1101 | 4 | LEFT JOIN trie_node_storage | 1102 | 4 | ON trie_node_storage.node_hash = COALESCE(trie_node.hash, trie_node_trieref.hash) | 1103 | 4 | | 1104 | 4 | WHERE | 1105 | 4 | -- Don't pull items that have already finished searching. | 1106 | 4 | next_key.node_hash IS NOT NULL AND next_key.key_search_remain IS NOT NULL AND (next_key.key_search_remain != X'' OR (next_key.node_is_branch AND :skip_branches)) | 1107 | 4 | -- See explanation above. | 1108 | 4 | AND trie_node_child_before.hash IS NULL | 1109 | 4 | -- Don't generate an item if there's nowhere to go to. | 1110 | 4 | AND (HEX(SUBSTR(next_key.key_search_remain, 1, 1)) = '10' OR trie_node_child.child_num IS NOT NULL) | 1111 | 4 | -- Stop iterating if the child's partial key is before the searched key. | 1112 | 4 | AND (trie_node.hash IS NULL OR NOT (COALESCE(SUBSTR(next_key.key_search_remain, 1, 1), X'') = trie_node_child.child_num AND COALESCE(SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node.partial_key)), X'') > trie_node.partial_key)) | 1113 | 4 | ), | 1114 | 4 | | 1115 | 4 | -- Now keep only the entries of `next_key` which have finished iterating. | 1116 | 4 | terminal_next_key(incomplete_storage, node_full_key, output) AS ( | 1117 | 4 | SELECT | 1118 | 4 | CASE | 1119 | 4 | WHEN COALESCE(SUBSTR(node_full_key, 1, LENGTH(:prefix)), X'') != :prefix THEN FALSE | 1120 | 4 | ELSE key_search_remain IS NULL | 1121 | 4 | END, | 1122 | 4 | node_full_key, | 1123 | 4 | CASE | 1124 | 4 | WHEN node_hash IS NULL THEN NULL | 1125 | 4 | WHEN COALESCE(SUBSTR(node_full_key, 1, LENGTH(:prefix)), X'') = :prefix THEN node_full_key | 1126 | 4 | ELSE NULL | 1127 | 4 | END | 1128 | 4 | FROM next_key | 1129 | 4 | WHERE key_search_remain IS NULL OR (LENGTH(key_search_remain) = 0 AND (NOT :skip_branches OR NOT node_is_branch)) | 1130 | 4 | ) | 1131 | 4 | | 1132 | 4 | SELECT | 1133 | 4 | COUNT(blocks.hash) >= 1, | 1134 | 4 | COALESCE(terminal_next_key.incomplete_storage, FALSE), | 1135 | 4 | terminal_next_key.output | 1136 | 4 | FROM blocks | 1137 | 4 | LEFT JOIN terminal_next_key | 1138 | 4 | WHERE blocks.hash = :block_hash | 1139 | 4 | -- We pick the entry of `terminal_next_key` with the smallest full key. Note that | 1140 | 4 | -- it might seem like a good idea to not using any GROUP BY and instead just do | 1141 | 4 | -- `ORDER BY node_full_key ASC LIMIT 1`, but doing so sometimes leads to SQLite | 1142 | 4 | -- not picking the entry with the smallest full key for a reason I couldn't | 1143 | 4 | -- figure out. | 1144 | 4 | AND (terminal_next_key.node_full_key IS NULL OR terminal_next_key.node_full_key = (SELECT MIN(node_full_key) FROM terminal_next_key)) | 1145 | 4 | LIMIT 1"#, | 1146 | 4 | ) | 1147 | 4 | .map_err(|err| { | 1148 | | StorageAccessError::Corrupted(CorruptedError::Internal( | 1149 | | InternalError(err), | 1150 | | )) | 1151 | 4 | })?0 ; | 1152 | | | 1153 | | // In order to debug the SQL query above (for example in case of a failing test), | 1154 | | // uncomment this block: | 1155 | | // | 1156 | | /*println!("{:?}", { | 1157 | | let mut statement = connection | 1158 | | .prepare_cached( | 1159 | | r#" | 1160 | | WITH RECURSIVE | 1161 | | copy-paste the definition of next_key here | 1162 | | | 1163 | | SELECT * FROM next_key"#).unwrap(); | 1164 | | statement | 1165 | | .query_map( | 1166 | | rusqlite::named_params! { | 1167 | | ":block_hash": &block_hash[..], | 1168 | | ":key": key_nibbles, | 1169 | | //":prefix": prefix_nibbles, | 1170 | | ":skip_branches": !branch_nodes | 1171 | | }, | 1172 | | |row| { | 1173 | | let node_hash = row.get::<_, Option<Vec<u8>>>(0)?.map(hex::encode); | 1174 | | let node_is_branch = row.get::<_, Option<i64>>(1)?.map(|n| n != 0); | 1175 | | let node_full_key = row.get::<_, Option<Vec<u8>>>(2)?; | 1176 | | let search_remain = row.get::<_, Option<Vec<u8>>>(3)?; | 1177 | | Ok((node_hash, node_is_branch, node_full_key, search_remain)) | 1178 | | }, | 1179 | | ) | 1180 | | .unwrap() | 1181 | | .collect::<Vec<_>>() | 1182 | | });*/ | 1183 | | | 1184 | 4 | let result = statement | 1185 | 4 | .query_row( | 1186 | 4 | rusqlite::named_params! { | 1187 | 4 | ":block_hash": &block_hash[..], | 1188 | 4 | ":key": key_nibbles, | 1189 | 4 | ":prefix": prefix_nibbles, | 1190 | 4 | ":skip_branches": !branch_nodes | 1191 | 4 | }, | 1192 | 4 | |row| { | 1193 | | let block_is_known = row.get::<_, i64>(0)? != 0; | 1194 | | let incomplete_storage = row.get::<_, i64>(1)? != 0; | 1195 | | let next_key = row.get::<_, Option<Vec<u8>>>(2)?; | 1196 | | Ok((block_is_known, incomplete_storage, next_key)) | 1197 | 4 | }, | 1198 | 4 | ) | 1199 | 4 | .optional() | 1200 | 4 | .map_err(|err| { | 1201 | | StorageAccessError::Corrupted(CorruptedError::Internal(InternalError(err))) | 1202 | 4 | })?0 ; | 1203 | | | 1204 | 4 | let Some((block_is_known, incomplete_storage, mut next_key)) = result else { | 1205 | 0 | return Ok(None); | 1206 | | }; | 1207 | | | 1208 | 4 | if !block_is_known { | 1209 | 0 | return Err(StorageAccessError::UnknownBlock); | 1210 | 4 | } | 1211 | 4 | | 1212 | 4 | if incomplete_storage { | 1213 | 2 | return Err(StorageAccessError::IncompleteStorage); | 1214 | 2 | } | 1215 | 2 | | 1216 | 2 | if parent_tries_paths_nibbles_length != 0 { | 1217 | 0 | next_key = next_key.map(|nk| nk[parent_tries_paths_nibbles_length..].to_vec()); | 1218 | 2 | } | 1219 | | | 1220 | 2 | Ok(next_key) | 1221 | 4 | } |
_RINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1C_B1B_EINtNtNtB1K_5array4iter8IntoIterhKj3_EB1B_EB7_ Line | Count | Source | 953 | 5 | pub fn block_storage_next_key( | 954 | 5 | &self, | 955 | 5 | block_hash: &[u8; 32], | 956 | 5 | parent_tries_paths_nibbles: impl Iterator<Item = impl Iterator<Item = u8>>, | 957 | 5 | key_nibbles: impl Iterator<Item = u8>, | 958 | 5 | prefix_nibbles: impl Iterator<Item = u8>, | 959 | 5 | branch_nodes: bool, | 960 | 5 | ) -> Result<Option<Vec<u8>>, StorageAccessError> { | 961 | 5 | // Process the iterators at the very beginning and before locking the database, in order | 962 | 5 | // to avoid a deadlock in case the `next()` function of one of the iterators accesses | 963 | 5 | // the database as well. | 964 | 5 | let parent_tries_paths_nibbles = parent_tries_paths_nibbles | 965 | 5 | .flat_map(|t| t.inspect(|n| assert!(*n < 16)).chain(iter::once(0x10))) | 966 | 5 | .collect::<Vec<_>>(); | 967 | 5 | let parent_tries_paths_nibbles_length = parent_tries_paths_nibbles.len(); | 968 | 5 | let key_nibbles = { | 969 | 5 | let mut v = parent_tries_paths_nibbles.clone(); | 970 | 5 | v.extend(key_nibbles.inspect(|n| assert!(*n < 16))); | 971 | 5 | v | 972 | 5 | }; | 973 | 5 | let prefix_nibbles = { | 974 | 5 | let mut v = parent_tries_paths_nibbles; | 975 | 5 | v.extend(prefix_nibbles.inspect(|n| assert!(*n < 16))); | 976 | 5 | v | 977 | 5 | }; | 978 | 5 | | 979 | 5 | let connection = self.database.lock(); | 980 | | | 981 | | // Sorry for that extremely complicated SQL statement. While the logic isn't actually very | 982 | | // complicated, we have to jump through many hoops in order to go around quirks in the | 983 | | // SQL language. | 984 | | // If you want to work on this SQL code, there is no miracle: write tests, and if a test | 985 | | // fails debug the content of `next_key` to find out where the iteration doesn't behave | 986 | | // as expected. | 987 | | // TODO: this algorithm relies the fact that leaf nodes always have a storage value, which isn't exactly clear in the schema ; however not relying on this makes it way harder to write | 988 | | // TODO: trie_root_ref system untested and most likely not working | 989 | | // TODO: infinite loop if there's a loop in the trie; detect this | 990 | | // TODO: could also check the prefix while iterating instead of only at the very end, which could maybe save many lookups | 991 | 5 | let mut statement = connection | 992 | 5 | .prepare_cached( | 993 | 5 | r#" | 994 | 5 | WITH RECURSIVE | 995 | 5 | -- We build a temporary table `next_key`, inserting entries one after one as we | 996 | 5 | -- descend the trie by trying to match entries with `:key`. | 997 | 5 | -- At each iteration, `node_hash` is the root where to continue the search, | 998 | 5 | -- `node_is_branch` is true if `node_hash` is a branch node, `node_full_key` is | 999 | 5 | -- the key of `node_hash` (that we build along the way) and serves as the final | 1000 | 5 | -- result, and `key_search_remain` contains the `:key` that remains to be matched. | 1001 | 5 | -- Can also be NULL to indicate that the search ended because the node necessary to | 1002 | 5 | -- continue was missing from the database, in which case the values of | 1003 | 5 | -- `node_hash` and `node_is_branch` have irrelevant values, and the value of | 1004 | 5 | -- `node_full_key` is the "best known key". | 1005 | 5 | -- If `:skip_branches` is false, the search ends when `key_search_remain` is null | 1006 | 5 | -- or empty. If `:skip_branches` is true, the search ends when `key_search_remain` | 1007 | 5 | -- is null or empty and that `node_is_branch` is false. | 1008 | 5 | -- | 1009 | 5 | -- `next_key` has zero elements if the block can't be found in the database or if | 1010 | 5 | -- the trie has no next key at all. These two situations need to be differentiated | 1011 | 5 | -- in the final SELECT statement. | 1012 | 5 | -- | 1013 | 5 | -- When encountering a node, we follow both the child that exactly matches `:key` | 1014 | 5 | -- and also the first child that is strictly superior to `:key`. This is necessary | 1015 | 5 | -- because `:key` might be equal to something like `ffffffff...`, in which case the | 1016 | 5 | -- result will be after any equal match. | 1017 | 5 | -- This means that the number of entries in `next_key` at the end of the recursion | 1018 | 5 | -- is something like `2 * depth_in_trie(key)`. | 1019 | 5 | -- In order to obtain the final result, we take the entry in `next_key` with the | 1020 | 5 | -- minimal `node_full_key` amongst the ones that have finished the search. | 1021 | 5 | -- | 1022 | 5 | -- Note that in the code below we do a lot of `COALESCE(SUBSTR(...), X'')`. This | 1023 | 5 | -- is because, for some reason, `SUBSTR(X'', ...)` always produces `NULL`. For this | 1024 | 5 | -- reason, it is also not possible to automatically pass NULL values | 1025 | 5 | -- through `SUSBTR`, and we have to use CASE/IIFs instead. | 1026 | 5 | next_key(node_hash, node_is_branch, node_full_key, key_search_remain) AS ( | 1027 | 5 | SELECT | 1028 | 5 | CASE | 1029 | 5 | WHEN trie_node.hash IS NULL | 1030 | 5 | THEN NULL | 1031 | 5 | WHEN COALESCE(SUBSTR(:key, 1, LENGTH(trie_node.partial_key)), X'') <= trie_node.partial_key | 1032 | 5 | THEN trie_node.hash | 1033 | 5 | ELSE | 1034 | 5 | NULL | 1035 | 5 | END, | 1036 | 5 | trie_node_storage.value IS NULL AND trie_node_storage.trie_root_ref IS NULL, | 1037 | 5 | COALESCE(trie_node.partial_key, X''), | 1038 | 5 | CASE | 1039 | 5 | WHEN trie_node.partial_key IS NULL | 1040 | 5 | THEN NULL | 1041 | 5 | WHEN COALESCE(SUBSTR(:key, 1, LENGTH(trie_node.partial_key)), X'') <= trie_node.partial_key | 1042 | 5 | THEN COALESCE(SUBSTR(:key, 1 + LENGTH(trie_node.partial_key)), X'') | 1043 | 5 | ELSE | 1044 | 5 | X'' -- The partial key is strictly inferior to `:key` | 1045 | 5 | END | 1046 | 5 | FROM blocks | 1047 | 5 | LEFT JOIN trie_node ON trie_node.hash = blocks.state_trie_root_hash | 1048 | 5 | LEFT JOIN trie_node_storage ON trie_node_storage.node_hash = trie_node.hash | 1049 | 5 | WHERE blocks.hash = :block_hash | 1050 | 5 | | 1051 | 5 | UNION ALL | 1052 | 5 | SELECT | 1053 | 5 | COALESCE(trie_node.hash, trie_node_trieref.hash), | 1054 | 5 | trie_node_storage.value IS NULL AND trie_node_storage.trie_root_ref IS NULL, | 1055 | 5 | CASE | 1056 | 5 | WHEN trie_node_child.child_num IS NULL | 1057 | 5 | THEN next_key.node_full_key | 1058 | 5 | WHEN trie_node.partial_key IS NULL AND trie_node_trieref.partial_key IS NULL | 1059 | 5 | THEN CAST(next_key.node_full_key || trie_node_child.child_num AS BLOB) | 1060 | 5 | ELSE | 1061 | 5 | CAST(next_key.node_full_key || trie_node_child.child_num || COALESCE(trie_node.partial_key, trie_node_trieref.partial_key) AS BLOB) | 1062 | 5 | END, | 1063 | 5 | CASE | 1064 | 5 | WHEN trie_node_child.child_num IS NOT NULL AND trie_node.partial_key IS NULL | 1065 | 5 | THEN NULL -- Child exists but is missing from database | 1066 | 5 | WHEN HEX(SUBSTR(next_key.key_search_remain, 1, 1)) = '10' AND trie_node_trieref.hash IS NULL | 1067 | 5 | THEN NULL -- Trie reference exists but is missing from database | 1068 | 5 | WHEN SUBSTR(next_key.key_search_remain, 1, 1) = trie_node_child.child_num AND SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node.partial_key)) = trie_node.partial_key | 1069 | 5 | THEN SUBSTR(next_key.key_search_remain, 2 + LENGTH(trie_node.partial_key)) -- Equal match, continue iterating | 1070 | 5 | WHEN SUBSTR(next_key.key_search_remain, 1, 1) = trie_node_child.child_num AND SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node.partial_key)) < trie_node.partial_key | 1071 | 5 | THEN X'' -- Searched key is before the node we are iterating to, thus we cut the search short | 1072 | 5 | WHEN HEX(SUBSTR(next_key.key_search_remain, 1, 1)) = '10' AND COALESCE(SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node_trieref.partial_key)), X'') = trie_node_trieref.partial_key | 1073 | 5 | THEN COALESCE(SUBSTR(next_key.key_search_remain, 2 + LENGTH(trie_node_trieref.partial_key)), X'') | 1074 | 5 | ELSE | 1075 | 5 | X'' -- Shouldn't be reachable. | 1076 | 5 | END | 1077 | 5 | FROM next_key | 1078 | 5 | | 1079 | 5 | LEFT JOIN trie_node_child | 1080 | 5 | ON next_key.node_hash = trie_node_child.hash | 1081 | 5 | AND CASE WHEN LENGTH(next_key.key_search_remain) = 0 THEN TRUE | 1082 | 5 | ELSE SUBSTR(next_key.key_search_remain, 1, 1) <= trie_node_child.child_num END | 1083 | 5 | LEFT JOIN trie_node ON trie_node.hash = trie_node_child.child_hash | 1084 | 5 | | 1085 | 5 | -- We want to keep only situations where `trie_node_child` is either | 1086 | 5 | -- equal to the key, or the first child strictly superior to the key. In | 1087 | 5 | -- order to do that, we try to find another child that is strictly | 1088 | 5 | -- in-between the key and `trie_node_child`. In the `WHERE` clause at the | 1089 | 5 | -- bottom, we only keep rows where `trie_node_child_before` is NULL. | 1090 | 5 | LEFT JOIN trie_node_child AS trie_node_child_before | 1091 | 5 | ON next_key.node_hash = trie_node_child_before.hash | 1092 | 5 | AND trie_node_child_before.child_num < trie_node_child.child_num | 1093 | 5 | AND (next_key.key_search_remain = X'' OR trie_node_child_before.child_num > SUBSTR(next_key.key_search_remain, 1, 1)) | 1094 | 5 | | 1095 | 5 | LEFT JOIN trie_node_storage AS trie_node_storage_trieref | 1096 | 5 | ON HEX(SUBSTR(next_key.key_search_remain, 1, 1)) = '10' AND next_key.node_hash = trie_node_storage_trieref.node_hash AND trie_node_storage_trieref.trie_root_ref IS NOT NULL | 1097 | 5 | LEFT JOIN trie_node AS trie_node_trieref | 1098 | 5 | ON trie_node_trieref.hash = trie_node_storage_trieref.node_hash | 1099 | 5 | AND COALESCE(SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node_trieref.partial_key)), X'') <= trie_node_trieref.partial_key | 1100 | 5 | | 1101 | 5 | LEFT JOIN trie_node_storage | 1102 | 5 | ON trie_node_storage.node_hash = COALESCE(trie_node.hash, trie_node_trieref.hash) | 1103 | 5 | | 1104 | 5 | WHERE | 1105 | 5 | -- Don't pull items that have already finished searching. | 1106 | 5 | next_key.node_hash IS NOT NULL AND next_key.key_search_remain IS NOT NULL AND (next_key.key_search_remain != X'' OR (next_key.node_is_branch AND :skip_branches)) | 1107 | 5 | -- See explanation above. | 1108 | 5 | AND trie_node_child_before.hash IS NULL | 1109 | 5 | -- Don't generate an item if there's nowhere to go to. | 1110 | 5 | AND (HEX(SUBSTR(next_key.key_search_remain, 1, 1)) = '10' OR trie_node_child.child_num IS NOT NULL) | 1111 | 5 | -- Stop iterating if the child's partial key is before the searched key. | 1112 | 5 | AND (trie_node.hash IS NULL OR NOT (COALESCE(SUBSTR(next_key.key_search_remain, 1, 1), X'') = trie_node_child.child_num AND COALESCE(SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node.partial_key)), X'') > trie_node.partial_key)) | 1113 | 5 | ), | 1114 | 5 | | 1115 | 5 | -- Now keep only the entries of `next_key` which have finished iterating. | 1116 | 5 | terminal_next_key(incomplete_storage, node_full_key, output) AS ( | 1117 | 5 | SELECT | 1118 | 5 | CASE | 1119 | 5 | WHEN COALESCE(SUBSTR(node_full_key, 1, LENGTH(:prefix)), X'') != :prefix THEN FALSE | 1120 | 5 | ELSE key_search_remain IS NULL | 1121 | 5 | END, | 1122 | 5 | node_full_key, | 1123 | 5 | CASE | 1124 | 5 | WHEN node_hash IS NULL THEN NULL | 1125 | 5 | WHEN COALESCE(SUBSTR(node_full_key, 1, LENGTH(:prefix)), X'') = :prefix THEN node_full_key | 1126 | 5 | ELSE NULL | 1127 | 5 | END | 1128 | 5 | FROM next_key | 1129 | 5 | WHERE key_search_remain IS NULL OR (LENGTH(key_search_remain) = 0 AND (NOT :skip_branches OR NOT node_is_branch)) | 1130 | 5 | ) | 1131 | 5 | | 1132 | 5 | SELECT | 1133 | 5 | COUNT(blocks.hash) >= 1, | 1134 | 5 | COALESCE(terminal_next_key.incomplete_storage, FALSE), | 1135 | 5 | terminal_next_key.output | 1136 | 5 | FROM blocks | 1137 | 5 | LEFT JOIN terminal_next_key | 1138 | 5 | WHERE blocks.hash = :block_hash | 1139 | 5 | -- We pick the entry of `terminal_next_key` with the smallest full key. Note that | 1140 | 5 | -- it might seem like a good idea to not using any GROUP BY and instead just do | 1141 | 5 | -- `ORDER BY node_full_key ASC LIMIT 1`, but doing so sometimes leads to SQLite | 1142 | 5 | -- not picking the entry with the smallest full key for a reason I couldn't | 1143 | 5 | -- figure out. | 1144 | 5 | AND (terminal_next_key.node_full_key IS NULL OR terminal_next_key.node_full_key = (SELECT MIN(node_full_key) FROM terminal_next_key)) | 1145 | 5 | LIMIT 1"#, | 1146 | 5 | ) | 1147 | 5 | .map_err(|err| { | 1148 | | StorageAccessError::Corrupted(CorruptedError::Internal( | 1149 | | InternalError(err), | 1150 | | )) | 1151 | 5 | })?0 ; | 1152 | | | 1153 | | // In order to debug the SQL query above (for example in case of a failing test), | 1154 | | // uncomment this block: | 1155 | | // | 1156 | | /*println!("{:?}", { | 1157 | | let mut statement = connection | 1158 | | .prepare_cached( | 1159 | | r#" | 1160 | | WITH RECURSIVE | 1161 | | copy-paste the definition of next_key here | 1162 | | | 1163 | | SELECT * FROM next_key"#).unwrap(); | 1164 | | statement | 1165 | | .query_map( | 1166 | | rusqlite::named_params! { | 1167 | | ":block_hash": &block_hash[..], | 1168 | | ":key": key_nibbles, | 1169 | | //":prefix": prefix_nibbles, | 1170 | | ":skip_branches": !branch_nodes | 1171 | | }, | 1172 | | |row| { | 1173 | | let node_hash = row.get::<_, Option<Vec<u8>>>(0)?.map(hex::encode); | 1174 | | let node_is_branch = row.get::<_, Option<i64>>(1)?.map(|n| n != 0); | 1175 | | let node_full_key = row.get::<_, Option<Vec<u8>>>(2)?; | 1176 | | let search_remain = row.get::<_, Option<Vec<u8>>>(3)?; | 1177 | | Ok((node_hash, node_is_branch, node_full_key, search_remain)) | 1178 | | }, | 1179 | | ) | 1180 | | .unwrap() | 1181 | | .collect::<Vec<_>>() | 1182 | | });*/ | 1183 | | | 1184 | 5 | let result = statement | 1185 | 5 | .query_row( | 1186 | 5 | rusqlite::named_params! { | 1187 | 5 | ":block_hash": &block_hash[..], | 1188 | 5 | ":key": key_nibbles, | 1189 | 5 | ":prefix": prefix_nibbles, | 1190 | 5 | ":skip_branches": !branch_nodes | 1191 | 5 | }, | 1192 | 5 | |row| { | 1193 | | let block_is_known = row.get::<_, i64>(0)? != 0; | 1194 | | let incomplete_storage = row.get::<_, i64>(1)? != 0; | 1195 | | let next_key = row.get::<_, Option<Vec<u8>>>(2)?; | 1196 | | Ok((block_is_known, incomplete_storage, next_key)) | 1197 | 5 | }, | 1198 | 5 | ) | 1199 | 5 | .optional() | 1200 | 5 | .map_err(|err| { | 1201 | | StorageAccessError::Corrupted(CorruptedError::Internal(InternalError(err))) | 1202 | 5 | })?0 ; | 1203 | | | 1204 | 5 | let Some((block_is_known, incomplete_storage, mut next_key)) = result else { | 1205 | 0 | return Ok(None); | 1206 | | }; | 1207 | | | 1208 | 5 | if !block_is_known { | 1209 | 0 | return Err(StorageAccessError::UnknownBlock); | 1210 | 5 | } | 1211 | 5 | | 1212 | 5 | if incomplete_storage { | 1213 | 2 | return Err(StorageAccessError::IncompleteStorage); | 1214 | 3 | } | 1215 | 3 | | 1216 | 3 | if parent_tries_paths_nibbles_length != 0 { | 1217 | 0 | next_key = next_key.map(|nk| nk[parent_tries_paths_nibbles_length..].to_vec()); | 1218 | 3 | } | 1219 | | | 1220 | 3 | Ok(next_key) | 1221 | 5 | } |
_RINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1C_B1B_EINtNtNtB1K_5array4iter8IntoIterhKj3_EB2E_EB7_ Line | Count | Source | 953 | 1 | pub fn block_storage_next_key( | 954 | 1 | &self, | 955 | 1 | block_hash: &[u8; 32], | 956 | 1 | parent_tries_paths_nibbles: impl Iterator<Item = impl Iterator<Item = u8>>, | 957 | 1 | key_nibbles: impl Iterator<Item = u8>, | 958 | 1 | prefix_nibbles: impl Iterator<Item = u8>, | 959 | 1 | branch_nodes: bool, | 960 | 1 | ) -> Result<Option<Vec<u8>>, StorageAccessError> { | 961 | 1 | // Process the iterators at the very beginning and before locking the database, in order | 962 | 1 | // to avoid a deadlock in case the `next()` function of one of the iterators accesses | 963 | 1 | // the database as well. | 964 | 1 | let parent_tries_paths_nibbles = parent_tries_paths_nibbles | 965 | 1 | .flat_map(|t| t.inspect(|n| assert!(*n < 16)).chain(iter::once(0x10))) | 966 | 1 | .collect::<Vec<_>>(); | 967 | 1 | let parent_tries_paths_nibbles_length = parent_tries_paths_nibbles.len(); | 968 | 1 | let key_nibbles = { | 969 | 1 | let mut v = parent_tries_paths_nibbles.clone(); | 970 | 1 | v.extend(key_nibbles.inspect(|n| assert!(*n < 16))); | 971 | 1 | v | 972 | 1 | }; | 973 | 1 | let prefix_nibbles = { | 974 | 1 | let mut v = parent_tries_paths_nibbles; | 975 | 1 | v.extend(prefix_nibbles.inspect(|n| assert!(*n < 16))); | 976 | 1 | v | 977 | 1 | }; | 978 | 1 | | 979 | 1 | let connection = self.database.lock(); | 980 | | | 981 | | // Sorry for that extremely complicated SQL statement. While the logic isn't actually very | 982 | | // complicated, we have to jump through many hoops in order to go around quirks in the | 983 | | // SQL language. | 984 | | // If you want to work on this SQL code, there is no miracle: write tests, and if a test | 985 | | // fails debug the content of `next_key` to find out where the iteration doesn't behave | 986 | | // as expected. | 987 | | // TODO: this algorithm relies the fact that leaf nodes always have a storage value, which isn't exactly clear in the schema ; however not relying on this makes it way harder to write | 988 | | // TODO: trie_root_ref system untested and most likely not working | 989 | | // TODO: infinite loop if there's a loop in the trie; detect this | 990 | | // TODO: could also check the prefix while iterating instead of only at the very end, which could maybe save many lookups | 991 | 1 | let mut statement = connection | 992 | 1 | .prepare_cached( | 993 | 1 | r#" | 994 | 1 | WITH RECURSIVE | 995 | 1 | -- We build a temporary table `next_key`, inserting entries one after one as we | 996 | 1 | -- descend the trie by trying to match entries with `:key`. | 997 | 1 | -- At each iteration, `node_hash` is the root where to continue the search, | 998 | 1 | -- `node_is_branch` is true if `node_hash` is a branch node, `node_full_key` is | 999 | 1 | -- the key of `node_hash` (that we build along the way) and serves as the final | 1000 | 1 | -- result, and `key_search_remain` contains the `:key` that remains to be matched. | 1001 | 1 | -- Can also be NULL to indicate that the search ended because the node necessary to | 1002 | 1 | -- continue was missing from the database, in which case the values of | 1003 | 1 | -- `node_hash` and `node_is_branch` have irrelevant values, and the value of | 1004 | 1 | -- `node_full_key` is the "best known key". | 1005 | 1 | -- If `:skip_branches` is false, the search ends when `key_search_remain` is null | 1006 | 1 | -- or empty. If `:skip_branches` is true, the search ends when `key_search_remain` | 1007 | 1 | -- is null or empty and that `node_is_branch` is false. | 1008 | 1 | -- | 1009 | 1 | -- `next_key` has zero elements if the block can't be found in the database or if | 1010 | 1 | -- the trie has no next key at all. These two situations need to be differentiated | 1011 | 1 | -- in the final SELECT statement. | 1012 | 1 | -- | 1013 | 1 | -- When encountering a node, we follow both the child that exactly matches `:key` | 1014 | 1 | -- and also the first child that is strictly superior to `:key`. This is necessary | 1015 | 1 | -- because `:key` might be equal to something like `ffffffff...`, in which case the | 1016 | 1 | -- result will be after any equal match. | 1017 | 1 | -- This means that the number of entries in `next_key` at the end of the recursion | 1018 | 1 | -- is something like `2 * depth_in_trie(key)`. | 1019 | 1 | -- In order to obtain the final result, we take the entry in `next_key` with the | 1020 | 1 | -- minimal `node_full_key` amongst the ones that have finished the search. | 1021 | 1 | -- | 1022 | 1 | -- Note that in the code below we do a lot of `COALESCE(SUBSTR(...), X'')`. This | 1023 | 1 | -- is because, for some reason, `SUBSTR(X'', ...)` always produces `NULL`. For this | 1024 | 1 | -- reason, it is also not possible to automatically pass NULL values | 1025 | 1 | -- through `SUSBTR`, and we have to use CASE/IIFs instead. | 1026 | 1 | next_key(node_hash, node_is_branch, node_full_key, key_search_remain) AS ( | 1027 | 1 | SELECT | 1028 | 1 | CASE | 1029 | 1 | WHEN trie_node.hash IS NULL | 1030 | 1 | THEN NULL | 1031 | 1 | WHEN COALESCE(SUBSTR(:key, 1, LENGTH(trie_node.partial_key)), X'') <= trie_node.partial_key | 1032 | 1 | THEN trie_node.hash | 1033 | 1 | ELSE | 1034 | 1 | NULL | 1035 | 1 | END, | 1036 | 1 | trie_node_storage.value IS NULL AND trie_node_storage.trie_root_ref IS NULL, | 1037 | 1 | COALESCE(trie_node.partial_key, X''), | 1038 | 1 | CASE | 1039 | 1 | WHEN trie_node.partial_key IS NULL | 1040 | 1 | THEN NULL | 1041 | 1 | WHEN COALESCE(SUBSTR(:key, 1, LENGTH(trie_node.partial_key)), X'') <= trie_node.partial_key | 1042 | 1 | THEN COALESCE(SUBSTR(:key, 1 + LENGTH(trie_node.partial_key)), X'') | 1043 | 1 | ELSE | 1044 | 1 | X'' -- The partial key is strictly inferior to `:key` | 1045 | 1 | END | 1046 | 1 | FROM blocks | 1047 | 1 | LEFT JOIN trie_node ON trie_node.hash = blocks.state_trie_root_hash | 1048 | 1 | LEFT JOIN trie_node_storage ON trie_node_storage.node_hash = trie_node.hash | 1049 | 1 | WHERE blocks.hash = :block_hash | 1050 | 1 | | 1051 | 1 | UNION ALL | 1052 | 1 | SELECT | 1053 | 1 | COALESCE(trie_node.hash, trie_node_trieref.hash), | 1054 | 1 | trie_node_storage.value IS NULL AND trie_node_storage.trie_root_ref IS NULL, | 1055 | 1 | CASE | 1056 | 1 | WHEN trie_node_child.child_num IS NULL | 1057 | 1 | THEN next_key.node_full_key | 1058 | 1 | WHEN trie_node.partial_key IS NULL AND trie_node_trieref.partial_key IS NULL | 1059 | 1 | THEN CAST(next_key.node_full_key || trie_node_child.child_num AS BLOB) | 1060 | 1 | ELSE | 1061 | 1 | CAST(next_key.node_full_key || trie_node_child.child_num || COALESCE(trie_node.partial_key, trie_node_trieref.partial_key) AS BLOB) | 1062 | 1 | END, | 1063 | 1 | CASE | 1064 | 1 | WHEN trie_node_child.child_num IS NOT NULL AND trie_node.partial_key IS NULL | 1065 | 1 | THEN NULL -- Child exists but is missing from database | 1066 | 1 | WHEN HEX(SUBSTR(next_key.key_search_remain, 1, 1)) = '10' AND trie_node_trieref.hash IS NULL | 1067 | 1 | THEN NULL -- Trie reference exists but is missing from database | 1068 | 1 | WHEN SUBSTR(next_key.key_search_remain, 1, 1) = trie_node_child.child_num AND SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node.partial_key)) = trie_node.partial_key | 1069 | 1 | THEN SUBSTR(next_key.key_search_remain, 2 + LENGTH(trie_node.partial_key)) -- Equal match, continue iterating | 1070 | 1 | WHEN SUBSTR(next_key.key_search_remain, 1, 1) = trie_node_child.child_num AND SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node.partial_key)) < trie_node.partial_key | 1071 | 1 | THEN X'' -- Searched key is before the node we are iterating to, thus we cut the search short | 1072 | 1 | WHEN HEX(SUBSTR(next_key.key_search_remain, 1, 1)) = '10' AND COALESCE(SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node_trieref.partial_key)), X'') = trie_node_trieref.partial_key | 1073 | 1 | THEN COALESCE(SUBSTR(next_key.key_search_remain, 2 + LENGTH(trie_node_trieref.partial_key)), X'') | 1074 | 1 | ELSE | 1075 | 1 | X'' -- Shouldn't be reachable. | 1076 | 1 | END | 1077 | 1 | FROM next_key | 1078 | 1 | | 1079 | 1 | LEFT JOIN trie_node_child | 1080 | 1 | ON next_key.node_hash = trie_node_child.hash | 1081 | 1 | AND CASE WHEN LENGTH(next_key.key_search_remain) = 0 THEN TRUE | 1082 | 1 | ELSE SUBSTR(next_key.key_search_remain, 1, 1) <= trie_node_child.child_num END | 1083 | 1 | LEFT JOIN trie_node ON trie_node.hash = trie_node_child.child_hash | 1084 | 1 | | 1085 | 1 | -- We want to keep only situations where `trie_node_child` is either | 1086 | 1 | -- equal to the key, or the first child strictly superior to the key. In | 1087 | 1 | -- order to do that, we try to find another child that is strictly | 1088 | 1 | -- in-between the key and `trie_node_child`. In the `WHERE` clause at the | 1089 | 1 | -- bottom, we only keep rows where `trie_node_child_before` is NULL. | 1090 | 1 | LEFT JOIN trie_node_child AS trie_node_child_before | 1091 | 1 | ON next_key.node_hash = trie_node_child_before.hash | 1092 | 1 | AND trie_node_child_before.child_num < trie_node_child.child_num | 1093 | 1 | AND (next_key.key_search_remain = X'' OR trie_node_child_before.child_num > SUBSTR(next_key.key_search_remain, 1, 1)) | 1094 | 1 | | 1095 | 1 | LEFT JOIN trie_node_storage AS trie_node_storage_trieref | 1096 | 1 | ON HEX(SUBSTR(next_key.key_search_remain, 1, 1)) = '10' AND next_key.node_hash = trie_node_storage_trieref.node_hash AND trie_node_storage_trieref.trie_root_ref IS NOT NULL | 1097 | 1 | LEFT JOIN trie_node AS trie_node_trieref | 1098 | 1 | ON trie_node_trieref.hash = trie_node_storage_trieref.node_hash | 1099 | 1 | AND COALESCE(SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node_trieref.partial_key)), X'') <= trie_node_trieref.partial_key | 1100 | 1 | | 1101 | 1 | LEFT JOIN trie_node_storage | 1102 | 1 | ON trie_node_storage.node_hash = COALESCE(trie_node.hash, trie_node_trieref.hash) | 1103 | 1 | | 1104 | 1 | WHERE | 1105 | 1 | -- Don't pull items that have already finished searching. | 1106 | 1 | next_key.node_hash IS NOT NULL AND next_key.key_search_remain IS NOT NULL AND (next_key.key_search_remain != X'' OR (next_key.node_is_branch AND :skip_branches)) | 1107 | 1 | -- See explanation above. | 1108 | 1 | AND trie_node_child_before.hash IS NULL | 1109 | 1 | -- Don't generate an item if there's nowhere to go to. | 1110 | 1 | AND (HEX(SUBSTR(next_key.key_search_remain, 1, 1)) = '10' OR trie_node_child.child_num IS NOT NULL) | 1111 | 1 | -- Stop iterating if the child's partial key is before the searched key. | 1112 | 1 | AND (trie_node.hash IS NULL OR NOT (COALESCE(SUBSTR(next_key.key_search_remain, 1, 1), X'') = trie_node_child.child_num AND COALESCE(SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node.partial_key)), X'') > trie_node.partial_key)) | 1113 | 1 | ), | 1114 | 1 | | 1115 | 1 | -- Now keep only the entries of `next_key` which have finished iterating. | 1116 | 1 | terminal_next_key(incomplete_storage, node_full_key, output) AS ( | 1117 | 1 | SELECT | 1118 | 1 | CASE | 1119 | 1 | WHEN COALESCE(SUBSTR(node_full_key, 1, LENGTH(:prefix)), X'') != :prefix THEN FALSE | 1120 | 1 | ELSE key_search_remain IS NULL | 1121 | 1 | END, | 1122 | 1 | node_full_key, | 1123 | 1 | CASE | 1124 | 1 | WHEN node_hash IS NULL THEN NULL | 1125 | 1 | WHEN COALESCE(SUBSTR(node_full_key, 1, LENGTH(:prefix)), X'') = :prefix THEN node_full_key | 1126 | 1 | ELSE NULL | 1127 | 1 | END | 1128 | 1 | FROM next_key | 1129 | 1 | WHERE key_search_remain IS NULL OR (LENGTH(key_search_remain) = 0 AND (NOT :skip_branches OR NOT node_is_branch)) | 1130 | 1 | ) | 1131 | 1 | | 1132 | 1 | SELECT | 1133 | 1 | COUNT(blocks.hash) >= 1, | 1134 | 1 | COALESCE(terminal_next_key.incomplete_storage, FALSE), | 1135 | 1 | terminal_next_key.output | 1136 | 1 | FROM blocks | 1137 | 1 | LEFT JOIN terminal_next_key | 1138 | 1 | WHERE blocks.hash = :block_hash | 1139 | 1 | -- We pick the entry of `terminal_next_key` with the smallest full key. Note that | 1140 | 1 | -- it might seem like a good idea to not using any GROUP BY and instead just do | 1141 | 1 | -- `ORDER BY node_full_key ASC LIMIT 1`, but doing so sometimes leads to SQLite | 1142 | 1 | -- not picking the entry with the smallest full key for a reason I couldn't | 1143 | 1 | -- figure out. | 1144 | 1 | AND (terminal_next_key.node_full_key IS NULL OR terminal_next_key.node_full_key = (SELECT MIN(node_full_key) FROM terminal_next_key)) | 1145 | 1 | LIMIT 1"#, | 1146 | 1 | ) | 1147 | 1 | .map_err(|err| { | 1148 | | StorageAccessError::Corrupted(CorruptedError::Internal( | 1149 | | InternalError(err), | 1150 | | )) | 1151 | 1 | })?0 ; | 1152 | | | 1153 | | // In order to debug the SQL query above (for example in case of a failing test), | 1154 | | // uncomment this block: | 1155 | | // | 1156 | | /*println!("{:?}", { | 1157 | | let mut statement = connection | 1158 | | .prepare_cached( | 1159 | | r#" | 1160 | | WITH RECURSIVE | 1161 | | copy-paste the definition of next_key here | 1162 | | | 1163 | | SELECT * FROM next_key"#).unwrap(); | 1164 | | statement | 1165 | | .query_map( | 1166 | | rusqlite::named_params! { | 1167 | | ":block_hash": &block_hash[..], | 1168 | | ":key": key_nibbles, | 1169 | | //":prefix": prefix_nibbles, | 1170 | | ":skip_branches": !branch_nodes | 1171 | | }, | 1172 | | |row| { | 1173 | | let node_hash = row.get::<_, Option<Vec<u8>>>(0)?.map(hex::encode); | 1174 | | let node_is_branch = row.get::<_, Option<i64>>(1)?.map(|n| n != 0); | 1175 | | let node_full_key = row.get::<_, Option<Vec<u8>>>(2)?; | 1176 | | let search_remain = row.get::<_, Option<Vec<u8>>>(3)?; | 1177 | | Ok((node_hash, node_is_branch, node_full_key, search_remain)) | 1178 | | }, | 1179 | | ) | 1180 | | .unwrap() | 1181 | | .collect::<Vec<_>>() | 1182 | | });*/ | 1183 | | | 1184 | 1 | let result = statement | 1185 | 1 | .query_row( | 1186 | 1 | rusqlite::named_params! { | 1187 | 1 | ":block_hash": &block_hash[..], | 1188 | 1 | ":key": key_nibbles, | 1189 | 1 | ":prefix": prefix_nibbles, | 1190 | 1 | ":skip_branches": !branch_nodes | 1191 | 1 | }, | 1192 | 1 | |row| { | 1193 | | let block_is_known = row.get::<_, i64>(0)? != 0; | 1194 | | let incomplete_storage = row.get::<_, i64>(1)? != 0; | 1195 | | let next_key = row.get::<_, Option<Vec<u8>>>(2)?; | 1196 | | Ok((block_is_known, incomplete_storage, next_key)) | 1197 | 1 | }, | 1198 | 1 | ) | 1199 | 1 | .optional() | 1200 | 1 | .map_err(|err| { | 1201 | | StorageAccessError::Corrupted(CorruptedError::Internal(InternalError(err))) | 1202 | 1 | })?0 ; | 1203 | | | 1204 | 1 | let Some((block_is_known, incomplete_storage, mut next_key)) = result else { | 1205 | 0 | return Ok(None); | 1206 | | }; | 1207 | | | 1208 | 1 | if !block_is_known { | 1209 | 0 | return Err(StorageAccessError::UnknownBlock); | 1210 | 1 | } | 1211 | 1 | | 1212 | 1 | if incomplete_storage { | 1213 | 0 | return Err(StorageAccessError::IncompleteStorage); | 1214 | 1 | } | 1215 | 1 | | 1216 | 1 | if parent_tries_paths_nibbles_length != 0 { | 1217 | 0 | next_key = next_key.map(|nk| nk[parent_tries_paths_nibbles_length..].to_vec()); | 1218 | 1 | } | 1219 | | | 1220 | 1 | Ok(next_key) | 1221 | 1 | } |
_RINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1C_B1B_EINtNtNtB1K_5array4iter8IntoIterhKj8_EB1B_EB7_ Line | Count | Source | 953 | 2 | pub fn block_storage_next_key( | 954 | 2 | &self, | 955 | 2 | block_hash: &[u8; 32], | 956 | 2 | parent_tries_paths_nibbles: impl Iterator<Item = impl Iterator<Item = u8>>, | 957 | 2 | key_nibbles: impl Iterator<Item = u8>, | 958 | 2 | prefix_nibbles: impl Iterator<Item = u8>, | 959 | 2 | branch_nodes: bool, | 960 | 2 | ) -> Result<Option<Vec<u8>>, StorageAccessError> { | 961 | 2 | // Process the iterators at the very beginning and before locking the database, in order | 962 | 2 | // to avoid a deadlock in case the `next()` function of one of the iterators accesses | 963 | 2 | // the database as well. | 964 | 2 | let parent_tries_paths_nibbles = parent_tries_paths_nibbles | 965 | 2 | .flat_map(|t| t.inspect(|n| assert!(*n < 16)).chain(iter::once(0x10))) | 966 | 2 | .collect::<Vec<_>>(); | 967 | 2 | let parent_tries_paths_nibbles_length = parent_tries_paths_nibbles.len(); | 968 | 2 | let key_nibbles = { | 969 | 2 | let mut v = parent_tries_paths_nibbles.clone(); | 970 | 2 | v.extend(key_nibbles.inspect(|n| assert!(*n < 16))); | 971 | 2 | v | 972 | 2 | }; | 973 | 2 | let prefix_nibbles = { | 974 | 2 | let mut v = parent_tries_paths_nibbles; | 975 | 2 | v.extend(prefix_nibbles.inspect(|n| assert!(*n < 16))); | 976 | 2 | v | 977 | 2 | }; | 978 | 2 | | 979 | 2 | let connection = self.database.lock(); | 980 | | | 981 | | // Sorry for that extremely complicated SQL statement. While the logic isn't actually very | 982 | | // complicated, we have to jump through many hoops in order to go around quirks in the | 983 | | // SQL language. | 984 | | // If you want to work on this SQL code, there is no miracle: write tests, and if a test | 985 | | // fails debug the content of `next_key` to find out where the iteration doesn't behave | 986 | | // as expected. | 987 | | // TODO: this algorithm relies the fact that leaf nodes always have a storage value, which isn't exactly clear in the schema ; however not relying on this makes it way harder to write | 988 | | // TODO: trie_root_ref system untested and most likely not working | 989 | | // TODO: infinite loop if there's a loop in the trie; detect this | 990 | | // TODO: could also check the prefix while iterating instead of only at the very end, which could maybe save many lookups | 991 | 2 | let mut statement = connection | 992 | 2 | .prepare_cached( | 993 | 2 | r#" | 994 | 2 | WITH RECURSIVE | 995 | 2 | -- We build a temporary table `next_key`, inserting entries one after one as we | 996 | 2 | -- descend the trie by trying to match entries with `:key`. | 997 | 2 | -- At each iteration, `node_hash` is the root where to continue the search, | 998 | 2 | -- `node_is_branch` is true if `node_hash` is a branch node, `node_full_key` is | 999 | 2 | -- the key of `node_hash` (that we build along the way) and serves as the final | 1000 | 2 | -- result, and `key_search_remain` contains the `:key` that remains to be matched. | 1001 | 2 | -- Can also be NULL to indicate that the search ended because the node necessary to | 1002 | 2 | -- continue was missing from the database, in which case the values of | 1003 | 2 | -- `node_hash` and `node_is_branch` have irrelevant values, and the value of | 1004 | 2 | -- `node_full_key` is the "best known key". | 1005 | 2 | -- If `:skip_branches` is false, the search ends when `key_search_remain` is null | 1006 | 2 | -- or empty. If `:skip_branches` is true, the search ends when `key_search_remain` | 1007 | 2 | -- is null or empty and that `node_is_branch` is false. | 1008 | 2 | -- | 1009 | 2 | -- `next_key` has zero elements if the block can't be found in the database or if | 1010 | 2 | -- the trie has no next key at all. These two situations need to be differentiated | 1011 | 2 | -- in the final SELECT statement. | 1012 | 2 | -- | 1013 | 2 | -- When encountering a node, we follow both the child that exactly matches `:key` | 1014 | 2 | -- and also the first child that is strictly superior to `:key`. This is necessary | 1015 | 2 | -- because `:key` might be equal to something like `ffffffff...`, in which case the | 1016 | 2 | -- result will be after any equal match. | 1017 | 2 | -- This means that the number of entries in `next_key` at the end of the recursion | 1018 | 2 | -- is something like `2 * depth_in_trie(key)`. | 1019 | 2 | -- In order to obtain the final result, we take the entry in `next_key` with the | 1020 | 2 | -- minimal `node_full_key` amongst the ones that have finished the search. | 1021 | 2 | -- | 1022 | 2 | -- Note that in the code below we do a lot of `COALESCE(SUBSTR(...), X'')`. This | 1023 | 2 | -- is because, for some reason, `SUBSTR(X'', ...)` always produces `NULL`. For this | 1024 | 2 | -- reason, it is also not possible to automatically pass NULL values | 1025 | 2 | -- through `SUSBTR`, and we have to use CASE/IIFs instead. | 1026 | 2 | next_key(node_hash, node_is_branch, node_full_key, key_search_remain) AS ( | 1027 | 2 | SELECT | 1028 | 2 | CASE | 1029 | 2 | WHEN trie_node.hash IS NULL | 1030 | 2 | THEN NULL | 1031 | 2 | WHEN COALESCE(SUBSTR(:key, 1, LENGTH(trie_node.partial_key)), X'') <= trie_node.partial_key | 1032 | 2 | THEN trie_node.hash | 1033 | 2 | ELSE | 1034 | 2 | NULL | 1035 | 2 | END, | 1036 | 2 | trie_node_storage.value IS NULL AND trie_node_storage.trie_root_ref IS NULL, | 1037 | 2 | COALESCE(trie_node.partial_key, X''), | 1038 | 2 | CASE | 1039 | 2 | WHEN trie_node.partial_key IS NULL | 1040 | 2 | THEN NULL | 1041 | 2 | WHEN COALESCE(SUBSTR(:key, 1, LENGTH(trie_node.partial_key)), X'') <= trie_node.partial_key | 1042 | 2 | THEN COALESCE(SUBSTR(:key, 1 + LENGTH(trie_node.partial_key)), X'') | 1043 | 2 | ELSE | 1044 | 2 | X'' -- The partial key is strictly inferior to `:key` | 1045 | 2 | END | 1046 | 2 | FROM blocks | 1047 | 2 | LEFT JOIN trie_node ON trie_node.hash = blocks.state_trie_root_hash | 1048 | 2 | LEFT JOIN trie_node_storage ON trie_node_storage.node_hash = trie_node.hash | 1049 | 2 | WHERE blocks.hash = :block_hash | 1050 | 2 | | 1051 | 2 | UNION ALL | 1052 | 2 | SELECT | 1053 | 2 | COALESCE(trie_node.hash, trie_node_trieref.hash), | 1054 | 2 | trie_node_storage.value IS NULL AND trie_node_storage.trie_root_ref IS NULL, | 1055 | 2 | CASE | 1056 | 2 | WHEN trie_node_child.child_num IS NULL | 1057 | 2 | THEN next_key.node_full_key | 1058 | 2 | WHEN trie_node.partial_key IS NULL AND trie_node_trieref.partial_key IS NULL | 1059 | 2 | THEN CAST(next_key.node_full_key || trie_node_child.child_num AS BLOB) | 1060 | 2 | ELSE | 1061 | 2 | CAST(next_key.node_full_key || trie_node_child.child_num || COALESCE(trie_node.partial_key, trie_node_trieref.partial_key) AS BLOB) | 1062 | 2 | END, | 1063 | 2 | CASE | 1064 | 2 | WHEN trie_node_child.child_num IS NOT NULL AND trie_node.partial_key IS NULL | 1065 | 2 | THEN NULL -- Child exists but is missing from database | 1066 | 2 | WHEN HEX(SUBSTR(next_key.key_search_remain, 1, 1)) = '10' AND trie_node_trieref.hash IS NULL | 1067 | 2 | THEN NULL -- Trie reference exists but is missing from database | 1068 | 2 | WHEN SUBSTR(next_key.key_search_remain, 1, 1) = trie_node_child.child_num AND SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node.partial_key)) = trie_node.partial_key | 1069 | 2 | THEN SUBSTR(next_key.key_search_remain, 2 + LENGTH(trie_node.partial_key)) -- Equal match, continue iterating | 1070 | 2 | WHEN SUBSTR(next_key.key_search_remain, 1, 1) = trie_node_child.child_num AND SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node.partial_key)) < trie_node.partial_key | 1071 | 2 | THEN X'' -- Searched key is before the node we are iterating to, thus we cut the search short | 1072 | 2 | WHEN HEX(SUBSTR(next_key.key_search_remain, 1, 1)) = '10' AND COALESCE(SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node_trieref.partial_key)), X'') = trie_node_trieref.partial_key | 1073 | 2 | THEN COALESCE(SUBSTR(next_key.key_search_remain, 2 + LENGTH(trie_node_trieref.partial_key)), X'') | 1074 | 2 | ELSE | 1075 | 2 | X'' -- Shouldn't be reachable. | 1076 | 2 | END | 1077 | 2 | FROM next_key | 1078 | 2 | | 1079 | 2 | LEFT JOIN trie_node_child | 1080 | 2 | ON next_key.node_hash = trie_node_child.hash | 1081 | 2 | AND CASE WHEN LENGTH(next_key.key_search_remain) = 0 THEN TRUE | 1082 | 2 | ELSE SUBSTR(next_key.key_search_remain, 1, 1) <= trie_node_child.child_num END | 1083 | 2 | LEFT JOIN trie_node ON trie_node.hash = trie_node_child.child_hash | 1084 | 2 | | 1085 | 2 | -- We want to keep only situations where `trie_node_child` is either | 1086 | 2 | -- equal to the key, or the first child strictly superior to the key. In | 1087 | 2 | -- order to do that, we try to find another child that is strictly | 1088 | 2 | -- in-between the key and `trie_node_child`. In the `WHERE` clause at the | 1089 | 2 | -- bottom, we only keep rows where `trie_node_child_before` is NULL. | 1090 | 2 | LEFT JOIN trie_node_child AS trie_node_child_before | 1091 | 2 | ON next_key.node_hash = trie_node_child_before.hash | 1092 | 2 | AND trie_node_child_before.child_num < trie_node_child.child_num | 1093 | 2 | AND (next_key.key_search_remain = X'' OR trie_node_child_before.child_num > SUBSTR(next_key.key_search_remain, 1, 1)) | 1094 | 2 | | 1095 | 2 | LEFT JOIN trie_node_storage AS trie_node_storage_trieref | 1096 | 2 | ON HEX(SUBSTR(next_key.key_search_remain, 1, 1)) = '10' AND next_key.node_hash = trie_node_storage_trieref.node_hash AND trie_node_storage_trieref.trie_root_ref IS NOT NULL | 1097 | 2 | LEFT JOIN trie_node AS trie_node_trieref | 1098 | 2 | ON trie_node_trieref.hash = trie_node_storage_trieref.node_hash | 1099 | 2 | AND COALESCE(SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node_trieref.partial_key)), X'') <= trie_node_trieref.partial_key | 1100 | 2 | | 1101 | 2 | LEFT JOIN trie_node_storage | 1102 | 2 | ON trie_node_storage.node_hash = COALESCE(trie_node.hash, trie_node_trieref.hash) | 1103 | 2 | | 1104 | 2 | WHERE | 1105 | 2 | -- Don't pull items that have already finished searching. | 1106 | 2 | next_key.node_hash IS NOT NULL AND next_key.key_search_remain IS NOT NULL AND (next_key.key_search_remain != X'' OR (next_key.node_is_branch AND :skip_branches)) | 1107 | 2 | -- See explanation above. | 1108 | 2 | AND trie_node_child_before.hash IS NULL | 1109 | 2 | -- Don't generate an item if there's nowhere to go to. | 1110 | 2 | AND (HEX(SUBSTR(next_key.key_search_remain, 1, 1)) = '10' OR trie_node_child.child_num IS NOT NULL) | 1111 | 2 | -- Stop iterating if the child's partial key is before the searched key. | 1112 | 2 | AND (trie_node.hash IS NULL OR NOT (COALESCE(SUBSTR(next_key.key_search_remain, 1, 1), X'') = trie_node_child.child_num AND COALESCE(SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node.partial_key)), X'') > trie_node.partial_key)) | 1113 | 2 | ), | 1114 | 2 | | 1115 | 2 | -- Now keep only the entries of `next_key` which have finished iterating. | 1116 | 2 | terminal_next_key(incomplete_storage, node_full_key, output) AS ( | 1117 | 2 | SELECT | 1118 | 2 | CASE | 1119 | 2 | WHEN COALESCE(SUBSTR(node_full_key, 1, LENGTH(:prefix)), X'') != :prefix THEN FALSE | 1120 | 2 | ELSE key_search_remain IS NULL | 1121 | 2 | END, | 1122 | 2 | node_full_key, | 1123 | 2 | CASE | 1124 | 2 | WHEN node_hash IS NULL THEN NULL | 1125 | 2 | WHEN COALESCE(SUBSTR(node_full_key, 1, LENGTH(:prefix)), X'') = :prefix THEN node_full_key | 1126 | 2 | ELSE NULL | 1127 | 2 | END | 1128 | 2 | FROM next_key | 1129 | 2 | WHERE key_search_remain IS NULL OR (LENGTH(key_search_remain) = 0 AND (NOT :skip_branches OR NOT node_is_branch)) | 1130 | 2 | ) | 1131 | 2 | | 1132 | 2 | SELECT | 1133 | 2 | COUNT(blocks.hash) >= 1, | 1134 | 2 | COALESCE(terminal_next_key.incomplete_storage, FALSE), | 1135 | 2 | terminal_next_key.output | 1136 | 2 | FROM blocks | 1137 | 2 | LEFT JOIN terminal_next_key | 1138 | 2 | WHERE blocks.hash = :block_hash | 1139 | 2 | -- We pick the entry of `terminal_next_key` with the smallest full key. Note that | 1140 | 2 | -- it might seem like a good idea to not using any GROUP BY and instead just do | 1141 | 2 | -- `ORDER BY node_full_key ASC LIMIT 1`, but doing so sometimes leads to SQLite | 1142 | 2 | -- not picking the entry with the smallest full key for a reason I couldn't | 1143 | 2 | -- figure out. | 1144 | 2 | AND (terminal_next_key.node_full_key IS NULL OR terminal_next_key.node_full_key = (SELECT MIN(node_full_key) FROM terminal_next_key)) | 1145 | 2 | LIMIT 1"#, | 1146 | 2 | ) | 1147 | 2 | .map_err(|err| { | 1148 | | StorageAccessError::Corrupted(CorruptedError::Internal( | 1149 | | InternalError(err), | 1150 | | )) | 1151 | 2 | })?0 ; | 1152 | | | 1153 | | // In order to debug the SQL query above (for example in case of a failing test), | 1154 | | // uncomment this block: | 1155 | | // | 1156 | | /*println!("{:?}", { | 1157 | | let mut statement = connection | 1158 | | .prepare_cached( | 1159 | | r#" | 1160 | | WITH RECURSIVE | 1161 | | copy-paste the definition of next_key here | 1162 | | | 1163 | | SELECT * FROM next_key"#).unwrap(); | 1164 | | statement | 1165 | | .query_map( | 1166 | | rusqlite::named_params! { | 1167 | | ":block_hash": &block_hash[..], | 1168 | | ":key": key_nibbles, | 1169 | | //":prefix": prefix_nibbles, | 1170 | | ":skip_branches": !branch_nodes | 1171 | | }, | 1172 | | |row| { | 1173 | | let node_hash = row.get::<_, Option<Vec<u8>>>(0)?.map(hex::encode); | 1174 | | let node_is_branch = row.get::<_, Option<i64>>(1)?.map(|n| n != 0); | 1175 | | let node_full_key = row.get::<_, Option<Vec<u8>>>(2)?; | 1176 | | let search_remain = row.get::<_, Option<Vec<u8>>>(3)?; | 1177 | | Ok((node_hash, node_is_branch, node_full_key, search_remain)) | 1178 | | }, | 1179 | | ) | 1180 | | .unwrap() | 1181 | | .collect::<Vec<_>>() | 1182 | | });*/ | 1183 | | | 1184 | 2 | let result = statement | 1185 | 2 | .query_row( | 1186 | 2 | rusqlite::named_params! { | 1187 | 2 | ":block_hash": &block_hash[..], | 1188 | 2 | ":key": key_nibbles, | 1189 | 2 | ":prefix": prefix_nibbles, | 1190 | 2 | ":skip_branches": !branch_nodes | 1191 | 2 | }, | 1192 | 2 | |row| { | 1193 | | let block_is_known = row.get::<_, i64>(0)? != 0; | 1194 | | let incomplete_storage = row.get::<_, i64>(1)? != 0; | 1195 | | let next_key = row.get::<_, Option<Vec<u8>>>(2)?; | 1196 | | Ok((block_is_known, incomplete_storage, next_key)) | 1197 | 2 | }, | 1198 | 2 | ) | 1199 | 2 | .optional() | 1200 | 2 | .map_err(|err| { | 1201 | | StorageAccessError::Corrupted(CorruptedError::Internal(InternalError(err))) | 1202 | 2 | })?0 ; | 1203 | | | 1204 | 2 | let Some((block_is_known, incomplete_storage, mut next_key)) = result else { | 1205 | 0 | return Ok(None); | 1206 | | }; | 1207 | | | 1208 | 2 | if !block_is_known { | 1209 | 0 | return Err(StorageAccessError::UnknownBlock); | 1210 | 2 | } | 1211 | 2 | | 1212 | 2 | if incomplete_storage { | 1213 | 1 | return Err(StorageAccessError::IncompleteStorage); | 1214 | 1 | } | 1215 | 1 | | 1216 | 1 | if parent_tries_paths_nibbles_length != 0 { | 1217 | 0 | next_key = next_key.map(|nk| nk[parent_tries_paths_nibbles_length..].to_vec()); | 1218 | 1 | } | 1219 | | | 1220 | 1 | Ok(next_key) | 1221 | 2 | } |
Unexecuted instantiation: _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1D_B1C_EINtNtNtB1J_8adapters3map3MapINtCs1qmLyiTSqYF_6either6EitherINtNtNtB7_4trie6nibble14BytesToNibblesINtNtB2K_6copied6CopiedINtNtNtB1L_5slice4iter4IterhEEEIB38_IB3D_INtNvNtB7_4util11as_ref_iter4IterIB38_RShINtNvMs5_NtNtNtB7_8executor2vm11interpreterNtB64_11Interpreter11read_memory12AccessOffsetB5S_EEhEEINtNtB2K_7flatten7FlatMapINtNtB2K_5chain5ChainIB2G_IB7w_IB4C_NtNtB68_20trie_root_calculator14InProgressNodeEIB7V_INtNtB1H_4once4OnceIB38_ANtB3F_6Nibblej1_RINtNtCsdZExvAaxgia_5alloc3vec3VecB9J_EEEINtNtB1L_6option8IntoIterB9D_EENCNvMs3_B8w_NtB8w_5Inner21current_node_full_key0ENcNtIB38_B9D_B9Z_E4Left0EIB9l_Bc0_EEIB5h_Bcq_B9J_EIB5j_B9J_Bcq_EEEENvYhINtNtB1L_7convert4FromB9J_E4fromEIB2G_IB38_IB3D_IB5h_IB38_B5N_B5S_EhEEIB38_B37_IB1D_B9J_EEEBd1_EEB7_ Unexecuted instantiation: _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2B_6option8IntoIterINtB1H_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB44_14SyncBackground12author_block0s5_00EINtNtB2x_6copied6CopiedINtNtNtB2B_5slice4iter4IterhEEB5D_ECsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2B_6option8IntoIterINtB1H_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s6_00EINtNtB2x_6copied6CopiedINtNtNtB2B_5slice4iter4IterhEEB5e_ECsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2B_6option8IntoIterINtB1H_3VechEENCNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sd_00EINtNtB2x_6copied6CopiedINtNtNtB2B_5slice4iter4IterhEEB5H_EB45_ _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase22block_storage_next_keyINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1D_B1C_EINtNtNtB1J_8adapters6copied6CopiedINtNtNtB1L_5slice4iter4IterhEEB2F_ECsiUjFBJteJ7x_17smoldot_full_node Line | Count | Source | 953 | 51 | pub fn block_storage_next_key( | 954 | 51 | &self, | 955 | 51 | block_hash: &[u8; 32], | 956 | 51 | parent_tries_paths_nibbles: impl Iterator<Item = impl Iterator<Item = u8>>, | 957 | 51 | key_nibbles: impl Iterator<Item = u8>, | 958 | 51 | prefix_nibbles: impl Iterator<Item = u8>, | 959 | 51 | branch_nodes: bool, | 960 | 51 | ) -> Result<Option<Vec<u8>>, StorageAccessError> { | 961 | 51 | // Process the iterators at the very beginning and before locking the database, in order | 962 | 51 | // to avoid a deadlock in case the `next()` function of one of the iterators accesses | 963 | 51 | // the database as well. | 964 | 51 | let parent_tries_paths_nibbles = parent_tries_paths_nibbles | 965 | 51 | .flat_map(|t| t.inspect(|n| assert!(*n < 16)).chain(iter::once(0x10))) | 966 | 51 | .collect::<Vec<_>>(); | 967 | 51 | let parent_tries_paths_nibbles_length = parent_tries_paths_nibbles.len(); | 968 | 51 | let key_nibbles = { | 969 | 51 | let mut v = parent_tries_paths_nibbles.clone(); | 970 | 51 | v.extend(key_nibbles.inspect(|n| assert!(*n < 16))); | 971 | 51 | v | 972 | 51 | }; | 973 | 51 | let prefix_nibbles = { | 974 | 51 | let mut v = parent_tries_paths_nibbles; | 975 | 51 | v.extend(prefix_nibbles.inspect(|n| assert!(*n < 16))); | 976 | 51 | v | 977 | 51 | }; | 978 | 51 | | 979 | 51 | let connection = self.database.lock(); | 980 | | | 981 | | // Sorry for that extremely complicated SQL statement. While the logic isn't actually very | 982 | | // complicated, we have to jump through many hoops in order to go around quirks in the | 983 | | // SQL language. | 984 | | // If you want to work on this SQL code, there is no miracle: write tests, and if a test | 985 | | // fails debug the content of `next_key` to find out where the iteration doesn't behave | 986 | | // as expected. | 987 | | // TODO: this algorithm relies the fact that leaf nodes always have a storage value, which isn't exactly clear in the schema ; however not relying on this makes it way harder to write | 988 | | // TODO: trie_root_ref system untested and most likely not working | 989 | | // TODO: infinite loop if there's a loop in the trie; detect this | 990 | | // TODO: could also check the prefix while iterating instead of only at the very end, which could maybe save many lookups | 991 | 51 | let mut statement = connection | 992 | 51 | .prepare_cached( | 993 | 51 | r#" | 994 | 51 | WITH RECURSIVE | 995 | 51 | -- We build a temporary table `next_key`, inserting entries one after one as we | 996 | 51 | -- descend the trie by trying to match entries with `:key`. | 997 | 51 | -- At each iteration, `node_hash` is the root where to continue the search, | 998 | 51 | -- `node_is_branch` is true if `node_hash` is a branch node, `node_full_key` is | 999 | 51 | -- the key of `node_hash` (that we build along the way) and serves as the final | 1000 | 51 | -- result, and `key_search_remain` contains the `:key` that remains to be matched. | 1001 | 51 | -- Can also be NULL to indicate that the search ended because the node necessary to | 1002 | 51 | -- continue was missing from the database, in which case the values of | 1003 | 51 | -- `node_hash` and `node_is_branch` have irrelevant values, and the value of | 1004 | 51 | -- `node_full_key` is the "best known key". | 1005 | 51 | -- If `:skip_branches` is false, the search ends when `key_search_remain` is null | 1006 | 51 | -- or empty. If `:skip_branches` is true, the search ends when `key_search_remain` | 1007 | 51 | -- is null or empty and that `node_is_branch` is false. | 1008 | 51 | -- | 1009 | 51 | -- `next_key` has zero elements if the block can't be found in the database or if | 1010 | 51 | -- the trie has no next key at all. These two situations need to be differentiated | 1011 | 51 | -- in the final SELECT statement. | 1012 | 51 | -- | 1013 | 51 | -- When encountering a node, we follow both the child that exactly matches `:key` | 1014 | 51 | -- and also the first child that is strictly superior to `:key`. This is necessary | 1015 | 51 | -- because `:key` might be equal to something like `ffffffff...`, in which case the | 1016 | 51 | -- result will be after any equal match. | 1017 | 51 | -- This means that the number of entries in `next_key` at the end of the recursion | 1018 | 51 | -- is something like `2 * depth_in_trie(key)`. | 1019 | 51 | -- In order to obtain the final result, we take the entry in `next_key` with the | 1020 | 51 | -- minimal `node_full_key` amongst the ones that have finished the search. | 1021 | 51 | -- | 1022 | 51 | -- Note that in the code below we do a lot of `COALESCE(SUBSTR(...), X'')`. This | 1023 | 51 | -- is because, for some reason, `SUBSTR(X'', ...)` always produces `NULL`. For this | 1024 | 51 | -- reason, it is also not possible to automatically pass NULL values | 1025 | 51 | -- through `SUSBTR`, and we have to use CASE/IIFs instead. | 1026 | 51 | next_key(node_hash, node_is_branch, node_full_key, key_search_remain) AS ( | 1027 | 51 | SELECT | 1028 | 51 | CASE | 1029 | 51 | WHEN trie_node.hash IS NULL | 1030 | 51 | THEN NULL | 1031 | 51 | WHEN COALESCE(SUBSTR(:key, 1, LENGTH(trie_node.partial_key)), X'') <= trie_node.partial_key | 1032 | 51 | THEN trie_node.hash | 1033 | 51 | ELSE | 1034 | 51 | NULL | 1035 | 51 | END, | 1036 | 51 | trie_node_storage.value IS NULL AND trie_node_storage.trie_root_ref IS NULL, | 1037 | 51 | COALESCE(trie_node.partial_key, X''), | 1038 | 51 | CASE | 1039 | 51 | WHEN trie_node.partial_key IS NULL | 1040 | 51 | THEN NULL | 1041 | 51 | WHEN COALESCE(SUBSTR(:key, 1, LENGTH(trie_node.partial_key)), X'') <= trie_node.partial_key | 1042 | 51 | THEN COALESCE(SUBSTR(:key, 1 + LENGTH(trie_node.partial_key)), X'') | 1043 | 51 | ELSE | 1044 | 51 | X'' -- The partial key is strictly inferior to `:key` | 1045 | 51 | END | 1046 | 51 | FROM blocks | 1047 | 51 | LEFT JOIN trie_node ON trie_node.hash = blocks.state_trie_root_hash | 1048 | 51 | LEFT JOIN trie_node_storage ON trie_node_storage.node_hash = trie_node.hash | 1049 | 51 | WHERE blocks.hash = :block_hash | 1050 | 51 | | 1051 | 51 | UNION ALL | 1052 | 51 | SELECT | 1053 | 51 | COALESCE(trie_node.hash, trie_node_trieref.hash), | 1054 | 51 | trie_node_storage.value IS NULL AND trie_node_storage.trie_root_ref IS NULL, | 1055 | 51 | CASE | 1056 | 51 | WHEN trie_node_child.child_num IS NULL | 1057 | 51 | THEN next_key.node_full_key | 1058 | 51 | WHEN trie_node.partial_key IS NULL AND trie_node_trieref.partial_key IS NULL | 1059 | 51 | THEN CAST(next_key.node_full_key || trie_node_child.child_num AS BLOB) | 1060 | 51 | ELSE | 1061 | 51 | CAST(next_key.node_full_key || trie_node_child.child_num || COALESCE(trie_node.partial_key, trie_node_trieref.partial_key) AS BLOB) | 1062 | 51 | END, | 1063 | 51 | CASE | 1064 | 51 | WHEN trie_node_child.child_num IS NOT NULL AND trie_node.partial_key IS NULL | 1065 | 51 | THEN NULL -- Child exists but is missing from database | 1066 | 51 | WHEN HEX(SUBSTR(next_key.key_search_remain, 1, 1)) = '10' AND trie_node_trieref.hash IS NULL | 1067 | 51 | THEN NULL -- Trie reference exists but is missing from database | 1068 | 51 | WHEN SUBSTR(next_key.key_search_remain, 1, 1) = trie_node_child.child_num AND SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node.partial_key)) = trie_node.partial_key | 1069 | 51 | THEN SUBSTR(next_key.key_search_remain, 2 + LENGTH(trie_node.partial_key)) -- Equal match, continue iterating | 1070 | 51 | WHEN SUBSTR(next_key.key_search_remain, 1, 1) = trie_node_child.child_num AND SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node.partial_key)) < trie_node.partial_key | 1071 | 51 | THEN X'' -- Searched key is before the node we are iterating to, thus we cut the search short | 1072 | 51 | WHEN HEX(SUBSTR(next_key.key_search_remain, 1, 1)) = '10' AND COALESCE(SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node_trieref.partial_key)), X'') = trie_node_trieref.partial_key | 1073 | 51 | THEN COALESCE(SUBSTR(next_key.key_search_remain, 2 + LENGTH(trie_node_trieref.partial_key)), X'') | 1074 | 51 | ELSE | 1075 | 51 | X'' -- Shouldn't be reachable. | 1076 | 51 | END | 1077 | 51 | FROM next_key | 1078 | 51 | | 1079 | 51 | LEFT JOIN trie_node_child | 1080 | 51 | ON next_key.node_hash = trie_node_child.hash | 1081 | 51 | AND CASE WHEN LENGTH(next_key.key_search_remain) = 0 THEN TRUE | 1082 | 51 | ELSE SUBSTR(next_key.key_search_remain, 1, 1) <= trie_node_child.child_num END | 1083 | 51 | LEFT JOIN trie_node ON trie_node.hash = trie_node_child.child_hash | 1084 | 51 | | 1085 | 51 | -- We want to keep only situations where `trie_node_child` is either | 1086 | 51 | -- equal to the key, or the first child strictly superior to the key. In | 1087 | 51 | -- order to do that, we try to find another child that is strictly | 1088 | 51 | -- in-between the key and `trie_node_child`. In the `WHERE` clause at the | 1089 | 51 | -- bottom, we only keep rows where `trie_node_child_before` is NULL. | 1090 | 51 | LEFT JOIN trie_node_child AS trie_node_child_before | 1091 | 51 | ON next_key.node_hash = trie_node_child_before.hash | 1092 | 51 | AND trie_node_child_before.child_num < trie_node_child.child_num | 1093 | 51 | AND (next_key.key_search_remain = X'' OR trie_node_child_before.child_num > SUBSTR(next_key.key_search_remain, 1, 1)) | 1094 | 51 | | 1095 | 51 | LEFT JOIN trie_node_storage AS trie_node_storage_trieref | 1096 | 51 | ON HEX(SUBSTR(next_key.key_search_remain, 1, 1)) = '10' AND next_key.node_hash = trie_node_storage_trieref.node_hash AND trie_node_storage_trieref.trie_root_ref IS NOT NULL | 1097 | 51 | LEFT JOIN trie_node AS trie_node_trieref | 1098 | 51 | ON trie_node_trieref.hash = trie_node_storage_trieref.node_hash | 1099 | 51 | AND COALESCE(SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node_trieref.partial_key)), X'') <= trie_node_trieref.partial_key | 1100 | 51 | | 1101 | 51 | LEFT JOIN trie_node_storage | 1102 | 51 | ON trie_node_storage.node_hash = COALESCE(trie_node.hash, trie_node_trieref.hash) | 1103 | 51 | | 1104 | 51 | WHERE | 1105 | 51 | -- Don't pull items that have already finished searching. | 1106 | 51 | next_key.node_hash IS NOT NULL AND next_key.key_search_remain IS NOT NULL AND (next_key.key_search_remain != X'' OR (next_key.node_is_branch AND :skip_branches)) | 1107 | 51 | -- See explanation above. | 1108 | 51 | AND trie_node_child_before.hash IS NULL | 1109 | 51 | -- Don't generate an item if there's nowhere to go to. | 1110 | 51 | AND (HEX(SUBSTR(next_key.key_search_remain, 1, 1)) = '10' OR trie_node_child.child_num IS NOT NULL) | 1111 | 51 | -- Stop iterating if the child's partial key is before the searched key. | 1112 | 51 | AND (trie_node.hash IS NULL OR NOT (COALESCE(SUBSTR(next_key.key_search_remain, 1, 1), X'') = trie_node_child.child_num AND COALESCE(SUBSTR(next_key.key_search_remain, 2, LENGTH(trie_node.partial_key)), X'') > trie_node.partial_key)) | 1113 | 51 | ), | 1114 | 51 | | 1115 | 51 | -- Now keep only the entries of `next_key` which have finished iterating. | 1116 | 51 | terminal_next_key(incomplete_storage, node_full_key, output) AS ( | 1117 | 51 | SELECT | 1118 | 51 | CASE | 1119 | 51 | WHEN COALESCE(SUBSTR(node_full_key, 1, LENGTH(:prefix)), X'') != :prefix THEN FALSE | 1120 | 51 | ELSE key_search_remain IS NULL | 1121 | 51 | END, | 1122 | 51 | node_full_key, | 1123 | 51 | CASE | 1124 | 51 | WHEN node_hash IS NULL THEN NULL | 1125 | 51 | WHEN COALESCE(SUBSTR(node_full_key, 1, LENGTH(:prefix)), X'') = :prefix THEN node_full_key | 1126 | 51 | ELSE NULL | 1127 | 51 | END | 1128 | 51 | FROM next_key | 1129 | 51 | WHERE key_search_remain IS NULL OR (LENGTH(key_search_remain) = 0 AND (NOT :skip_branches OR NOT node_is_branch)) | 1130 | 51 | ) | 1131 | 51 | | 1132 | 51 | SELECT | 1133 | 51 | COUNT(blocks.hash) >= 1, | 1134 | 51 | COALESCE(terminal_next_key.incomplete_storage, FALSE), | 1135 | 51 | terminal_next_key.output | 1136 | 51 | FROM blocks | 1137 | 51 | LEFT JOIN terminal_next_key | 1138 | 51 | WHERE blocks.hash = :block_hash | 1139 | 51 | -- We pick the entry of `terminal_next_key` with the smallest full key. Note that | 1140 | 51 | -- it might seem like a good idea to not using any GROUP BY and instead just do | 1141 | 51 | -- `ORDER BY node_full_key ASC LIMIT 1`, but doing so sometimes leads to SQLite | 1142 | 51 | -- not picking the entry with the smallest full key for a reason I couldn't | 1143 | 51 | -- figure out. | 1144 | 51 | AND (terminal_next_key.node_full_key IS NULL OR terminal_next_key.node_full_key = (SELECT MIN(node_full_key) FROM terminal_next_key)) | 1145 | 51 | LIMIT 1"#, | 1146 | 51 | ) | 1147 | 51 | .map_err(|err| { | 1148 | | StorageAccessError::Corrupted(CorruptedError::Internal( | 1149 | | InternalError(err), | 1150 | | )) | 1151 | 51 | })?0 ; | 1152 | | | 1153 | | // In order to debug the SQL query above (for example in case of a failing test), | 1154 | | // uncomment this block: | 1155 | | // | 1156 | | /*println!("{:?}", { | 1157 | | let mut statement = connection | 1158 | | .prepare_cached( | 1159 | | r#" | 1160 | | WITH RECURSIVE | 1161 | | copy-paste the definition of next_key here | 1162 | | | 1163 | | SELECT * FROM next_key"#).unwrap(); | 1164 | | statement | 1165 | | .query_map( | 1166 | | rusqlite::named_params! { | 1167 | | ":block_hash": &block_hash[..], | 1168 | | ":key": key_nibbles, | 1169 | | //":prefix": prefix_nibbles, | 1170 | | ":skip_branches": !branch_nodes | 1171 | | }, | 1172 | | |row| { | 1173 | | let node_hash = row.get::<_, Option<Vec<u8>>>(0)?.map(hex::encode); | 1174 | | let node_is_branch = row.get::<_, Option<i64>>(1)?.map(|n| n != 0); | 1175 | | let node_full_key = row.get::<_, Option<Vec<u8>>>(2)?; | 1176 | | let search_remain = row.get::<_, Option<Vec<u8>>>(3)?; | 1177 | | Ok((node_hash, node_is_branch, node_full_key, search_remain)) | 1178 | | }, | 1179 | | ) | 1180 | | .unwrap() | 1181 | | .collect::<Vec<_>>() | 1182 | | });*/ | 1183 | | | 1184 | 51 | let result = statement | 1185 | 51 | .query_row( | 1186 | 51 | rusqlite::named_params! { | 1187 | 51 | ":block_hash": &block_hash[..], | 1188 | 51 | ":key": key_nibbles, | 1189 | 51 | ":prefix": prefix_nibbles, | 1190 | 51 | ":skip_branches": !branch_nodes | 1191 | 51 | }, | 1192 | 51 | |row| { | 1193 | | let block_is_known = row.get::<_, i64>(0)? != 0; | 1194 | | let incomplete_storage = row.get::<_, i64>(1)? != 0; | 1195 | | let next_key = row.get::<_, Option<Vec<u8>>>(2)?; | 1196 | | Ok((block_is_known, incomplete_storage, next_key)) | 1197 | 51 | }, | 1198 | 51 | ) | 1199 | 51 | .optional() | 1200 | 51 | .map_err(|err| { | 1201 | | StorageAccessError::Corrupted(CorruptedError::Internal(InternalError(err))) | 1202 | 51 | })?0 ; | 1203 | | | 1204 | 51 | let Some((block_is_known, incomplete_storage, mut next_key)) = result else { | 1205 | 0 | return Ok(None); | 1206 | | }; | 1207 | | | 1208 | 51 | if !block_is_known { | 1209 | 1 | return Err(StorageAccessError::UnknownBlock); | 1210 | 50 | } | 1211 | 50 | | 1212 | 50 | if incomplete_storage { | 1213 | 0 | return Err(StorageAccessError::IncompleteStorage); | 1214 | 50 | } | 1215 | 50 | | 1216 | 50 | if parent_tries_paths_nibbles_length != 0 { | 1217 | 0 | next_key = next_key.map(|nk| nk[parent_tries_paths_nibbles_length..].to_vec()); | 1218 | 50 | } | 1219 | | | 1220 | 50 | Ok(next_key) | 1221 | 51 | } |
Unexecuted instantiation: _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2B_6option8IntoIterINtB1H_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB44_14SyncBackground12author_block0s5_00EINtNtB2x_6copied6CopiedINtNtNtB2B_5slice4iter4IterhEEB5D_ECscDgN54JpMGG_6author Unexecuted instantiation: _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2B_6option8IntoIterINtB1H_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s6_00EINtNtB2x_6copied6CopiedINtNtNtB2B_5slice4iter4IterhEEB5e_ECscDgN54JpMGG_6author Unexecuted instantiation: _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2B_6option8IntoIterINtB1H_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB44_14SyncBackground12author_block0s5_00EINtNtB2x_6copied6CopiedINtNtNtB2B_5slice4iter4IterhEEB5D_ECsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase22block_storage_next_keyINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2B_6option8IntoIterINtB1H_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s6_00EINtNtB2x_6copied6CopiedINtNtNtB2B_5slice4iter4IterhEEB5e_ECsibGXYHQB8Ea_25json_rpc_general_requests |
1222 | | |
1223 | | /// Returns the Merkle value of the trie node in the storage that is the closest descendant |
1224 | | /// of the provided key. |
1225 | | /// |
1226 | | /// `key_nibbles` must be an iterator to the **nibbles** of the key. |
1227 | | /// |
1228 | | /// `parent_tries_paths_nibbles` is a list of keys to follow in order to find the root of the |
1229 | | /// trie into which `key_nibbles` should be searched. |
1230 | | /// |
1231 | | /// Returns `None` if `parent_tries_paths_nibbles` didn't lead to any trie, or if there is no |
1232 | | /// such descendant. |
1233 | | /// |
1234 | | /// # Panics |
1235 | | /// |
1236 | | /// Panics if any of the values yielded by `parent_tries_paths_nibbles` or `key_nibbles` is |
1237 | | /// superior or equal to 16. |
1238 | | /// |
1239 | 1.04M | pub fn block_storage_closest_descendant_merkle_value( |
1240 | 1.04M | &self, |
1241 | 1.04M | block_hash: &[u8; 32], |
1242 | 1.04M | parent_tries_paths_nibbles: impl Iterator<Item = impl Iterator<Item = u8>>, |
1243 | 1.04M | key_nibbles: impl Iterator<Item = u8>, |
1244 | 1.04M | ) -> Result<Option<Vec<u8>>, StorageAccessError> { |
1245 | 1.04M | // Process the iterators at the very beginning and before locking the database, in order |
1246 | 1.04M | // to avoid a deadlock in case the `next()` function of one of the iterators accesses |
1247 | 1.04M | // the database as well. |
1248 | 1.04M | let key_vectored = parent_tries_paths_nibbles |
1249 | 1.04M | .flat_map(|t| t.inspect(0 |n| assert!(*n < 160 )0 ).chain(iter::once(0x10))0 ) Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB21_B20_EINtNtNtB27_8adapters3map3MapINtNtB38_6copied6CopiedINtNtNtB29_5slice4iter4IterNtNtNtB9_4trie6nibble6NibbleEENvYhINtNtB29_7convert4FromB4j_E4fromEE0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB21_B20_EINtNtNtB29_5array4iter8IntoIterhKj0_EE0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB21_B20_EINtNtNtB29_5array4iter8IntoIterhKj1_EE0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB21_B20_EINtNtNtB29_5array4iter8IntoIterhKj2_EE0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB21_B20_EINtNtNtB29_5array4iter8IntoIterhKj3_EE0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB21_B20_EINtNtNtB29_5array4iter8IntoIterhKj4_EE0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB21_B20_EINtNtNtB29_5array4iter8IntoIterhKj5_EE0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB21_B20_EINtNtNtB29_5array4iter8IntoIterhKj8_EE0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB22_B21_EINtNtNtB28_8adapters3map3MapINtNtB39_7flatten7FlatMapIB3x_INtNtNtB2a_5slice4iter4IterNtNtNtB9_8executor20trie_root_calculator14InProgressNodeEINtNtB39_5chain5ChainINtNtB26_4once4OnceINtCs1qmLyiTSqYF_6either6EitherANtNtNtB9_4trie6nibble6Nibblej1_RINtNtCsdZExvAaxgia_5alloc3vec3VecB6w_EEEINtNtB2a_6option8IntoIterB60_EENCNvMs3_B4t_NtB4t_5Inner21current_node_full_key0EINtNvNtB9_4util11as_ref_iter4IterB60_B6w_EIB91_B6w_B60_EENvYhINtNtB2a_7convert4FromB6w_E4fromEE0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB30_6option8IntoIterINtB26_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB4t_14SyncBackground12author_block0s2_00EINtNtB2W_6copied6CopiedINtNtNtB30_5slice4iter4IterhEEE0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB30_6option8IntoIterINtB26_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s3_00EINtNtB2W_6copied6CopiedINtNtNtB30_5slice4iter4IterhEEE0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB30_6option8IntoIterINtB26_3VechEENCNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sa_00EINtNtB2W_6copied6CopiedINtNtNtB30_5slice4iter4IterhEEE0B4u_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB30_6option8IntoIterINtB26_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB4t_14SyncBackground12author_block0s2_00EINtNtB2W_6copied6CopiedINtNtNtB30_5slice4iter4IterhEEE0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB30_6option8IntoIterINtB26_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s3_00EINtNtB2W_6copied6CopiedINtNtNtB30_5slice4iter4IterhEEE0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB30_6option8IntoIterINtB26_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB4t_14SyncBackground12author_block0s2_00EINtNtB2W_6copied6CopiedINtNtNtB30_5slice4iter4IterhEEE0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB30_6option8IntoIterINtB26_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s3_00EINtNtB2W_6copied6CopiedINtNtNtB30_5slice4iter4IterhEEE0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB23_B22_EINtNtNtB29_8adapters3map3MapINtNtB3a_6copied6CopiedINtNtNtB2b_5slice4iter4IterNtNtNtBb_4trie6nibble6NibbleEENvYhINtNtB2b_7convert4FromB4l_E4fromEE00Bb_ Unexecuted instantiation: _RNCNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB23_B22_EINtNtNtB2b_5array4iter8IntoIterhKj0_EE00Bb_ Unexecuted instantiation: _RNCNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB23_B22_EINtNtNtB2b_5array4iter8IntoIterhKj1_EE00Bb_ Unexecuted instantiation: _RNCNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB23_B22_EINtNtNtB2b_5array4iter8IntoIterhKj2_EE00Bb_ Unexecuted instantiation: _RNCNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB23_B22_EINtNtNtB2b_5array4iter8IntoIterhKj3_EE00Bb_ Unexecuted instantiation: _RNCNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB23_B22_EINtNtNtB2b_5array4iter8IntoIterhKj4_EE00Bb_ Unexecuted instantiation: _RNCNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB23_B22_EINtNtNtB2b_5array4iter8IntoIterhKj5_EE00Bb_ Unexecuted instantiation: _RNCNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB23_B22_EINtNtNtB2b_5array4iter8IntoIterhKj8_EE00Bb_ Unexecuted instantiation: _RNCNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB24_B23_EINtNtNtB2a_8adapters3map3MapINtNtB3b_7flatten7FlatMapIB3z_INtNtNtB2c_5slice4iter4IterNtNtNtBb_8executor20trie_root_calculator14InProgressNodeEINtNtB3b_5chain5ChainINtNtB28_4once4OnceINtCs1qmLyiTSqYF_6either6EitherANtNtNtBb_4trie6nibble6Nibblej1_RINtNtCsdZExvAaxgia_5alloc3vec3VecB6y_EEEINtNtB2c_6option8IntoIterB62_EENCNvMs3_B4v_NtB4v_5Inner21current_node_full_key0EINtNvNtBb_4util11as_ref_iter4IterB62_B6y_EIB93_B6y_B62_EENvYhINtNtB2c_7convert4FromB6y_E4fromEE00Bb_ Unexecuted instantiation: _RNCNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB32_6option8IntoIterINtB28_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB4v_14SyncBackground12author_block0s2_00EINtNtB2Y_6copied6CopiedINtNtNtB32_5slice4iter4IterhEEE00CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB32_6option8IntoIterINtB28_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s3_00EINtNtB2Y_6copied6CopiedINtNtNtB32_5slice4iter4IterhEEE00CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB32_6option8IntoIterINtB28_3VechEENCNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sa_00EINtNtB2Y_6copied6CopiedINtNtNtB32_5slice4iter4IterhEEE00B4w_ Unexecuted instantiation: _RNCNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB32_6option8IntoIterINtB28_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB4v_14SyncBackground12author_block0s2_00EINtNtB2Y_6copied6CopiedINtNtNtB32_5slice4iter4IterhEEE00CscDgN54JpMGG_6author Unexecuted instantiation: _RNCNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB32_6option8IntoIterINtB28_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s3_00EINtNtB2Y_6copied6CopiedINtNtNtB32_5slice4iter4IterhEEE00CscDgN54JpMGG_6author Unexecuted instantiation: _RNCNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB32_6option8IntoIterINtB28_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB4v_14SyncBackground12author_block0s2_00EINtNtB2Y_6copied6CopiedINtNtNtB32_5slice4iter4IterhEEE00CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB7_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB32_6option8IntoIterINtB28_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s3_00EINtNtB2Y_6copied6CopiedINtNtNtB32_5slice4iter4IterhEEE00CsibGXYHQB8Ea_25json_rpc_general_requests |
1250 | 4.19M | .chain(key_nibbles.inspect(|n| assert!(*n < 16))) _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB21_B20_EINtNtNtB27_8adapters3map3MapINtNtB38_6copied6CopiedINtNtNtB29_5slice4iter4IterNtNtNtB9_4trie6nibble6NibbleEENvYhINtNtB29_7convert4FromB4j_E4fromEEs_0B9_ Line | Count | Source | 1250 | 4.19M | .chain(key_nibbles.inspect(|n| assert!(*n < 16))) |
Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB21_B20_EINtNtNtB29_5array4iter8IntoIterhKj0_EEs_0B9_ _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB21_B20_EINtNtNtB29_5array4iter8IntoIterhKj1_EEs_0B9_ Line | Count | Source | 1250 | 3 | .chain(key_nibbles.inspect(|n| assert!(*n < 16))) |
_RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB21_B20_EINtNtNtB29_5array4iter8IntoIterhKj2_EEs_0B9_ Line | Count | Source | 1250 | 6 | .chain(key_nibbles.inspect(|n| assert!(*n < 16))) |
_RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB21_B20_EINtNtNtB29_5array4iter8IntoIterhKj3_EEs_0B9_ Line | Count | Source | 1250 | 12 | .chain(key_nibbles.inspect(|n| assert!(*n < 16))) |
_RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB21_B20_EINtNtNtB29_5array4iter8IntoIterhKj4_EEs_0B9_ Line | Count | Source | 1250 | 12 | .chain(key_nibbles.inspect(|n| assert!(*n < 16))) |
_RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB21_B20_EINtNtNtB29_5array4iter8IntoIterhKj5_EEs_0B9_ Line | Count | Source | 1250 | 5 | .chain(key_nibbles.inspect(|n| assert!(*n < 16))) |
_RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB21_B20_EINtNtNtB29_5array4iter8IntoIterhKj8_EEs_0B9_ Line | Count | Source | 1250 | 8 | .chain(key_nibbles.inspect(|n| assert!(*n < 16))) |
Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB22_B21_EINtNtNtB28_8adapters3map3MapINtNtB39_7flatten7FlatMapIB3x_INtNtNtB2a_5slice4iter4IterNtNtNtB9_8executor20trie_root_calculator14InProgressNodeEINtNtB39_5chain5ChainINtNtB26_4once4OnceINtCs1qmLyiTSqYF_6either6EitherANtNtNtB9_4trie6nibble6Nibblej1_RINtNtCsdZExvAaxgia_5alloc3vec3VecB6w_EEEINtNtB2a_6option8IntoIterB60_EENCNvMs3_B4t_NtB4t_5Inner21current_node_full_key0EINtNvNtB9_4util11as_ref_iter4IterB60_B6w_EIB91_B6w_B60_EENvYhINtNtB2a_7convert4FromB6w_E4fromEEs_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB30_6option8IntoIterINtB26_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB4t_14SyncBackground12author_block0s2_00EINtNtB2W_6copied6CopiedINtNtNtB30_5slice4iter4IterhEEEs_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB30_6option8IntoIterINtB26_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s3_00EINtNtB2W_6copied6CopiedINtNtNtB30_5slice4iter4IterhEEEs_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB30_6option8IntoIterINtB26_3VechEENCNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sa_00EINtNtB2W_6copied6CopiedINtNtNtB30_5slice4iter4IterhEEEs_0B4u_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB30_6option8IntoIterINtB26_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB4t_14SyncBackground12author_block0s2_00EINtNtB2W_6copied6CopiedINtNtNtB30_5slice4iter4IterhEEEs_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB30_6option8IntoIterINtB26_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s3_00EINtNtB2W_6copied6CopiedINtNtNtB30_5slice4iter4IterhEEEs_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB30_6option8IntoIterINtB26_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB4t_14SyncBackground12author_block0s2_00EINtNtB2W_6copied6CopiedINtNtNtB30_5slice4iter4IterhEEEs_0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB30_6option8IntoIterINtB26_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s3_00EINtNtB2W_6copied6CopiedINtNtNtB30_5slice4iter4IterhEEEs_0CsibGXYHQB8Ea_25json_rpc_general_requests |
1251 | 1.04M | .collect::<Vec<_>>(); |
1252 | 1.04M | |
1253 | 1.04M | let connection = self.database.lock(); |
1254 | | |
1255 | | // TODO: trie_root_ref system untested |
1256 | | // TODO: infinite loop if there's a loop in the trie; detect this |
1257 | 1.04M | let mut statement = connection |
1258 | 1.04M | .prepare_cached( |
1259 | 1.04M | r#" |
1260 | 1.04M | WITH RECURSIVE |
1261 | 1.04M | -- At the end of the recursive statement, `closest_descendant` must always contain |
1262 | 1.04M | -- at most one item where `search_remain` is either empty or null. Empty |
1263 | 1.04M | -- indicates that we have found a match, while null means that the search has |
1264 | 1.04M | -- been interrupted due to a storage entry not being in the database. If |
1265 | 1.04M | -- `search_remain` is null, then `node_hash` is irrelevant. |
1266 | 1.04M | -- If `closest_descendant` doesn't have any entry where `search_remain` is empty |
1267 | 1.04M | -- or null, then the request key doesn't have any descendant. |
1268 | 1.04M | closest_descendant(node_hash, search_remain) AS ( |
1269 | 1.04M | SELECT |
1270 | 1.04M | blocks.state_trie_root_hash, |
1271 | 1.04M | CASE |
1272 | 1.04M | WHEN trie_node.partial_key IS NULL AND LENGTH(:key) = 0 |
1273 | 1.04M | THEN X'' -- Trie root node isn't in database, but since key is empty we have a match anyway |
1274 | 1.04M | WHEN trie_node.partial_key IS NULL AND LENGTH(:key) != 0 |
1275 | 1.04M | THEN NULL -- Trie root node isn't in database and we can't iterate further |
1276 | 1.04M | ELSE |
1277 | 1.04M | COALESCE(SUBSTR(:key, 1 + LENGTH(trie_node.partial_key)), X'') |
1278 | 1.04M | END |
1279 | 1.04M | FROM blocks |
1280 | 1.04M | LEFT JOIN trie_node ON blocks.state_trie_root_hash = trie_node.hash |
1281 | 1.04M | WHERE blocks.hash = :block_hash |
1282 | 1.04M | AND ( |
1283 | 1.04M | trie_node.partial_key IS NULL |
1284 | 1.04M | OR COALESCE(SUBSTR(trie_node.partial_key, 1, LENGTH(:key)), X'') = :key |
1285 | 1.04M | OR COALESCE(SUBSTR(:key, 1, LENGTH(trie_node.partial_key)), X'') = trie_node.partial_key |
1286 | 1.04M | ) |
1287 | 1.04M | |
1288 | 1.04M | UNION ALL |
1289 | 1.04M | SELECT |
1290 | 1.04M | COALESCE(trie_node_child.child_hash, trie_node_storage.trie_root_ref), |
1291 | 1.04M | CASE |
1292 | 1.04M | WHEN trie_node_child.child_hash IS NULL AND HEX(SUBSTR(closest_descendant.search_remain, 1, 1)) != '10' |
1293 | 1.04M | THEN X'' -- No child matching the key. |
1294 | 1.04M | WHEN trie_node_child.child_hash IS NOT NULL AND trie_node.hash IS NULL AND LENGTH(closest_descendant.search_remain) = 1 |
1295 | 1.04M | THEN X'' -- Descendant node not in trie but we know that it's the result. |
1296 | 1.04M | WHEN trie_node_child.child_hash IS NOT NULL AND trie_node.hash IS NULL |
1297 | 1.04M | THEN NULL -- Descendant node not in trie. |
1298 | 1.04M | WHEN COALESCE(SUBSTR(trie_node.partial_key, 1, LENGTH(closest_descendant.search_remain) - 1), X'') = COALESCE(SUBSTR(closest_descendant.search_remain, 2), X'') |
1299 | 1.04M | OR COALESCE(SUBSTR(closest_descendant.search_remain, 2, LENGTH(trie_node.partial_key)), X'') = trie_node.partial_key |
1300 | 1.04M | THEN SUBSTR(closest_descendant.search_remain, 2 + LENGTH(trie_node.partial_key)) |
1301 | 1.04M | ELSE |
1302 | 1.04M | X'' -- Unreachable. |
1303 | 1.04M | END |
1304 | 1.04M | FROM closest_descendant |
1305 | 1.04M | LEFT JOIN trie_node_child ON closest_descendant.node_hash = trie_node_child.hash |
1306 | 1.04M | AND SUBSTR(closest_descendant.search_remain, 1, 1) = trie_node_child.child_num |
1307 | 1.04M | LEFT JOIN trie_node ON trie_node.hash = trie_node_child.child_hash |
1308 | 1.04M | LEFT JOIN trie_node_storage |
1309 | 1.04M | ON closest_descendant.node_hash = trie_node_storage.node_hash |
1310 | 1.04M | AND HEX(SUBSTR(closest_descendant.search_remain, 1, 1)) = '10' |
1311 | 1.04M | AND trie_node_storage.trie_root_ref IS NOT NULL |
1312 | 1.04M | WHERE |
1313 | 1.04M | LENGTH(closest_descendant.search_remain) >= 1 |
1314 | 1.04M | AND ( |
1315 | 1.04M | trie_node.hash IS NULL |
1316 | 1.04M | OR COALESCE(SUBSTR(trie_node.partial_key, 1, LENGTH(closest_descendant.search_remain) - 1), X'') = COALESCE(SUBSTR(closest_descendant.search_remain, 2), X'') |
1317 | 1.04M | OR COALESCE(SUBSTR(closest_descendant.search_remain, 2, LENGTH(trie_node.partial_key)), X'') = trie_node.partial_key |
1318 | 1.04M | ) |
1319 | 1.04M | ) |
1320 | 1.04M | SELECT COUNT(blocks.hash) >= 1, closest_descendant.node_hash IS NOT NULL AND closest_descendant.search_remain IS NULL, closest_descendant.node_hash |
1321 | 1.04M | FROM blocks |
1322 | 1.04M | LEFT JOIN closest_descendant ON LENGTH(closest_descendant.search_remain) = 0 OR closest_descendant.search_remain IS NULL |
1323 | 1.04M | WHERE blocks.hash = :block_hash |
1324 | 1.04M | LIMIT 1"#, |
1325 | 1.04M | ) |
1326 | 1.04M | .map_err(|err| { |
1327 | 0 | StorageAccessError::Corrupted(CorruptedError::Internal( |
1328 | 0 | InternalError(err), |
1329 | 0 | )) |
1330 | 1.04M | })?0 ; Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB21_B20_EINtNtNtB27_8adapters3map3MapINtNtB38_6copied6CopiedINtNtNtB29_5slice4iter4IterNtNtNtB9_4trie6nibble6NibbleEENvYhINtNtB29_7convert4FromB4j_E4fromEEs0_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB21_B20_EINtNtNtB29_5array4iter8IntoIterhKj0_EEs0_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB21_B20_EINtNtNtB29_5array4iter8IntoIterhKj1_EEs0_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB21_B20_EINtNtNtB29_5array4iter8IntoIterhKj2_EEs0_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB21_B20_EINtNtNtB29_5array4iter8IntoIterhKj3_EEs0_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB21_B20_EINtNtNtB29_5array4iter8IntoIterhKj4_EEs0_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB21_B20_EINtNtNtB29_5array4iter8IntoIterhKj5_EEs0_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB21_B20_EINtNtNtB29_5array4iter8IntoIterhKj8_EEs0_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB22_B21_EINtNtNtB28_8adapters3map3MapINtNtB39_7flatten7FlatMapIB3x_INtNtNtB2a_5slice4iter4IterNtNtNtB9_8executor20trie_root_calculator14InProgressNodeEINtNtB39_5chain5ChainINtNtB26_4once4OnceINtCs1qmLyiTSqYF_6either6EitherANtNtNtB9_4trie6nibble6Nibblej1_RINtNtCsdZExvAaxgia_5alloc3vec3VecB6w_EEEINtNtB2a_6option8IntoIterB60_EENCNvMs3_B4t_NtB4t_5Inner21current_node_full_key0EINtNvNtB9_4util11as_ref_iter4IterB60_B6w_EIB91_B6w_B60_EENvYhINtNtB2a_7convert4FromB6w_E4fromEEs0_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB30_6option8IntoIterINtB26_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB4t_14SyncBackground12author_block0s2_00EINtNtB2W_6copied6CopiedINtNtNtB30_5slice4iter4IterhEEEs0_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB30_6option8IntoIterINtB26_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s3_00EINtNtB2W_6copied6CopiedINtNtNtB30_5slice4iter4IterhEEEs0_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB30_6option8IntoIterINtB26_3VechEENCNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sa_00EINtNtB2W_6copied6CopiedINtNtNtB30_5slice4iter4IterhEEEs0_0B4u_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB30_6option8IntoIterINtB26_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB4t_14SyncBackground12author_block0s2_00EINtNtB2W_6copied6CopiedINtNtNtB30_5slice4iter4IterhEEEs0_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB30_6option8IntoIterINtB26_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s3_00EINtNtB2W_6copied6CopiedINtNtNtB30_5slice4iter4IterhEEEs0_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB30_6option8IntoIterINtB26_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB4t_14SyncBackground12author_block0s2_00EINtNtB2W_6copied6CopiedINtNtNtB30_5slice4iter4IterhEEEs0_0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB30_6option8IntoIterINtB26_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s3_00EINtNtB2W_6copied6CopiedINtNtNtB30_5slice4iter4IterhEEEs0_0CsibGXYHQB8Ea_25json_rpc_general_requests |
1331 | | |
1332 | | // In order to debug the SQL query above (for example in case of a failing test), |
1333 | | // uncomment this block: |
1334 | | // |
1335 | | /*println!("{:?}", { |
1336 | | let mut statement = connection |
1337 | | .prepare_cached( |
1338 | | r#" |
1339 | | WITH RECURSIVE |
1340 | | copy-paste the definition of closest_descendant here |
1341 | | |
1342 | | SELECT * FROM closest_descendant"#).unwrap(); |
1343 | | statement |
1344 | | .query_map( |
1345 | | rusqlite::named_params! { |
1346 | | ":block_hash": &block_hash[..], |
1347 | | ":key": key_vectored, |
1348 | | }, |
1349 | | |row| { |
1350 | | let node_hash = row.get::<_, Option<Vec<u8>>>(0)?.map(hex::encode); |
1351 | | let search_remain = row.get::<_, Option<Vec<u8>>>(1)?; |
1352 | | Ok((node_hash, search_remain)) |
1353 | | }, |
1354 | | ) |
1355 | | .unwrap() |
1356 | | .collect::<Vec<_>>() |
1357 | | });*/ |
1358 | | |
1359 | 1.04M | let (has_block, incomplete_storage, merkle_value) = statement |
1360 | 1.04M | .query_row( |
1361 | 1.04M | rusqlite::named_params! { |
1362 | 1.04M | ":block_hash": &block_hash[..], |
1363 | 1.04M | ":key": key_vectored, |
1364 | 1.04M | }, |
1365 | 1.04M | |row| { |
1366 | 1.04M | let has_block = row.get::<_, i64>(0)?0 != 0; |
1367 | 1.04M | let incomplete_storage = row.get::<_, i64>(1)?0 != 0; |
1368 | 1.04M | let merkle_value = row.get::<_, Option<Vec<u8>>>(2)?0 ; |
1369 | 1.04M | Ok((has_block, incomplete_storage, merkle_value)) |
1370 | 1.04M | }, _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB21_B20_EINtNtNtB27_8adapters3map3MapINtNtB38_6copied6CopiedINtNtNtB29_5slice4iter4IterNtNtNtB9_4trie6nibble6NibbleEENvYhINtNtB29_7convert4FromB4j_E4fromEEs1_0B9_ Line | Count | Source | 1365 | 1.04M | |row| { | 1366 | 1.04M | let has_block = row.get::<_, i64>(0)?0 != 0; | 1367 | 1.04M | let incomplete_storage = row.get::<_, i64>(1)?0 != 0; | 1368 | 1.04M | let merkle_value = row.get::<_, Option<Vec<u8>>>(2)?0 ; | 1369 | 1.04M | Ok((has_block, incomplete_storage, merkle_value)) | 1370 | 1.04M | }, |
_RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB21_B20_EINtNtNtB29_5array4iter8IntoIterhKj0_EEs1_0B9_ Line | Count | Source | 1365 | 2 | |row| { | 1366 | 2 | let has_block = row.get::<_, i64>(0)?0 != 0; | 1367 | 2 | let incomplete_storage = row.get::<_, i64>(1)?0 != 0; | 1368 | 2 | let merkle_value = row.get::<_, Option<Vec<u8>>>(2)?0 ; | 1369 | 2 | Ok((has_block, incomplete_storage, merkle_value)) | 1370 | 2 | }, |
_RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB21_B20_EINtNtNtB29_5array4iter8IntoIterhKj1_EEs1_0B9_ Line | Count | Source | 1365 | 3 | |row| { | 1366 | 3 | let has_block = row.get::<_, i64>(0)?0 != 0; | 1367 | 3 | let incomplete_storage = row.get::<_, i64>(1)?0 != 0; | 1368 | 3 | let merkle_value = row.get::<_, Option<Vec<u8>>>(2)?0 ; | 1369 | 3 | Ok((has_block, incomplete_storage, merkle_value)) | 1370 | 3 | }, |
_RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB21_B20_EINtNtNtB29_5array4iter8IntoIterhKj2_EEs1_0B9_ Line | Count | Source | 1365 | 3 | |row| { | 1366 | 3 | let has_block = row.get::<_, i64>(0)?0 != 0; | 1367 | 3 | let incomplete_storage = row.get::<_, i64>(1)?0 != 0; | 1368 | 3 | let merkle_value = row.get::<_, Option<Vec<u8>>>(2)?0 ; | 1369 | 3 | Ok((has_block, incomplete_storage, merkle_value)) | 1370 | 3 | }, |
_RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB21_B20_EINtNtNtB29_5array4iter8IntoIterhKj3_EEs1_0B9_ Line | Count | Source | 1365 | 4 | |row| { | 1366 | 4 | let has_block = row.get::<_, i64>(0)?0 != 0; | 1367 | 4 | let incomplete_storage = row.get::<_, i64>(1)?0 != 0; | 1368 | 4 | let merkle_value = row.get::<_, Option<Vec<u8>>>(2)?0 ; | 1369 | 4 | Ok((has_block, incomplete_storage, merkle_value)) | 1370 | 4 | }, |
_RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB21_B20_EINtNtNtB29_5array4iter8IntoIterhKj4_EEs1_0B9_ Line | Count | Source | 1365 | 3 | |row| { | 1366 | 3 | let has_block = row.get::<_, i64>(0)?0 != 0; | 1367 | 3 | let incomplete_storage = row.get::<_, i64>(1)?0 != 0; | 1368 | 3 | let merkle_value = row.get::<_, Option<Vec<u8>>>(2)?0 ; | 1369 | 3 | Ok((has_block, incomplete_storage, merkle_value)) | 1370 | 3 | }, |
_RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB21_B20_EINtNtNtB29_5array4iter8IntoIterhKj5_EEs1_0B9_ Line | Count | Source | 1365 | 1 | |row| { | 1366 | 1 | let has_block = row.get::<_, i64>(0)?0 != 0; | 1367 | 1 | let incomplete_storage = row.get::<_, i64>(1)?0 != 0; | 1368 | 1 | let merkle_value = row.get::<_, Option<Vec<u8>>>(2)?0 ; | 1369 | 1 | Ok((has_block, incomplete_storage, merkle_value)) | 1370 | 1 | }, |
_RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB21_B20_EINtNtNtB29_5array4iter8IntoIterhKj8_EEs1_0B9_ Line | Count | Source | 1365 | 1 | |row| { | 1366 | 1 | let has_block = row.get::<_, i64>(0)?0 != 0; | 1367 | 1 | let incomplete_storage = row.get::<_, i64>(1)?0 != 0; | 1368 | 1 | let merkle_value = row.get::<_, Option<Vec<u8>>>(2)?0 ; | 1369 | 1 | Ok((has_block, incomplete_storage, merkle_value)) | 1370 | 1 | }, |
Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB22_B21_EINtNtNtB28_8adapters3map3MapINtNtB39_7flatten7FlatMapIB3x_INtNtNtB2a_5slice4iter4IterNtNtNtB9_8executor20trie_root_calculator14InProgressNodeEINtNtB39_5chain5ChainINtNtB26_4once4OnceINtCs1qmLyiTSqYF_6either6EitherANtNtNtB9_4trie6nibble6Nibblej1_RINtNtCsdZExvAaxgia_5alloc3vec3VecB6w_EEEINtNtB2a_6option8IntoIterB60_EENCNvMs3_B4t_NtB4t_5Inner21current_node_full_key0EINtNvNtB9_4util11as_ref_iter4IterB60_B6w_EIB91_B6w_B60_EENvYhINtNtB2a_7convert4FromB6w_E4fromEEs1_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB30_6option8IntoIterINtB26_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB4t_14SyncBackground12author_block0s2_00EINtNtB2W_6copied6CopiedINtNtNtB30_5slice4iter4IterhEEEs1_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB30_6option8IntoIterINtB26_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s3_00EINtNtB2W_6copied6CopiedINtNtNtB30_5slice4iter4IterhEEEs1_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB30_6option8IntoIterINtB26_3VechEENCNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sa_00EINtNtB2W_6copied6CopiedINtNtNtB30_5slice4iter4IterhEEEs1_0B4u_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB30_6option8IntoIterINtB26_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB4t_14SyncBackground12author_block0s2_00EINtNtB2W_6copied6CopiedINtNtNtB30_5slice4iter4IterhEEEs1_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB30_6option8IntoIterINtB26_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s3_00EINtNtB2W_6copied6CopiedINtNtNtB30_5slice4iter4IterhEEEs1_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB30_6option8IntoIterINtB26_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB4t_14SyncBackground12author_block0s2_00EINtNtB2W_6copied6CopiedINtNtNtB30_5slice4iter4IterhEEEs1_0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB30_6option8IntoIterINtB26_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s3_00EINtNtB2W_6copied6CopiedINtNtNtB30_5slice4iter4IterhEEEs1_0CsibGXYHQB8Ea_25json_rpc_general_requests |
1371 | 1.04M | ) |
1372 | 1.04M | .map_err(|err| { |
1373 | 0 | StorageAccessError::Corrupted(CorruptedError::Internal(InternalError(err))) |
1374 | 1.04M | })?0 ; Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB21_B20_EINtNtNtB27_8adapters3map3MapINtNtB38_6copied6CopiedINtNtNtB29_5slice4iter4IterNtNtNtB9_4trie6nibble6NibbleEENvYhINtNtB29_7convert4FromB4j_E4fromEEs2_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB21_B20_EINtNtNtB29_5array4iter8IntoIterhKj0_EEs2_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB21_B20_EINtNtNtB29_5array4iter8IntoIterhKj1_EEs2_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB21_B20_EINtNtNtB29_5array4iter8IntoIterhKj2_EEs2_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB21_B20_EINtNtNtB29_5array4iter8IntoIterhKj3_EEs2_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB21_B20_EINtNtNtB29_5array4iter8IntoIterhKj4_EEs2_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB21_B20_EINtNtNtB29_5array4iter8IntoIterhKj5_EEs2_0B9_ Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB21_B20_EINtNtNtB29_5array4iter8IntoIterhKj8_EEs2_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB22_B21_EINtNtNtB28_8adapters3map3MapINtNtB39_7flatten7FlatMapIB3x_INtNtNtB2a_5slice4iter4IterNtNtNtB9_8executor20trie_root_calculator14InProgressNodeEINtNtB39_5chain5ChainINtNtB26_4once4OnceINtCs1qmLyiTSqYF_6either6EitherANtNtNtB9_4trie6nibble6Nibblej1_RINtNtCsdZExvAaxgia_5alloc3vec3VecB6w_EEEINtNtB2a_6option8IntoIterB60_EENCNvMs3_B4t_NtB4t_5Inner21current_node_full_key0EINtNvNtB9_4util11as_ref_iter4IterB60_B6w_EIB91_B6w_B60_EENvYhINtNtB2a_7convert4FromB6w_E4fromEEs2_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB30_6option8IntoIterINtB26_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB4t_14SyncBackground12author_block0s2_00EINtNtB2W_6copied6CopiedINtNtNtB30_5slice4iter4IterhEEEs2_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB30_6option8IntoIterINtB26_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s3_00EINtNtB2W_6copied6CopiedINtNtNtB30_5slice4iter4IterhEEEs2_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB30_6option8IntoIterINtB26_3VechEENCNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sa_00EINtNtB2W_6copied6CopiedINtNtNtB30_5slice4iter4IterhEEEs2_0B4u_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB30_6option8IntoIterINtB26_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB4t_14SyncBackground12author_block0s2_00EINtNtB2W_6copied6CopiedINtNtNtB30_5slice4iter4IterhEEEs2_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB30_6option8IntoIterINtB26_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s3_00EINtNtB2W_6copied6CopiedINtNtNtB30_5slice4iter4IterhEEEs2_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB30_6option8IntoIterINtB26_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB4t_14SyncBackground12author_block0s2_00EINtNtB2W_6copied6CopiedINtNtNtB30_5slice4iter4IterhEEEs2_0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB30_6option8IntoIterINtB26_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s3_00EINtNtB2W_6copied6CopiedINtNtNtB30_5slice4iter4IterhEEEs2_0CsibGXYHQB8Ea_25json_rpc_general_requests |
1375 | | |
1376 | 1.04M | if !has_block { |
1377 | 1 | return Err(StorageAccessError::UnknownBlock); |
1378 | 1.04M | } |
1379 | 1.04M | |
1380 | 1.04M | if incomplete_storage { |
1381 | 4 | return Err(StorageAccessError::IncompleteStorage); |
1382 | 1.04M | } |
1383 | 1.04M | |
1384 | 1.04M | Ok(merkle_value) |
1385 | 1.04M | } _RINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1Z_B1Y_EINtNtNtB25_8adapters3map3MapINtNtB36_6copied6CopiedINtNtNtB27_5slice4iter4IterNtNtNtB7_4trie6nibble6NibbleEENvYhINtNtB27_7convert4FromB4h_E4fromEEB7_ Line | Count | Source | 1239 | 1.04M | pub fn block_storage_closest_descendant_merkle_value( | 1240 | 1.04M | &self, | 1241 | 1.04M | block_hash: &[u8; 32], | 1242 | 1.04M | parent_tries_paths_nibbles: impl Iterator<Item = impl Iterator<Item = u8>>, | 1243 | 1.04M | key_nibbles: impl Iterator<Item = u8>, | 1244 | 1.04M | ) -> Result<Option<Vec<u8>>, StorageAccessError> { | 1245 | 1.04M | // Process the iterators at the very beginning and before locking the database, in order | 1246 | 1.04M | // to avoid a deadlock in case the `next()` function of one of the iterators accesses | 1247 | 1.04M | // the database as well. | 1248 | 1.04M | let key_vectored = parent_tries_paths_nibbles | 1249 | 1.04M | .flat_map(|t| t.inspect(|n| assert!(*n < 16)).chain(iter::once(0x10))) | 1250 | 1.04M | .chain(key_nibbles.inspect(|n| assert!(*n < 16))) | 1251 | 1.04M | .collect::<Vec<_>>(); | 1252 | 1.04M | | 1253 | 1.04M | let connection = self.database.lock(); | 1254 | | | 1255 | | // TODO: trie_root_ref system untested | 1256 | | // TODO: infinite loop if there's a loop in the trie; detect this | 1257 | 1.04M | let mut statement = connection | 1258 | 1.04M | .prepare_cached( | 1259 | 1.04M | r#" | 1260 | 1.04M | WITH RECURSIVE | 1261 | 1.04M | -- At the end of the recursive statement, `closest_descendant` must always contain | 1262 | 1.04M | -- at most one item where `search_remain` is either empty or null. Empty | 1263 | 1.04M | -- indicates that we have found a match, while null means that the search has | 1264 | 1.04M | -- been interrupted due to a storage entry not being in the database. If | 1265 | 1.04M | -- `search_remain` is null, then `node_hash` is irrelevant. | 1266 | 1.04M | -- If `closest_descendant` doesn't have any entry where `search_remain` is empty | 1267 | 1.04M | -- or null, then the request key doesn't have any descendant. | 1268 | 1.04M | closest_descendant(node_hash, search_remain) AS ( | 1269 | 1.04M | SELECT | 1270 | 1.04M | blocks.state_trie_root_hash, | 1271 | 1.04M | CASE | 1272 | 1.04M | WHEN trie_node.partial_key IS NULL AND LENGTH(:key) = 0 | 1273 | 1.04M | THEN X'' -- Trie root node isn't in database, but since key is empty we have a match anyway | 1274 | 1.04M | WHEN trie_node.partial_key IS NULL AND LENGTH(:key) != 0 | 1275 | 1.04M | THEN NULL -- Trie root node isn't in database and we can't iterate further | 1276 | 1.04M | ELSE | 1277 | 1.04M | COALESCE(SUBSTR(:key, 1 + LENGTH(trie_node.partial_key)), X'') | 1278 | 1.04M | END | 1279 | 1.04M | FROM blocks | 1280 | 1.04M | LEFT JOIN trie_node ON blocks.state_trie_root_hash = trie_node.hash | 1281 | 1.04M | WHERE blocks.hash = :block_hash | 1282 | 1.04M | AND ( | 1283 | 1.04M | trie_node.partial_key IS NULL | 1284 | 1.04M | OR COALESCE(SUBSTR(trie_node.partial_key, 1, LENGTH(:key)), X'') = :key | 1285 | 1.04M | OR COALESCE(SUBSTR(:key, 1, LENGTH(trie_node.partial_key)), X'') = trie_node.partial_key | 1286 | 1.04M | ) | 1287 | 1.04M | | 1288 | 1.04M | UNION ALL | 1289 | 1.04M | SELECT | 1290 | 1.04M | COALESCE(trie_node_child.child_hash, trie_node_storage.trie_root_ref), | 1291 | 1.04M | CASE | 1292 | 1.04M | WHEN trie_node_child.child_hash IS NULL AND HEX(SUBSTR(closest_descendant.search_remain, 1, 1)) != '10' | 1293 | 1.04M | THEN X'' -- No child matching the key. | 1294 | 1.04M | WHEN trie_node_child.child_hash IS NOT NULL AND trie_node.hash IS NULL AND LENGTH(closest_descendant.search_remain) = 1 | 1295 | 1.04M | THEN X'' -- Descendant node not in trie but we know that it's the result. | 1296 | 1.04M | WHEN trie_node_child.child_hash IS NOT NULL AND trie_node.hash IS NULL | 1297 | 1.04M | THEN NULL -- Descendant node not in trie. | 1298 | 1.04M | WHEN COALESCE(SUBSTR(trie_node.partial_key, 1, LENGTH(closest_descendant.search_remain) - 1), X'') = COALESCE(SUBSTR(closest_descendant.search_remain, 2), X'') | 1299 | 1.04M | OR COALESCE(SUBSTR(closest_descendant.search_remain, 2, LENGTH(trie_node.partial_key)), X'') = trie_node.partial_key | 1300 | 1.04M | THEN SUBSTR(closest_descendant.search_remain, 2 + LENGTH(trie_node.partial_key)) | 1301 | 1.04M | ELSE | 1302 | 1.04M | X'' -- Unreachable. | 1303 | 1.04M | END | 1304 | 1.04M | FROM closest_descendant | 1305 | 1.04M | LEFT JOIN trie_node_child ON closest_descendant.node_hash = trie_node_child.hash | 1306 | 1.04M | AND SUBSTR(closest_descendant.search_remain, 1, 1) = trie_node_child.child_num | 1307 | 1.04M | LEFT JOIN trie_node ON trie_node.hash = trie_node_child.child_hash | 1308 | 1.04M | LEFT JOIN trie_node_storage | 1309 | 1.04M | ON closest_descendant.node_hash = trie_node_storage.node_hash | 1310 | 1.04M | AND HEX(SUBSTR(closest_descendant.search_remain, 1, 1)) = '10' | 1311 | 1.04M | AND trie_node_storage.trie_root_ref IS NOT NULL | 1312 | 1.04M | WHERE | 1313 | 1.04M | LENGTH(closest_descendant.search_remain) >= 1 | 1314 | 1.04M | AND ( | 1315 | 1.04M | trie_node.hash IS NULL | 1316 | 1.04M | OR COALESCE(SUBSTR(trie_node.partial_key, 1, LENGTH(closest_descendant.search_remain) - 1), X'') = COALESCE(SUBSTR(closest_descendant.search_remain, 2), X'') | 1317 | 1.04M | OR COALESCE(SUBSTR(closest_descendant.search_remain, 2, LENGTH(trie_node.partial_key)), X'') = trie_node.partial_key | 1318 | 1.04M | ) | 1319 | 1.04M | ) | 1320 | 1.04M | SELECT COUNT(blocks.hash) >= 1, closest_descendant.node_hash IS NOT NULL AND closest_descendant.search_remain IS NULL, closest_descendant.node_hash | 1321 | 1.04M | FROM blocks | 1322 | 1.04M | LEFT JOIN closest_descendant ON LENGTH(closest_descendant.search_remain) = 0 OR closest_descendant.search_remain IS NULL | 1323 | 1.04M | WHERE blocks.hash = :block_hash | 1324 | 1.04M | LIMIT 1"#, | 1325 | 1.04M | ) | 1326 | 1.04M | .map_err(|err| { | 1327 | | StorageAccessError::Corrupted(CorruptedError::Internal( | 1328 | | InternalError(err), | 1329 | | )) | 1330 | 1.04M | })?0 ; | 1331 | | | 1332 | | // In order to debug the SQL query above (for example in case of a failing test), | 1333 | | // uncomment this block: | 1334 | | // | 1335 | | /*println!("{:?}", { | 1336 | | let mut statement = connection | 1337 | | .prepare_cached( | 1338 | | r#" | 1339 | | WITH RECURSIVE | 1340 | | copy-paste the definition of closest_descendant here | 1341 | | | 1342 | | SELECT * FROM closest_descendant"#).unwrap(); | 1343 | | statement | 1344 | | .query_map( | 1345 | | rusqlite::named_params! { | 1346 | | ":block_hash": &block_hash[..], | 1347 | | ":key": key_vectored, | 1348 | | }, | 1349 | | |row| { | 1350 | | let node_hash = row.get::<_, Option<Vec<u8>>>(0)?.map(hex::encode); | 1351 | | let search_remain = row.get::<_, Option<Vec<u8>>>(1)?; | 1352 | | Ok((node_hash, search_remain)) | 1353 | | }, | 1354 | | ) | 1355 | | .unwrap() | 1356 | | .collect::<Vec<_>>() | 1357 | | });*/ | 1358 | | | 1359 | 1.04M | let (has_block, incomplete_storage, merkle_value) = statement | 1360 | 1.04M | .query_row( | 1361 | 1.04M | rusqlite::named_params! { | 1362 | 1.04M | ":block_hash": &block_hash[..], | 1363 | 1.04M | ":key": key_vectored, | 1364 | 1.04M | }, | 1365 | 1.04M | |row| { | 1366 | | let has_block = row.get::<_, i64>(0)? != 0; | 1367 | | let incomplete_storage = row.get::<_, i64>(1)? != 0; | 1368 | | let merkle_value = row.get::<_, Option<Vec<u8>>>(2)?; | 1369 | | Ok((has_block, incomplete_storage, merkle_value)) | 1370 | 1.04M | }, | 1371 | 1.04M | ) | 1372 | 1.04M | .map_err(|err| { | 1373 | | StorageAccessError::Corrupted(CorruptedError::Internal(InternalError(err))) | 1374 | 1.04M | })?0 ; | 1375 | | | 1376 | 1.04M | if !has_block { | 1377 | 0 | return Err(StorageAccessError::UnknownBlock); | 1378 | 1.04M | } | 1379 | 1.04M | | 1380 | 1.04M | if incomplete_storage { | 1381 | 0 | return Err(StorageAccessError::IncompleteStorage); | 1382 | 1.04M | } | 1383 | 1.04M | | 1384 | 1.04M | Ok(merkle_value) | 1385 | 1.04M | } |
_RINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1Z_B1Y_EINtNtNtB27_5array4iter8IntoIterhKj0_EEB7_ Line | Count | Source | 1239 | 2 | pub fn block_storage_closest_descendant_merkle_value( | 1240 | 2 | &self, | 1241 | 2 | block_hash: &[u8; 32], | 1242 | 2 | parent_tries_paths_nibbles: impl Iterator<Item = impl Iterator<Item = u8>>, | 1243 | 2 | key_nibbles: impl Iterator<Item = u8>, | 1244 | 2 | ) -> Result<Option<Vec<u8>>, StorageAccessError> { | 1245 | 2 | // Process the iterators at the very beginning and before locking the database, in order | 1246 | 2 | // to avoid a deadlock in case the `next()` function of one of the iterators accesses | 1247 | 2 | // the database as well. | 1248 | 2 | let key_vectored = parent_tries_paths_nibbles | 1249 | 2 | .flat_map(|t| t.inspect(|n| assert!(*n < 16)).chain(iter::once(0x10))) | 1250 | 2 | .chain(key_nibbles.inspect(|n| assert!(*n < 16))) | 1251 | 2 | .collect::<Vec<_>>(); | 1252 | 2 | | 1253 | 2 | let connection = self.database.lock(); | 1254 | | | 1255 | | // TODO: trie_root_ref system untested | 1256 | | // TODO: infinite loop if there's a loop in the trie; detect this | 1257 | 2 | let mut statement = connection | 1258 | 2 | .prepare_cached( | 1259 | 2 | r#" | 1260 | 2 | WITH RECURSIVE | 1261 | 2 | -- At the end of the recursive statement, `closest_descendant` must always contain | 1262 | 2 | -- at most one item where `search_remain` is either empty or null. Empty | 1263 | 2 | -- indicates that we have found a match, while null means that the search has | 1264 | 2 | -- been interrupted due to a storage entry not being in the database. If | 1265 | 2 | -- `search_remain` is null, then `node_hash` is irrelevant. | 1266 | 2 | -- If `closest_descendant` doesn't have any entry where `search_remain` is empty | 1267 | 2 | -- or null, then the request key doesn't have any descendant. | 1268 | 2 | closest_descendant(node_hash, search_remain) AS ( | 1269 | 2 | SELECT | 1270 | 2 | blocks.state_trie_root_hash, | 1271 | 2 | CASE | 1272 | 2 | WHEN trie_node.partial_key IS NULL AND LENGTH(:key) = 0 | 1273 | 2 | THEN X'' -- Trie root node isn't in database, but since key is empty we have a match anyway | 1274 | 2 | WHEN trie_node.partial_key IS NULL AND LENGTH(:key) != 0 | 1275 | 2 | THEN NULL -- Trie root node isn't in database and we can't iterate further | 1276 | 2 | ELSE | 1277 | 2 | COALESCE(SUBSTR(:key, 1 + LENGTH(trie_node.partial_key)), X'') | 1278 | 2 | END | 1279 | 2 | FROM blocks | 1280 | 2 | LEFT JOIN trie_node ON blocks.state_trie_root_hash = trie_node.hash | 1281 | 2 | WHERE blocks.hash = :block_hash | 1282 | 2 | AND ( | 1283 | 2 | trie_node.partial_key IS NULL | 1284 | 2 | OR COALESCE(SUBSTR(trie_node.partial_key, 1, LENGTH(:key)), X'') = :key | 1285 | 2 | OR COALESCE(SUBSTR(:key, 1, LENGTH(trie_node.partial_key)), X'') = trie_node.partial_key | 1286 | 2 | ) | 1287 | 2 | | 1288 | 2 | UNION ALL | 1289 | 2 | SELECT | 1290 | 2 | COALESCE(trie_node_child.child_hash, trie_node_storage.trie_root_ref), | 1291 | 2 | CASE | 1292 | 2 | WHEN trie_node_child.child_hash IS NULL AND HEX(SUBSTR(closest_descendant.search_remain, 1, 1)) != '10' | 1293 | 2 | THEN X'' -- No child matching the key. | 1294 | 2 | WHEN trie_node_child.child_hash IS NOT NULL AND trie_node.hash IS NULL AND LENGTH(closest_descendant.search_remain) = 1 | 1295 | 2 | THEN X'' -- Descendant node not in trie but we know that it's the result. | 1296 | 2 | WHEN trie_node_child.child_hash IS NOT NULL AND trie_node.hash IS NULL | 1297 | 2 | THEN NULL -- Descendant node not in trie. | 1298 | 2 | WHEN COALESCE(SUBSTR(trie_node.partial_key, 1, LENGTH(closest_descendant.search_remain) - 1), X'') = COALESCE(SUBSTR(closest_descendant.search_remain, 2), X'') | 1299 | 2 | OR COALESCE(SUBSTR(closest_descendant.search_remain, 2, LENGTH(trie_node.partial_key)), X'') = trie_node.partial_key | 1300 | 2 | THEN SUBSTR(closest_descendant.search_remain, 2 + LENGTH(trie_node.partial_key)) | 1301 | 2 | ELSE | 1302 | 2 | X'' -- Unreachable. | 1303 | 2 | END | 1304 | 2 | FROM closest_descendant | 1305 | 2 | LEFT JOIN trie_node_child ON closest_descendant.node_hash = trie_node_child.hash | 1306 | 2 | AND SUBSTR(closest_descendant.search_remain, 1, 1) = trie_node_child.child_num | 1307 | 2 | LEFT JOIN trie_node ON trie_node.hash = trie_node_child.child_hash | 1308 | 2 | LEFT JOIN trie_node_storage | 1309 | 2 | ON closest_descendant.node_hash = trie_node_storage.node_hash | 1310 | 2 | AND HEX(SUBSTR(closest_descendant.search_remain, 1, 1)) = '10' | 1311 | 2 | AND trie_node_storage.trie_root_ref IS NOT NULL | 1312 | 2 | WHERE | 1313 | 2 | LENGTH(closest_descendant.search_remain) >= 1 | 1314 | 2 | AND ( | 1315 | 2 | trie_node.hash IS NULL | 1316 | 2 | OR COALESCE(SUBSTR(trie_node.partial_key, 1, LENGTH(closest_descendant.search_remain) - 1), X'') = COALESCE(SUBSTR(closest_descendant.search_remain, 2), X'') | 1317 | 2 | OR COALESCE(SUBSTR(closest_descendant.search_remain, 2, LENGTH(trie_node.partial_key)), X'') = trie_node.partial_key | 1318 | 2 | ) | 1319 | 2 | ) | 1320 | 2 | SELECT COUNT(blocks.hash) >= 1, closest_descendant.node_hash IS NOT NULL AND closest_descendant.search_remain IS NULL, closest_descendant.node_hash | 1321 | 2 | FROM blocks | 1322 | 2 | LEFT JOIN closest_descendant ON LENGTH(closest_descendant.search_remain) = 0 OR closest_descendant.search_remain IS NULL | 1323 | 2 | WHERE blocks.hash = :block_hash | 1324 | 2 | LIMIT 1"#, | 1325 | 2 | ) | 1326 | 2 | .map_err(|err| { | 1327 | | StorageAccessError::Corrupted(CorruptedError::Internal( | 1328 | | InternalError(err), | 1329 | | )) | 1330 | 2 | })?0 ; | 1331 | | | 1332 | | // In order to debug the SQL query above (for example in case of a failing test), | 1333 | | // uncomment this block: | 1334 | | // | 1335 | | /*println!("{:?}", { | 1336 | | let mut statement = connection | 1337 | | .prepare_cached( | 1338 | | r#" | 1339 | | WITH RECURSIVE | 1340 | | copy-paste the definition of closest_descendant here | 1341 | | | 1342 | | SELECT * FROM closest_descendant"#).unwrap(); | 1343 | | statement | 1344 | | .query_map( | 1345 | | rusqlite::named_params! { | 1346 | | ":block_hash": &block_hash[..], | 1347 | | ":key": key_vectored, | 1348 | | }, | 1349 | | |row| { | 1350 | | let node_hash = row.get::<_, Option<Vec<u8>>>(0)?.map(hex::encode); | 1351 | | let search_remain = row.get::<_, Option<Vec<u8>>>(1)?; | 1352 | | Ok((node_hash, search_remain)) | 1353 | | }, | 1354 | | ) | 1355 | | .unwrap() | 1356 | | .collect::<Vec<_>>() | 1357 | | });*/ | 1358 | | | 1359 | 2 | let (has_block, incomplete_storage, merkle_value) = statement | 1360 | 2 | .query_row( | 1361 | 2 | rusqlite::named_params! { | 1362 | 2 | ":block_hash": &block_hash[..], | 1363 | 2 | ":key": key_vectored, | 1364 | 2 | }, | 1365 | 2 | |row| { | 1366 | | let has_block = row.get::<_, i64>(0)? != 0; | 1367 | | let incomplete_storage = row.get::<_, i64>(1)? != 0; | 1368 | | let merkle_value = row.get::<_, Option<Vec<u8>>>(2)?; | 1369 | | Ok((has_block, incomplete_storage, merkle_value)) | 1370 | 2 | }, | 1371 | 2 | ) | 1372 | 2 | .map_err(|err| { | 1373 | | StorageAccessError::Corrupted(CorruptedError::Internal(InternalError(err))) | 1374 | 2 | })?0 ; | 1375 | | | 1376 | 2 | if !has_block { | 1377 | 1 | return Err(StorageAccessError::UnknownBlock); | 1378 | 1 | } | 1379 | 1 | | 1380 | 1 | if incomplete_storage { | 1381 | 0 | return Err(StorageAccessError::IncompleteStorage); | 1382 | 1 | } | 1383 | 1 | | 1384 | 1 | Ok(merkle_value) | 1385 | 2 | } |
_RINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1Z_B1Y_EINtNtNtB27_5array4iter8IntoIterhKj1_EEB7_ Line | Count | Source | 1239 | 3 | pub fn block_storage_closest_descendant_merkle_value( | 1240 | 3 | &self, | 1241 | 3 | block_hash: &[u8; 32], | 1242 | 3 | parent_tries_paths_nibbles: impl Iterator<Item = impl Iterator<Item = u8>>, | 1243 | 3 | key_nibbles: impl Iterator<Item = u8>, | 1244 | 3 | ) -> Result<Option<Vec<u8>>, StorageAccessError> { | 1245 | 3 | // Process the iterators at the very beginning and before locking the database, in order | 1246 | 3 | // to avoid a deadlock in case the `next()` function of one of the iterators accesses | 1247 | 3 | // the database as well. | 1248 | 3 | let key_vectored = parent_tries_paths_nibbles | 1249 | 3 | .flat_map(|t| t.inspect(|n| assert!(*n < 16)).chain(iter::once(0x10))) | 1250 | 3 | .chain(key_nibbles.inspect(|n| assert!(*n < 16))) | 1251 | 3 | .collect::<Vec<_>>(); | 1252 | 3 | | 1253 | 3 | let connection = self.database.lock(); | 1254 | | | 1255 | | // TODO: trie_root_ref system untested | 1256 | | // TODO: infinite loop if there's a loop in the trie; detect this | 1257 | 3 | let mut statement = connection | 1258 | 3 | .prepare_cached( | 1259 | 3 | r#" | 1260 | 3 | WITH RECURSIVE | 1261 | 3 | -- At the end of the recursive statement, `closest_descendant` must always contain | 1262 | 3 | -- at most one item where `search_remain` is either empty or null. Empty | 1263 | 3 | -- indicates that we have found a match, while null means that the search has | 1264 | 3 | -- been interrupted due to a storage entry not being in the database. If | 1265 | 3 | -- `search_remain` is null, then `node_hash` is irrelevant. | 1266 | 3 | -- If `closest_descendant` doesn't have any entry where `search_remain` is empty | 1267 | 3 | -- or null, then the request key doesn't have any descendant. | 1268 | 3 | closest_descendant(node_hash, search_remain) AS ( | 1269 | 3 | SELECT | 1270 | 3 | blocks.state_trie_root_hash, | 1271 | 3 | CASE | 1272 | 3 | WHEN trie_node.partial_key IS NULL AND LENGTH(:key) = 0 | 1273 | 3 | THEN X'' -- Trie root node isn't in database, but since key is empty we have a match anyway | 1274 | 3 | WHEN trie_node.partial_key IS NULL AND LENGTH(:key) != 0 | 1275 | 3 | THEN NULL -- Trie root node isn't in database and we can't iterate further | 1276 | 3 | ELSE | 1277 | 3 | COALESCE(SUBSTR(:key, 1 + LENGTH(trie_node.partial_key)), X'') | 1278 | 3 | END | 1279 | 3 | FROM blocks | 1280 | 3 | LEFT JOIN trie_node ON blocks.state_trie_root_hash = trie_node.hash | 1281 | 3 | WHERE blocks.hash = :block_hash | 1282 | 3 | AND ( | 1283 | 3 | trie_node.partial_key IS NULL | 1284 | 3 | OR COALESCE(SUBSTR(trie_node.partial_key, 1, LENGTH(:key)), X'') = :key | 1285 | 3 | OR COALESCE(SUBSTR(:key, 1, LENGTH(trie_node.partial_key)), X'') = trie_node.partial_key | 1286 | 3 | ) | 1287 | 3 | | 1288 | 3 | UNION ALL | 1289 | 3 | SELECT | 1290 | 3 | COALESCE(trie_node_child.child_hash, trie_node_storage.trie_root_ref), | 1291 | 3 | CASE | 1292 | 3 | WHEN trie_node_child.child_hash IS NULL AND HEX(SUBSTR(closest_descendant.search_remain, 1, 1)) != '10' | 1293 | 3 | THEN X'' -- No child matching the key. | 1294 | 3 | WHEN trie_node_child.child_hash IS NOT NULL AND trie_node.hash IS NULL AND LENGTH(closest_descendant.search_remain) = 1 | 1295 | 3 | THEN X'' -- Descendant node not in trie but we know that it's the result. | 1296 | 3 | WHEN trie_node_child.child_hash IS NOT NULL AND trie_node.hash IS NULL | 1297 | 3 | THEN NULL -- Descendant node not in trie. | 1298 | 3 | WHEN COALESCE(SUBSTR(trie_node.partial_key, 1, LENGTH(closest_descendant.search_remain) - 1), X'') = COALESCE(SUBSTR(closest_descendant.search_remain, 2), X'') | 1299 | 3 | OR COALESCE(SUBSTR(closest_descendant.search_remain, 2, LENGTH(trie_node.partial_key)), X'') = trie_node.partial_key | 1300 | 3 | THEN SUBSTR(closest_descendant.search_remain, 2 + LENGTH(trie_node.partial_key)) | 1301 | 3 | ELSE | 1302 | 3 | X'' -- Unreachable. | 1303 | 3 | END | 1304 | 3 | FROM closest_descendant | 1305 | 3 | LEFT JOIN trie_node_child ON closest_descendant.node_hash = trie_node_child.hash | 1306 | 3 | AND SUBSTR(closest_descendant.search_remain, 1, 1) = trie_node_child.child_num | 1307 | 3 | LEFT JOIN trie_node ON trie_node.hash = trie_node_child.child_hash | 1308 | 3 | LEFT JOIN trie_node_storage | 1309 | 3 | ON closest_descendant.node_hash = trie_node_storage.node_hash | 1310 | 3 | AND HEX(SUBSTR(closest_descendant.search_remain, 1, 1)) = '10' | 1311 | 3 | AND trie_node_storage.trie_root_ref IS NOT NULL | 1312 | 3 | WHERE | 1313 | 3 | LENGTH(closest_descendant.search_remain) >= 1 | 1314 | 3 | AND ( | 1315 | 3 | trie_node.hash IS NULL | 1316 | 3 | OR COALESCE(SUBSTR(trie_node.partial_key, 1, LENGTH(closest_descendant.search_remain) - 1), X'') = COALESCE(SUBSTR(closest_descendant.search_remain, 2), X'') | 1317 | 3 | OR COALESCE(SUBSTR(closest_descendant.search_remain, 2, LENGTH(trie_node.partial_key)), X'') = trie_node.partial_key | 1318 | 3 | ) | 1319 | 3 | ) | 1320 | 3 | SELECT COUNT(blocks.hash) >= 1, closest_descendant.node_hash IS NOT NULL AND closest_descendant.search_remain IS NULL, closest_descendant.node_hash | 1321 | 3 | FROM blocks | 1322 | 3 | LEFT JOIN closest_descendant ON LENGTH(closest_descendant.search_remain) = 0 OR closest_descendant.search_remain IS NULL | 1323 | 3 | WHERE blocks.hash = :block_hash | 1324 | 3 | LIMIT 1"#, | 1325 | 3 | ) | 1326 | 3 | .map_err(|err| { | 1327 | | StorageAccessError::Corrupted(CorruptedError::Internal( | 1328 | | InternalError(err), | 1329 | | )) | 1330 | 3 | })?0 ; | 1331 | | | 1332 | | // In order to debug the SQL query above (for example in case of a failing test), | 1333 | | // uncomment this block: | 1334 | | // | 1335 | | /*println!("{:?}", { | 1336 | | let mut statement = connection | 1337 | | .prepare_cached( | 1338 | | r#" | 1339 | | WITH RECURSIVE | 1340 | | copy-paste the definition of closest_descendant here | 1341 | | | 1342 | | SELECT * FROM closest_descendant"#).unwrap(); | 1343 | | statement | 1344 | | .query_map( | 1345 | | rusqlite::named_params! { | 1346 | | ":block_hash": &block_hash[..], | 1347 | | ":key": key_vectored, | 1348 | | }, | 1349 | | |row| { | 1350 | | let node_hash = row.get::<_, Option<Vec<u8>>>(0)?.map(hex::encode); | 1351 | | let search_remain = row.get::<_, Option<Vec<u8>>>(1)?; | 1352 | | Ok((node_hash, search_remain)) | 1353 | | }, | 1354 | | ) | 1355 | | .unwrap() | 1356 | | .collect::<Vec<_>>() | 1357 | | });*/ | 1358 | | | 1359 | 3 | let (has_block, incomplete_storage, merkle_value) = statement | 1360 | 3 | .query_row( | 1361 | 3 | rusqlite::named_params! { | 1362 | 3 | ":block_hash": &block_hash[..], | 1363 | 3 | ":key": key_vectored, | 1364 | 3 | }, | 1365 | 3 | |row| { | 1366 | | let has_block = row.get::<_, i64>(0)? != 0; | 1367 | | let incomplete_storage = row.get::<_, i64>(1)? != 0; | 1368 | | let merkle_value = row.get::<_, Option<Vec<u8>>>(2)?; | 1369 | | Ok((has_block, incomplete_storage, merkle_value)) | 1370 | 3 | }, | 1371 | 3 | ) | 1372 | 3 | .map_err(|err| { | 1373 | | StorageAccessError::Corrupted(CorruptedError::Internal(InternalError(err))) | 1374 | 3 | })?0 ; | 1375 | | | 1376 | 3 | if !has_block { | 1377 | 0 | return Err(StorageAccessError::UnknownBlock); | 1378 | 3 | } | 1379 | 3 | | 1380 | 3 | if incomplete_storage { | 1381 | 1 | return Err(StorageAccessError::IncompleteStorage); | 1382 | 2 | } | 1383 | 2 | | 1384 | 2 | Ok(merkle_value) | 1385 | 3 | } |
_RINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1Z_B1Y_EINtNtNtB27_5array4iter8IntoIterhKj2_EEB7_ Line | Count | Source | 1239 | 3 | pub fn block_storage_closest_descendant_merkle_value( | 1240 | 3 | &self, | 1241 | 3 | block_hash: &[u8; 32], | 1242 | 3 | parent_tries_paths_nibbles: impl Iterator<Item = impl Iterator<Item = u8>>, | 1243 | 3 | key_nibbles: impl Iterator<Item = u8>, | 1244 | 3 | ) -> Result<Option<Vec<u8>>, StorageAccessError> { | 1245 | 3 | // Process the iterators at the very beginning and before locking the database, in order | 1246 | 3 | // to avoid a deadlock in case the `next()` function of one of the iterators accesses | 1247 | 3 | // the database as well. | 1248 | 3 | let key_vectored = parent_tries_paths_nibbles | 1249 | 3 | .flat_map(|t| t.inspect(|n| assert!(*n < 16)).chain(iter::once(0x10))) | 1250 | 3 | .chain(key_nibbles.inspect(|n| assert!(*n < 16))) | 1251 | 3 | .collect::<Vec<_>>(); | 1252 | 3 | | 1253 | 3 | let connection = self.database.lock(); | 1254 | | | 1255 | | // TODO: trie_root_ref system untested | 1256 | | // TODO: infinite loop if there's a loop in the trie; detect this | 1257 | 3 | let mut statement = connection | 1258 | 3 | .prepare_cached( | 1259 | 3 | r#" | 1260 | 3 | WITH RECURSIVE | 1261 | 3 | -- At the end of the recursive statement, `closest_descendant` must always contain | 1262 | 3 | -- at most one item where `search_remain` is either empty or null. Empty | 1263 | 3 | -- indicates that we have found a match, while null means that the search has | 1264 | 3 | -- been interrupted due to a storage entry not being in the database. If | 1265 | 3 | -- `search_remain` is null, then `node_hash` is irrelevant. | 1266 | 3 | -- If `closest_descendant` doesn't have any entry where `search_remain` is empty | 1267 | 3 | -- or null, then the request key doesn't have any descendant. | 1268 | 3 | closest_descendant(node_hash, search_remain) AS ( | 1269 | 3 | SELECT | 1270 | 3 | blocks.state_trie_root_hash, | 1271 | 3 | CASE | 1272 | 3 | WHEN trie_node.partial_key IS NULL AND LENGTH(:key) = 0 | 1273 | 3 | THEN X'' -- Trie root node isn't in database, but since key is empty we have a match anyway | 1274 | 3 | WHEN trie_node.partial_key IS NULL AND LENGTH(:key) != 0 | 1275 | 3 | THEN NULL -- Trie root node isn't in database and we can't iterate further | 1276 | 3 | ELSE | 1277 | 3 | COALESCE(SUBSTR(:key, 1 + LENGTH(trie_node.partial_key)), X'') | 1278 | 3 | END | 1279 | 3 | FROM blocks | 1280 | 3 | LEFT JOIN trie_node ON blocks.state_trie_root_hash = trie_node.hash | 1281 | 3 | WHERE blocks.hash = :block_hash | 1282 | 3 | AND ( | 1283 | 3 | trie_node.partial_key IS NULL | 1284 | 3 | OR COALESCE(SUBSTR(trie_node.partial_key, 1, LENGTH(:key)), X'') = :key | 1285 | 3 | OR COALESCE(SUBSTR(:key, 1, LENGTH(trie_node.partial_key)), X'') = trie_node.partial_key | 1286 | 3 | ) | 1287 | 3 | | 1288 | 3 | UNION ALL | 1289 | 3 | SELECT | 1290 | 3 | COALESCE(trie_node_child.child_hash, trie_node_storage.trie_root_ref), | 1291 | 3 | CASE | 1292 | 3 | WHEN trie_node_child.child_hash IS NULL AND HEX(SUBSTR(closest_descendant.search_remain, 1, 1)) != '10' | 1293 | 3 | THEN X'' -- No child matching the key. | 1294 | 3 | WHEN trie_node_child.child_hash IS NOT NULL AND trie_node.hash IS NULL AND LENGTH(closest_descendant.search_remain) = 1 | 1295 | 3 | THEN X'' -- Descendant node not in trie but we know that it's the result. | 1296 | 3 | WHEN trie_node_child.child_hash IS NOT NULL AND trie_node.hash IS NULL | 1297 | 3 | THEN NULL -- Descendant node not in trie. | 1298 | 3 | WHEN COALESCE(SUBSTR(trie_node.partial_key, 1, LENGTH(closest_descendant.search_remain) - 1), X'') = COALESCE(SUBSTR(closest_descendant.search_remain, 2), X'') | 1299 | 3 | OR COALESCE(SUBSTR(closest_descendant.search_remain, 2, LENGTH(trie_node.partial_key)), X'') = trie_node.partial_key | 1300 | 3 | THEN SUBSTR(closest_descendant.search_remain, 2 + LENGTH(trie_node.partial_key)) | 1301 | 3 | ELSE | 1302 | 3 | X'' -- Unreachable. | 1303 | 3 | END | 1304 | 3 | FROM closest_descendant | 1305 | 3 | LEFT JOIN trie_node_child ON closest_descendant.node_hash = trie_node_child.hash | 1306 | 3 | AND SUBSTR(closest_descendant.search_remain, 1, 1) = trie_node_child.child_num | 1307 | 3 | LEFT JOIN trie_node ON trie_node.hash = trie_node_child.child_hash | 1308 | 3 | LEFT JOIN trie_node_storage | 1309 | 3 | ON closest_descendant.node_hash = trie_node_storage.node_hash | 1310 | 3 | AND HEX(SUBSTR(closest_descendant.search_remain, 1, 1)) = '10' | 1311 | 3 | AND trie_node_storage.trie_root_ref IS NOT NULL | 1312 | 3 | WHERE | 1313 | 3 | LENGTH(closest_descendant.search_remain) >= 1 | 1314 | 3 | AND ( | 1315 | 3 | trie_node.hash IS NULL | 1316 | 3 | OR COALESCE(SUBSTR(trie_node.partial_key, 1, LENGTH(closest_descendant.search_remain) - 1), X'') = COALESCE(SUBSTR(closest_descendant.search_remain, 2), X'') | 1317 | 3 | OR COALESCE(SUBSTR(closest_descendant.search_remain, 2, LENGTH(trie_node.partial_key)), X'') = trie_node.partial_key | 1318 | 3 | ) | 1319 | 3 | ) | 1320 | 3 | SELECT COUNT(blocks.hash) >= 1, closest_descendant.node_hash IS NOT NULL AND closest_descendant.search_remain IS NULL, closest_descendant.node_hash | 1321 | 3 | FROM blocks | 1322 | 3 | LEFT JOIN closest_descendant ON LENGTH(closest_descendant.search_remain) = 0 OR closest_descendant.search_remain IS NULL | 1323 | 3 | WHERE blocks.hash = :block_hash | 1324 | 3 | LIMIT 1"#, | 1325 | 3 | ) | 1326 | 3 | .map_err(|err| { | 1327 | | StorageAccessError::Corrupted(CorruptedError::Internal( | 1328 | | InternalError(err), | 1329 | | )) | 1330 | 3 | })?0 ; | 1331 | | | 1332 | | // In order to debug the SQL query above (for example in case of a failing test), | 1333 | | // uncomment this block: | 1334 | | // | 1335 | | /*println!("{:?}", { | 1336 | | let mut statement = connection | 1337 | | .prepare_cached( | 1338 | | r#" | 1339 | | WITH RECURSIVE | 1340 | | copy-paste the definition of closest_descendant here | 1341 | | | 1342 | | SELECT * FROM closest_descendant"#).unwrap(); | 1343 | | statement | 1344 | | .query_map( | 1345 | | rusqlite::named_params! { | 1346 | | ":block_hash": &block_hash[..], | 1347 | | ":key": key_vectored, | 1348 | | }, | 1349 | | |row| { | 1350 | | let node_hash = row.get::<_, Option<Vec<u8>>>(0)?.map(hex::encode); | 1351 | | let search_remain = row.get::<_, Option<Vec<u8>>>(1)?; | 1352 | | Ok((node_hash, search_remain)) | 1353 | | }, | 1354 | | ) | 1355 | | .unwrap() | 1356 | | .collect::<Vec<_>>() | 1357 | | });*/ | 1358 | | | 1359 | 3 | let (has_block, incomplete_storage, merkle_value) = statement | 1360 | 3 | .query_row( | 1361 | 3 | rusqlite::named_params! { | 1362 | 3 | ":block_hash": &block_hash[..], | 1363 | 3 | ":key": key_vectored, | 1364 | 3 | }, | 1365 | 3 | |row| { | 1366 | | let has_block = row.get::<_, i64>(0)? != 0; | 1367 | | let incomplete_storage = row.get::<_, i64>(1)? != 0; | 1368 | | let merkle_value = row.get::<_, Option<Vec<u8>>>(2)?; | 1369 | | Ok((has_block, incomplete_storage, merkle_value)) | 1370 | 3 | }, | 1371 | 3 | ) | 1372 | 3 | .map_err(|err| { | 1373 | | StorageAccessError::Corrupted(CorruptedError::Internal(InternalError(err))) | 1374 | 3 | })?0 ; | 1375 | | | 1376 | 3 | if !has_block { | 1377 | 0 | return Err(StorageAccessError::UnknownBlock); | 1378 | 3 | } | 1379 | 3 | | 1380 | 3 | if incomplete_storage { | 1381 | 1 | return Err(StorageAccessError::IncompleteStorage); | 1382 | 2 | } | 1383 | 2 | | 1384 | 2 | Ok(merkle_value) | 1385 | 3 | } |
_RINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1Z_B1Y_EINtNtNtB27_5array4iter8IntoIterhKj3_EEB7_ Line | Count | Source | 1239 | 4 | pub fn block_storage_closest_descendant_merkle_value( | 1240 | 4 | &self, | 1241 | 4 | block_hash: &[u8; 32], | 1242 | 4 | parent_tries_paths_nibbles: impl Iterator<Item = impl Iterator<Item = u8>>, | 1243 | 4 | key_nibbles: impl Iterator<Item = u8>, | 1244 | 4 | ) -> Result<Option<Vec<u8>>, StorageAccessError> { | 1245 | 4 | // Process the iterators at the very beginning and before locking the database, in order | 1246 | 4 | // to avoid a deadlock in case the `next()` function of one of the iterators accesses | 1247 | 4 | // the database as well. | 1248 | 4 | let key_vectored = parent_tries_paths_nibbles | 1249 | 4 | .flat_map(|t| t.inspect(|n| assert!(*n < 16)).chain(iter::once(0x10))) | 1250 | 4 | .chain(key_nibbles.inspect(|n| assert!(*n < 16))) | 1251 | 4 | .collect::<Vec<_>>(); | 1252 | 4 | | 1253 | 4 | let connection = self.database.lock(); | 1254 | | | 1255 | | // TODO: trie_root_ref system untested | 1256 | | // TODO: infinite loop if there's a loop in the trie; detect this | 1257 | 4 | let mut statement = connection | 1258 | 4 | .prepare_cached( | 1259 | 4 | r#" | 1260 | 4 | WITH RECURSIVE | 1261 | 4 | -- At the end of the recursive statement, `closest_descendant` must always contain | 1262 | 4 | -- at most one item where `search_remain` is either empty or null. Empty | 1263 | 4 | -- indicates that we have found a match, while null means that the search has | 1264 | 4 | -- been interrupted due to a storage entry not being in the database. If | 1265 | 4 | -- `search_remain` is null, then `node_hash` is irrelevant. | 1266 | 4 | -- If `closest_descendant` doesn't have any entry where `search_remain` is empty | 1267 | 4 | -- or null, then the request key doesn't have any descendant. | 1268 | 4 | closest_descendant(node_hash, search_remain) AS ( | 1269 | 4 | SELECT | 1270 | 4 | blocks.state_trie_root_hash, | 1271 | 4 | CASE | 1272 | 4 | WHEN trie_node.partial_key IS NULL AND LENGTH(:key) = 0 | 1273 | 4 | THEN X'' -- Trie root node isn't in database, but since key is empty we have a match anyway | 1274 | 4 | WHEN trie_node.partial_key IS NULL AND LENGTH(:key) != 0 | 1275 | 4 | THEN NULL -- Trie root node isn't in database and we can't iterate further | 1276 | 4 | ELSE | 1277 | 4 | COALESCE(SUBSTR(:key, 1 + LENGTH(trie_node.partial_key)), X'') | 1278 | 4 | END | 1279 | 4 | FROM blocks | 1280 | 4 | LEFT JOIN trie_node ON blocks.state_trie_root_hash = trie_node.hash | 1281 | 4 | WHERE blocks.hash = :block_hash | 1282 | 4 | AND ( | 1283 | 4 | trie_node.partial_key IS NULL | 1284 | 4 | OR COALESCE(SUBSTR(trie_node.partial_key, 1, LENGTH(:key)), X'') = :key | 1285 | 4 | OR COALESCE(SUBSTR(:key, 1, LENGTH(trie_node.partial_key)), X'') = trie_node.partial_key | 1286 | 4 | ) | 1287 | 4 | | 1288 | 4 | UNION ALL | 1289 | 4 | SELECT | 1290 | 4 | COALESCE(trie_node_child.child_hash, trie_node_storage.trie_root_ref), | 1291 | 4 | CASE | 1292 | 4 | WHEN trie_node_child.child_hash IS NULL AND HEX(SUBSTR(closest_descendant.search_remain, 1, 1)) != '10' | 1293 | 4 | THEN X'' -- No child matching the key. | 1294 | 4 | WHEN trie_node_child.child_hash IS NOT NULL AND trie_node.hash IS NULL AND LENGTH(closest_descendant.search_remain) = 1 | 1295 | 4 | THEN X'' -- Descendant node not in trie but we know that it's the result. | 1296 | 4 | WHEN trie_node_child.child_hash IS NOT NULL AND trie_node.hash IS NULL | 1297 | 4 | THEN NULL -- Descendant node not in trie. | 1298 | 4 | WHEN COALESCE(SUBSTR(trie_node.partial_key, 1, LENGTH(closest_descendant.search_remain) - 1), X'') = COALESCE(SUBSTR(closest_descendant.search_remain, 2), X'') | 1299 | 4 | OR COALESCE(SUBSTR(closest_descendant.search_remain, 2, LENGTH(trie_node.partial_key)), X'') = trie_node.partial_key | 1300 | 4 | THEN SUBSTR(closest_descendant.search_remain, 2 + LENGTH(trie_node.partial_key)) | 1301 | 4 | ELSE | 1302 | 4 | X'' -- Unreachable. | 1303 | 4 | END | 1304 | 4 | FROM closest_descendant | 1305 | 4 | LEFT JOIN trie_node_child ON closest_descendant.node_hash = trie_node_child.hash | 1306 | 4 | AND SUBSTR(closest_descendant.search_remain, 1, 1) = trie_node_child.child_num | 1307 | 4 | LEFT JOIN trie_node ON trie_node.hash = trie_node_child.child_hash | 1308 | 4 | LEFT JOIN trie_node_storage | 1309 | 4 | ON closest_descendant.node_hash = trie_node_storage.node_hash | 1310 | 4 | AND HEX(SUBSTR(closest_descendant.search_remain, 1, 1)) = '10' | 1311 | 4 | AND trie_node_storage.trie_root_ref IS NOT NULL | 1312 | 4 | WHERE | 1313 | 4 | LENGTH(closest_descendant.search_remain) >= 1 | 1314 | 4 | AND ( | 1315 | 4 | trie_node.hash IS NULL | 1316 | 4 | OR COALESCE(SUBSTR(trie_node.partial_key, 1, LENGTH(closest_descendant.search_remain) - 1), X'') = COALESCE(SUBSTR(closest_descendant.search_remain, 2), X'') | 1317 | 4 | OR COALESCE(SUBSTR(closest_descendant.search_remain, 2, LENGTH(trie_node.partial_key)), X'') = trie_node.partial_key | 1318 | 4 | ) | 1319 | 4 | ) | 1320 | 4 | SELECT COUNT(blocks.hash) >= 1, closest_descendant.node_hash IS NOT NULL AND closest_descendant.search_remain IS NULL, closest_descendant.node_hash | 1321 | 4 | FROM blocks | 1322 | 4 | LEFT JOIN closest_descendant ON LENGTH(closest_descendant.search_remain) = 0 OR closest_descendant.search_remain IS NULL | 1323 | 4 | WHERE blocks.hash = :block_hash | 1324 | 4 | LIMIT 1"#, | 1325 | 4 | ) | 1326 | 4 | .map_err(|err| { | 1327 | | StorageAccessError::Corrupted(CorruptedError::Internal( | 1328 | | InternalError(err), | 1329 | | )) | 1330 | 4 | })?0 ; | 1331 | | | 1332 | | // In order to debug the SQL query above (for example in case of a failing test), | 1333 | | // uncomment this block: | 1334 | | // | 1335 | | /*println!("{:?}", { | 1336 | | let mut statement = connection | 1337 | | .prepare_cached( | 1338 | | r#" | 1339 | | WITH RECURSIVE | 1340 | | copy-paste the definition of closest_descendant here | 1341 | | | 1342 | | SELECT * FROM closest_descendant"#).unwrap(); | 1343 | | statement | 1344 | | .query_map( | 1345 | | rusqlite::named_params! { | 1346 | | ":block_hash": &block_hash[..], | 1347 | | ":key": key_vectored, | 1348 | | }, | 1349 | | |row| { | 1350 | | let node_hash = row.get::<_, Option<Vec<u8>>>(0)?.map(hex::encode); | 1351 | | let search_remain = row.get::<_, Option<Vec<u8>>>(1)?; | 1352 | | Ok((node_hash, search_remain)) | 1353 | | }, | 1354 | | ) | 1355 | | .unwrap() | 1356 | | .collect::<Vec<_>>() | 1357 | | });*/ | 1358 | | | 1359 | 4 | let (has_block, incomplete_storage, merkle_value) = statement | 1360 | 4 | .query_row( | 1361 | 4 | rusqlite::named_params! { | 1362 | 4 | ":block_hash": &block_hash[..], | 1363 | 4 | ":key": key_vectored, | 1364 | 4 | }, | 1365 | 4 | |row| { | 1366 | | let has_block = row.get::<_, i64>(0)? != 0; | 1367 | | let incomplete_storage = row.get::<_, i64>(1)? != 0; | 1368 | | let merkle_value = row.get::<_, Option<Vec<u8>>>(2)?; | 1369 | | Ok((has_block, incomplete_storage, merkle_value)) | 1370 | 4 | }, | 1371 | 4 | ) | 1372 | 4 | .map_err(|err| { | 1373 | | StorageAccessError::Corrupted(CorruptedError::Internal(InternalError(err))) | 1374 | 4 | })?0 ; | 1375 | | | 1376 | 4 | if !has_block { | 1377 | 0 | return Err(StorageAccessError::UnknownBlock); | 1378 | 4 | } | 1379 | 4 | | 1380 | 4 | if incomplete_storage { | 1381 | 0 | return Err(StorageAccessError::IncompleteStorage); | 1382 | 4 | } | 1383 | 4 | | 1384 | 4 | Ok(merkle_value) | 1385 | 4 | } |
_RINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1Z_B1Y_EINtNtNtB27_5array4iter8IntoIterhKj4_EEB7_ Line | Count | Source | 1239 | 3 | pub fn block_storage_closest_descendant_merkle_value( | 1240 | 3 | &self, | 1241 | 3 | block_hash: &[u8; 32], | 1242 | 3 | parent_tries_paths_nibbles: impl Iterator<Item = impl Iterator<Item = u8>>, | 1243 | 3 | key_nibbles: impl Iterator<Item = u8>, | 1244 | 3 | ) -> Result<Option<Vec<u8>>, StorageAccessError> { | 1245 | 3 | // Process the iterators at the very beginning and before locking the database, in order | 1246 | 3 | // to avoid a deadlock in case the `next()` function of one of the iterators accesses | 1247 | 3 | // the database as well. | 1248 | 3 | let key_vectored = parent_tries_paths_nibbles | 1249 | 3 | .flat_map(|t| t.inspect(|n| assert!(*n < 16)).chain(iter::once(0x10))) | 1250 | 3 | .chain(key_nibbles.inspect(|n| assert!(*n < 16))) | 1251 | 3 | .collect::<Vec<_>>(); | 1252 | 3 | | 1253 | 3 | let connection = self.database.lock(); | 1254 | | | 1255 | | // TODO: trie_root_ref system untested | 1256 | | // TODO: infinite loop if there's a loop in the trie; detect this | 1257 | 3 | let mut statement = connection | 1258 | 3 | .prepare_cached( | 1259 | 3 | r#" | 1260 | 3 | WITH RECURSIVE | 1261 | 3 | -- At the end of the recursive statement, `closest_descendant` must always contain | 1262 | 3 | -- at most one item where `search_remain` is either empty or null. Empty | 1263 | 3 | -- indicates that we have found a match, while null means that the search has | 1264 | 3 | -- been interrupted due to a storage entry not being in the database. If | 1265 | 3 | -- `search_remain` is null, then `node_hash` is irrelevant. | 1266 | 3 | -- If `closest_descendant` doesn't have any entry where `search_remain` is empty | 1267 | 3 | -- or null, then the request key doesn't have any descendant. | 1268 | 3 | closest_descendant(node_hash, search_remain) AS ( | 1269 | 3 | SELECT | 1270 | 3 | blocks.state_trie_root_hash, | 1271 | 3 | CASE | 1272 | 3 | WHEN trie_node.partial_key IS NULL AND LENGTH(:key) = 0 | 1273 | 3 | THEN X'' -- Trie root node isn't in database, but since key is empty we have a match anyway | 1274 | 3 | WHEN trie_node.partial_key IS NULL AND LENGTH(:key) != 0 | 1275 | 3 | THEN NULL -- Trie root node isn't in database and we can't iterate further | 1276 | 3 | ELSE | 1277 | 3 | COALESCE(SUBSTR(:key, 1 + LENGTH(trie_node.partial_key)), X'') | 1278 | 3 | END | 1279 | 3 | FROM blocks | 1280 | 3 | LEFT JOIN trie_node ON blocks.state_trie_root_hash = trie_node.hash | 1281 | 3 | WHERE blocks.hash = :block_hash | 1282 | 3 | AND ( | 1283 | 3 | trie_node.partial_key IS NULL | 1284 | 3 | OR COALESCE(SUBSTR(trie_node.partial_key, 1, LENGTH(:key)), X'') = :key | 1285 | 3 | OR COALESCE(SUBSTR(:key, 1, LENGTH(trie_node.partial_key)), X'') = trie_node.partial_key | 1286 | 3 | ) | 1287 | 3 | | 1288 | 3 | UNION ALL | 1289 | 3 | SELECT | 1290 | 3 | COALESCE(trie_node_child.child_hash, trie_node_storage.trie_root_ref), | 1291 | 3 | CASE | 1292 | 3 | WHEN trie_node_child.child_hash IS NULL AND HEX(SUBSTR(closest_descendant.search_remain, 1, 1)) != '10' | 1293 | 3 | THEN X'' -- No child matching the key. | 1294 | 3 | WHEN trie_node_child.child_hash IS NOT NULL AND trie_node.hash IS NULL AND LENGTH(closest_descendant.search_remain) = 1 | 1295 | 3 | THEN X'' -- Descendant node not in trie but we know that it's the result. | 1296 | 3 | WHEN trie_node_child.child_hash IS NOT NULL AND trie_node.hash IS NULL | 1297 | 3 | THEN NULL -- Descendant node not in trie. | 1298 | 3 | WHEN COALESCE(SUBSTR(trie_node.partial_key, 1, LENGTH(closest_descendant.search_remain) - 1), X'') = COALESCE(SUBSTR(closest_descendant.search_remain, 2), X'') | 1299 | 3 | OR COALESCE(SUBSTR(closest_descendant.search_remain, 2, LENGTH(trie_node.partial_key)), X'') = trie_node.partial_key | 1300 | 3 | THEN SUBSTR(closest_descendant.search_remain, 2 + LENGTH(trie_node.partial_key)) | 1301 | 3 | ELSE | 1302 | 3 | X'' -- Unreachable. | 1303 | 3 | END | 1304 | 3 | FROM closest_descendant | 1305 | 3 | LEFT JOIN trie_node_child ON closest_descendant.node_hash = trie_node_child.hash | 1306 | 3 | AND SUBSTR(closest_descendant.search_remain, 1, 1) = trie_node_child.child_num | 1307 | 3 | LEFT JOIN trie_node ON trie_node.hash = trie_node_child.child_hash | 1308 | 3 | LEFT JOIN trie_node_storage | 1309 | 3 | ON closest_descendant.node_hash = trie_node_storage.node_hash | 1310 | 3 | AND HEX(SUBSTR(closest_descendant.search_remain, 1, 1)) = '10' | 1311 | 3 | AND trie_node_storage.trie_root_ref IS NOT NULL | 1312 | 3 | WHERE | 1313 | 3 | LENGTH(closest_descendant.search_remain) >= 1 | 1314 | 3 | AND ( | 1315 | 3 | trie_node.hash IS NULL | 1316 | 3 | OR COALESCE(SUBSTR(trie_node.partial_key, 1, LENGTH(closest_descendant.search_remain) - 1), X'') = COALESCE(SUBSTR(closest_descendant.search_remain, 2), X'') | 1317 | 3 | OR COALESCE(SUBSTR(closest_descendant.search_remain, 2, LENGTH(trie_node.partial_key)), X'') = trie_node.partial_key | 1318 | 3 | ) | 1319 | 3 | ) | 1320 | 3 | SELECT COUNT(blocks.hash) >= 1, closest_descendant.node_hash IS NOT NULL AND closest_descendant.search_remain IS NULL, closest_descendant.node_hash | 1321 | 3 | FROM blocks | 1322 | 3 | LEFT JOIN closest_descendant ON LENGTH(closest_descendant.search_remain) = 0 OR closest_descendant.search_remain IS NULL | 1323 | 3 | WHERE blocks.hash = :block_hash | 1324 | 3 | LIMIT 1"#, | 1325 | 3 | ) | 1326 | 3 | .map_err(|err| { | 1327 | | StorageAccessError::Corrupted(CorruptedError::Internal( | 1328 | | InternalError(err), | 1329 | | )) | 1330 | 3 | })?0 ; | 1331 | | | 1332 | | // In order to debug the SQL query above (for example in case of a failing test), | 1333 | | // uncomment this block: | 1334 | | // | 1335 | | /*println!("{:?}", { | 1336 | | let mut statement = connection | 1337 | | .prepare_cached( | 1338 | | r#" | 1339 | | WITH RECURSIVE | 1340 | | copy-paste the definition of closest_descendant here | 1341 | | | 1342 | | SELECT * FROM closest_descendant"#).unwrap(); | 1343 | | statement | 1344 | | .query_map( | 1345 | | rusqlite::named_params! { | 1346 | | ":block_hash": &block_hash[..], | 1347 | | ":key": key_vectored, | 1348 | | }, | 1349 | | |row| { | 1350 | | let node_hash = row.get::<_, Option<Vec<u8>>>(0)?.map(hex::encode); | 1351 | | let search_remain = row.get::<_, Option<Vec<u8>>>(1)?; | 1352 | | Ok((node_hash, search_remain)) | 1353 | | }, | 1354 | | ) | 1355 | | .unwrap() | 1356 | | .collect::<Vec<_>>() | 1357 | | });*/ | 1358 | | | 1359 | 3 | let (has_block, incomplete_storage, merkle_value) = statement | 1360 | 3 | .query_row( | 1361 | 3 | rusqlite::named_params! { | 1362 | 3 | ":block_hash": &block_hash[..], | 1363 | 3 | ":key": key_vectored, | 1364 | 3 | }, | 1365 | 3 | |row| { | 1366 | | let has_block = row.get::<_, i64>(0)? != 0; | 1367 | | let incomplete_storage = row.get::<_, i64>(1)? != 0; | 1368 | | let merkle_value = row.get::<_, Option<Vec<u8>>>(2)?; | 1369 | | Ok((has_block, incomplete_storage, merkle_value)) | 1370 | 3 | }, | 1371 | 3 | ) | 1372 | 3 | .map_err(|err| { | 1373 | | StorageAccessError::Corrupted(CorruptedError::Internal(InternalError(err))) | 1374 | 3 | })?0 ; | 1375 | | | 1376 | 3 | if !has_block { | 1377 | 0 | return Err(StorageAccessError::UnknownBlock); | 1378 | 3 | } | 1379 | 3 | | 1380 | 3 | if incomplete_storage { | 1381 | 1 | return Err(StorageAccessError::IncompleteStorage); | 1382 | 2 | } | 1383 | 2 | | 1384 | 2 | Ok(merkle_value) | 1385 | 3 | } |
_RINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1Z_B1Y_EINtNtNtB27_5array4iter8IntoIterhKj5_EEB7_ Line | Count | Source | 1239 | 1 | pub fn block_storage_closest_descendant_merkle_value( | 1240 | 1 | &self, | 1241 | 1 | block_hash: &[u8; 32], | 1242 | 1 | parent_tries_paths_nibbles: impl Iterator<Item = impl Iterator<Item = u8>>, | 1243 | 1 | key_nibbles: impl Iterator<Item = u8>, | 1244 | 1 | ) -> Result<Option<Vec<u8>>, StorageAccessError> { | 1245 | 1 | // Process the iterators at the very beginning and before locking the database, in order | 1246 | 1 | // to avoid a deadlock in case the `next()` function of one of the iterators accesses | 1247 | 1 | // the database as well. | 1248 | 1 | let key_vectored = parent_tries_paths_nibbles | 1249 | 1 | .flat_map(|t| t.inspect(|n| assert!(*n < 16)).chain(iter::once(0x10))) | 1250 | 1 | .chain(key_nibbles.inspect(|n| assert!(*n < 16))) | 1251 | 1 | .collect::<Vec<_>>(); | 1252 | 1 | | 1253 | 1 | let connection = self.database.lock(); | 1254 | | | 1255 | | // TODO: trie_root_ref system untested | 1256 | | // TODO: infinite loop if there's a loop in the trie; detect this | 1257 | 1 | let mut statement = connection | 1258 | 1 | .prepare_cached( | 1259 | 1 | r#" | 1260 | 1 | WITH RECURSIVE | 1261 | 1 | -- At the end of the recursive statement, `closest_descendant` must always contain | 1262 | 1 | -- at most one item where `search_remain` is either empty or null. Empty | 1263 | 1 | -- indicates that we have found a match, while null means that the search has | 1264 | 1 | -- been interrupted due to a storage entry not being in the database. If | 1265 | 1 | -- `search_remain` is null, then `node_hash` is irrelevant. | 1266 | 1 | -- If `closest_descendant` doesn't have any entry where `search_remain` is empty | 1267 | 1 | -- or null, then the request key doesn't have any descendant. | 1268 | 1 | closest_descendant(node_hash, search_remain) AS ( | 1269 | 1 | SELECT | 1270 | 1 | blocks.state_trie_root_hash, | 1271 | 1 | CASE | 1272 | 1 | WHEN trie_node.partial_key IS NULL AND LENGTH(:key) = 0 | 1273 | 1 | THEN X'' -- Trie root node isn't in database, but since key is empty we have a match anyway | 1274 | 1 | WHEN trie_node.partial_key IS NULL AND LENGTH(:key) != 0 | 1275 | 1 | THEN NULL -- Trie root node isn't in database and we can't iterate further | 1276 | 1 | ELSE | 1277 | 1 | COALESCE(SUBSTR(:key, 1 + LENGTH(trie_node.partial_key)), X'') | 1278 | 1 | END | 1279 | 1 | FROM blocks | 1280 | 1 | LEFT JOIN trie_node ON blocks.state_trie_root_hash = trie_node.hash | 1281 | 1 | WHERE blocks.hash = :block_hash | 1282 | 1 | AND ( | 1283 | 1 | trie_node.partial_key IS NULL | 1284 | 1 | OR COALESCE(SUBSTR(trie_node.partial_key, 1, LENGTH(:key)), X'') = :key | 1285 | 1 | OR COALESCE(SUBSTR(:key, 1, LENGTH(trie_node.partial_key)), X'') = trie_node.partial_key | 1286 | 1 | ) | 1287 | 1 | | 1288 | 1 | UNION ALL | 1289 | 1 | SELECT | 1290 | 1 | COALESCE(trie_node_child.child_hash, trie_node_storage.trie_root_ref), | 1291 | 1 | CASE | 1292 | 1 | WHEN trie_node_child.child_hash IS NULL AND HEX(SUBSTR(closest_descendant.search_remain, 1, 1)) != '10' | 1293 | 1 | THEN X'' -- No child matching the key. | 1294 | 1 | WHEN trie_node_child.child_hash IS NOT NULL AND trie_node.hash IS NULL AND LENGTH(closest_descendant.search_remain) = 1 | 1295 | 1 | THEN X'' -- Descendant node not in trie but we know that it's the result. | 1296 | 1 | WHEN trie_node_child.child_hash IS NOT NULL AND trie_node.hash IS NULL | 1297 | 1 | THEN NULL -- Descendant node not in trie. | 1298 | 1 | WHEN COALESCE(SUBSTR(trie_node.partial_key, 1, LENGTH(closest_descendant.search_remain) - 1), X'') = COALESCE(SUBSTR(closest_descendant.search_remain, 2), X'') | 1299 | 1 | OR COALESCE(SUBSTR(closest_descendant.search_remain, 2, LENGTH(trie_node.partial_key)), X'') = trie_node.partial_key | 1300 | 1 | THEN SUBSTR(closest_descendant.search_remain, 2 + LENGTH(trie_node.partial_key)) | 1301 | 1 | ELSE | 1302 | 1 | X'' -- Unreachable. | 1303 | 1 | END | 1304 | 1 | FROM closest_descendant | 1305 | 1 | LEFT JOIN trie_node_child ON closest_descendant.node_hash = trie_node_child.hash | 1306 | 1 | AND SUBSTR(closest_descendant.search_remain, 1, 1) = trie_node_child.child_num | 1307 | 1 | LEFT JOIN trie_node ON trie_node.hash = trie_node_child.child_hash | 1308 | 1 | LEFT JOIN trie_node_storage | 1309 | 1 | ON closest_descendant.node_hash = trie_node_storage.node_hash | 1310 | 1 | AND HEX(SUBSTR(closest_descendant.search_remain, 1, 1)) = '10' | 1311 | 1 | AND trie_node_storage.trie_root_ref IS NOT NULL | 1312 | 1 | WHERE | 1313 | 1 | LENGTH(closest_descendant.search_remain) >= 1 | 1314 | 1 | AND ( | 1315 | 1 | trie_node.hash IS NULL | 1316 | 1 | OR COALESCE(SUBSTR(trie_node.partial_key, 1, LENGTH(closest_descendant.search_remain) - 1), X'') = COALESCE(SUBSTR(closest_descendant.search_remain, 2), X'') | 1317 | 1 | OR COALESCE(SUBSTR(closest_descendant.search_remain, 2, LENGTH(trie_node.partial_key)), X'') = trie_node.partial_key | 1318 | 1 | ) | 1319 | 1 | ) | 1320 | 1 | SELECT COUNT(blocks.hash) >= 1, closest_descendant.node_hash IS NOT NULL AND closest_descendant.search_remain IS NULL, closest_descendant.node_hash | 1321 | 1 | FROM blocks | 1322 | 1 | LEFT JOIN closest_descendant ON LENGTH(closest_descendant.search_remain) = 0 OR closest_descendant.search_remain IS NULL | 1323 | 1 | WHERE blocks.hash = :block_hash | 1324 | 1 | LIMIT 1"#, | 1325 | 1 | ) | 1326 | 1 | .map_err(|err| { | 1327 | | StorageAccessError::Corrupted(CorruptedError::Internal( | 1328 | | InternalError(err), | 1329 | | )) | 1330 | 1 | })?0 ; | 1331 | | | 1332 | | // In order to debug the SQL query above (for example in case of a failing test), | 1333 | | // uncomment this block: | 1334 | | // | 1335 | | /*println!("{:?}", { | 1336 | | let mut statement = connection | 1337 | | .prepare_cached( | 1338 | | r#" | 1339 | | WITH RECURSIVE | 1340 | | copy-paste the definition of closest_descendant here | 1341 | | | 1342 | | SELECT * FROM closest_descendant"#).unwrap(); | 1343 | | statement | 1344 | | .query_map( | 1345 | | rusqlite::named_params! { | 1346 | | ":block_hash": &block_hash[..], | 1347 | | ":key": key_vectored, | 1348 | | }, | 1349 | | |row| { | 1350 | | let node_hash = row.get::<_, Option<Vec<u8>>>(0)?.map(hex::encode); | 1351 | | let search_remain = row.get::<_, Option<Vec<u8>>>(1)?; | 1352 | | Ok((node_hash, search_remain)) | 1353 | | }, | 1354 | | ) | 1355 | | .unwrap() | 1356 | | .collect::<Vec<_>>() | 1357 | | });*/ | 1358 | | | 1359 | 1 | let (has_block, incomplete_storage, merkle_value) = statement | 1360 | 1 | .query_row( | 1361 | 1 | rusqlite::named_params! { | 1362 | 1 | ":block_hash": &block_hash[..], | 1363 | 1 | ":key": key_vectored, | 1364 | 1 | }, | 1365 | 1 | |row| { | 1366 | | let has_block = row.get::<_, i64>(0)? != 0; | 1367 | | let incomplete_storage = row.get::<_, i64>(1)? != 0; | 1368 | | let merkle_value = row.get::<_, Option<Vec<u8>>>(2)?; | 1369 | | Ok((has_block, incomplete_storage, merkle_value)) | 1370 | 1 | }, | 1371 | 1 | ) | 1372 | 1 | .map_err(|err| { | 1373 | | StorageAccessError::Corrupted(CorruptedError::Internal(InternalError(err))) | 1374 | 1 | })?0 ; | 1375 | | | 1376 | 1 | if !has_block { | 1377 | 0 | return Err(StorageAccessError::UnknownBlock); | 1378 | 1 | } | 1379 | 1 | | 1380 | 1 | if incomplete_storage { | 1381 | 1 | return Err(StorageAccessError::IncompleteStorage); | 1382 | 0 | } | 1383 | 0 |
| 1384 | 0 | Ok(merkle_value) | 1385 | 1 | } |
_RINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB1Z_B1Y_EINtNtNtB27_5array4iter8IntoIterhKj8_EEB7_ Line | Count | Source | 1239 | 1 | pub fn block_storage_closest_descendant_merkle_value( | 1240 | 1 | &self, | 1241 | 1 | block_hash: &[u8; 32], | 1242 | 1 | parent_tries_paths_nibbles: impl Iterator<Item = impl Iterator<Item = u8>>, | 1243 | 1 | key_nibbles: impl Iterator<Item = u8>, | 1244 | 1 | ) -> Result<Option<Vec<u8>>, StorageAccessError> { | 1245 | 1 | // Process the iterators at the very beginning and before locking the database, in order | 1246 | 1 | // to avoid a deadlock in case the `next()` function of one of the iterators accesses | 1247 | 1 | // the database as well. | 1248 | 1 | let key_vectored = parent_tries_paths_nibbles | 1249 | 1 | .flat_map(|t| t.inspect(|n| assert!(*n < 16)).chain(iter::once(0x10))) | 1250 | 1 | .chain(key_nibbles.inspect(|n| assert!(*n < 16))) | 1251 | 1 | .collect::<Vec<_>>(); | 1252 | 1 | | 1253 | 1 | let connection = self.database.lock(); | 1254 | | | 1255 | | // TODO: trie_root_ref system untested | 1256 | | // TODO: infinite loop if there's a loop in the trie; detect this | 1257 | 1 | let mut statement = connection | 1258 | 1 | .prepare_cached( | 1259 | 1 | r#" | 1260 | 1 | WITH RECURSIVE | 1261 | 1 | -- At the end of the recursive statement, `closest_descendant` must always contain | 1262 | 1 | -- at most one item where `search_remain` is either empty or null. Empty | 1263 | 1 | -- indicates that we have found a match, while null means that the search has | 1264 | 1 | -- been interrupted due to a storage entry not being in the database. If | 1265 | 1 | -- `search_remain` is null, then `node_hash` is irrelevant. | 1266 | 1 | -- If `closest_descendant` doesn't have any entry where `search_remain` is empty | 1267 | 1 | -- or null, then the request key doesn't have any descendant. | 1268 | 1 | closest_descendant(node_hash, search_remain) AS ( | 1269 | 1 | SELECT | 1270 | 1 | blocks.state_trie_root_hash, | 1271 | 1 | CASE | 1272 | 1 | WHEN trie_node.partial_key IS NULL AND LENGTH(:key) = 0 | 1273 | 1 | THEN X'' -- Trie root node isn't in database, but since key is empty we have a match anyway | 1274 | 1 | WHEN trie_node.partial_key IS NULL AND LENGTH(:key) != 0 | 1275 | 1 | THEN NULL -- Trie root node isn't in database and we can't iterate further | 1276 | 1 | ELSE | 1277 | 1 | COALESCE(SUBSTR(:key, 1 + LENGTH(trie_node.partial_key)), X'') | 1278 | 1 | END | 1279 | 1 | FROM blocks | 1280 | 1 | LEFT JOIN trie_node ON blocks.state_trie_root_hash = trie_node.hash | 1281 | 1 | WHERE blocks.hash = :block_hash | 1282 | 1 | AND ( | 1283 | 1 | trie_node.partial_key IS NULL | 1284 | 1 | OR COALESCE(SUBSTR(trie_node.partial_key, 1, LENGTH(:key)), X'') = :key | 1285 | 1 | OR COALESCE(SUBSTR(:key, 1, LENGTH(trie_node.partial_key)), X'') = trie_node.partial_key | 1286 | 1 | ) | 1287 | 1 | | 1288 | 1 | UNION ALL | 1289 | 1 | SELECT | 1290 | 1 | COALESCE(trie_node_child.child_hash, trie_node_storage.trie_root_ref), | 1291 | 1 | CASE | 1292 | 1 | WHEN trie_node_child.child_hash IS NULL AND HEX(SUBSTR(closest_descendant.search_remain, 1, 1)) != '10' | 1293 | 1 | THEN X'' -- No child matching the key. | 1294 | 1 | WHEN trie_node_child.child_hash IS NOT NULL AND trie_node.hash IS NULL AND LENGTH(closest_descendant.search_remain) = 1 | 1295 | 1 | THEN X'' -- Descendant node not in trie but we know that it's the result. | 1296 | 1 | WHEN trie_node_child.child_hash IS NOT NULL AND trie_node.hash IS NULL | 1297 | 1 | THEN NULL -- Descendant node not in trie. | 1298 | 1 | WHEN COALESCE(SUBSTR(trie_node.partial_key, 1, LENGTH(closest_descendant.search_remain) - 1), X'') = COALESCE(SUBSTR(closest_descendant.search_remain, 2), X'') | 1299 | 1 | OR COALESCE(SUBSTR(closest_descendant.search_remain, 2, LENGTH(trie_node.partial_key)), X'') = trie_node.partial_key | 1300 | 1 | THEN SUBSTR(closest_descendant.search_remain, 2 + LENGTH(trie_node.partial_key)) | 1301 | 1 | ELSE | 1302 | 1 | X'' -- Unreachable. | 1303 | 1 | END | 1304 | 1 | FROM closest_descendant | 1305 | 1 | LEFT JOIN trie_node_child ON closest_descendant.node_hash = trie_node_child.hash | 1306 | 1 | AND SUBSTR(closest_descendant.search_remain, 1, 1) = trie_node_child.child_num | 1307 | 1 | LEFT JOIN trie_node ON trie_node.hash = trie_node_child.child_hash | 1308 | 1 | LEFT JOIN trie_node_storage | 1309 | 1 | ON closest_descendant.node_hash = trie_node_storage.node_hash | 1310 | 1 | AND HEX(SUBSTR(closest_descendant.search_remain, 1, 1)) = '10' | 1311 | 1 | AND trie_node_storage.trie_root_ref IS NOT NULL | 1312 | 1 | WHERE | 1313 | 1 | LENGTH(closest_descendant.search_remain) >= 1 | 1314 | 1 | AND ( | 1315 | 1 | trie_node.hash IS NULL | 1316 | 1 | OR COALESCE(SUBSTR(trie_node.partial_key, 1, LENGTH(closest_descendant.search_remain) - 1), X'') = COALESCE(SUBSTR(closest_descendant.search_remain, 2), X'') | 1317 | 1 | OR COALESCE(SUBSTR(closest_descendant.search_remain, 2, LENGTH(trie_node.partial_key)), X'') = trie_node.partial_key | 1318 | 1 | ) | 1319 | 1 | ) | 1320 | 1 | SELECT COUNT(blocks.hash) >= 1, closest_descendant.node_hash IS NOT NULL AND closest_descendant.search_remain IS NULL, closest_descendant.node_hash | 1321 | 1 | FROM blocks | 1322 | 1 | LEFT JOIN closest_descendant ON LENGTH(closest_descendant.search_remain) = 0 OR closest_descendant.search_remain IS NULL | 1323 | 1 | WHERE blocks.hash = :block_hash | 1324 | 1 | LIMIT 1"#, | 1325 | 1 | ) | 1326 | 1 | .map_err(|err| { | 1327 | | StorageAccessError::Corrupted(CorruptedError::Internal( | 1328 | | InternalError(err), | 1329 | | )) | 1330 | 1 | })?0 ; | 1331 | | | 1332 | | // In order to debug the SQL query above (for example in case of a failing test), | 1333 | | // uncomment this block: | 1334 | | // | 1335 | | /*println!("{:?}", { | 1336 | | let mut statement = connection | 1337 | | .prepare_cached( | 1338 | | r#" | 1339 | | WITH RECURSIVE | 1340 | | copy-paste the definition of closest_descendant here | 1341 | | | 1342 | | SELECT * FROM closest_descendant"#).unwrap(); | 1343 | | statement | 1344 | | .query_map( | 1345 | | rusqlite::named_params! { | 1346 | | ":block_hash": &block_hash[..], | 1347 | | ":key": key_vectored, | 1348 | | }, | 1349 | | |row| { | 1350 | | let node_hash = row.get::<_, Option<Vec<u8>>>(0)?.map(hex::encode); | 1351 | | let search_remain = row.get::<_, Option<Vec<u8>>>(1)?; | 1352 | | Ok((node_hash, search_remain)) | 1353 | | }, | 1354 | | ) | 1355 | | .unwrap() | 1356 | | .collect::<Vec<_>>() | 1357 | | });*/ | 1358 | | | 1359 | 1 | let (has_block, incomplete_storage, merkle_value) = statement | 1360 | 1 | .query_row( | 1361 | 1 | rusqlite::named_params! { | 1362 | 1 | ":block_hash": &block_hash[..], | 1363 | 1 | ":key": key_vectored, | 1364 | 1 | }, | 1365 | 1 | |row| { | 1366 | | let has_block = row.get::<_, i64>(0)? != 0; | 1367 | | let incomplete_storage = row.get::<_, i64>(1)? != 0; | 1368 | | let merkle_value = row.get::<_, Option<Vec<u8>>>(2)?; | 1369 | | Ok((has_block, incomplete_storage, merkle_value)) | 1370 | 1 | }, | 1371 | 1 | ) | 1372 | 1 | .map_err(|err| { | 1373 | | StorageAccessError::Corrupted(CorruptedError::Internal(InternalError(err))) | 1374 | 1 | })?0 ; | 1375 | | | 1376 | 1 | if !has_block { | 1377 | 0 | return Err(StorageAccessError::UnknownBlock); | 1378 | 1 | } | 1379 | 1 | | 1380 | 1 | if incomplete_storage { | 1381 | 0 | return Err(StorageAccessError::IncompleteStorage); | 1382 | 1 | } | 1383 | 1 | | 1384 | 1 | Ok(merkle_value) | 1385 | 1 | } |
Unexecuted instantiation: _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyhEIB20_B1Z_EINtNtNtB26_8adapters3map3MapINtNtB37_7flatten7FlatMapIB3v_INtNtNtB28_5slice4iter4IterNtNtNtB7_8executor20trie_root_calculator14InProgressNodeEINtNtB37_5chain5ChainINtNtB24_4once4OnceINtCs1qmLyiTSqYF_6either6EitherANtNtNtB7_4trie6nibble6Nibblej1_RINtNtCsdZExvAaxgia_5alloc3vec3VecB6u_EEEINtNtB28_6option8IntoIterB5Y_EENCNvMs3_B4r_NtB4r_5Inner21current_node_full_key0EINtNvNtB7_4util11as_ref_iter4IterB5Y_B6u_EIB8Z_B6u_B5Y_EENvYhINtNtB28_7convert4FromB6u_E4fromEEB7_ Unexecuted instantiation: _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2Y_6option8IntoIterINtB24_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB4r_14SyncBackground12author_block0s2_00EINtNtB2U_6copied6CopiedINtNtNtB2Y_5slice4iter4IterhEEECsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2Y_6option8IntoIterINtB24_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s3_00EINtNtB2U_6copied6CopiedINtNtNtB2Y_5slice4iter4IterhEEECsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2Y_6option8IntoIterINtB24_3VechEENCNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sa_00EINtNtB2U_6copied6CopiedINtNtNtB2Y_5slice4iter4IterhEEEB4s_ Unexecuted instantiation: _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2Y_6option8IntoIterINtB24_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB4r_14SyncBackground12author_block0s2_00EINtNtB2U_6copied6CopiedINtNtNtB2Y_5slice4iter4IterhEEECscDgN54JpMGG_6author Unexecuted instantiation: _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2Y_6option8IntoIterINtB24_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s3_00EINtNtB2U_6copied6CopiedINtNtNtB2Y_5slice4iter4IterhEEECscDgN54JpMGG_6author Unexecuted instantiation: _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2Y_6option8IntoIterINtB24_3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB4r_14SyncBackground12author_block0s2_00EINtNtB2U_6copied6CopiedINtNtNtB2Y_5slice4iter4IterhEEECsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase45block_storage_closest_descendant_merkle_valueINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterhEINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtB2Y_6option8IntoIterINtB24_3VechEENCNCNCNvNtCsiUjFBJteJ7x_17smoldot_full_node17consensus_service12runtime_call0s3_00EINtNtB2U_6copied6CopiedINtNtNtB2Y_5slice4iter4IterhEEECsibGXYHQB8Ea_25json_rpc_general_requests |
1386 | | |
1387 | | /// Inserts a block in the database and sets it as the finalized block. |
1388 | | /// |
1389 | | /// The parent of the block doesn't need to be present in the database. |
1390 | | /// |
1391 | | /// If the block is already in the database, it is replaced by the one provided. |
1392 | 1.04k | pub fn reset<'a>( |
1393 | 1.04k | &self, |
1394 | 1.04k | finalized_block_header: &[u8], |
1395 | 1.04k | finalized_block_body: impl ExactSizeIterator<Item = &'a [u8]>, |
1396 | 1.04k | finalized_block_justification: Option<Vec<u8>>, |
1397 | 1.04k | ) -> Result<(), CorruptedError> { |
1398 | 1.04k | // Start a transaction to insert everything in one go. |
1399 | 1.04k | let mut database = self.database.lock(); |
1400 | 1.04k | let transaction = database |
1401 | 1.04k | .transaction() |
1402 | 1.04k | .map_err(|err| CorruptedError::Internal(InternalError(err))0 )?0 ; Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyRShEE0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetpE0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyRShEE0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtB1v_5slice4iter4IterINtNtCsdZExvAaxgia_5alloc3vec3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB3m_14SyncBackground14process_blocks0s_00EE0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyRShEE0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtB1v_5slice4iter4IterINtNtCsdZExvAaxgia_5alloc3vec3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB3m_14SyncBackground14process_blocks0s_00EE0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyRShEE0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtB1v_5slice4iter4IterINtNtCsdZExvAaxgia_5alloc3vec3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB3m_14SyncBackground14process_blocks0s_00EE0CsibGXYHQB8Ea_25json_rpc_general_requests |
1403 | | |
1404 | | // Temporarily disable foreign key checks in order to make the initial insertion easier, |
1405 | | // as we don't have to make sure that trie nodes are sorted. |
1406 | | // Note that this is immediately disabled again when we `COMMIT`. |
1407 | 1.04k | transaction |
1408 | 1.04k | .execute("PRAGMA defer_foreign_keys = ON", ()) |
1409 | 1.04k | .map_err(|err| CorruptedError::Internal(InternalError(err))0 )?0 ; Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyRShEEs_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetpEs_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyRShEEs_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtB1v_5slice4iter4IterINtNtCsdZExvAaxgia_5alloc3vec3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB3m_14SyncBackground14process_blocks0s_00EEs_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyRShEEs_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtB1v_5slice4iter4IterINtNtCsdZExvAaxgia_5alloc3vec3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB3m_14SyncBackground14process_blocks0s_00EEs_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyRShEEs_0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtB1v_5slice4iter4IterINtNtCsdZExvAaxgia_5alloc3vec3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB3m_14SyncBackground14process_blocks0s_00EEs_0CsibGXYHQB8Ea_25json_rpc_general_requests |
1410 | | |
1411 | 1.04k | let finalized_block_hash = header::hash_from_scale_encoded_header(finalized_block_header); |
1412 | 1.04k | // TODO: this module shouldn't decode blocks |
1413 | 1.04k | let decoded = header::decode(finalized_block_header, self.block_number_bytes).unwrap(); |
1414 | 1.04k | |
1415 | 1.04k | transaction |
1416 | 1.04k | .prepare_cached( |
1417 | 1.04k | "INSERT OR REPLACE INTO blocks(hash, parent_hash, state_trie_root_hash, number, header, is_best_chain, justification) VALUES(?, ?, ?, ?, ?, TRUE, ?)", |
1418 | 1.04k | ) |
1419 | 1.04k | .map_err(|err| CorruptedError::Internal(InternalError(err))0 )?0 Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyRShEEs0_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetpEs0_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyRShEEs0_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtB1v_5slice4iter4IterINtNtCsdZExvAaxgia_5alloc3vec3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB3m_14SyncBackground14process_blocks0s_00EEs0_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyRShEEs0_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtB1v_5slice4iter4IterINtNtCsdZExvAaxgia_5alloc3vec3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB3m_14SyncBackground14process_blocks0s_00EEs0_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyRShEEs0_0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtB1v_5slice4iter4IterINtNtCsdZExvAaxgia_5alloc3vec3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB3m_14SyncBackground14process_blocks0s_00EEs0_0CsibGXYHQB8Ea_25json_rpc_general_requests |
1420 | | .execute(( |
1421 | 1.04k | &finalized_block_hash[..], |
1422 | 1.04k | if decoded.number != 0 { |
1423 | 0 | Some(&decoded.parent_hash[..]) |
1424 | 1.04k | } else { None }, |
1425 | 1.04k | &decoded.state_root[..], |
1426 | 1.04k | i64::try_from(decoded.number).unwrap(), |
1427 | 1.04k | finalized_block_header, |
1428 | 1.04k | finalized_block_justification.as_deref(), |
1429 | 1.04k | )) |
1430 | 1.04k | .map_err(|err| CorruptedError::Internal(InternalError(err))0 )?0 ; Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyRShEEs1_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetpEs1_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyRShEEs1_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtB1v_5slice4iter4IterINtNtCsdZExvAaxgia_5alloc3vec3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB3m_14SyncBackground14process_blocks0s_00EEs1_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyRShEEs1_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtB1v_5slice4iter4IterINtNtCsdZExvAaxgia_5alloc3vec3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB3m_14SyncBackground14process_blocks0s_00EEs1_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyRShEEs1_0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtB1v_5slice4iter4IterINtNtCsdZExvAaxgia_5alloc3vec3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB3m_14SyncBackground14process_blocks0s_00EEs1_0CsibGXYHQB8Ea_25json_rpc_general_requests |
1431 | | |
1432 | 1.04k | transaction |
1433 | 1.04k | .execute( |
1434 | 1.04k | "DELETE FROM blocks_body WHERE hash = ?", |
1435 | 1.04k | (&finalized_block_hash[..],), |
1436 | 1.04k | ) |
1437 | 1.04k | .map_err(|err| CorruptedError::Internal(InternalError(err))0 )?0 ; Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyRShEEs2_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetpEs2_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyRShEEs2_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtB1v_5slice4iter4IterINtNtCsdZExvAaxgia_5alloc3vec3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB3m_14SyncBackground14process_blocks0s_00EEs2_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyRShEEs2_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtB1v_5slice4iter4IterINtNtCsdZExvAaxgia_5alloc3vec3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB3m_14SyncBackground14process_blocks0s_00EEs2_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyRShEEs2_0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtB1v_5slice4iter4IterINtNtCsdZExvAaxgia_5alloc3vec3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB3m_14SyncBackground14process_blocks0s_00EEs2_0CsibGXYHQB8Ea_25json_rpc_general_requests |
1438 | | |
1439 | | { |
1440 | 1.04k | let mut statement = transaction |
1441 | 1.04k | .prepare_cached( |
1442 | 1.04k | "INSERT OR IGNORE INTO blocks_body(hash, idx, extrinsic) VALUES(?, ?, ?)", |
1443 | 1.04k | ) |
1444 | 1.04k | .map_err(|err| CorruptedError::Internal(InternalError(err))0 )?0 ; Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyRShEEs3_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetpEs3_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyRShEEs3_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtB1v_5slice4iter4IterINtNtCsdZExvAaxgia_5alloc3vec3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB3m_14SyncBackground14process_blocks0s_00EEs3_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyRShEEs3_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtB1v_5slice4iter4IterINtNtCsdZExvAaxgia_5alloc3vec3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB3m_14SyncBackground14process_blocks0s_00EEs3_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyRShEEs3_0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtB1v_5slice4iter4IterINtNtCsdZExvAaxgia_5alloc3vec3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB3m_14SyncBackground14process_blocks0s_00EEs3_0CsibGXYHQB8Ea_25json_rpc_general_requests |
1445 | 1.04k | for (index, item0 ) in finalized_block_body.enumerate() { |
1446 | 0 | statement |
1447 | 0 | .execute(( |
1448 | 0 | &finalized_block_hash[..], |
1449 | 0 | i64::try_from(index).unwrap(), |
1450 | 0 | item, |
1451 | 0 | )) |
1452 | 0 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?; Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyRShEEs4_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetpEs4_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyRShEEs4_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtB1v_5slice4iter4IterINtNtCsdZExvAaxgia_5alloc3vec3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB3m_14SyncBackground14process_blocks0s_00EEs4_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyRShEEs4_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtB1v_5slice4iter4IterINtNtCsdZExvAaxgia_5alloc3vec3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB3m_14SyncBackground14process_blocks0s_00EEs4_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyRShEEs4_0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtB1v_5slice4iter4IterINtNtCsdZExvAaxgia_5alloc3vec3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB3m_14SyncBackground14process_blocks0s_00EEs4_0CsibGXYHQB8Ea_25json_rpc_general_requests |
1453 | | } |
1454 | | } |
1455 | | |
1456 | 1.04k | meta_set_blob(&transaction, "best", &finalized_block_hash[..])?0 ; |
1457 | 1.04k | meta_set_number(&transaction, "finalized", decoded.number)?0 ; |
1458 | | |
1459 | 1.04k | transaction |
1460 | 1.04k | .commit() |
1461 | 1.04k | .map_err(|err| CorruptedError::Internal(InternalError(err))0 )?0 ; Unexecuted instantiation: _RNCINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyRShEEs5_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetpEs5_0B9_ Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyRShEEs5_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtB1v_5slice4iter4IterINtNtCsdZExvAaxgia_5alloc3vec3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB3m_14SyncBackground14process_blocks0s_00EEs5_0CsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyRShEEs5_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtB1v_5slice4iter4IterINtNtCsdZExvAaxgia_5alloc3vec3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB3m_14SyncBackground14process_blocks0s_00EEs5_0CscDgN54JpMGG_6author Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyRShEEs5_0CsibGXYHQB8Ea_25json_rpc_general_requests Unexecuted instantiation: _RNCINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtB1v_5slice4iter4IterINtNtCsdZExvAaxgia_5alloc3vec3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB3m_14SyncBackground14process_blocks0s_00EEs5_0CsibGXYHQB8Ea_25json_rpc_general_requests |
1462 | | |
1463 | 1.04k | Ok(()) |
1464 | 1.04k | } _RINvMNtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyRShEEB7_ Line | Count | Source | 1392 | 1.02k | pub fn reset<'a>( | 1393 | 1.02k | &self, | 1394 | 1.02k | finalized_block_header: &[u8], | 1395 | 1.02k | finalized_block_body: impl ExactSizeIterator<Item = &'a [u8]>, | 1396 | 1.02k | finalized_block_justification: Option<Vec<u8>>, | 1397 | 1.02k | ) -> Result<(), CorruptedError> { | 1398 | 1.02k | // Start a transaction to insert everything in one go. | 1399 | 1.02k | let mut database = self.database.lock(); | 1400 | 1.02k | let transaction = database | 1401 | 1.02k | .transaction() | 1402 | 1.02k | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 1403 | | | 1404 | | // Temporarily disable foreign key checks in order to make the initial insertion easier, | 1405 | | // as we don't have to make sure that trie nodes are sorted. | 1406 | | // Note that this is immediately disabled again when we `COMMIT`. | 1407 | 1.02k | transaction | 1408 | 1.02k | .execute("PRAGMA defer_foreign_keys = ON", ()) | 1409 | 1.02k | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 1410 | | | 1411 | 1.02k | let finalized_block_hash = header::hash_from_scale_encoded_header(finalized_block_header); | 1412 | 1.02k | // TODO: this module shouldn't decode blocks | 1413 | 1.02k | let decoded = header::decode(finalized_block_header, self.block_number_bytes).unwrap(); | 1414 | 1.02k | | 1415 | 1.02k | transaction | 1416 | 1.02k | .prepare_cached( | 1417 | 1.02k | "INSERT OR REPLACE INTO blocks(hash, parent_hash, state_trie_root_hash, number, header, is_best_chain, justification) VALUES(?, ?, ?, ?, ?, TRUE, ?)", | 1418 | 1.02k | ) | 1419 | 1.02k | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 | 1420 | | .execute(( | 1421 | 1.02k | &finalized_block_hash[..], | 1422 | 1.02k | if decoded.number != 0 { | 1423 | 0 | Some(&decoded.parent_hash[..]) | 1424 | 1.02k | } else { None }, | 1425 | 1.02k | &decoded.state_root[..], | 1426 | 1.02k | i64::try_from(decoded.number).unwrap(), | 1427 | 1.02k | finalized_block_header, | 1428 | 1.02k | finalized_block_justification.as_deref(), | 1429 | 1.02k | )) | 1430 | 1.02k | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 1431 | | | 1432 | 1.02k | transaction | 1433 | 1.02k | .execute( | 1434 | 1.02k | "DELETE FROM blocks_body WHERE hash = ?", | 1435 | 1.02k | (&finalized_block_hash[..],), | 1436 | 1.02k | ) | 1437 | 1.02k | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 1438 | | | 1439 | | { | 1440 | 1.02k | let mut statement = transaction | 1441 | 1.02k | .prepare_cached( | 1442 | 1.02k | "INSERT OR IGNORE INTO blocks_body(hash, idx, extrinsic) VALUES(?, ?, ?)", | 1443 | 1.02k | ) | 1444 | 1.02k | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 1445 | 1.02k | for (index, item0 ) in finalized_block_body.enumerate() { | 1446 | 0 | statement | 1447 | 0 | .execute(( | 1448 | 0 | &finalized_block_hash[..], | 1449 | 0 | i64::try_from(index).unwrap(), | 1450 | 0 | item, | 1451 | 0 | )) | 1452 | 0 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?; | 1453 | | } | 1454 | | } | 1455 | | | 1456 | 1.02k | meta_set_blob(&transaction, "best", &finalized_block_hash[..])?0 ; | 1457 | 1.02k | meta_set_number(&transaction, "finalized", decoded.number)?0 ; | 1458 | | | 1459 | 1.02k | transaction | 1460 | 1.02k | .commit() | 1461 | 1.02k | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 1462 | | | 1463 | 1.02k | Ok(()) | 1464 | 1.02k | } |
Unexecuted instantiation: _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase5resetpEB7_ _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyRShEECsiLzmwikkc22_14json_rpc_basic Line | Count | Source | 1392 | 2 | pub fn reset<'a>( | 1393 | 2 | &self, | 1394 | 2 | finalized_block_header: &[u8], | 1395 | 2 | finalized_block_body: impl ExactSizeIterator<Item = &'a [u8]>, | 1396 | 2 | finalized_block_justification: Option<Vec<u8>>, | 1397 | 2 | ) -> Result<(), CorruptedError> { | 1398 | 2 | // Start a transaction to insert everything in one go. | 1399 | 2 | let mut database = self.database.lock(); | 1400 | 2 | let transaction = database | 1401 | 2 | .transaction() | 1402 | 2 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 1403 | | | 1404 | | // Temporarily disable foreign key checks in order to make the initial insertion easier, | 1405 | | // as we don't have to make sure that trie nodes are sorted. | 1406 | | // Note that this is immediately disabled again when we `COMMIT`. | 1407 | 2 | transaction | 1408 | 2 | .execute("PRAGMA defer_foreign_keys = ON", ()) | 1409 | 2 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 1410 | | | 1411 | 2 | let finalized_block_hash = header::hash_from_scale_encoded_header(finalized_block_header); | 1412 | 2 | // TODO: this module shouldn't decode blocks | 1413 | 2 | let decoded = header::decode(finalized_block_header, self.block_number_bytes).unwrap(); | 1414 | 2 | | 1415 | 2 | transaction | 1416 | 2 | .prepare_cached( | 1417 | 2 | "INSERT OR REPLACE INTO blocks(hash, parent_hash, state_trie_root_hash, number, header, is_best_chain, justification) VALUES(?, ?, ?, ?, ?, TRUE, ?)", | 1418 | 2 | ) | 1419 | 2 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 | 1420 | | .execute(( | 1421 | 2 | &finalized_block_hash[..], | 1422 | 2 | if decoded.number != 0 { | 1423 | 0 | Some(&decoded.parent_hash[..]) | 1424 | 2 | } else { None }, | 1425 | 2 | &decoded.state_root[..], | 1426 | 2 | i64::try_from(decoded.number).unwrap(), | 1427 | 2 | finalized_block_header, | 1428 | 2 | finalized_block_justification.as_deref(), | 1429 | 2 | )) | 1430 | 2 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 1431 | | | 1432 | 2 | transaction | 1433 | 2 | .execute( | 1434 | 2 | "DELETE FROM blocks_body WHERE hash = ?", | 1435 | 2 | (&finalized_block_hash[..],), | 1436 | 2 | ) | 1437 | 2 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 1438 | | | 1439 | | { | 1440 | 2 | let mut statement = transaction | 1441 | 2 | .prepare_cached( | 1442 | 2 | "INSERT OR IGNORE INTO blocks_body(hash, idx, extrinsic) VALUES(?, ?, ?)", | 1443 | 2 | ) | 1444 | 2 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 1445 | 2 | for (index, item0 ) in finalized_block_body.enumerate() { | 1446 | 0 | statement | 1447 | 0 | .execute(( | 1448 | 0 | &finalized_block_hash[..], | 1449 | 0 | i64::try_from(index).unwrap(), | 1450 | 0 | item, | 1451 | 0 | )) | 1452 | 0 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?; | 1453 | | } | 1454 | | } | 1455 | | | 1456 | 2 | meta_set_blob(&transaction, "best", &finalized_block_hash[..])?0 ; | 1457 | 2 | meta_set_number(&transaction, "finalized", decoded.number)?0 ; | 1458 | | | 1459 | 2 | transaction | 1460 | 2 | .commit() | 1461 | 2 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 1462 | | | 1463 | 2 | Ok(()) | 1464 | 2 | } |
Unexecuted instantiation: _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtB1t_5slice4iter4IterINtNtCsdZExvAaxgia_5alloc3vec3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB3k_14SyncBackground14process_blocks0s_00EECsiLzmwikkc22_14json_rpc_basic Unexecuted instantiation: _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyRShEECscDgN54JpMGG_6author Unexecuted instantiation: _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtB1t_5slice4iter4IterINtNtCsdZExvAaxgia_5alloc3vec3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB3k_14SyncBackground14process_blocks0s_00EECscDgN54JpMGG_6author _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyRShEECsibGXYHQB8Ea_25json_rpc_general_requests Line | Count | Source | 1392 | 19 | pub fn reset<'a>( | 1393 | 19 | &self, | 1394 | 19 | finalized_block_header: &[u8], | 1395 | 19 | finalized_block_body: impl ExactSizeIterator<Item = &'a [u8]>, | 1396 | 19 | finalized_block_justification: Option<Vec<u8>>, | 1397 | 19 | ) -> Result<(), CorruptedError> { | 1398 | 19 | // Start a transaction to insert everything in one go. | 1399 | 19 | let mut database = self.database.lock(); | 1400 | 19 | let transaction = database | 1401 | 19 | .transaction() | 1402 | 19 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 1403 | | | 1404 | | // Temporarily disable foreign key checks in order to make the initial insertion easier, | 1405 | | // as we don't have to make sure that trie nodes are sorted. | 1406 | | // Note that this is immediately disabled again when we `COMMIT`. | 1407 | 19 | transaction | 1408 | 19 | .execute("PRAGMA defer_foreign_keys = ON", ()) | 1409 | 19 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 1410 | | | 1411 | 19 | let finalized_block_hash = header::hash_from_scale_encoded_header(finalized_block_header); | 1412 | 19 | // TODO: this module shouldn't decode blocks | 1413 | 19 | let decoded = header::decode(finalized_block_header, self.block_number_bytes).unwrap(); | 1414 | 19 | | 1415 | 19 | transaction | 1416 | 19 | .prepare_cached( | 1417 | 19 | "INSERT OR REPLACE INTO blocks(hash, parent_hash, state_trie_root_hash, number, header, is_best_chain, justification) VALUES(?, ?, ?, ?, ?, TRUE, ?)", | 1418 | 19 | ) | 1419 | 19 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 | 1420 | | .execute(( | 1421 | 19 | &finalized_block_hash[..], | 1422 | 19 | if decoded.number != 0 { | 1423 | 0 | Some(&decoded.parent_hash[..]) | 1424 | 19 | } else { None }, | 1425 | 19 | &decoded.state_root[..], | 1426 | 19 | i64::try_from(decoded.number).unwrap(), | 1427 | 19 | finalized_block_header, | 1428 | 19 | finalized_block_justification.as_deref(), | 1429 | 19 | )) | 1430 | 19 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 1431 | | | 1432 | 19 | transaction | 1433 | 19 | .execute( | 1434 | 19 | "DELETE FROM blocks_body WHERE hash = ?", | 1435 | 19 | (&finalized_block_hash[..],), | 1436 | 19 | ) | 1437 | 19 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 1438 | | | 1439 | | { | 1440 | 19 | let mut statement = transaction | 1441 | 19 | .prepare_cached( | 1442 | 19 | "INSERT OR IGNORE INTO blocks_body(hash, idx, extrinsic) VALUES(?, ?, ?)", | 1443 | 19 | ) | 1444 | 19 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 1445 | 19 | for (index, item0 ) in finalized_block_body.enumerate() { | 1446 | 0 | statement | 1447 | 0 | .execute(( | 1448 | 0 | &finalized_block_hash[..], | 1449 | 0 | i64::try_from(index).unwrap(), | 1450 | 0 | item, | 1451 | 0 | )) | 1452 | 0 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?; | 1453 | | } | 1454 | | } | 1455 | | | 1456 | 19 | meta_set_blob(&transaction, "best", &finalized_block_hash[..])?0 ; | 1457 | 19 | meta_set_number(&transaction, "finalized", decoded.number)?0 ; | 1458 | | | 1459 | 19 | transaction | 1460 | 19 | .commit() | 1461 | 19 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 1462 | | | 1463 | 19 | Ok(()) | 1464 | 19 | } |
Unexecuted instantiation: _RINvMNtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB3_18SqliteFullDatabase5resetINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtNtB1t_5slice4iter4IterINtNtCsdZExvAaxgia_5alloc3vec3VechEENCNCNCNvMs_NtCsiUjFBJteJ7x_17smoldot_full_node17consensus_serviceNtB3k_14SyncBackground14process_blocks0s_00EECsibGXYHQB8Ea_25json_rpc_general_requests |
1465 | | } |
1466 | | |
1467 | | impl fmt::Debug for SqliteFullDatabase { |
1468 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
1469 | 0 | f.debug_tuple("SqliteFullDatabase").finish() |
1470 | 0 | } Unexecuted instantiation: _RNvXs_NtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabaseNtNtCsaYZPK01V26L_4core3fmt5Debug3fmt Unexecuted instantiation: _RNvXs_NtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB4_18SqliteFullDatabaseNtNtCsaYZPK01V26L_4core3fmt5Debug3fmt |
1471 | | } |
1472 | | |
1473 | | impl Drop for SqliteFullDatabase { |
1474 | 1.02k | fn drop(&mut self) { |
1475 | 1.02k | if !std::thread::panicking() { |
1476 | 1.02k | // The SQLite documentation recommends running `PRAGMA optimize` when the database |
1477 | 1.02k | // closes. |
1478 | 1.02k | // TODO: it is also recommended to do this every 2 hours |
1479 | 1.02k | let _ = self.database.get_mut().execute("PRAGMA optimize", ()); |
1480 | 1.02k | }0 |
1481 | 1.02k | } _RNvXs0_NtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabaseNtNtNtCsaYZPK01V26L_4core3ops4drop4Drop4drop Line | Count | Source | 1474 | 1.02k | fn drop(&mut self) { | 1475 | 1.02k | if !std::thread::panicking() { | 1476 | 1.02k | // The SQLite documentation recommends running `PRAGMA optimize` when the database | 1477 | 1.02k | // closes. | 1478 | 1.02k | // TODO: it is also recommended to do this every 2 hours | 1479 | 1.02k | let _ = self.database.get_mut().execute("PRAGMA optimize", ()); | 1480 | 1.02k | }0 | 1481 | 1.02k | } |
Unexecuted instantiation: _RNvXs0_NtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18SqliteFullDatabaseNtNtNtCsaYZPK01V26L_4core3ops4drop4Drop4drop |
1482 | | } |
1483 | | |
1484 | | /// See [`SqliteFullDatabase::finalized_and_above_missing_trie_nodes_unordered`]. |
1485 | | #[derive(Debug)] |
1486 | | pub struct MissingTrieNode { |
1487 | | /// Blocks the trie node is known to belong to. |
1488 | | /// |
1489 | | /// Guaranteed to never be empty. |
1490 | | /// |
1491 | | /// Only contains blocks whose number is superior or equal to the latest finalized block |
1492 | | /// number. |
1493 | | pub blocks: Vec<MissingTrieNodeBlock>, |
1494 | | /// Hash of the missing trie node. |
1495 | | pub trie_node_hash: [u8; 32], |
1496 | | } |
1497 | | |
1498 | | /// See [`MissingTrieNode::blocks`]. |
1499 | | #[derive(Debug)] |
1500 | | pub struct MissingTrieNodeBlock { |
1501 | | /// Hash of the block. |
1502 | | pub hash: [u8; 32], |
1503 | | /// Height of the block. |
1504 | | pub number: u64, |
1505 | | /// Path of the parent tries leading to the trie node. |
1506 | | pub parent_tries_paths_nibbles: Vec<Vec<u8>>, |
1507 | | /// Nibbles that compose the key of the trie node. |
1508 | | pub trie_node_key_nibbles: Vec<u8>, |
1509 | | } |
1510 | | |
1511 | | pub struct InsertTrieNode<'a> { |
1512 | | pub merkle_value: Cow<'a, [u8]>, |
1513 | | pub partial_key_nibbles: Cow<'a, [u8]>, |
1514 | | pub children_merkle_values: [Option<Cow<'a, [u8]>>; 16], |
1515 | | pub storage_value: InsertTrieNodeStorageValue<'a>, |
1516 | | } |
1517 | | |
1518 | | pub enum InsertTrieNodeStorageValue<'a> { |
1519 | | NoValue, |
1520 | | Value { |
1521 | | value: Cow<'a, [u8]>, |
1522 | | /// If `true`, the value is equal to the Merkle value of the root of another trie. |
1523 | | references_merkle_value: bool, |
1524 | | }, |
1525 | | } |
1526 | | |
1527 | | /// Error while calling [`SqliteFullDatabase::insert`]. |
1528 | 0 | #[derive(Debug, derive_more::Display, derive_more::From)] Unexecuted instantiation: _RNvXs4_NtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_11InsertErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt Unexecuted instantiation: _RNvXs4_NtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_11InsertErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt |
1529 | | pub enum InsertError { |
1530 | | /// Error accessing the database. |
1531 | | #[display(fmt = "{_0}")] |
1532 | | Corrupted(CorruptedError), |
1533 | | /// Block was already in the database. |
1534 | | Duplicate, |
1535 | | /// Error when decoding the header to import. |
1536 | | #[display(fmt = "Failed to decode header: {_0}")] |
1537 | | BadHeader(header::Error), |
1538 | | /// Parent of the block to insert isn't in the database. |
1539 | | MissingParent, |
1540 | | /// The new best block would be outside of the finalized chain. |
1541 | | BestNotInFinalizedChain, |
1542 | | } |
1543 | | |
1544 | | /// Error while calling [`SqliteFullDatabase::set_finalized`]. |
1545 | 0 | #[derive(Debug, derive_more::Display, derive_more::From)] Unexecuted instantiation: _RNvXs8_NtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_17SetFinalizedErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt Unexecuted instantiation: _RNvXs8_NtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_17SetFinalizedErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt |
1546 | | pub enum SetFinalizedError { |
1547 | | /// Error accessing the database. |
1548 | | Corrupted(CorruptedError), |
1549 | | /// New finalized block isn't in the database. |
1550 | | UnknownBlock, |
1551 | | /// New finalized block must be a child of the previous finalized block. |
1552 | | RevertForbidden, |
1553 | | } |
1554 | | |
1555 | | /// Error while accessing the storage of the finalized block. |
1556 | 0 | #[derive(Debug, derive_more::Display, derive_more::From)] Unexecuted instantiation: _RNvXsb_NtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_18StorageAccessErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt Unexecuted instantiation: _RNvXsb_NtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_18StorageAccessErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt |
1557 | | pub enum StorageAccessError { |
1558 | | /// Error accessing the database. |
1559 | | Corrupted(CorruptedError), |
1560 | | /// Some trie nodes of the storage of the requested block hash are missing. |
1561 | | IncompleteStorage, |
1562 | | /// Requested block couldn't be found in the database. |
1563 | | UnknownBlock, |
1564 | | } |
1565 | | |
1566 | | /// Error in the content of the database. |
1567 | | // TODO: document and see if any entry is unused |
1568 | 0 | #[derive(Debug, derive_more::Display)] Unexecuted instantiation: _RNvXse_NtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_14CorruptedErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt Unexecuted instantiation: _RNvXse_NtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_14CorruptedErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt |
1569 | | pub enum CorruptedError { |
1570 | | /// Block numbers are expected to be 64 bits. |
1571 | | // TODO: remove this and use stronger schema |
1572 | | InvalidNumber, |
1573 | | /// Finalized block number stored in the database doesn't match any block. |
1574 | | InvalidFinalizedNum, |
1575 | | /// A block hash is expected to be 32 bytes. This isn't the case. |
1576 | | InvalidBlockHashLen, |
1577 | | /// A trie hash is expected to be 32 bytes. This isn't the case. |
1578 | | InvalidTrieHashLen, |
1579 | | /// The parent of a block in the database couldn't be found in that same database. |
1580 | | BrokenChain, |
1581 | | /// Missing a key in the `meta` table. |
1582 | | MissingMetaKey, |
1583 | | /// Some parts of the database refer to a block by its hash, but the block's constituents |
1584 | | /// couldn't be found. |
1585 | | MissingBlockHeader, |
1586 | | /// The header of a block in the database has failed to decode. |
1587 | | #[display(fmt = "Corrupted block header: {_0}")] |
1588 | | BlockHeaderCorrupted(header::Error), |
1589 | | /// The version information about a storage entry has failed to decode. |
1590 | | InvalidTrieEntryVersion, |
1591 | | #[display(fmt = "Internal error: {_0}")] |
1592 | | Internal(InternalError), |
1593 | | } |
1594 | | |
1595 | | /// Low-level database error, such as an error while accessing the file system. |
1596 | 0 | #[derive(Debug, derive_more::Display)] Unexecuted instantiation: _RNvXsg_NtNtCsN16ciHI6Qf_7smoldot8database11full_sqliteNtB5_13InternalErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt Unexecuted instantiation: _RNvXsg_NtNtCseuYC0Zibziv_7smoldot8database11full_sqliteNtB5_13InternalErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt |
1597 | | pub struct InternalError(rusqlite::Error); |
1598 | | |
1599 | 26 | fn meta_get_blob( |
1600 | 26 | database: &rusqlite::Connection, |
1601 | 26 | key: &str, |
1602 | 26 | ) -> Result<Option<Vec<u8>>, CorruptedError> { |
1603 | 26 | let value = database |
1604 | 26 | .prepare_cached(r#"SELECT value_blob FROM meta WHERE key = ?"#) |
1605 | 26 | .map_err(|err| CorruptedError::Internal(InternalError(err))0 )?0 Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite13meta_get_blob0B7_ Unexecuted instantiation: _RNCNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite13meta_get_blob0B7_ |
1606 | 26 | .query_row((key,), |row| row.get::<_, Vec<u8>>(0)) Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite13meta_get_blobs_0B7_ _RNCNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite13meta_get_blobs_0B7_ Line | Count | Source | 1606 | 26 | .query_row((key,), |row| row.get::<_, Vec<u8>>(0)) |
|
1607 | 26 | .optional() |
1608 | 26 | .map_err(|err| CorruptedError::Internal(InternalError(err))0 )?0 ; Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite13meta_get_blobs0_0B7_ Unexecuted instantiation: _RNCNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite13meta_get_blobs0_0B7_ |
1609 | 26 | Ok(value) |
1610 | 26 | } Unexecuted instantiation: _RNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite13meta_get_blob _RNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite13meta_get_blob Line | Count | Source | 1599 | 26 | fn meta_get_blob( | 1600 | 26 | database: &rusqlite::Connection, | 1601 | 26 | key: &str, | 1602 | 26 | ) -> Result<Option<Vec<u8>>, CorruptedError> { | 1603 | 26 | let value = database | 1604 | 26 | .prepare_cached(r#"SELECT value_blob FROM meta WHERE key = ?"#) | 1605 | 26 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 | 1606 | 26 | .query_row((key,), |row| row.get::<_, Vec<u8>>(0)) | 1607 | 26 | .optional() | 1608 | 26 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 1609 | 26 | Ok(value) | 1610 | 26 | } |
|
1611 | | |
1612 | 21 | fn meta_get_number( |
1613 | 21 | database: &rusqlite::Connection, |
1614 | 21 | key: &str, |
1615 | 21 | ) -> Result<Option<u64>, CorruptedError> { |
1616 | 21 | let value = database |
1617 | 21 | .prepare_cached(r#"SELECT value_number FROM meta WHERE key = ?"#) |
1618 | 21 | .map_err(|err| CorruptedError::Internal(InternalError(err))0 )?0 Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite15meta_get_number0B7_ Unexecuted instantiation: _RNCNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite15meta_get_number0B7_ |
1619 | 21 | .query_row((key,), |row| row.get::<_, i64>(0)) Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite15meta_get_numbers_0B7_ _RNCNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite15meta_get_numbers_0B7_ Line | Count | Source | 1619 | 21 | .query_row((key,), |row| row.get::<_, i64>(0)) |
|
1620 | 21 | .optional() |
1621 | 21 | .map_err(|err| CorruptedError::Internal(InternalError(err))0 )?0 ; Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite15meta_get_numbers0_0B7_ Unexecuted instantiation: _RNCNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite15meta_get_numbers0_0B7_ |
1622 | 21 | Ok(value.map(|value| u64::from_ne_bytes(value.to_ne_bytes()))) Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite15meta_get_numbers1_0B7_ _RNCNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite15meta_get_numbers1_0B7_ Line | Count | Source | 1622 | 21 | Ok(value.map(|value| u64::from_ne_bytes(value.to_ne_bytes()))) |
|
1623 | 21 | } Unexecuted instantiation: _RNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite15meta_get_number _RNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite15meta_get_number Line | Count | Source | 1612 | 21 | fn meta_get_number( | 1613 | 21 | database: &rusqlite::Connection, | 1614 | 21 | key: &str, | 1615 | 21 | ) -> Result<Option<u64>, CorruptedError> { | 1616 | 21 | let value = database | 1617 | 21 | .prepare_cached(r#"SELECT value_number FROM meta WHERE key = ?"#) | 1618 | 21 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 | 1619 | 21 | .query_row((key,), |row| row.get::<_, i64>(0)) | 1620 | 21 | .optional() | 1621 | 21 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 1622 | 21 | Ok(value.map(|value| u64::from_ne_bytes(value.to_ne_bytes()))) | 1623 | 21 | } |
|
1624 | | |
1625 | 1.04k | fn meta_set_blob( |
1626 | 1.04k | database: &rusqlite::Connection, |
1627 | 1.04k | key: &str, |
1628 | 1.04k | value: &[u8], |
1629 | 1.04k | ) -> Result<(), CorruptedError> { |
1630 | 1.04k | database |
1631 | 1.04k | .prepare_cached(r#"INSERT OR REPLACE INTO meta(key, value_blob) VALUES (?, ?)"#) |
1632 | 1.04k | .map_err(|err| CorruptedError::Internal(InternalError(err))0 )?0 Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite13meta_set_blob0B7_ Unexecuted instantiation: _RNCNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite13meta_set_blob0B7_ |
1633 | 1.04k | .execute((key, value)) |
1634 | 1.04k | .map_err(|err| CorruptedError::Internal(InternalError(err))0 )?0 ; Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite13meta_set_blobs_0B7_ Unexecuted instantiation: _RNCNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite13meta_set_blobs_0B7_ |
1635 | 1.04k | Ok(()) |
1636 | 1.04k | } _RNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite13meta_set_blob Line | Count | Source | 1625 | 1.02k | fn meta_set_blob( | 1626 | 1.02k | database: &rusqlite::Connection, | 1627 | 1.02k | key: &str, | 1628 | 1.02k | value: &[u8], | 1629 | 1.02k | ) -> Result<(), CorruptedError> { | 1630 | 1.02k | database | 1631 | 1.02k | .prepare_cached(r#"INSERT OR REPLACE INTO meta(key, value_blob) VALUES (?, ?)"#) | 1632 | 1.02k | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 | 1633 | 1.02k | .execute((key, value)) | 1634 | 1.02k | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 1635 | 1.02k | Ok(()) | 1636 | 1.02k | } |
_RNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite13meta_set_blob Line | Count | Source | 1625 | 21 | fn meta_set_blob( | 1626 | 21 | database: &rusqlite::Connection, | 1627 | 21 | key: &str, | 1628 | 21 | value: &[u8], | 1629 | 21 | ) -> Result<(), CorruptedError> { | 1630 | 21 | database | 1631 | 21 | .prepare_cached(r#"INSERT OR REPLACE INTO meta(key, value_blob) VALUES (?, ?)"#) | 1632 | 21 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 | 1633 | 21 | .execute((key, value)) | 1634 | 21 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 1635 | 21 | Ok(()) | 1636 | 21 | } |
|
1637 | | |
1638 | 1.04k | fn meta_set_number( |
1639 | 1.04k | database: &rusqlite::Connection, |
1640 | 1.04k | key: &str, |
1641 | 1.04k | value: u64, |
1642 | 1.04k | ) -> Result<(), CorruptedError> { |
1643 | 1.04k | database |
1644 | 1.04k | .prepare_cached(r#"INSERT OR REPLACE INTO meta(key, value_number) VALUES (?, ?)"#) |
1645 | 1.04k | .map_err(|err| CorruptedError::Internal(InternalError(err))0 )?0 Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite15meta_set_number0B7_ Unexecuted instantiation: _RNCNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite15meta_set_number0B7_ |
1646 | 1.04k | .execute((key, i64::from_ne_bytes(value.to_ne_bytes()))) |
1647 | 1.04k | .map_err(|err| CorruptedError::Internal(InternalError(err))0 )?0 ; Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite15meta_set_numbers_0B7_ Unexecuted instantiation: _RNCNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite15meta_set_numbers_0B7_ |
1648 | 1.04k | Ok(()) |
1649 | 1.04k | } _RNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite15meta_set_number Line | Count | Source | 1638 | 1.02k | fn meta_set_number( | 1639 | 1.02k | database: &rusqlite::Connection, | 1640 | 1.02k | key: &str, | 1641 | 1.02k | value: u64, | 1642 | 1.02k | ) -> Result<(), CorruptedError> { | 1643 | 1.02k | database | 1644 | 1.02k | .prepare_cached(r#"INSERT OR REPLACE INTO meta(key, value_number) VALUES (?, ?)"#) | 1645 | 1.02k | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 | 1646 | 1.02k | .execute((key, i64::from_ne_bytes(value.to_ne_bytes()))) | 1647 | 1.02k | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 1648 | 1.02k | Ok(()) | 1649 | 1.02k | } |
_RNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite15meta_set_number Line | Count | Source | 1638 | 21 | fn meta_set_number( | 1639 | 21 | database: &rusqlite::Connection, | 1640 | 21 | key: &str, | 1641 | 21 | value: u64, | 1642 | 21 | ) -> Result<(), CorruptedError> { | 1643 | 21 | database | 1644 | 21 | .prepare_cached(r#"INSERT OR REPLACE INTO meta(key, value_number) VALUES (?, ?)"#) | 1645 | 21 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 | 1646 | 21 | .execute((key, i64::from_ne_bytes(value.to_ne_bytes()))) | 1647 | 21 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 1648 | 21 | Ok(()) | 1649 | 21 | } |
|
1650 | | |
1651 | 0 | fn has_block(database: &rusqlite::Connection, hash: &[u8]) -> Result<bool, CorruptedError> { |
1652 | 0 | database |
1653 | 0 | .prepare_cached(r#"SELECT COUNT(*) FROM blocks WHERE hash = ?"#) |
1654 | 0 | .map_err(|err| CorruptedError::Internal(InternalError(err)))? Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite9has_block0B7_ Unexecuted instantiation: _RNCNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite9has_block0B7_ |
1655 | 0 | .query_row((hash,), |row| Ok(row.get_unwrap::<_, i64>(0) != 0)) Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite9has_blocks_0B7_ Unexecuted instantiation: _RNCNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite9has_blocks_0B7_ |
1656 | 0 | .map_err(|err| CorruptedError::Internal(InternalError(err))) Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite9has_blocks0_0B7_ Unexecuted instantiation: _RNCNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite9has_blocks0_0B7_ |
1657 | 0 | } Unexecuted instantiation: _RNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite9has_block Unexecuted instantiation: _RNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite9has_block |
1658 | | |
1659 | | // TODO: the fact that the meta table stores blobs makes it impossible to use joins ; fix that |
1660 | 21 | fn finalized_num(database: &rusqlite::Connection) -> Result<u64, CorruptedError> { |
1661 | 21 | meta_get_number(database, "finalized")?0 .ok_or(CorruptedError::MissingMetaKey) |
1662 | 21 | } Unexecuted instantiation: _RNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite13finalized_num _RNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite13finalized_num Line | Count | Source | 1660 | 21 | fn finalized_num(database: &rusqlite::Connection) -> Result<u64, CorruptedError> { | 1661 | 21 | meta_get_number(database, "finalized")?0 .ok_or(CorruptedError::MissingMetaKey) | 1662 | 21 | } |
|
1663 | | |
1664 | 1.12k | fn finalized_hash(database: &rusqlite::Connection) -> Result<[u8; 32], CorruptedError> { |
1665 | 1.12k | let value = database |
1666 | 1.12k | .prepare_cached(r#"SELECT hash FROM blocks WHERE number = (SELECT value_number FROM meta WHERE key = "finalized")"#) |
1667 | 1.12k | .map_err(|err| CorruptedError::Internal(InternalError(err))0 )?0 Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite14finalized_hash0B7_ Unexecuted instantiation: _RNCNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite14finalized_hash0B7_ |
1668 | 1.12k | .query_row((), |row| row.get::<_, Vec<u8>>(0)) _RNCNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite14finalized_hashs_0B7_ Line | Count | Source | 1668 | 1.02k | .query_row((), |row| row.get::<_, Vec<u8>>(0)) |
_RNCNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite14finalized_hashs_0B7_ Line | Count | Source | 1668 | 105 | .query_row((), |row| row.get::<_, Vec<u8>>(0)) |
|
1669 | 1.12k | .optional() |
1670 | 1.12k | .map_err(|err| CorruptedError::Internal(InternalError(err))0 )?0 Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite14finalized_hashs0_0B7_ Unexecuted instantiation: _RNCNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite14finalized_hashs0_0B7_ |
1671 | 1.12k | .ok_or(CorruptedError::InvalidFinalizedNum)?0 ; |
1672 | | |
1673 | 1.12k | if value.len() == 32 { |
1674 | 1.12k | let mut out = [0; 32]; |
1675 | 1.12k | out.copy_from_slice(&value); |
1676 | 1.12k | Ok(out) |
1677 | | } else { |
1678 | 0 | Err(CorruptedError::InvalidBlockHashLen) |
1679 | | } |
1680 | 1.12k | } _RNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite14finalized_hash Line | Count | Source | 1664 | 1.02k | fn finalized_hash(database: &rusqlite::Connection) -> Result<[u8; 32], CorruptedError> { | 1665 | 1.02k | let value = database | 1666 | 1.02k | .prepare_cached(r#"SELECT hash FROM blocks WHERE number = (SELECT value_number FROM meta WHERE key = "finalized")"#) | 1667 | 1.02k | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 | 1668 | 1.02k | .query_row((), |row| row.get::<_, Vec<u8>>(0)) | 1669 | 1.02k | .optional() | 1670 | 1.02k | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 | 1671 | 1.02k | .ok_or(CorruptedError::InvalidFinalizedNum)?0 ; | 1672 | | | 1673 | 1.02k | if value.len() == 32 { | 1674 | 1.02k | let mut out = [0; 32]; | 1675 | 1.02k | out.copy_from_slice(&value); | 1676 | 1.02k | Ok(out) | 1677 | | } else { | 1678 | 0 | Err(CorruptedError::InvalidBlockHashLen) | 1679 | | } | 1680 | 1.02k | } |
_RNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite14finalized_hash Line | Count | Source | 1664 | 105 | fn finalized_hash(database: &rusqlite::Connection) -> Result<[u8; 32], CorruptedError> { | 1665 | 105 | let value = database | 1666 | 105 | .prepare_cached(r#"SELECT hash FROM blocks WHERE number = (SELECT value_number FROM meta WHERE key = "finalized")"#) | 1667 | 105 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 | 1668 | 105 | .query_row((), |row| row.get::<_, Vec<u8>>(0)) | 1669 | 105 | .optional() | 1670 | 105 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 | 1671 | 105 | .ok_or(CorruptedError::InvalidFinalizedNum)?0 ; | 1672 | | | 1673 | 105 | if value.len() == 32 { | 1674 | 105 | let mut out = [0; 32]; | 1675 | 105 | out.copy_from_slice(&value); | 1676 | 105 | Ok(out) | 1677 | | } else { | 1678 | 0 | Err(CorruptedError::InvalidBlockHashLen) | 1679 | | } | 1680 | 105 | } |
|
1681 | | |
1682 | 45 | fn block_hashes_by_number( |
1683 | 45 | database: &rusqlite::Connection, |
1684 | 45 | number: u64, |
1685 | 45 | ) -> Result<Vec<[u8; 32]>, CorruptedError> { |
1686 | 45 | let number = match i64::try_from(number) { |
1687 | 45 | Ok(n) => n, |
1688 | 0 | Err(_) => return Ok(Vec::new()), |
1689 | | }; |
1690 | | |
1691 | 45 | database |
1692 | 45 | .prepare_cached(r#"SELECT hash FROM blocks WHERE number = ?"#) |
1693 | 45 | .map_err(|err| CorruptedError::Internal(InternalError(err))0 )?0 Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite22block_hashes_by_number0B7_ Unexecuted instantiation: _RNCNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite22block_hashes_by_number0B7_ |
1694 | 45 | .query_map((number,), |row| row.get::<_, Vec<u8>>(0)) _RNCNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite22block_hashes_by_numbers_0B7_ Line | Count | Source | 1694 | 45 | .query_map((number,), |row| row.get::<_, Vec<u8>>(0)) |
Unexecuted instantiation: _RNCNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite22block_hashes_by_numbers_0B7_ |
1695 | 45 | .map_err(|err| CorruptedError::Internal(InternalError(err))0 )?0 Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite22block_hashes_by_numbers0_0B7_ Unexecuted instantiation: _RNCNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite22block_hashes_by_numbers0_0B7_ |
1696 | 45 | .map(|value| { |
1697 | 45 | let value = value.map_err(|err| CorruptedError::Internal(InternalError(err))0 )?0 ; Unexecuted instantiation: _RNCNCNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite22block_hashes_by_numbers1_00B9_ Unexecuted instantiation: _RNCNCNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite22block_hashes_by_numbers1_00B9_ |
1698 | 45 | <[u8; 32]>::try_from(&value[..]).map_err(|_| CorruptedError::InvalidBlockHashLen0 ) Unexecuted instantiation: _RNCNCNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite22block_hashes_by_numbers1_0s_0B9_ Unexecuted instantiation: _RNCNCNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite22block_hashes_by_numbers1_0s_0B9_ |
1699 | 45 | }) _RNCNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite22block_hashes_by_numbers1_0B7_ Line | Count | Source | 1696 | 45 | .map(|value| { | 1697 | 45 | let value = value.map_err(|err| CorruptedError::Internal(InternalError(err)))?0 ; | 1698 | 45 | <[u8; 32]>::try_from(&value[..]).map_err(|_| CorruptedError::InvalidBlockHashLen) | 1699 | 45 | }) |
Unexecuted instantiation: _RNCNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite22block_hashes_by_numbers1_0B7_ |
1700 | 45 | .collect::<Result<Vec<_>, _>>() |
1701 | 45 | } _RNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite22block_hashes_by_number Line | Count | Source | 1682 | 45 | fn block_hashes_by_number( | 1683 | 45 | database: &rusqlite::Connection, | 1684 | 45 | number: u64, | 1685 | 45 | ) -> Result<Vec<[u8; 32]>, CorruptedError> { | 1686 | 45 | let number = match i64::try_from(number) { | 1687 | 45 | Ok(n) => n, | 1688 | 0 | Err(_) => return Ok(Vec::new()), | 1689 | | }; | 1690 | | | 1691 | 45 | database | 1692 | 45 | .prepare_cached(r#"SELECT hash FROM blocks WHERE number = ?"#) | 1693 | 45 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 | 1694 | 45 | .query_map((number,), |row| row.get::<_, Vec<u8>>(0)) | 1695 | 45 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?0 | 1696 | 45 | .map(|value| { | 1697 | | let value = value.map_err(|err| CorruptedError::Internal(InternalError(err)))?; | 1698 | | <[u8; 32]>::try_from(&value[..]).map_err(|_| CorruptedError::InvalidBlockHashLen) | 1699 | 45 | }) | 1700 | 45 | .collect::<Result<Vec<_>, _>>() | 1701 | 45 | } |
Unexecuted instantiation: _RNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite22block_hashes_by_number |
1702 | | |
1703 | 0 | fn block_header( |
1704 | 0 | database: &rusqlite::Connection, |
1705 | 0 | hash: &[u8; 32], |
1706 | 0 | ) -> Result<Option<Vec<u8>>, CorruptedError> { |
1707 | 0 | database |
1708 | 0 | .prepare_cached(r#"SELECT header FROM blocks WHERE hash = ?"#) |
1709 | 0 | .map_err(|err| CorruptedError::Internal(InternalError(err)))? Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite12block_header0B7_ Unexecuted instantiation: _RNCNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite12block_header0B7_ |
1710 | 0 | .query_row((&hash[..],), |row| row.get::<_, Vec<u8>>(0)) Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite12block_headers_0B7_ Unexecuted instantiation: _RNCNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite12block_headers_0B7_ |
1711 | 0 | .optional() |
1712 | 0 | .map_err(|err| CorruptedError::Internal(InternalError(err))) Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite12block_headers0_0B7_ Unexecuted instantiation: _RNCNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite12block_headers0_0B7_ |
1713 | 0 | } Unexecuted instantiation: _RNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite12block_header Unexecuted instantiation: _RNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite12block_header |
1714 | | |
1715 | 0 | fn set_best_chain( |
1716 | 0 | database: &rusqlite::Connection, |
1717 | 0 | new_best_block_hash: &[u8], |
1718 | 0 | ) -> Result<(), CorruptedError> { |
1719 | | // TODO: can this not be embedded in the SQL statement below? |
1720 | 0 | let current_best = meta_get_blob(database, "best")?.ok_or(CorruptedError::MissingMetaKey)?; |
1721 | | |
1722 | | // TODO: untested except in the most basic situation |
1723 | | // In the SQL below, the temporary table `changes` is built by walking down (highest to lowest |
1724 | | // block number) the new best chain and old best chain. While walking down, the iteration |
1725 | | // keeps track of the block hashes and their number. If the new best chain has a higher number |
1726 | | // than the old best chain, then only the new best chain is iterated, and vice versa. If the |
1727 | | // new and old best chain have the same number, they are both iterated, and it is possible to |
1728 | | // compare the block hashes in order to know when to stop iterating. In the context of this |
1729 | | // algorithm, a `NULL` block hash represents "one past the new/old best block", which allows |
1730 | | // to not include the new/old best block in the temporary table until it needs to be included. |
1731 | 0 | database |
1732 | 0 | .prepare_cached( |
1733 | 0 | r#" |
1734 | 0 | WITH RECURSIVE |
1735 | 0 | changes(block_to_include, block_to_retract, block_to_include_number, block_to_retract_number) AS ( |
1736 | 0 | SELECT NULL, NULL, blocks_inc.number + 1, blocks_ret.number + 1 |
1737 | 0 | FROM blocks AS blocks_inc, blocks as blocks_ret |
1738 | 0 | WHERE blocks_inc.hash = :new_best AND blocks_ret.hash = :current_best |
1739 | 0 | UNION ALL |
1740 | 0 | SELECT |
1741 | 0 | CASE WHEN changes.block_to_include_number >= changes.block_to_retract_number THEN |
1742 | 0 | COALESCE(blocks_inc.parent_hash, :new_best) |
1743 | 0 | ELSE |
1744 | 0 | changes.block_to_include |
1745 | 0 | END, |
1746 | 0 | CASE WHEN changes.block_to_retract_number >= changes.block_to_include_number THEN |
1747 | 0 | COALESCE(blocks_ret.parent_hash, :current_best) |
1748 | 0 | ELSE |
1749 | 0 | changes.block_to_retract |
1750 | 0 | END, |
1751 | 0 | CASE WHEN changes.block_to_include_number >= block_to_retract_number THEN changes.block_to_include_number - 1 |
1752 | 0 | ELSE changes.block_to_include_number END, |
1753 | 0 | CASE WHEN changes.block_to_retract_number >= changes.block_to_include_number THEN changes.block_to_retract_number - 1 |
1754 | 0 | ELSE changes.block_to_retract_number END |
1755 | 0 | FROM changes |
1756 | 0 | LEFT JOIN blocks AS blocks_inc ON blocks_inc.hash = changes.block_to_include |
1757 | 0 | LEFT JOIN blocks AS blocks_ret ON blocks_ret.hash = changes.block_to_retract |
1758 | 0 | WHERE changes.block_to_include_number != changes.block_to_retract_number |
1759 | 0 | OR COALESCE(blocks_inc.parent_hash, :new_best) != COALESCE(blocks_ret.parent_hash, :current_best) |
1760 | 0 | ) |
1761 | 0 | UPDATE blocks SET is_best_chain = (blocks.hash = changes.block_to_include) |
1762 | 0 | FROM changes |
1763 | 0 | WHERE blocks.hash = changes.block_to_include OR blocks.hash = changes.block_to_retract; |
1764 | 0 | "#, |
1765 | 0 | ) |
1766 | 0 | .map_err(|err| CorruptedError::Internal(InternalError(err)))? Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite14set_best_chain0B7_ Unexecuted instantiation: _RNCNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite14set_best_chain0B7_ |
1767 | 0 | .execute(rusqlite::named_params! { |
1768 | 0 | ":current_best": current_best, |
1769 | 0 | ":new_best": new_best_block_hash |
1770 | 0 | }) |
1771 | 0 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?; Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite14set_best_chains_0B7_ Unexecuted instantiation: _RNCNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite14set_best_chains_0B7_ |
1772 | | |
1773 | 0 | meta_set_blob(database, "best", new_best_block_hash)?; |
1774 | 0 | Ok(()) |
1775 | 0 | } Unexecuted instantiation: _RNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite14set_best_chain Unexecuted instantiation: _RNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite14set_best_chain |
1776 | | |
1777 | 0 | fn purge_block(database: &rusqlite::Connection, hash: &[u8]) -> Result<(), CorruptedError> { |
1778 | 0 | purge_block_storage(database, hash)?; |
1779 | 0 | database |
1780 | 0 | .prepare_cached("DELETE FROM blocks_body WHERE hash = ?") |
1781 | 0 | .map_err(|err| CorruptedError::Internal(InternalError(err)))? Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite11purge_block0B7_ Unexecuted instantiation: _RNCNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite11purge_block0B7_ |
1782 | 0 | .execute((hash,)) |
1783 | 0 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?; Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite11purge_blocks_0B7_ Unexecuted instantiation: _RNCNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite11purge_blocks_0B7_ |
1784 | 0 | database |
1785 | 0 | .prepare_cached("DELETE FROM blocks WHERE hash = ?") |
1786 | 0 | .map_err(|err| CorruptedError::Internal(InternalError(err)))? Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite11purge_blocks0_0B7_ Unexecuted instantiation: _RNCNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite11purge_blocks0_0B7_ |
1787 | 0 | .execute((hash,)) |
1788 | 0 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?; Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite11purge_blocks1_0B7_ Unexecuted instantiation: _RNCNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite11purge_blocks1_0B7_ |
1789 | 0 | Ok(()) |
1790 | 0 | } Unexecuted instantiation: _RNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite11purge_block Unexecuted instantiation: _RNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite11purge_block |
1791 | | |
1792 | 0 | fn purge_block_storage(database: &rusqlite::Connection, hash: &[u8]) -> Result<(), CorruptedError> { |
1793 | | // TODO: untested |
1794 | | |
1795 | 0 | let state_trie_root_hash = database |
1796 | 0 | .prepare_cached(r#"SELECT state_trie_root_hash FROM blocks WHERE hash = ?"#) |
1797 | 0 | .map_err(|err| CorruptedError::Internal(InternalError(err)))? Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite19purge_block_storage0B7_ Unexecuted instantiation: _RNCNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite19purge_block_storage0B7_ |
1798 | 0 | .query_row((hash,), |row| row.get::<_, Vec<u8>>(0)) Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite19purge_block_storages_0B7_ Unexecuted instantiation: _RNCNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite19purge_block_storages_0B7_ |
1799 | 0 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?; Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite19purge_block_storages0_0B7_ Unexecuted instantiation: _RNCNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite19purge_block_storages0_0B7_ |
1800 | | |
1801 | 0 | database |
1802 | 0 | .prepare_cached( |
1803 | 0 | r#" |
1804 | 0 | UPDATE blocks SET state_trie_root_hash = NULL |
1805 | 0 | WHERE hash = :block_hash |
1806 | 0 | "#, |
1807 | 0 | ) |
1808 | 0 | .map_err(|err| CorruptedError::Internal(InternalError(err)))? Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite19purge_block_storages1_0B7_ Unexecuted instantiation: _RNCNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite19purge_block_storages1_0B7_ |
1809 | 0 | .execute(rusqlite::named_params! { |
1810 | 0 | ":block_hash": hash, |
1811 | 0 | }) |
1812 | 0 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?; Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite19purge_block_storages2_0B7_ Unexecuted instantiation: _RNCNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite19purge_block_storages2_0B7_ |
1813 | | |
1814 | | // TODO: doesn't delete everything in the situation where a single node with a merkle value is referenced multiple times from the same trie |
1815 | | // TODO: currently doesn't follow `trie_root_ref` |
1816 | 0 | database |
1817 | 0 | .prepare_cached(r#" |
1818 | 0 | WITH RECURSIVE |
1819 | 0 | to_delete(node_hash) AS ( |
1820 | 0 | SELECT trie_node.hash |
1821 | 0 | FROM trie_node |
1822 | 0 | LEFT JOIN blocks ON blocks.hash != :block_hash AND blocks.state_trie_root_hash = trie_node.hash |
1823 | 0 | LEFT JOIN trie_node_storage ON trie_node_storage.trie_root_ref = trie_node.hash |
1824 | 0 | WHERE trie_node.hash = :state_trie_root_hash AND blocks.hash IS NULL AND trie_node_storage.node_hash IS NULL |
1825 | 0 | UNION ALL |
1826 | 0 | SELECT trie_node_child.child_hash |
1827 | 0 | FROM to_delete |
1828 | 0 | JOIN trie_node_child ON trie_node_child.hash = to_delete.node_hash |
1829 | 0 | LEFT JOIN blocks ON blocks.state_trie_root_hash = trie_node_child.child_hash |
1830 | 0 | LEFT JOIN trie_node_storage ON trie_node_storage.trie_root_ref = to_delete.node_hash |
1831 | 0 | WHERE blocks.hash IS NULL AND trie_node_storage.node_hash IS NULL |
1832 | 0 | ) |
1833 | 0 | DELETE FROM trie_node |
1834 | 0 | WHERE hash IN (SELECT node_hash FROM to_delete) |
1835 | 0 | "#) |
1836 | 0 | .map_err(|err| CorruptedError::Internal(InternalError(err)))? Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite19purge_block_storages3_0B7_ Unexecuted instantiation: _RNCNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite19purge_block_storages3_0B7_ |
1837 | 0 | .execute(rusqlite::named_params! { |
1838 | 0 | ":state_trie_root_hash": &state_trie_root_hash, |
1839 | 0 | ":block_hash": hash, |
1840 | 0 | }) |
1841 | 0 | .map_err(|err| CorruptedError::Internal(InternalError(err)))?; Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite19purge_block_storages4_0B7_ Unexecuted instantiation: _RNCNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite19purge_block_storages4_0B7_ |
1842 | 0 | Ok(()) |
1843 | 0 | } Unexecuted instantiation: _RNvNtNtCsN16ciHI6Qf_7smoldot8database11full_sqlite19purge_block_storage Unexecuted instantiation: _RNvNtNtCseuYC0Zibziv_7smoldot8database11full_sqlite19purge_block_storage |